惯性聚合 高效追踪和阅读你感兴趣的博客、新闻、科技资讯
阅读原文 在惯性聚合中打开

推荐订阅源

Cloudbric
Cloudbric
WordPress大学
WordPress大学
博客园 - 叶小钗
B
Blog RSS Feed
T
Tailwind CSS Blog
奇客Solidot–传递最新科技情报
奇客Solidot–传递最新科技情报
美团技术团队
Scott Helme
Scott Helme
D
Darknet – Hacking Tools, Hacker News & Cyber Security
Hugging Face - Blog
Hugging Face - Blog
T
Threat Research - Cisco Blogs
B
Blog
V
V2EX
Simon Willison's Weblog
Simon Willison's Weblog
I
Intezer
Threat Intelligence Blog | Flashpoint
Threat Intelligence Blog | Flashpoint
T
Threatpost
Cisco Talos Blog
Cisco Talos Blog
阮一峰的网络日志
阮一峰的网络日志
C
Cybersecurity and Infrastructure Security Agency CISA
PCI Perspectives
PCI Perspectives
雷峰网
雷峰网
The Register - Security
The Register - Security
博客园 - 【当耐特】
Google DeepMind News
Google DeepMind News
N
News and Events Feed by Topic
H
Hackread – Cybersecurity News, Data Breaches, AI and More
U
Unit 42
Security Latest
Security Latest
NISL@THU
NISL@THU
腾讯CDC
S
SegmentFault 最新的问题
小众软件
小众软件
The GitHub Blog
The GitHub Blog
月光博客
月光博客
A
Arctic Wolf
让小产品的独立变现更简单 - ezindie.com
让小产品的独立变现更简单 - ezindie.com
OSCHINA 社区最新新闻
OSCHINA 社区最新新闻
N
Netflix TechBlog - Medium
IT之家
IT之家
D
DataBreaches.Net
C
CXSECURITY Database RSS Feed - CXSecurity.com
N
News | PayPal Newsroom
L
LINUX DO - 最新话题
博客园 - 司徒正美
大猫的无限游戏
大猫的无限游戏
Cyber Security Advisories - MS-ISAC
Cyber Security Advisories - MS-ISAC
J
Java Code Geeks
TaoSecurity Blog
TaoSecurity Blog
P
Privacy International News Feed

記緒漂流

Cloudflare One 集成 OAuth 访问控制 | 記緒漂流 Cloudflare One 集成 OAuth 访问控制 打造专属记账助手 Numb | 記緒漂流 Numb 三月自白 | 記緒漂流 三月自白 相反的你和我・同频的心与心 | 記緒漂流 相反的你和我・同频的心与心 使用 Tailscale 内网穿透 | 記緒漂流 使用 Tailscale 内网穿透 在《HackHub》中成为世界首富 | 記緒漂流 在《HackHub》中成为世界首富 社交距离 | 記緒漂流 社交距离 UNIQLO × Akamai 联名 T 恤 | 記緒漂流 UNIQLO × Akamai 联名 T 恤 Windows 按键重映射 | 記緒漂流 Windows 按键重映射 少女・魔法・救赎 | 記緒漂流 少女・魔法・救赎 Live 初体验 | 記緒漂流 Live 初体验 《剧场版总集篇 GIRLS BAND CRY【后篇】嘿,未来。》观后有感 | 記緒漂流 《剧场版总集篇 GIRLS BAND CRY【后篇】嘿,未来。》观后有感 从 UnoCSS 迁至 Tailwind CSS | 記緒漂流 从 UnoCSS 迁至 Tailwind CSS AI² 与站点 LOGO | 記緒漂流 AI² 与站点 LOGO YAML 多行语法 | 記緒漂流 YAML 多行语法 所以我放弃了 npm | 記緒漂流 所以我放弃了 npm 上下求索,以飨四方 | 記緒漂流 上下求索,以飨四方 理想、偏见与「博友圈」 | 記緒漂流 理想、偏见与「博友圈」 甲流就医纪实 | 記緒漂流 甲流就医纪实
打造专属记账助手 | 記緒漂流
TuyuriTio · 2026-04-27 · via 記緒漂流

有些诧异,自从我开始记账,已过将近半年,除了吃饭和撸管,我似乎从未在一件事上坚持以至于养成习惯。曾经尝试过练字,好歹持续的一个月,可一旦停下,便再不拾起。

可能是记账能实实在在地让我感受到自己当下正在经历也什么吧,同时也是对过往的一种记录,不失为想要回头看时的选择之一。

