跳到正文

agentacct

读你那些编码 agent 本来就会写下的会话文件,把 agent 通过 MCP 和钩子自报的活儿记下来,用一份公开价目表给 token 估价,然后全放进一个不出本机的本地面板里——而且始终把「agent 说自己做完了」和「有检查证明它做完了」当成两件事。

Screenshot of agentacct
编辑截图, 30 Sep 2026agentacct ↗

这是什么

一个本地优先的编码 agent 工作账本。它读各家客户端本来就会写的会话文件——Claude Code 的 JSONL 记录、Codex 的 SQLite 状态与 rollout、Hermes 的状态库、OpenCode 的会话汇总、OpenClaw 的 JSONL、DeepSeek Harness 的压缩日志、Kimi Code 按请求写的用量事件——同时通过 MCP 与宿主钩子记下 agent 自报做过的事,然后用真实的会话 id 把两边接起来,并给每一个接合处标上置信度。一个任务最后变成一张回执:参与的会话、记录下来的结果、带退出码的检查、活动时间线、token,以及用 ≈ 画出来的成本估计——因为那是按价目表算的估计,不是账单。什么都不出本机:不要账号、没有遥测、不存 provider 密钥,也不会去接管它没有亲手启动的 agent。它以一个 Python 命令行与终端界面加一个已签名的 macOS 应用交付,八个客户端各有强弱公开可查的接入路径。

谁做的这个仓库挂在 GitHub 账号 mikehasa 名下,提交历史里有 484 次提交中的 210 次算在他头上;第二个账号 FZ2000 落地 159 次,另外三个人各一到三次,还有 110 次提交根本没有关联账号。元数据里没有出现真名,而 homepage 字段指向 https://trytofu.ai,那个页面的标题是「Tofu puts your AI-built app online in 10 minutes」,讲的是另一个产品。

它是怎么搭起来的

组成 · 6

一个证据内核加几片很薄的边缘。客户端报上来的一切先归一成不可变信封,追加进一个持久 spool,再索引进一个可重建的 SQLite 投影;回执、时间线、用量立方、分歧这些派生视图都是从那个 spool 重算出来的,而不是写回去。组织这一切的规则是:任何适配器都不许把自己的说法升级成事实——钩子能证明一次工具调用发生过,却证明不了它的含义;MCP 能证明 agent 说过什么,却证明不了 provider 收了多少钱;客户端日志能证明 token,却证明不了目标。于是两条流始终分开,用真实的 id 接起来并带上置信度标签,而缺失、部分、互相冲突都是产品状态,不是在查询时抹平的错误。成本被刻意放在内核之外:一份本地、可刷新、可固定的公开价目表快照,作用在客户端自报的 token 上,覆盖不到的情况就保持未知。另有一个分开的存储负责运行控制——尝试、批准、预算、排程——前提是 agentacct 只管自己亲手启动的工作;也正因为如此,采集层是失败即放行,而整个产品不授予它任何权限也能读。

