Agent 不该每次都从零开始,聊聊 Hugging Face 的 funes
一个 Agent 花了两个小时翻仓库、读日志、试错,终于搞清楚为什么一个看起来很普通的 streaming parser 不能动。第二天换了个会话,它又从 README 开始问起。
这不是模型变笨了。是我们把最贵的那部分工作,推理过程、失败路径和当时的判断,留在了一个几乎不会再被翻开的会话窗口里。
Hugging Face 9 月 3 日发布的 funes,想处理的正是这个尴尬问题。它不是再造一个会替你总结历史的 SaaS,也不是往 prompt 里塞一段模糊的长期记忆。它把 Claude Code、Codex、pi、Hermes 的会话轨迹索引成一个本地 Lance dataset,Agent 需要时自己检索原始片段,并能一路回到来源会话和具体轮次。
我觉得这个方向比「给 Agent 加记忆」听起来要克制得多,也更接近工程现实。真正缺的不是一份漂亮的个人档案,而是可追溯的工作证据。
会话结束以后,为什么经验也跟着蒸发
做过 Agent 开发的人,大概都见过这种场景。一个任务拖了半天,Agent 查了十几个文件,跑过三轮测试,发现某个 API 不能重试并不是文档没写,而是旧客户端会在重放时重复扣费。你让它在当前会话里解释,往往讲得很清楚。
可一旦窗口压缩、模型切换,或者你第二天在另一台机器上继续,事情就变了。
你只能手动写 handoff。写得短,关键约束会丢。写得长,新会话一上来又背着一大包上下文,token 成本和注意力一起膨胀。更糟的是,handoff 通常只记录「做了什么」,很少保留「为什么没走另外那条路」。
后者才是维护里最值钱的东西。
代码的当前状态可以从 Git 读出来。设计动机、被否掉的方案、一次报错背后的排查顺序,Git 很难替你保存。很多团队把这些散在 PR 评论、Issue、飞书群和某位同事的脑子里。Agent 产生的 trace 其实也一样,只是量更大,消失得更快。
funes 的文章里有一句判断我很认同,trace 只是潜在记忆,不是记忆本身。把 JSONL 日志堆在磁盘上,和把知识库建好,中间还隔着解析、切块、检索、排序以及出处展示。你无法靠一次 grep 回答「上次为什么放弃 streaming parser」。
这句话不花哨,但很关键。
它存的不是结论,而是能回看的证据
funes 的第一层设计很朴素。运行 funes add hermes 或对应的 Agent 名称后,它会为现有会话建立初始索引,给 Agent 安装 recall 和 get 工具,并在完成的 turn 后做增量索引。历史内容可以分批回填,不需要每次重新处理全部日志。
这几步看着像常规 RAG,区别藏在数据模型里。
它把不同 Agent 的 trace 统一成 turn 和 block 的形状,再切块、向量化并写入本地 Lance 数据集。查询不是只跑 embedding 相似度,而是混合 vector search 和 BM25,融合排序后再用 cross-encoder rerank,最后按时间重新加权,并把命中片段的邻近块带回来。
这套组合没有神秘之处。反而因为没有神秘之处,才值得开发者认真看一眼。
只做向量检索,像函数名、错误码、配置键这种精确线索很容易漏。只做关键词检索,又很难找回「当时为了降低重试风险而改成队列」这种语义关系。rerank 能压掉表面相似但没用的片段,邻近块则避免 Agent 只拿到一句脱离语境的话。对代码工作流而言,答案常常不在一个 turn 里,而藏在「发现问题」和「确认约束」之间的两三轮对话。
更重要的是,recall 返回原始文本,不只吐一段被二次概括过的摘要,同时标出 Agent、时间戳、会话与轮次。命中结果还会附带 get,能打开完整 turn 和上下文。
这里的取舍很清楚。摘要型记忆追求轻,原始证据型记忆追求能复核。前者适合记住偏好和稳定事实,后者更适合工程决策。
别把两件事混在一起。
为什么「一份可查询的数据集」比「一个记忆服务」更顺手
funes 把本地 memory 定义为 Lance dataset,需要跨机器时再绑定到自己名下的 Hugging Face dataset。它默认私有,另一个机器执行同样的绑定命令后,可以继续读同一份记忆。远端数据会缓存到本地,热查询仍在本机完成。
这个设计绕开了一个很容易被忽略的问题。很多 Agent memory 产品一旦上线,就顺手把工作过程变成了供应商托管的数据。你得相信它的存储策略、账号体系、导出能力和未来定价。项目越重要,迁移越痛。
dataset 的思路没那么性感,但边界更干净。记忆的载体是你拥有的数据集,Hub 提供的是版本、权限、分发和缓存能力。要停止用某个 Agent,数据不会跟着 Agent 一起锁死。要换另一台机器,也不必把所有历史手工塞进新工具。
对 Channing 这类会在不同 Agent 之间切换的人,这一点很实际。今天用 Hermes 做定时任务,明天用 Codex 改 Java 服务,后天可能让 Claude Code 帮忙看前端。不同模型的上下文各自断开很正常,但项目的判断不该跟着断开。
当然,跨 Agent 也不是免费午餐。
不同工具记录 trace 的颗粒度不一样。有的会保存完整工具输出,有的会截断,有的把思考和执行拆成不同字段。funes 统一格式后能让检索跑起来,却不能自动消除原始记录质量的差异。一个从不写清决策理由、只会闷头改文件的 Agent,即使被索引了,召回的也仍然是一堆难读的动作日志。
记忆层不能替代好习惯。它只能让好习惯不那么容易丢。
长会话的敌人,不只是价格
很多人碰到上下文变长,第一反应是 compaction。把前面的对话压成一段总结,再继续干活。这当然合理,特别是任务还在同一个会话内时。
问题是压缩并不总能判断什么该留下。
funes 引用了自己的 handoff-vs-recall benchmark,任务被设计成必须依赖旧会话知识才能回答。它比较了 compaction、人工 handoff 和 recall。文中报告,recall 在两个任务上都是成本最低的路径,其中一个任务比 handoff 便宜约 8 倍,另一个约 4 倍。compaction 在一个任务上完成,另一个任务没有得到可用结果。
这组数字还不能当成通用结论。benchmark 是项目方发布的,任务设计、模型选择、检索参数都会影响结果。我不会因为两个任务就宣布 recall 一定比摘要强。
但失败形态很有启发。压缩失败时,它不是完全没记住,而是把真正影响判断的细节压平了。比如「已经试过 A」会留下,「A 在某个边界条件下会双写」可能没留下。后者恰好决定了下一个 Agent 是否会浪费半小时再踩一次坑。
你想想看,软件工程里的关键信息经常就是这种反直觉的小约束。它在总代码量里不显眼,在一段摘要里也不显眼,只有当同一个 bug 再次出现时才突然变得无比昂贵。
所以我不认为 recall 会取代 compaction。它们处理的是两层问题。compaction 用来维持当前任务的连续性,recall 用来让跨会话、跨模型的历史仍然能被找到。前者像把正在读的书夹一张书签,后者像给整个书房建立索引。
一个 Java 和 Spring 团队可以从哪里开始
如果你维护的是 Java 或 Spring 服务,我不建议一上来就把所有 Agent session 同步到团队 memory。先在一个边界清晰的项目上跑通,才是比较稳的做法。
可以挑一个常见但容易反复踩坑的区域,比如支付回调、消息消费幂等、Spring Boot 升级,或者 LangGraph 工作流的状态恢复。让 Agent 在排查时遵守一个简单约定,每次确认关键约束时,留下具体的文件、命令、失败现象与决定原因。之后让下一次会话主动调用 recall,检查它是否真能回答旧问题。
我会用下面四个问题验收,而不是盯着索引数量。
- 新 Agent 能不能指出上次改动涉及哪些文件,而不是只给出模糊结论
- 它能不能复述被放弃方案的失败条件
- 它给出的引用能不能打开并找到原始 turn
- 检索失败时,它会不会明确说不知道,而不是把相似片段拼成一个自信答案
最后一个尤其重要。funes 的 ask 被设计成只读查询,检索证据不足时应当承认无法回答,而不是硬补。这个产品姿态是对的。对工程团队来说,一个带出处的「没找到」,常常比一段流畅的错误记忆有用得多。
可以再往前走一步,把 recall 放进 Agent 的工作流,而不是把它当成一个人类搜索框。比如任务里出现旧模块名、历史 ADR、重复报错时,Agent 先查记忆,再读当前代码,再决定是否修改。这样做的顺序很重要,历史理由能帮助定位,但不能越过当前仓库事实。
历史是线索,不是权限。
它离生产级团队记忆,还有几道坎
funes 很新。发布文章给出的体验不错,但我没有在生产团队里运行过它,也没有替它验证过 redaction 的边界。因此这里不把它写成「装上就解决组织知识管理」的万能药。
第一个问题是隐私。项目方表示,索引阶段会先做凭据脱敏,发布到 Hub 前再扫描每个 chunk,疑似 secret 会被拦下。这个双重检查比完全不做强得多,但安全团队仍应把它当作防线之一,不是最终保证。会话里可能有客户数据、内部架构、访问 URL,甚至不是传统 secret 的敏感上下文。把本地 memory 绑定到远端前,先看 SECURITY.md、先跑一批无敏感项目、先确认访问控制,这些步骤不能省。
第二个问题是噪声。Agent 一天能产生大量「打开文件」「运行测试」「再试一次」之类的记录。索引能够增量化,不代表召回自然就会准确。项目规模一大,recency 权重、chunk 大小、保留周期和跨仓库隔离都会变成工程配置,而不是安装脚本能替你做完的事。
第三个问题是知识过期。旧决策有来源不等于它还有效。Spring Boot 升级后、依赖替换后、业务规则变更后,最危险的不是找不到历史,而是找到了已经失效的历史还照着执行。这里应该把 Git commit、分支、仓库版本一并纳入 trace 的筛选条件。funes 文章强调 session、时间和 turn 的溯源,这给了基础,但团队还得自己定义「什么情况下旧记忆只能当参考」。
最后是权限模型。个人跨机器记忆很自然,团队共享就复杂得多。一个新同事的 Agent 是否应该读到所有历史讨论。离职成员留下的会话是否可继续检索。公开开源项目的 working memory 又该如何去掉私密讨论。dataset 的所有权很清晰,组织治理仍然不会自动出现。
这些麻烦没有让 funes 失去价值。恰恰相反,它把问题暴露在一个可操作的位置上,而不是用「长期记忆」四个字把问题盖住。
Agent 的记忆,应该像 Git blame 一样可追问
我最喜欢 funes 的地方,不是它用了 Lance,也不是它把多个 Agent 接到了一起。而是它拒绝在写入时把一段工作经历蒸馏成一句看似正确的事实。
工程世界已经被这种不可追问的结论坑过太多次。有人说「这里不能改」,没人知道是谁说的,也没人知道当年的条件还在不在。Agent memory 如果只是把这种口口相传自动化,最后只会更快地制造技术债。
好的记忆应该允许你继续追问。谁做的判断。在什么会话里。基于哪段输出。前后还有什么条件。答案不可靠时,能否回到原始材料自己再判断。
funes 目前给出的,是一条值得继续观察的路径。它不承诺 Agent 永远记得一切,只做一件更朴素的事,把已经发生过的推理和试错,变成下一次可以找到、可以核验、也可以推翻的证据。
下次一个新 Agent 又从零开始时,问题不该是「它怎么这么健忘」。更该问的是,我们有没有把上一次的工作,留成一份能被未来继续使用的记录。