抉择

正如先前所说,我一直在使用「快捷记账」这款软件,UI 是丑了些,但胜在快捷、简洁。

原本一切都很不错,但有个致命的缺陷,本位币之外货币间的汇率只能通过其与本位币的汇率间接计算,这将导致记录「用美元购汇英镑」的条目时会产生误差,而这部分误差则需要以手续费的形式来填补。当然其实拆成与本位币的两笔交易可以避免问题,但实在不是我所预期的,也有些污染账本。

此外,对于帮人垫付或代购的场景,虽然在数值上不会出现差错,但逻辑上需要通过一些别扭的方式。因此,虽然习惯依旧存在,但早已在寻找新的记账工具。

Gemini 推荐的首先是 Bluecoins,但操作逻辑相差较大,功能算是强大但也有不尽人意的地方,于是 Pass 了。

而上周在与其的对话中,提到了一嘴 beancount,一款纯文本时的复式记账…哲学?

初识

它的内核只是一些被结构化后的账本文本文件,前端仅仅是 Python 构建的语法解析器和一个不具备编辑能力的 Web 分析页面 fava

因此真正记账的工作实际上是像编写代码(现在似乎被叫成古法编程了吧😥)一样手敲文本,因此似乎没有好用的客户端。的确有个第三方平台 Beancount.io,我也是马上就注册了,但注册后的账户始终无法在 Android 端登录,只能在 Web 端查看,这个只能后边想研究的时候再捣鼓捣鼓了。

beancount 的语法并不复杂,一条基本的多币种账目大概长这样:

2026-04-26 * "商家" "描述"
  Assets:Credit:Card         -20 CNY
  Expenses:Food              400 JPY @@ 20 CNY

应当还是具备一定的人类可读性,而且还能扩展,支持拆分款项。

以下是由 Gemini 生成的示例:

你获得了 100 股 AAPL 股票,公司为了帮你交税,自动卖掉了其中的 35 股,剩下的 65 股进入你的账户,同时涉及了券商佣金和极其精确的汇率折算。

2026-04-26 * "Big Tech Corp" "RSU Vesting & Sell-to-Cover (AAPL)" #Work #Investment
  meta-data: "Vesting Statement #2026-04-Q2"
  statement-url: "https://example.com/statements/"

  ; 确认总收入 (100股的当时市价)
  Income:Salary:RSU                            -18500.00 USD
    vesting_date: "2026-04-26"

  ; 股票入账:此时 100 股以 $185.00 的价格进入资产账户
  Assets:Investments:Broker:AAPL                     100 AAPL {185.00 USD, 2026-04-20} @ 185.00 USD

  ; 立即执行 Sell-to-Cover:卖出 35 股用于缴税
  Assets:Investments:Broker:AAPL                     -35 AAPL {185.00 USD} @ 184.95 USD

  ; 记录卖出的这 35 股产生的现金(暂时存放在券商现金账户)
  Assets:Investments:Broker:Cash                 6473.25 USD  ; (35 * 184.95)

  ; 从券商现金账户中扣除各种预扣税(联邦、州、社会安全、医保)
  Expenses:Taxes:Income:Federal                  4625.00 USD
  Expenses:Taxes:Income:State                     925.00 USD
  Expenses:Taxes:SocialSecurity                   777.00 USD
  Expenses:Taxes:Medicare                         146.25 USD
  Assets:Investments:Broker:Cash                -6473.25 USD

  ; 记录资本损益 (Capital Gains)
  Income:CapitalGains:AAPL                          1.75 USD

  ; 扣除券商交易佣金
  Expenses:Fees:Brokerage                           5.00 USD
  Assets:Investments:Broker:Cash                   -5.00 USD

  ; 剩余 65 股保留在账户中,作为长期持有的资产
  ; 该部分已由第一条 Posting 和第三条 Posting 的差值自动平衡

不知为何,我对这种极高的自定义程度情有独钟,虽然事情变得复杂起来,但是心血来潮依旧想要尝试一番。

启动

Beancount.io 提供了 Help Center,可以结合官方文档一起入门。

之前的记账软件支持导出 Excel,于是 Vibe Coding 了一个迁移脚本,将这近一年以来的账目全部转换为 beancount 文件。

VS Code 上有两个不错的插件:基本语法支持格式化工具

接着花了一天时间微调,最后看到报表那一刻,成就感油然而生。

Demo