src/agentacct/
93 个文件、4.1 MB。553 KB 的 cli.py 和 454 KB 的 client_usage.py 扛着命令面与每一家的导入器;旁边是 work_ledger.py(233 KB)、evidence_store.py(234 KB)、api.py(216 KB)、tui.py(178 KB)、hooks.py(143 KB)、mcp.py(126 KB),以及定价那一对 cost.py(40 KB)与 pricing_catalog.py(26 KB)。两个小子包分别放证据采集的适配器、清单与注册表,以及本地连接器。
apps/agentacct/
305 个文件、43.6 MB:一个 SwiftUI 写的 macOS 应用,光 SetupModel.swift 就有 161 KB,后面跟着 WorkPane(135 KB)、DashboardPane(100 KB)、ReceiptsPane(81 KB)、WorksetsPane(80 KB)和 Theme(52 KB)。它的测试是快照式的,明暗两套参考图成对提交在以平台命名的目录里。
tests/
167 个文件、3.6 MB,最重的是 302 KB 的 test_client_usage.py、164 KB 的 test_mcp.py、104 KB 的 test_evidence_store.py,以及一个 99 KB 的 token 浏览器测试。测试在 CI 里跑 Python 3.11、3.12 与 3.13,发版 pull request 会把它写进说明——0.12.7 那棵树上是通过 4,895 条、跳过 3 条。
docs/ 与 INSTALL.md
docs/ 下十二个文档(193 KB)加一份 33 KB 的 INSTALL.md:33 KB 的参考手册、28 KB 的逐客户端接入指南、24 KB 的用量与成本真相表、覆盖矩阵、架构说明、安全边界、隐私威胁模型和证据架构 RFC。覆盖矩阵是由 scripts/gen_coverage_matrix.py 从代码里的能力清单生成的,所以对外承诺和实现不会悄悄错开。
packaging/ 与 .github/workflows/
build-dmg.sh、freeze-cli.sh(PyInstaller)、verify-dmg.sh、source-provenance.sh 和一个命令行负载校验器,四条工作流里有一条 5.8 KB 的发布任务,会在 tag 与打包版本不一致时拒绝发布。应用的所有版本字符串都从 pyproject.toml 推导,不另存一份。
design-plans/ 与 animation-plans/
一叠工作底稿:一份数据质量计划,含七个文档与四个审计或夹具脚本;一次原生 macOS 大改,带三个大夹具;一项「会话步骤可读性」研究,散在三个文件里的 100 个编号场景;一次用量限额改版,含 100 个编号评审文件以及批评与综合笔记;动画计划下还有一份 215 KB 的场景矩阵。

取舍,以及它替代了什么

  • 两条证据流靠 id 接合并各自标注,而不是合成一个数字 替代 把用量和记录下来的工作揉成一个数

    README 把它写成规则——「Missing beats wrong」——并说用量与记录工作之间的每一处接合都带 exact、high、medium、low 之一,没被证明的关联显示为缺口而不是零。用量真相表把同一个想法延伸到来源上:MCP 证明 agent 说过什么,本地导入证明解析到了什么,两者都不会变成 provider 的账单。

  • 成本来自公开价目表,并永远标成估计 替代 声称那是 provider 或订阅账单

    README 说成本是按价目表算的估计、用 ≈ 标出,而且根本没有账单接口;真相表补充说,订阅或编码套餐用户并不按 token 付费,那张表是社区维护价目表的快照而不是 provider 的价目单,而且价目表没覆盖到的模型 id 会停在未知,而不是拿一个相邻价格顶上。

  • 只控制 agentacct 自己启动的工作 替代 去接管或监管已经在跑的 agent

    安全文档列出了它不做的事——不扫描机器上已有的 agent 进程、不接管已有会话、不暂停/杀死/检视不是自己启动的进程、默认不改全局客户端配置——而采集层是失败即放行,所以一个坏掉或被移动过的适配器永远挡不住宿主 agent 的一次工具调用。

  • 客户端不报 token 时显示「不可用」,而不是零 替代 显示一个被测量出来的零

    Cursor 这条通道只读 composer 身份、时间戳、显式模型元数据和子节点链接;覆盖矩阵说它绝不捏造 token 用量、缓存用量、成本、标题或项目,接入指南也说缺失的用量保持不可用,而不是变成一个测出来的零。

  • 两次改名之后仍然接受旧名字 替代 与旧标识符干净切割

    0.5.0 改了包名与 MCP 工具名,0.5.1 改了环境变量前缀;changelog 写明旧名字永久继续接受,而当两个别名被设成不同值时 agentacct 会直接拒绝,而不是悄悄挑一个。项目级存储目录至今还带着最早的那个名字:.agent-sentinel/state。

依据README.md 与 README.zh-CN.md、docs/architecture.md、docs/reference.md、docs/usage-truth-table.md、docs/coverage-matrix.md、docs/coding-agent-integrations.md、docs/safety-boundaries.md、docs/multi-source-evidence-architecture.md、docs/agentacct-workflow-instructions.md、CHANGELOG.md、各发版 pull request 正文,以及完整的文件树及其体积。

制作过程

