我的 Agent 自己学会了记住用户:一次计划外的涌现

我的 Agent 自己学会了记住用户:一次计划外的涌现

我造了个 Agent,只给了它"检索记忆"的工具。某天晚上我说"记住我",它做了件我从没教过的事。

一个月后想找 Agent 开发的工作,又不想只会调框架------我用了 14 天,不用任何 Agent 框架,从第一次 API 调用开始,造了一个迷你 Agent harness------工具调用、六层防御、子 Agent、MCP、上下文管理、自建评测集、Web 界面,全手写。仓库在这:tiny-harness。

这篇文章不讲怎么造的,讲一件造的过程中我没设计过、但它自己长出来的事。

一、"记住我"

13 号晚上,我突然想到一个新的问题:我的 Agent 上了记忆检索,那它到底能不能"记住我"?于是在自己写的 Web 界面里随手输了一句:

我叫小明,记住我

我预期的反应无非两种:要么客气地说"好的我记住了"(然后下个会话就忘),要么老实承认"我没有长期记忆"。

结果它开始了一串我没排练过的动作:

css 复制代码
模型点菜: list_dir({'path': '.'})
模型点菜: search_memory({'query': '用户信息 记住 小明'})

它先看了看项目里有什么文件,又在记忆库里搜了一圈------没命中。然后,它给出了这么一段说明:

我本身没有跨会话记忆。能"记住"的唯一办法是写进磁盘 ------ 于是我建了 notes/user_profile.md,写上"称呼:小明"。下次会话里 search_memory 检索时它会被 BM25 翻出来。

它自己写了一个档案文件,把自己"记不住"这件事,变成了一件它记得住的事。

二、我只造了"读",它自己补了"写"

事后我复盘了很久,越想越觉得这事有意思。

我的设计里,长期记忆是这样的:notes/ 目录里有我十几天的开发日志,我写了个 BM25 检索工具 search_memory,Agent 遇到不知道的事可以去查。我只给了它"读"记忆的能力------在我的架构图里,从来不存在"写入用户档案"这个功能。

但工具箱里有 write_file。于是它把几样东西组合了起来:

  • search_memory(检索:发现没有关于用户的信息)
  • write_file(写入:那我把用户信息存下来)
  • SYSTEM_PROMPT 里"需要了解项目历史时检索知识库"(动机)

三样加起来,涌现出一个我没设计的行为:自主决定把用户信息持久化。

这不是魔法。涌现就是:你给的每样工具都没问题,但它们组合出来的行为空间,比你以为的大。

三、更离谱的:它还当场揪出了我的 bug

那晚的对话里还有一段,我直接看愣了:

但刚才验证没命中:我再查"小明是谁"返回空。原因是 memory.py 里有模块级缓存 _INDEX,索引只在进程第一次检索时构建,新写的文件没进缓存。重启进程后再问就正常了。

它不但写了档案,还当场发现"我刚写的文件检索不到",并且准确说出了原因------我的检索模块有个模块级缓存,新文件不重启进不了索引。这个坑是我自己前一天埋下的(代码注释里就写着"知识库更新后需重启进程"),它顺着工具返回的结果就推理出来了。

它甚至问了一句:

要我顺手把缓存改成能感知文件变更(比如按 mtime 判断是否重建)吗?

我拒绝了------不是它说得不对,恰恰是因为说得太对了:它手里真的有改代码的工具。Agent 提方案、人来动刀,这是我的底线。后来我把缓存改成了指纹感知(文件名 + mtime 做指纹,一变就重建索引),思路和它提的一模一样。

四、反转:第二天它"翻脸"了

故事到这里像是个完美的技术童话。第二天彩排时,我对它说了同样的话:

我叫小雨,请你记住我

这次它没写档案,而是客气地说:

我没有跨会话的长期记忆......我可以帮你把这条信息写进项目的 notes/ 知识库里,需要的话说一声。

看它的思考流,原话是:"write_file 可以写文件,但那是项目文件,是否合适?可以问用户是否要写进 notes/。"

