跳到主要内容

141 篇博文 含有标签「rag」

查看所有标签

分层内存压缩:你的智能体内存缺失的四个层级

· 阅读需 12 分钟
Tian Pan
Software Engineer

大多数智能体内存系统将一个四层的问题压缩成两层,然后在出现破绽时表现得大吃一惊。一个是当溢出上下文窗口时会被截断的会话缓冲区(conversation buffer),另一个是旧内容堆放其中的“长期记忆”向量数据库。那不是内存架构。那只是一个队列和一个杂物抽屉。

如果一个智能体连续三个周一向老用户询问同一个新手引导问题,这并不是因为模型不好,而是因为系统中没有一个地方能保存“该用户跨会话告知我的事情”,并且其生命周期不同于“所有用户告知我的关于产品如何运作的事情”。这是不同的记忆。它们有不同的访问模式、不同的隐私契约以及不同的遗忘规则。将它们混为一谈是架构上的错误——而且这是可以修复的。

无人测试的隐私边界:为什么“无状态”工具是 AI 时代的 IDOR

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个被标记为“无状态”的工具是运行时无法兑现的承诺。在函数签名的背后,坐落着 Redis 缓存、向量索引、嵌入存储、限流表、记忆层、热路径上的 LRU——其中任何一个都是共享的基底层,一个用户的数据可能会落在另一个用户的响应中。函数是无状态的。系统则不然。在 2026 年,这是我在 Agent 系统中看到的最常见的隐私漏洞,因为几乎没有人对此进行测试。

对于任何开发过经典 Web 应用的人来说,这种漏洞的形式都熟悉得令人沮丧。不安全的直接对象引用(IDOR)曾是 Bug 赏金猎人们十年来的家常便饭:一个请求处理程序接收记录 ID 并返回记录,却不检查调用者是否有权查看。AI 时代的版本是同样的漏洞,但影响范围更广:一个工具调用接收查询并返回数据,却不检查调用者的租户是否拥有该数据。查询是用自然语言表达的。缓存键是一个哈希值。检索是近似的。这些都不能免除你的授权责任,但其中的每一项都让漏洞在代码审查中更难被发现。

单向量版本标签:每个 Embedding 迁移背后的缺失列

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个新的嵌入模型发布了。基准测试数据提升了 4 %。一位 Staff 工程师提交了一个工单:“将 embedding 升级到 v3。”两周后,索引已完成重新嵌入,别名已切换,团队通过特性标志(feature flag)发布了变更。六周后,支持工单堆积如山。搜索结果“感觉不对劲”。复盘会召开了。没人能解释为什么出现了退化,因为没有系统崩溃,每个仪表盘显示的都是绿色。

问题不在于模型的更换。问题在于向量存储根本不知道哪些向量来自哪个模型。数据库里没有这一列。没有用于追踪哪些记录已回填的迁移表。没有 alembic_version 行,没有 schema_migrations 表,也没有先前状态的 pg_dump。团队将 embedding 升级视为一次简单的配置切换,而向量存储在模式(schema)层面缺乏能阻止他们犯错的概念。

Embedding 迁移需要数据库迁移二十年来一直依赖的相同产物:写入每个向量、在每次查询时检索、并作为切换和回滚准入准则的单条记录版本标签。这是大多数团队最容易忘记添加的一列,而后期补救的成本远高于前期添加。

Reranker 是你 RAG 评估中从未衡量的“静默”第二个模型

· 阅读需 12 分钟
Tian Pan
Software Engineer

一个典型的 RAG 流水线包含两个模型,而不是一个。检索器从向量数据库中提取 50 到 100 个候选文档,而重排序器(reranker)——无论是交叉编码器(cross-encoder)、LLM-as-judge 提示词还是混合方案——都会对这些候选文档进行重新评分,并将前 5 个结果交给回答模型。你的评估套件测量端到端的回答质量,测量检索器的 recall@k,但它并不测量重排序器。因此,当重排序器发生隐性偏移(drift)时,仪表盘上显示的“回答质量下降了 4 个点”却没有任何因果线索,团队会花费三天时间去调试一个根本不是问题的提示词。

重排序器是那个隐性的第二个模型。它介于检索器和生成器之间,拥有自己的评分分布、自己的提示词(如果是基于 LLM)或自己的权重(如果是交叉编码器),并且它可以独立于其他任何组件发生性能退化(regress)。大多数团队从未单独对它进行评分。他们编写的评估套件将流水线视为一个具有长上下文窗口的单一模型,而实际上它是两个串联的模型,且其中间接口并不属于任何一个团队。

检索膨胀:当“加个 RAG 就行”变成架构上的干扰

· 阅读需 12 分钟
Tian Pan
Software Engineer