6 个阶段
  1. 01

    两个月、42 个版本,以及第一周里的两次改名

    仓库建于 2026-07-24,第二天就发了 0.1.0:一个本地优先的账本,从本地会话文件里导入客户端自报的 token,通过 MCP 记下工作上下文,用置信度把两边接起来,画在一个本地面板上。六十八天之后,changelog 里已经有 42 个版本段落,最后一节停在 2026-09-30 的 0.12.9;提交历史有 484 次提交——210 次算在所有者名下,159 次属于第二位贡献者,110 次没有关联账号,另有五次分散在三个人身上。键盘前并不只有一双手:79 条提交带共同作者尾注,其中 74 条点名了某个 Claude 模型,最大的一群是那 59 条写着「Claude Fable 5.1」的。这个项目在第一周里还给自己改过两次名。0.5.0 把 MCP 工具从 sentinel_* 改成 agentacct_*,把 Python 包从 agent_chronicle 改成 agentacct;0.5.1 把 AGENTACCT_* 定为主要的环境变量前缀,同时宣布旧名字永久继续接受。留下来的痕迹是项目级存储目录——它到今天还叫 .agent-sentinel/state。

  2. 02

    工作步骤是 agent 自己写下来的,而证据压过措辞

    agentacct 不会盯着一个 agent 去猜它干了什么;步骤记录是 agent 自己写的。通过 MCP,agentacct_record_section 把一段工作以 started 打开,中途补 checkpoint,最后以 completed、blocked 或 handed_off 关闭;测试运行这类机器检查另有记录,并带上退出码;而装进客户端的钩子桥只补工具活动的节拍和工作目录下的相对文件元数据,绝不包含提示词、回复、思考或工具正文。一次检查算不算数,取决于它是怎么被观察到的,而不是 agent 怎么措辞——像「tests passed」这样的文字永远不会被解析成一个通过——而一个做完的任务只会渲染成 Reported(agent 自己说的)或 Verified(要求有一次晚于最后一次改动、且通过的检查)。还有两条尚未合并的 pull request 在把这份契约勒得更紧。编号 324 那一条,想让 progress 备注在段落以 completed 或 handed off 关闭时变成必填,长度 20 到 260 个字符,且最后一句必须以六个固定词之一开头,被拒绝时还会打印一个改好的示例调用;编号 335 那一条,则想把 agent 自己的说法——目标、最新的进展备注、下一步——放到被统计的指标之上,并标为 Agent reported。每一处与用量的接合都会带上 exact、high、medium 或 low,而继承来的会话 id 永远不会是 exact。

  3. 03

    七种本地格式、一本账,以及一个不报 token 的客户端

    token 的真相只来自各家客户端自己写下的文件,而它们写下的形状各不相同。Claude Code 从它 projects 目录下的 JSONL 记录里读;Codex 从 state_5.sqlite 加上 rollout JSONL 里读,而那里的原始输入本来就包含命中缓存的输入,所以导入器会减掉自报的缓存读与缓存写、归一出一个非缓存数字,并且拒绝把推理 token 再加一次——因为它本来就是输出的一部分。Hermes 来自 state.db 里的会话行;OpenCode 来自原生 opencode.db 的按会话汇总,实在没有数据库时退回导出的 JSON 事件流;OpenClaw 来自 JSONL 里的助手行;DeepSeek Harness 来自 ~/.dsh 下 Zstandard 压缩的会话日志,那里记了 token 却完全不记成本;Kimi Code 则来自 session_index.jsonl 索引加上每个会话 wire 流里按请求写的 usage.record 事件——那些是增量、从来不是累计值,所以导入器把它们加起来。Cursor 是那个刻意的例外:它的主库 state.vscdb 只给出 composer 身份、时间戳和子节点链接,而一个拿不到的 token 数字就保持「不可用」,不会变成零。支持的强度按能力而不是按 logo 公布——会话发现、用量导入、机械采集、MCP 语义、模型归属、缓存读、缓存写、一键安装各评各的——另外还有五个 agent 只列在路线图上,背后什么都没有。

  4. 04

    那个钱数从哪来,以及它不是什么东西

    金额是估计,而且被当成估计标出来。token 是 client_reported,从客户端自己的会话存储里读出来。成本是 estimated_from_tokens:把那些 token 乘上项目本地保存的 LiteLLM 公开、由社区维护的模型价目表快照,画成 ≈$;而 ~$ 表示一个已知不完整的部分合计。内置价目表刻意做得很小,到 gpt-5.5 就停了;任何价目行都没覆盖的模型 id 会停在成本未知,而不是去拿一个相邻价格凑数;缓存读的价是输入的 0.1 倍,缓存写按输入价;另外有三个别名把客户端自己的路由名映射到表上——claude-code 对 anthropic,codex 对 openai,DeepSeek Harness 的 deepseek-official 对 deepseek。快照一过七天就自己刷新,尽力而为,失败后一小时内不再重试,于是网络不可达永远不会挡住一次导入。文档对「这个数字不是什么」写得毫不含糊:订阅用户并不按 token 付费,所以这个估计不是他们实际付的钱,而且它根本没有账单接口。已存的用量行也不会自愈,因为一次扫描只会给现在有价目行覆盖的行重新定价——这就是为什么有一个补丁专门加了一条命令来解释某行为什么没有成本,也是为什么某个 Codex 模型缺一条价格会单独发一个版本。

  5. 05

    作者自己那台机器:22.8 GB 的证据,和一套先失败后写下的发版流程

    作者就是在写它的那台机器上用它,那些数字直接留在 changelog 里。只追加的证据 spool 涨到过 22.8 GB,而由它建出来的投影只有 277 MB,抽样显示其中约 94% 的字节是剪枝之后留下的死影子行。一次经过校验的冷压缩在编号 328 落地,但难的是证明它安全:在一个被剪枝过的存储上,「与线上投影完全相等」永远不可能成立,而从零重放那三百万行 spool 的速度大约是每秒 137 行——差不多六个小时。于是那道闸门改成了包含性证明,之后还得学会把离线窗口期间实时用量通道写进来的行排除在外。修好之后,作者那台机器的存储从 21.24 GiB 变成 131.7 MiB。同一种维护也出现在测试里:编号 340 重新校准了一个显示门槛,它已经从「低于 15%」漂到 3,592 个不重复工作标题里的 22.4%;编号 342 追的则是一个只因为两天夹具跨了不同日历日、而周粒度以周一为锚才出现的失败。发版用的 pull request 必须写明 macOS 磁盘镜像是要重建还是可以沿用,因为这个应用里嵌了一份冻结的 Python 命令行——这条规矩是在它失败之后写下的:连续四个版本发出去时根本没有磁盘镜像,下一个版本存在的主要意义就是把镜像补回来。

  6. 06

    它承认自己做不到的事,以及外面那一小圈回响

    agentacct 自称早期 alpha,并把局限写在能被查验的地方。它不去扫描或接管自己没有启动的 agent,不复制提示词、回复、思考或对话记录,不保存 provider 密钥,也不提供托管服务;Windows 只能通过 WSL 使用,那个已签名的 macOS 应用是给不想装 Python 的人准备的。覆盖矩阵承认同一个客户端可以同时拥有一条已被验证的用量通道和一条实验性的采集通道,而它确实如此:Kimi Code 的钩子桥已经在真实桌面会话里触发过——五个会话、64 条 payload——但还没有任何一次真实会话被观察到把记录下来的段落绑到自己会话 id 上;Cursor 只能证明会话存在,对 token 一无所知;OpenClaw 的路由元数据也还没接进来。日期维度是整个会话总量归到某个活动日,API 把这一点作为限制暴露出来,而不是假装能做到按天拆分。仓库里留着整套纸面痕迹:用量限额改版下 100 个编号评审文件,旁边还有批评与综合文档;动画计划里一份 215 KB 的场景矩阵;以及一份自带审计脚本的数据质量计划。外界的注意力小而具体——一位贡献者的可读性清理(changelog 里点名致谢)、另一个账号修掉一个过期导出文件的问题,以及一条主动找上门的接入请求:编号 349,来自 MemCode 的创始人,问要不要接受一个导入记忆保存与召回元数据的可选导入器,并承诺绝不摄入记忆正文、提示词、凭据或源码文件。

相关档案

全部档案 →