Beancount.io 和 fava 都提供了在线演示,可前往查看:

还是得根据自己需求取舍吧。

不过,说到 beancount 的存储方式,既然是纯文本,那么 Git 仓库就可以成为主流,这也是 Beancount.io 所使用的,因此,只要 Git 同步,账本可以部署到各个地方。

通过 https://<TOKEN>@github.com/USERNAME/REPO.git 的格式就可以访问私有仓库了。

所以 Beancount.io 提供的 ssh://git@git.beancount.io:2222 我应该还是会薅一下的😆~

构想

「看」的需求勉强解决了,但是「记」呢?总不能真的在手机的笔记软件上手打后回家整理吧…

记账,最忌讳麻烦,还是需要一款趁手的速记工具。

原本想着在本就小众的社区里继续试错寻找符合需求的客户端,但光是想想就很痛苦,干脆另寻出路。

Chat Bot 似乎是个不错的方式,打开聊天软件,说下怎么消费的,就可以自动分析并写入了。同时,除了记录,还可以让 Bot 帮忙分析账本,甚至可以连 Web UI 都不需要了,一举两得!

想法很美好,开始实现。


常用的社交软件中,Telegram 是 Bot 不错的天然载体,以此为基础进行开发。

但是部署 Bot 后端需要一个服务器…

原本以为会是个比较大的工程,顺便注册了 Dify 浅尝了一下工作流。但发现更需要 Bot 端的精确控制,于是很快注销了。

因为站点现在完全是 Serverless 的状态,先前的 VPS 也早已过期,从两年前开始申请的 Oracle Cloud Free Tier 依旧没过,部署环境有些困难…

但想起之前尝试的 Cloudflare Tunnel 没有实际应用场景,现在倒是可以试试,将其接上家中的 NAS,然后将 Bot 部署在上面的 Docker 就好。

至于 LLM之前在 Deepseek 充值的余额完全没机会用掉,现在似乎已经支持 Tool Calling,也是可以用上了。

这样一来,记账的流程大概就是:

向 TG Bot 发送消息 → 触发 Webhook → 通过 Cloudflare Tunnel 连接到 NAS → Bot 后端预处理消息 → 调用 LLM 分析并生成结构化账目数据 → Tool Calling 调用记账函数 → 写入暂存文件 → 回复 TG 消息

连通

首先确保链路的连通性,也就是让在 Telegram 上发送的消息能准确到达服务器。

在 Cloudflare One 中创建隧道,选择隧道类型为 Cloudflared

创建隧道
创建隧道

接着,根据提示在被连接的设备上安装 cloudflared,Cloudflare 很贴心地为 Windows、Mac、Debian 系和 Red Hat 系 Linux 以及 Docker 都提供了安装和运行指导,可以说非常方便。

安装连接器
安装连接器

然后,为这条隧道添加二级域名,同时配置需要连接的 Web 服务的本地接口。

配置应用程序路由
配置应用程序路由

看到控制台输出后基本就没什么大问题了。


再配置 Telegram 侧,创建 Bot 非常简单,对 @BotFather 发几条消息就好。

拿到 Token域名,就可以在开发中通过 Webhook 方式接入了。

Webhook 的配置主要通过 API 实现,有些麻烦,不过配置一次就好,也可以后续在开发时直接使用框架完成。


最后,自然是考虑安全问题。

分为 Webhook 安全性和 Bot 私密性。

一开始最好的打算是用 mTLS,从 TLS 握手阶段就杜绝未授权访问,但显然 Telegram 不会允许我上传客户端证书。

退而求其次,用 Service Tokens 在 Edge 侧拦截也不是不可以,但很遗憾 Bot 无法设置自定义 HTTP 请求头。

而在 TG Bot Webhook 的文档中可以发现,其包含一个 secret_token 字段,使 Bot 在 HTTP 请求头中加入 X-Telegram-Bot-Api-Secret-Token,而后在用户自己的后端中校验。

虽然是贼进家里来了才叫保安拦截,但或许总比没有好…

但利用 Cloudflare,倒是在 WAF 层面也可以再做些防护,验证这个请求头的同时,判断是否来自 Telegram 自己的 IP 段

接着要防止 Bot 拥有者之外的用户消息也被处理,首先通过 UserInfoBot 获取自己的 Telegram 账号 ID,Bot 框架提供了基于用户的筛选器,让其只放行这个 ID即可。

开发

重拾 Python

或许是因为过于小众,似乎没有发现较为合适的 TypeScript 迁移,只能硬着头皮开发 Python 了☹️。