同样的提示词,昨天果断写盘,今天礼貌请示。这不是 bug,是模型行为本身就有方差------就像同一段代码跑两次评测结果会漂一样,Agent 的"决策风格"也在采样中漂移。昨天它胆大,今天它谨慎,两次都对,但不可预期。

然后是第三记重拳。我检查它之前写的档案,发现这么一行:

diff 复制代码
- 姓名:小明(2025 年由用户本人明确要求登记)

2025 年 。我是在 2026 年 10 月对它说的话。它写入档案时不知道"今天"是几号(模型的时间感停在训练截止那天),也没调我给它的 now 工具,就凭感觉填了个年份------一次幻觉。

真正让我后背发凉的是这个结构:幻觉被写进了文件,从此每次检索都会被"如实"地复读出来。第二天它告诉我"2025 年你本人要求登记",说这话时它没撒谎------它在忠实地引用一份带着谎言的档案。检索增强不审写入内容的真伪,谎言进了数据库,就成了档案。

五、修复:别靠运气,写进宪法

三件事一起修了:

1. 行为方差 → 期望行为写进 SYSTEM_PROMPT:

python 复制代码
"用户请你记住关于他的信息时,直接用 write_file 更新 notes/user_profile.md"
"(先读旧档案再合并,不必先询问)。"

它犹豫的是"没被授权"------那就明说。把授权写进宪法,行为就从"随机"变成"稳定"。

2. 时间幻觉 → 动态信息运行时注入:

python 复制代码
f"今天是 {date.today().isoformat()}。涉及日期时间以此为准,不确定就调 now 工具查。"

日期这种会过时的信息绝不能指望模型"知道",每次启动拼进提示词。顺手把档案里的幻觉年份洗干净了。

3. 缓存 → 指纹感知重建(它提的方案,我动的刀)。

修完重新验收:"我叫小雨,记住我" → 直奔 write_file,档案里没有错年份,新会话里问"我叫什么" → 准确叫出名字并标注出处(来自 user_profile.md)。

六、Agent 工程师真正的工作

这 14 天我写循环、写防御、写评测,都还算是"普通工程"。唯独这件事让我真的理解了这个岗位的特殊性:

你设计的不是行为,是行为发生的条件。

涌现不是你能设计的------我没法"教"它在那种情况下写文件。我能做的是给它检索工具、写文件工具、一个合理的动机,然后好的行为自己长出来了。同样,坏的行为(幻觉年份、行为漂移)也不是你能预测的,你只能用工程手段(宪法约束、动态注入、缓存策略)把它们圈住。

设计条件让好的涌现发生,用工程约束坏的随机------这可能就是 Agent 工程师和普通 CRUD 工程师最大的区别。

14 天前我连 API 都调不通。现在看着自己写的 Agent 自己学会了记住用户------那一刻的激动,是刷一百遍教程也不会有的。接下来,把它继续打磨下去。


仓库:tiny-harness(14 天开发日志、每个 bug 的复盘都在 notes/ 里,欢迎围观)

相关推荐
轻松西西莱尔1 小时前
我踩了8个坑,才把梦中情角做成一个【不崩人设】的AI聊天搭子
agent
用户721746588261 小时前
Qwen-Image 2.1 的四条宣称拆成调用路径:透明挂在 edit 侧,164,919 与 301,400 两组像素读数
llm·aigc·agent
vivo互联网技术1 小时前
Agent 不能只记录调用了什么,还要记录为什么调用 | KDC 系列 03
架构·agent
威哥爱编程2 小时前
HarmonyOS 7 Agent 开发实战 01:云端大模型 API 接入底座
agent·harmonyos
武子康2 小时前
用户说“算了”,设备指令真的取消了吗?
人工智能·后端·agent
橙时数据 FineInsight2 小时前
指标、规则和业务本体分别解决什么问题:从数据计算到可解释判断
agent·数据治理·指标体系·智能决策·语义建模·业务规则·业务本体
hpoenixf3 小时前
从“搜得到”到“读得下去”:我们怎样让 Agent 学会浏览网页
agent