这种模式太熟悉了,以至于被视而不见。模型幻觉出了一个事实,于是团队增加了一个检索步骤。三周后,模型从不断增加的工具库中选错了工具,于是他们在工具目录上增加了一个检索步骤。模型的回答感觉太笼统,于是他们在过去的高质量回答上增加了一个检索步骤。一个季度过去了,系统现在变成了一堆检索器拼接在一起的提示词,而本质上,最初的问题依然存在。

改变的不是失败率 —— 而是失败模式的名称。“模型出错了”变成了“检索未命中”,这听起来更易处理,但事实并非如此。评估套件的分数更高了,因为从构造上讲,检索到的上下文对于测试集来说是分布内(in-distribution)的。生产环境的情况则截然不同,但到那时,架构已经有了三个检索层,每一层都有自己的嵌入模型、索引刷新频率和值班轮换,而且没有人想成为那个提议拆除它们的工程师。

这就是检索膨胀(retrieval sprawl)。这是一种架构上的分心:一种将难题(提示词设计、模型能力、模糊的规范)转移到更舒适的问题(信息检索工程)上,而实际上没有解决任何问题的方式。

你的向量数据库也有热点 Key:为什么 ANN 索引在生产成本上“撒了谎”

· 阅读需 12 分钟
Tian Pan
Software Engineer

你团队选择的向量索引是在一个生产环境中根本不存在的工作负载上进行基准测试的。每一个公开的 ANN(近似最近邻)基准测试 —— VIBE、ann-benchmarks、数据库厂商落地页上的对比表 —— 都是从语料库中均匀采样查询的,因此每个邻居查找的成本大致相同,每个分片承受的负载也大致相等。真实的检索流量并非如此。它呈现出齐普夫分布(Zipfian):极小部分的查询(今日新闻、趋势产品、循环的支持意图、客服团队整天收到的那几百个问题)命中的一小部分嵌入,其频率比中位数高出百倍。基准测试显示 HNSW 在 50ms p99 下的召回率为 0.97。而生产环境则显示一个分片正在熔化,其余的却闲得发慌。

这种不匹配并不是调优问题。而是向量检索继承了所有其他数据库工作负载的访问倾斜特性,而该领域标准化的索引在设计时并未考虑到这种特性。你的 KV 存储免费获得的缓存层 —— 预热你最常读取的行的操作系统页面缓存(OS page cache),针对热点 Key 的 LRU —— 在 ANN 中并不存在,因为图是按图结构顺序遍历的,而不是按访问顺序。热门嵌入在内存中依然是“冷的”,因为搜索算法的遍历模式在页面缓存看来是随机的,而你的“热门”集群位于单个分片上,其 CPU 运行火热,而集群的其他部分却在闲置。

Embedding 迁移是新时代的 Schema 迁移

· 阅读需 13 分钟
Tian Pan
Software Engineer

大多数团队在生产环境中第一次更换嵌入模型(embedding model)时,都会将其视为批处理作业。重新运行嵌入器,构建新索引,切换别名,然后部署。延迟保持正常。错误率为零。每个查询都有结果。然而,检索质量会在数周内悄悄下降,而没人察觉。因为症状是“用户抱怨答案感觉不对”,而不是监控面板上的红报警报。

这不仅仅是部署问题,而是一个团队决定盲目进行的架构迁移(schema migration)。旧的嵌入空间和新的嵌入空间是不同的参考系;以前表示“这两个段落关于同一个话题”的余弦几何(cosine geometry)在数值置信度上不再具有相同的含义。以前聚集在一起的文档和查询会以非均匀的方式漂移。在旧分布上训练的重排序器(re-rankers)会开始处理那些不再符合其学习规律的样本。对逐点相关性(pointwise relevance)评分正常的评估套件会漏掉这一切,因为没有任何单个文档移动得太远,但整个图谱发生了旋转。

如果将这种更换视为数据库迁移,几乎所有出错的情况都是可以预防的。如果将其视为批处理作业,那么回归(regressions)就会按照无人负责的进度表悄然降临。

知识截止期是 UX 界面,而非脚注

· 阅读需 14 分钟
Tian Pan
Software Engineer

模型有知识截止日期。用户不知道它是什么。产品在几乎所有情况下都不会告诉用户。当用户问了一个正确答案在三个月前已经改变的问题时,助手会给出一个言之凿凿的错误答案——这并非因为模型失效了,而是因为产品从未提供一种方式来标记这种信息鸿沟。你与用户之间的信任契约是隐性的、不对称的,并且每当世界发生变化而你的 UX 假装没有变化时,这种契约就会被悄然打破。

主流模式是将截止日期视为一个注脚:一段埋藏在帮助中心里的披露文本、一个无人阅读的 /about 页面,或者在第一周就被关闭的一次性工具提示。这种定位是一个 bug。知识截止日期不像“上下文长度”那样是模型的一个属性。它是一个 UX 界面——经过工程化、设计和演进——将其视为次要因素,会导致交付的产品在用户无法审计的语调下,围绕自身的无知进行编造。

