凌晨三点,我已连续三个晚上在调试一个AI代理。我手里端着一杯茶,在厨房里默默地盯着代码差异(diff),心里暗骂。这个代理竟然自信满满地重写了认证(auth)功能——而它所依据的代码块,竟然来自两个月前已从仓库中删除的一个分支。
那个代码块静静地躺在Qdrant里。它与我的查询的余弦相似度极高,在检索结果中位列第一。AI代理“诚实”地抓取了它,“诚实”地将其拼接进提示词(prompt),并“诚实”地生成了“正确”的补丁。然而,这个补丁是基于一个已经不存在的现实。
我合上笔记本电脑,陷入沉思:好吧,我拥有RAG(检索增强生成),我拥有向量,我拥有“长期记忆”——这是过去两年里所有AI会议宣传材料都在承诺的一切。那么,为什么我的代理会基于一段早已不存在的代码提出修复方案呢?
因为我的代理根本没有记忆。我的代理只有用余弦相似度代替BM25算法的搜索结果。这两句话之间,恰恰是“可在生产环境中信任的AI”与“你必须逐行看护的AI”之间的全部区别。
本文将深入探讨这种差异。并且,也将审视为什么我们这些工程师,要为未能清楚认识到这一点而承担责任。
“记忆”一词的贬值
坦率地说,在当前的AI代理系统中,所谓的“记忆”通常是怎样的呢?其流程通常是:原始文本被分割成512-1024个token的块;接着进行嵌入(embedding),使用BGE、text-embedding-3或OpenAI等模型;然后存储到向量数据库(如Qdrant、pgvector、Chroma、Pinecone);通过余弦相似度进行Top-k检索;最后将检索到的块拼接成提示词(prompt)。
这不是记忆,这是搜索。它本质上是2003年的老式Lucene系统,只不过被重新涂上了神经网络的色彩:用余弦相似度取代了TF-IDF,用嵌入向量取代了倒排索引。但其核心机制是相同的。
如果我们只称之为“向量搜索”或“语义检索”,我没有任何异议。把Lucene称为Lucene,这没问题。但当它被冠以“我的AI拥有长期记忆”的旗号进行推广时——抱歉,我的AI同时拥有似曾相识感(déjà vu)和失忆症(amnesia)。
这并非单纯的术语抱怨,而是关乎预期管理的问题。当一位工程师听到“记忆”时,他们会想象一个能够记住“谁在何时、何种情境下说了什么,以及当时和现在的事实差异”的系统。然而,当工程师得到RAG时,他们得到的只是一个Ctrl+F(搜索功能)。并且,他们没有围绕这个Ctrl+F构建一个诚实且带有明确限制的架构,反而建造了一个沙堡,然后困惑于为什么代理会混淆过去与现在。
你可以开着卡车穿过的三大漏洞
这里有三个具体的失败案例,每一个都是我在生产环境中捕获到的,而非理论探讨。
漏洞一:代码块(Chunk)并不知道它自己是一个代码块。
以设计文档中一段完全正常的声明为例:“我们转向JWT(JSON Web Token),因为不透明会话无法满足我们的流量需求。另一种选择是使用Redis集群的有状态会话,但由于客户的审计要求而被排除——他们不允许会话...”