Coding Agent 记住上次怎么改代码,就能省 Token 吗?我拆了 Singularity 的检索逻辑
同一个仓库里,昨天让 Coding Agent 加过一个快捷键,今天再让它改一个类似的功能,它可能又要从头搜索文件、读测试、踩一遍相似的坑。第一次探索当然有成本;问题是,第二次为什么还要交同样的学费?
最近在 Show HN 原帖 看到一个叫 Singularity 的开源项目。它试图把已经完成的代码修改变成可复用的工作流:记住该去哪几个位置、通常怎么验证、有哪些已知陷阱。下一次任务来了,先匹配可能有用的步骤,再交给 Agent。
这个方向很有吸引力。但我更关心另一件事:记忆被找出来以后,凭什么相信它还适合眼前的任务?
为弄清这个边界,我阅读了 Singularity 固定在 commit 9d241f2 的代码,并做了一组不依赖模型的轻量合成测试。先说清楚:这不是 Singularity 完整安装后的实测,更不是 Claude Code、Codex 的 Token 成本对照。它验证的是从源码抽出的匹配规则,以及这些规则容易误判的地方。
它记的不是聊天记录,而是做事路径
按照项目 README 的描述,Singularity 在成功完成修改后积累经验,再由模型把多次经验归纳为工作流。工作流可能告诉 Agent:某类功能通常改哪些文件、验证命令是什么、之前在哪里失败过。
这与把整个对话塞进长期记忆有区别。原始对话很长,包含大量只对当时有用的试探;工作流试图提取可重复的操作顺序。这里讨论的是工作流步骤与线索的复用,不是对项目是否存在其他缓存机制的全面审计。即便某一步曾经定位到具体文件,仓库结构变化后仍应重新核对位置。
需要区分两笔费用。学习工作流时可能要调用模型;使用某些已生成的工作流线索时,可以不用模型做检索。项目的 workflows-cues 路径正是后一种设计。
但不能据此说 Singularity 所有路径都零模型成本。我读到的 apps/cli/src/eval/StepSelector.ts 明确调用模型判断哪些步骤适用;而 apps/cli/src/eval/Memory.ts 又把 workflows-cues 描述为不需要模型的线索选择。更准确地说,前者是评估代码中让模型选择步骤的路径,后者是根据预先生成的文字线索筛选步骤的路径。二者选择步骤的成本结构不同,但这里没有对两条路径做真实任务的耗时或 Token 对照,不能据此推断哪条总体更优。
零模型调用的匹配,究竟做了什么
我继续读了 apps/cli/src/workflows/Cues.ts。
核心思路相当直接:先把任务描述转成统一大小写,尝试去掉含 not、never、without 等词的否定子句,再检查任务文本是否包含工作流预先生成的线索短语。命中允许短语、没有命中排除短语,才选中对应工作流;具体步骤还可以继续按线索筛掉。
这里没有向模型提问“这个任务是不是语义相似”。它更接近一个经过整理的字符串规则表。好处是便宜、可重复、速度快;代价是,文字重合不等于意图相同。
比如记忆的触发词是 keyboard shortcut。任务写“Add a keyboard shortcut”当然应该命中。任务写“Review keyboard shortcuts in documentation only”,包含同样的词,却不意味着用户要添加快捷键。
源码确实处理了部分显式否定,但并不等于能理解任意复杂的否定、讽刺、隐含条件或跨句意图。匹配器负责召回,不负责证明操作正确。
我用五组输入试了这个边界
我参照上述固定提交的 Cues.ts,把文本归一化、否定子句过滤、允许/排除线索的核心判断手工独立改写为一个 Node.js 标准库测试;它不是官方测试套件,也不保证覆盖上游全部边界条件。测试不安装 Singularity、不连接模型,也不读取真实代码仓库。
测试脚本和结果清单保存在私人研究记录中,未提供公开可下载副本;下表列出全部五组观察结果。这是一份源码规则的合成复现,不是上游 CLI 的端到端行为报告。
| 人工输入 | 匹配结果 | 我关心的点 |
|---|---|---|
| 添加键盘快捷键 | 命中 | 正常召回 |
| 修复数据库连接 | 不命中 | 不相关任务被排除 |
| 明确说不要添加快捷键 | 不命中 | 简单否定有效 |
| 只检查文档里的快捷键描述 | 命中 | 字面相似可能误召回 |
| 要添加快捷键,但记忆指向的旧文件已删除 | 命中 | 匹配本身不检查文件是否仍存在 |
预期输出是在执行前按这份合成匹配器的规则逐项设定的:相关任务应命中、无关任务应落空、明确否定应落空;含有触发短语但仅讨论文档的任务预计仍会命中,而任务文本中声明旧路径失效也预计不会触发文件系统核验。结果清单逐项列出了观察值,五个断言符合这些预期,包括最后两项故意设计的负面案例。所以“5/5 通过”不等于五个任务都被正确处理。它只表示我们对这段简化规则的预测被观察到;不能把它当成 Singularity 真实任务的准确率。
尤其是最后一项。单看 cueChoice 的文本匹配,无法知道仓库里某个文件是否已经移动、删除或重构。即便后续 Agent 会重新查看文件,这次召回仍可能把一条旧路线放到它面前。命中记忆是一条提示,不是仓库状态的证明。
README 里的节省幅度,应该怎么理解
作者公开了一组 Excalidraw 任务对照:在一个与记忆高度相关的长任务中,报告的 Token 数量从约 190 万降到约 42.3 万,成本从约 0.81 美元降到约 0.30 美元,轮次中位数从 28.5 降到 9.5。每侧只有约 3–4 次运行,且实验主要集中于少数仓库。
这些数字很值得进一步研究,但它们仍是作者报告的特定任务结果。当任务涉及未见过的修改,或只是很小的一次性修复,收益可能接近零;学习工作流本身也有额外成本。
更重要的是,减少模型读文件的次数,不一定等于降低总体工程成本。如果召回了错误步骤,Agent 可能花更多时间纠正;如果代码已大幅变化,旧记忆还可能带来额外排查。
我没有复跑作者的模型对照,也没有测量真实 Token 或美元开销;因此本文不估算独立节省百分比。要得到这个数字,至少需要同一批任务的有记忆/无记忆对照,计入学习调用、错误召回修复与最终完成成本。
我会怎么用这类记忆
对重复度高、目录结构稳定、测试流程明确的仓库,我愿意尝试工作流记忆,尤其是那些已经多次验证过的固定操作。但我会把记忆当成待核对的索引,而不是让 Agent 直接照做的命令。
实操上,可以给每条工作流带上来源提交、涉及路径、最近验证时间和适用条件。使用前先核对文件仍存在、测试命令仍有效;一旦连续几次召回后都需要人工纠错,就应该重新学习或停用这条记忆。这是我建议的防护办法,不是声称 Singularity 当前版本已经实现了完整的失效检测。
至于值不值得安装,判断标准也不该是“它说能省多少 Token”,而是你自己的任务是否经常重复。如果每次都是不同代码库、不同语言、不同问题,花时间建立工作流记忆可能没有想象中划算。
我更想看到下一步的公开对照:固定同一批真实任务,分别统计有效召回率、误召回次数、过时记忆次数、学习费用,以及最终总成本。只有把这些放在一起,才能知道记忆是在减少重复劳动,还是只是把探索成本换成了维护记忆的成本。
参考:Singularity 仓库 · 固定版本匹配源码 · 作者实验与限制