知识图谱的时效性与向量索引的时效性具有不同的 SLA

· 阅读需 12 分钟
Tian Pan
Software Engineer

向量索引即便有约 10% 的误差,也没人会惊慌。但知识图谱如果缺失了一条边,就可能导致有人向监管机构提交一份错误的答案。从数据工程组织的架构图来看,这两种故障模式如出一辙——都被归类为“索引陈旧”——并且它们共用同一个变更数据捕获(CDC)流水线,具有相同的延迟容忍度。流水线的规格是根据向量负载确定的,因为向量是更“大声”的消费者。图谱默默地继承了这些默认设置,而这种沉默本身就是 bug。

向量检索和图谱检索在数据陈旧时的失败表现截然不同。将它们视为同一种延迟问题,会导致你构建出的系统虽然在 RAG 基准测试中得分很高,但在多跳查询中却会产生隐蔽的错误——当然,这种“隐蔽错误”往往是用户最后才会察觉到的。解决方案不是更快的流水线,而是要认识到“陈旧”具有两种不同的含义,为每种边类别设计新鲜度分层,并在监管机构发现之前,通过评估机制捕捉到这种差异。

2026 年的长上下文 vs RAG:为什么它是基于功能的决策,而非架构信仰

· 阅读需 14 分钟
Tian Pan
Software Engineer

长上下文与 RAG 的经济学在两年内翻转了两次,而在那两个窗口期中选择了某种架构的团队,现在正处处支付着错误的代价。在 2024 年,趋势是将一切都塞进上下文窗口,因为窗口在不断扩大,而每 token 的价格在持续下降,因此检索流水线被斥为过时的繁琐工作。在 2025 年,共识发生了反转:关于“上下文腐烂”的研究表明,在百万级 token 的提示词中,窗口中部的有效召回率大幅下降,全窗口调用的延迟变成了用户体验问题,且账单变得非常惊人,于是检索技术重新得到了重用。到 2026 年,正确的答案不再是任何一种口号。它是一个在设计阶段做出的基于单个功能的决策,并记录下四个维度的权衡,因为为整个产品选择单一架构,是让每个功能同时出错的低成本方式。

一直困扰着团队的思维模型是将长上下文 vs RAG 视为路线图上的承诺,而不是针对每个界面的选择。你阅读了一篇有影响力的博客,选边站队,雇佣了擅长那一边的工程师,编写了一份将其规范化的平台文档,现在每个新功能无论是否合适,都采用了相同的架构。需要新鲜数据的功能忍受着陈旧的上下文。需要可扩展语料库的功能为他们永远不会使用的检索基础设施买单。需要引用来源的功能在发布时却缺失了这一项。这些都不是 bug。它们是将功能级决策视为产品级决策所带来的必然代价。

没人召集的索引策略委员会:超越一次性迁移的 RAG 语料库治理

· 阅读需 11 分钟
Tian Pan
Software Engineer

两年前,一个团队将他们的检索索引指向了 Wiki、Zendesk 导出文件以及公共文档的快照。上周,同一个索引返回了一个已弃用的运行手册(runbook),告诉 SRE 去重启一个已不存在的服务。该运行手册已经废弃了 18 个月。没人负责它的下线工作,所以没人把它删掉。Agent 自信地引用了它。模型没有错;错的是语料库(corpus)。

这是检索评估(retrieval evals)中不会出现的故障模式:语料库被视为一次性的工程决策,而实际上它是一个持续的治理问题。负责初始摄取(ingestion)的团队早已解散。本应标记出客户机密 PDF 的法律审查从未发生,因为没人告诉法务部门存在这个流水线(pipeline)。“新鲜度策略”(freshness strategy)只是一个在第三季度离职的人留下的 Slack 消息。检索索引变成了任何人抓取过的每一份文档的共享收件箱,而纳入标准已逐渐演变为“任何容易摄取的内容”。

RAG 读后写竞争:当你的向量索引引用了一个已不存在的文档

· 阅读需 11 分钟
Tian Pan
Software Engineer

一个用户在 14:32:07 向你的助手提问。你的检索器在 14:32:08 触发,从政策手册中提取了五个分块。模型思考了几秒钟,起草了回复,并在 14:32:12 流式传回了一个答案,自信地引用了第 4.3 节——而管理员在 14:32:10 刚刚删除了这一节,因为它有误。用户读到了一段来自已不存在文档的权威引用,甚至还附带了一个返回 404 的可点击链接。

你的技术栈中没有任何环节报错。检索器返回了有效的命中结果。模型生成了流利、有据可查的文字。引用的分块 ID 在检索发生时确实存在。然而,根据任何合理的定义,这个答案都是一个幻觉——并不是因为模型胡编乱造,而是因为在它观察世界与开口表达的间隙,底层数据已经发生了变化。

这就是 RAG 的“写后读竞争”(read-after-write race),而大多数生产级流水线对此毫无防备。