跳到主要内容
一个词的边界

一个词的边界

老墨
老墨

· 阅读约 5 分钟

旧稿是这样写的:

记住所有对话中提到的信息,以便后续使用。

十六个字。问题出在【所有】这两个字上——一个没有边界的词,等于没有给出任何边界。模型要么把整个对话历史当圣经供着,每一个超链接、每一个无关痛痒的寒暄都当成金科玉律存下来,然后在回答问题上变成一台没有分辨力的复读机;要么随机遗忘,反正没什么可失去的。两种结果离"按你的意图记住"都差着十万八千里。

最近正好读到马克·索马的一篇折腾记忆系统的文章,他在 dev.to 上发了一大串基准测试数据。250 个任务、五个轨道,把带记忆的版本和不带记忆的版本对着跑。结果耐人寻味:带记忆版得分 8.39,不带记忆版 8.27,差 0.12——十分制的十分之一。配对比较里带记忆版 53.8% 的胜率看着像有点优势,可审查员带着 74.4% 的第一位置偏差,位置一校正,那点优势基本就没了。只有一种情况记忆系统还守得住阵地:长同域序列任务,胜率 74%。

读到这我第一反应是"那这跟我的 prompt 有什么关系"。但再想,关系大了去了。

索马给了一个解释,说程序性记忆对提升输出质量几乎没有边际价值,因为它只是在重复模型权重里已有的知识。换句话说,大部分你以为要"记住"的东西,模型其实本来就会。这个判断翻译到我的旧稿里,就是那个【所有】——"所有对话中提到信息",其中九成是模型权重已经能处理的东西,存了等于没存,还占着上下文的位置。

V1 我把"所有"圈掉,换上"用户提供的":

记住用户提供的与项目相关的信息。

跑了几个测试,新问题出来了。这条 system prompt 挂在一个代码库维护的活儿上,模型记住了"用户叫张三"、"用户偏好 Python",但项目 API 上周刚换了认证方式这件事——因为出现在第三轮对话的角落里——漏了。用户问"新认证流程怎么接"的时候,模型自信地开始念已经被废弃的旧文档。

问题出在哪?【项目相关】这个修饰语是个漏斗,但漏斗的口朝哪完全没说。模型凭直觉猜"你说的相关是什么"——猜错的时候你根本不知道它错在哪。

V2 换个写法:

记住与默认假设不同的信息。

坏了。模型开始每轮汇报癖发作——"用户的措辞比较正式"、"用户今天用了两个感叹号",诸如此类。"与默认假设不同"这句话没有限定维度,模型只能每个方向都试一遍,输出的废话量翻了一倍,真正该记住的信息照样漏。

这里我想岔一句。索马在文章里提到,记忆系统应该按职责分——grounding、operational knowledge、episodic memory、reasoning,各管各的。我以前觉得这种分层方案是系统架构师关心的事,跟写 prompt 的人没关系。但现在想想,prompt 里给模型划边界的逻辑是一模一样的——你不告诉它存哪一类,它就全往一个筐里扔。

扯回来。

V2 的失败让我意识到,问题不在"记住"这个动词,也不在修饰语多少,在于整句话缺一个判断标准。什么该记、什么不该记,模型需要一个能对着打钩的清单,不是一个方向。

回头又翻索马那篇文章,他最后给了一句话:记忆应针对情境中的偏差。我想了很久这句话该有的样子,然后试了 V3:

记忆应针对情境中的偏差,而非通用能力。值得存储的是在模型权重之外的、无法从上下文中推断出来的事实。

这一版跑了三轮测试,效果明显好于前几版。原因在于那句"无法从上下文中推断"——它把前面所有版本的毛病一起治了:模型权重里已有的知识,不存;能从对话上下文直接推断的,不存;剩下的,才值得占那个位置。

那两个词——【情境】【偏差】——就是这条 prompt 的诗眼。【情境】划了范围,【偏差】定了标准。没有这两个词,记忆指令就是一句空话;有了它们,模型的判断才算是有了一个可以落笔的起点。

话说回来,索马那个长同域序列 74% 胜率的结果,我一直觉得没被充分解释。存储的信息确实派上了用场,但那是不是恰恰因为"长同域序列"里面能被记住的信息,恰好就属于"情境中的偏差"而不是通用知识?如果这个猜测对,那反而印证了 V3 的方向是准的——不是所有记忆都没用,是只有这种记忆才有用。

定稿先放这:

记住信息时,只存偏离该情境默认假设的内容。能从对话中直接推断或模型权重已覆盖的,省略。

相比旧稿,多了一些字,但这正是那类"应该长一点的改写"——短不是目的,贴合才是。模型能接住,就行。

老规矩,我不太敢说这是终版。【偏差】和【默认假设】这两个词——措辞上还是觉得有点模糊,模型会不会真的明白"默认假设"指的是什么,我会惦记着。先这么用着,哪天它理解岔了,再回来改一版。为了一个字纠结一下午,说出去有点丢人,但干这行不就是这么回事。