当然,我不想在 Windows 宿主机上管理 Python 版本以及安装 Visual Studio 的奇妙巨无霸编译组件,于是把开发环境布置在了 WSL 上,开发体验意外地很不错。而且有了完整的类 Unix 环境,我和 AI 执行命令也方便很多。

似乎还没准备 Python 环境,先装上 python-pip python-virtualenv 两个包。

生成并进入虚拟环境:

python -m venv .venv
source .venv/bin/activate

目前使用的依赖如下:

dotenv==0.9.9
beancount==3.2.0
beanquery==0.2.0
python-telegram-bot[webhooks]==22.7
openai==2.32.0

其中 beanquery 查询账本,beancount 创建账目,python-telegram-bot 作为 TG Bot 的框架。

在用 python-telegram-bot 全面接管 Webhook 之前,我先用 FastAPI 初步感受了下 Python 下的 Web 服务器。这个工具确实强大,还可以根据代码内容自动生成 Swagger 及其 OpenAPI 文档,为这种便利感到些惊叹。

Python 的开发体验似乎没有刚接触时那么糟糕了,也许是因为那时 Pylance 还没面世吧。
但是这个语言本身的各种特性还是令我感到恶心…

起初我想用指令明确告知 LLM 是分析还是记账,但发现不如把这一步也交给 LLM 判断…最终就只留下了单一工作流。

其实是因为发现指令后输入文本需要长按指令,觉得麻烦了就去掉了这个功能😝…

Tool Calling

在开始调用 LLM 之前,需要先编写工具确保功能完整性,起初规划了三个:查账记账汇率查询

查账

beancount 使用了一种类 SQL 的查询语言 Beancount Query Language(BQL),通过 bean-query 命令就可以把所有账本当作数据库一样查询和统计。

因此,实际上没什么需要思考的逻辑,将 BQL 语句传入工具,执行,并返回结果就是它的全部。

引入 subprocess 库,放入命令并指定输出格式为 CSV,同时把执行失败时的结果也作为返回值,作为 LLM 的反馈以使其自我纠正,便大功告成。

有些幽默的是,我只是大致浏览了一下,确认内容是自己所需之后,直接将对应的 Markdown 文件 发送 Gemini「总结一下」,就作为 Prompt 塞到 Tool 描述里边了。

记账

记账函数理论上也很简单,beancount 库有现成的格式校验类型和格式化工具,我只要告诉 LLM 它们分别是什么就行。

可库的参数类型太复杂,我需要将其全部拆解后,重新组装成适合描述生成的参数类型,过程实在痛苦。

好在重新封装后就没有任何难度了,将格式化后的文本原封不动加入暂存账本的末尾即可。

汇率查询

Frankfurter 是一个开源的汇率查询接口,虽然每天只更新一次,甚至会在节假日停止更新,但作为账本的分析而言已经足够。

只需提供基准货币、目标货币和指定日期即可获取,还是很便捷的。


我将这些工具放在 tools 目录下,并在 tools/__init__.py 中自动读取,并整合成 LLM 可以识别的 Tools 集合:

for file in os.listdir(os.path.dirname(__file__)):
    if file.endswith(".py") and file != "__init__.py":
        module_name = f"tools.{file[:-3]}"
        module = importlib.import_module(module_name)

        for name, obj in inspect.getmembers(module, inspect.iscoroutinefunction):
            if name.startswith("_"):
                continue

            sig = inspect.signature(obj)
            model_class = None

            for param in sig.parameters.values():
                if inspect.isclass(param.annotation) and issubclass(param.annotation, BaseModel):
                    model_class = param.annotation
                    break

            if model_class:
                tools_map[name] = {"func": obj, "model": model_class}
                tools_schemas.append(pydantic_function_tool(model=model_class, name=name))

这样以来,即使后续想要添加新的工具,也只要大大方方地在 tools 目录下编写,写完不需要做其它任何工作。

工作流

LLM 的 API 只是提供了一个大脑,而我需要将它们与工具结合起来,并赋予其自我反馈的能力,构成一个完整的 Agent(智能体)

上下文LLM 对话中是非常重要的存在,不过我面对的只是一个记账工具,不希望其过度联想过往对话产生不必要的麻烦,同时也能减少 Token 消耗。因此默认状态下,每段对话都是完成目标后立即抛弃。但也有需要上下文的时候,比如输入的信息太少,如「花了 100」,这时候 LLM 就必须向用户索要其它诸如开支账户、商户名称、货币单位等信息。

