3.5 长文档与RAG场景的缓存编排


文档摘要

3.5 长文档与RAG场景的缓存编排 本节摘要:当上下文从几千字膨胀到几万字的文档、或一堆检索片段,前缀缓存的摆法直接决定命中率。两种主流编排——"文档做前缀"适合单文档深度多轮,"检索片段拼接"灵活但难命中前缀缓存。本节把断点设计、RadixAttention 的文档树思路、以及语义缓存的分工一并讲清,给长上下文业务一张可落地的编排地图。 大上下文怎么摆:前缀与片段的取舍 3.4 节处理的是"系统提示词 + 短输入",前缀稳定、易变内容少,改造简单。真正考验编排的是长上下文:一份两万 token 的产品白皮书,或一次 RAG 检索回来的十几段碎片。这些东西体积大、又各有各的变动规律,前缀缓存怎么摆,命中率能差出数量级。

3.5 长文档与RAG场景的缓存编排

本节摘要:当上下文从几千字膨胀到几万字的文档、或一堆检索片段,前缀缓存的摆法直接决定命中率。两种主流编排——"文档做前缀"适合单文档深度多轮,"检索片段拼接"灵活但难命中前缀缓存。本节把断点设计、RadixAttention 的文档树思路、以及语义缓存的分工一并讲清,给长上下文业务一张可落地的编排地图。

大上下文怎么摆:前缀与片段的取舍

3.4 节处理的是"系统提示词 + 短输入",前缀稳定、易变内容少,改造简单。真正考验编排的是长上下文:一份两万 token 的产品白皮书,或一次 RAG 检索回来的十几段碎片。这些东西体积大、又各有各的变动规律,前缀缓存怎么摆,命中率能差出数量级。

这里先给一个总判断:前缀缓存只在"同一段内容被多个请求重复用作前缀"时划算。所以编排的本质问题只有一句——怎么让尽可能多的请求共享同一段稳定前缀。围绕它,长出两种相反的路子。

编排一:文档做前缀,问题轮换

最直白的摆法:把整份文档作为固定前缀,每个问题作为尾部变量。一份文档被十个不同问题问,十个请求的前缀完全相同,第一个付写价,后九个全走读价折扣。这对"深度阅读单文档"的场景几乎是白送的优化——文档越长,省得越狠。

它的代价是死板:所有请求必须围绕同一份文档。一旦换文档,前缀变了,旧缓存失效,要重写。但凡你的业务是"用户上传一份合同/论文/手册,然后连续追问",这条路就是首选。

下面是一段可运行的编排示例,文档作为前缀、问题轮换,靠前缀复用吃折扣:

# 长文档问答:文档做前缀,问题轮换(命中前缀缓存) DOCUMENT = "(一份约两万 token 的产品白皮书正文,完全稳定,不随问题变化)" SYSTEM = "你是基于文档作答的助手,只引用给定内容。" QUESTIONS = [ "这份白皮书第三章讲的是什么?", "它提到的部署步骤有哪几步?", "和上一版相比有哪些主要变更?", ] def ask(document, question): # 同一 document 作为前缀,三次请求共享命中;仅 question 部分每次全价 messages = [ {"role": "system", "content": SYSTEM}, {"role": "user", "content": f"文档:\n{document}\n\n问题:{question}"}, ] return call_model(messages) for q in QUESTIONS: answer = ask(DOCUMENT, q) print(answer)

在 Anthropic 上,可对 文档 段末尾打一个 cache_control 断点,把整份文档锁成一段独立缓存;在 OpenAI 上,只要文档跨过 1024 token 对齐且稳定,自动命中。无论哪家,后九个请求都只为新问题付钱。

编排二:检索片段拼接,按需取用

RAG 场景通常不是"一份固定文档",而是"每次根据用户问题,从知识库检索几段相关碎片,拼进提示词"。这种摆法的灵活性强——每个问题拿到最相关的片段,但代价是毁灭性的:不同问题检索到的片段组合几乎不同,前缀从检索结果起就变了,前缀缓存命中率极低,大部分请求都在付写价重算。

所以检索片段拼接和前缀缓存是天然犯冲的。它适合"问题发散、跨多文档、命中率本就不指望高"的检索问答;若硬要在这里靠前缀缓存省钱,往往得不偿失。正确的降本方向在第 4 章——用语义缓存复用"措辞不同但意思相同"的查询,而不是指望字节级的前缀命中。

多文档轮转的断点设计

业务若要在多份文档间轮转(如客服按用户问题路由到不同手册),断点设计要分层:把"公共系统设定"作为第一段稳定前缀,打一个断点;每份文档作为各自独立的第二段前缀,分别打断点。这样公共设定长期命中,文档段按路由命中对应那份。切忌把所有文档一股脑拼进一个前缀——那样任何一份变动都让整段失效。

更聪明的做法是借鉴第 2 章 RadixAttention 的"文档树"思路:把多份文档里共享的章节(如公共术语表、公司简介)抽成树的公共祖先前缀,各文档独有部分作为分支。请求某文档时,公共祖先那段被所有文档共享命中,独有分支只那份命中。这和 RadixAttention 在显存里按公共前缀复用 KV 是同一个思想,只是从单机显存放到了平台缓存——前缀越长越公共,回收的算力越多。

编排策略对比

把两种编排和它们的变体放一张表,选型一目了然:

维度 文档做前缀 检索片段拼接 文档树(共享祖先)
前缀命中率 高(单文档多问共享) 低(每问片段不同) 中高(公共段共享)
适用场景 单文档深度多轮 跨文档发散问答 多文档有公共内容
成本特征 文档段走读价折扣 片段常重写付写价 公共段折扣、分支段按文档
灵活性 弱(文档固定) 强(按需取片段) 中(需预先组织树)
与第4章语义缓存 不冲突,可叠加 主要依赖语义缓存 不冲突,可叠加

语义缓存与前缀缓存的分工

RAG 场景真正要的降本,是"语义层复用":用户问"怎么退款"和"退钱流程是什么",字面不同但语义相同,前缀缓存不认,语义缓存(第 4 章)认。两者分工清晰——前缀缓存管"字节级相同的前缀",语义缓存管"意思相同但措辞不同"的查询。在 RAG 里我倾向这样叠:稳定知识库或公共语料作为前缀缓存的固定段,省去每次重算大块背景;查询侧用语义缓存挡住重复语义的问题,省去重复的生成。前缀缓存负责"大块背景不重算",语义缓存负责"相似问题不重答",各管一段,合起来才是 RAG 的完整降本。

长上下文编排的落地建议

回到复用经济学:长上下文业务里,每一万 token 的文档都是一笔预付算力,前缀缓存是把这笔预付摊薄到多次请求的唯一杠杆。我的建议是默认走"文档做前缀",除非你的查询天然发散到不同文档且无法组织成树;那种情况别和前缀缓存较劲,直接把语义缓存(第 4 章)和可截断检索(第 7 章)请上场。编排不是选最先进技术,是选让"稳定内容被复用最多"的那一种。

关键直觉:长上下文省钱的秘诀,是让一份大文档被尽可能多的请求当作同一段前缀。文档轮转靠分层断点,发散查询靠语义缓存,别用错杠杆。


作者与出处
原作者: 灏天文库
整理: 灏天文库整理
本站整理收录,版权归原作者/开源协议所有;欢迎通过原文链接访问源仓库。
发布者: 作者: 灏天文库 转发
评论区 (0)
U