总结下来,Agent 工作流的流程大致如下:

等待用户响应

向用户索要信息」的步骤,也就是需要用户响应才能执行下一步的动作,成了流程控制上的一大难点,因为程序必须判断 LLM 是否存在这一意图。

在 System Prompt 加入特殊条件肯定是少不了的,想到的有两种方式:

  1. 添加特殊标识符(如 __AWAITING_RESPONSE__),程序判断包含后移除标识符,并存储会话历史。
  2. 编写专用的回复工具,程序判断是否调用该工具以决定是否存储会话历史。

我选择了后者。

不知道为什么,总觉得对前者执行成功的概率有些疑惑。再者,这可是 Telegram 的 Bot,不加点按钮交互啥的总觉得差点意思。

现在测试下来确实可以稳定继承上下文,还算不错吧。

Tip

图中聊天文本为虚构内容。
其中涉及的人物、事件等均为流程参考所需,与现实中的任何个人或团体无关。

以及格式渲染问题,Telegram 的支持有限已在修复中…

调教

这是整个开发过程中最痛苦的环节,也是我厌恶 AI 的原因之一。

用主观的自然语言去规范客观的运行结果,这件事自打与 AI 产生对话开始就令我匪夷所思。

在阅读了几篇 Prompt 编写指南后,我觉得最实在的方式还是在实践中逐步摸索。

没总结出什么规律,还是「水多加面,面多加水」的理,毕竟总有在预期外的内容出现,因此还是需要继续优化。

不过这对流程的确有非常大的影响。

例如先前它只是单纯地分析我输入的内容,每次格式、样式、内容都不一样,有些头疼。但现在我让它在记账前先分析我过去的账单是怎么记录的,模仿我的口吻和经验生成对应的数据。

虽然慢了不少,但结果还是比较满意的。

后续应该会再写个工具,作为历史账本风格的知识库吧。

部署

现在基本的流程已经跑通,大概可以上线测试了。

部署方式我想尝试下 Docker,毕竟不是特别喜欢自己的每个环境下都装着 Python…

发布平台就选择 GitHub,即其 ghcr.io

编写好 Dockerfile 后,运行命令编译镜像:

sudo docker build -t ghcr.io/USERNAME/CONTAINER:TAG .

此时运行 docker images 就可以看到自己编译的镜像了。

在上传之前,需要先创建 GitHub 的 Personal Access Token (classic),并且在终端中登录:

sudo docker login ghcr.io -u USERNAME

登录成功后就会弹出以下信息:

WARNING! Your credentials are stored unencrypted in '/root/.docker/config.json'.
Configure a credential helper to remove this warning. See
https://docs.docker.com/go/credential-store/

Login Succeeded

退出则执行命令 docker logout ghcr.io
编译过程中产生的废弃镜像则可以用 sudo docker image prune 清理。

接着发布:

sudo docker push ghcr.io/USERNAME/CONTAINER:TAG

如果一切顺利,那么就可以在 GitHub 主页的 Packages 下看到。

在那里点击 Connect this package to a repository,就可以显示在仓库中了。

结语

现在这个工具还停留在基本可用的阶段,留着一堆 TODO 等待去优化,比如上传小票图片自动识别文本并添加账目等等。

代码是前天开始写的,这两天感觉在持续对抗面对 Python 的负面情绪,无法和解,有些疲惫了。

说到底是次折腾,我不太确定它能否为我带来更便利更高质量的生活,但希望如此。


感觉自己的生活已经离不开 AI 了,虽然嫌弃,但身体很诚实。

比起 Google Search,Google Gemini 成为了我的主力搜索引擎

这种变化是潜移默化的,我只记得自己在第一次用 IP 纯净的梯子连上 ChatGPT 3 时,与之对话的兴奋感,但这也只是来自「我好像跟上时代了」的释怀,它似乎本就应该如此。

有些可怕,比起智囊,AI 更像是成为了自己的外置大脑,检索、识别、分析、开发、创想甚至思考。

它好像成为了我做某件事前的一个 Hook,没有 AI 的答复,我对生活中的不确定性愈发恐惧,安全感也在逐渐滑落。

以前,我们还会思考「没有手机的日子要怎么活」,而现在,在那之前,更会先开始担忧「没有 AI 的日子要怎么活」…


为坚持写完整篇文记(除了 Gemini 举例的部分)的自己感到诧异。