Claude 文档问答如何选长上下文与检索
文章比较全文输入、提示缓存和检索三种文档接入方式,并用 Python 示例说明如何统计令牌、粗估成本。示例未经测试,模型参数与价格需要对照当前文档核实。
AI 深度解读
Claude 能一次读下很多文档,还需要检索吗?作者的答案是:先数 token,再看文档是否稳定、请求能否复用,以及权限怎么划分。资料规模可控时,全文放进提示词并配合缓存,可能比搭建检索系统更省事;语料庞大、频繁变化或需要按用户隔离时,检索更合适。
这篇最有用的地方,是把选择落到了请求结构、计费和排查指标上。但它没有给出实测对照,代码也只是依据 Anthropic 文档写出的草稿,不能当成已经验证的生产方案。
先数 token,再决定要不要检索
能放进上下文窗口,只说明请求装得下,不代表模型一定能找准答案。
作者建议先用 SDK 计算请求的输入 token,不必真正运行生成。下面这段代码解决的是容量判断:把系统提示、文档和问题一起纳入统计。
import anthropic
client = anthropic.Anthropic()
count = client.messages.count_tokens(
model="claude-sonnet-5-5",
system="You answer questions about our policy handbook.",
messages=[{"role": "user", "content": handbook_text + "\n\nQuestion: ..."}],
)
print(count.input_tokens)
要看的是最后一行的 count.input_tokens。原文使用了 claude-sonnet-5-5,但没有验证这个模型标识及参数是否可用,复制前需要核对。
如果材料只占窗口的一小部分,可以考虑直接提供全文;如果已经接近上限,或整个语料库远大于窗口,就应转向检索。原文没有给出窗口容量,也没有定义「一小部分」的比例,不能据此画出统一阈值。
对员工手册、产品规格、合同模板这类稳定资料,作者提出全文加缓存:每次仍然提交文档,但让后续请求复用已经处理过的前缀。
下面展示缓存标记和用量检查的位置:
response = client.messages.create(
model="claude-sonnet-5-5",
max_tokens=500,
system=[
{"type": "text", "text": "You answer questions about our policy handbook."},
{
"type": "text",
"text": handbook_text,
"cache_control": {"type": "ephemeral"},
},
],
messages=[{"role": "user", "content": question}],
)
usage = response.usage
print(usage.cache_creation_input_tokens, usage.cache_read_input_tokens)
重点是 cache_control 所在的文档块,以及最后一行的两个字段。第二次及后续调用中,cache_read_input_tokens 应当体现复用;如果一直为零,就要检查前缀是否变化。
缓存能否省钱,取决于稳定前缀能否被持续复用。
因此,固定内容放前面,变化的问题放后面。时间戳或用户名如果出现在靠前的位置,也可能破坏匹配。缓存还有最小前缀长度和过期限制,原文没有提供具体数值,只提示按模型查阅文档。
检索缩小了输入,也增加了一道质量关
语料很大、更新频繁,或者不同用户只能访问不同文档时,作者更倾向于分块检索:先把资料切成片段,再挑出与问题相关的部分交给 Claude。
切块示例按空行拆分段落,默认 max_chars=3000、overlap=300。重叠部分用来保留相邻片段的衔接,减少一句话或一段论述被切断后丢失线索的情况。
这里的限制按字符计算,不是 token。另外,代码不会继续拆分单个超长段落,所以 3000 也不是严格的块长上限。这是从示例实现可以直接看出的边界。
检索部分用 embed 表示另行选择的嵌入模型,以余弦相似度排序,默认 k=5,再把五个片段拼进请求。生成仍使用 client.messages.create,设置 max_tokens=500,并要求只根据提供的摘录回答,资料不足就明确说明。
这套流程容易理解,但错误可能发生在模型回答之前:正确片段没进入前五名,或者答案需要跨多个章节,而检索只找回了其中一段。提示词再严格,也无法补回缺失的证据。
权限也要落实在检索流程里。作者把按用户授权列为采用检索的理由,但示例没有展示权限过滤,不能直接认为这段代码已经完成了隔离。
费用同样要算完整。原文的 monthly_input_cost 接收 doc_tokens、calls_per_month、price_per_mtok,以及 cache_write_mult、cache_read_mult。不缓存时,文档输入费用按「文档 token × 月调用次数 ÷ 1_000_000 × 每百万 token 单价」计算;缓存版本则简化为一次写入,其余全部读取。
这个假设适合做初步估算,却可能低估低频应用的费用:两次请求之间缓存已经过期,就要重新写入。检索路径虽然每次只发送几千 token,还要承担嵌入、向量存储和索引维护成本。
我的判断
作者说得对的一点,是没有把大窗口和检索当成非此即彼的技术立场。稳定的小型资料集,可以先从全文输入验证需求;大型或持续变化的知识库,则需要考虑检索带来的输入收缩和资料管理能力。
但「哪种更便宜、哪种答得更准」,这篇没有给对照数据。它没有展示真实请求量、缓存命中率、延迟,也没有提供答案质量评分,因此更适合作为方案筛选清单,而不是性能结论。
相比直接搭建向量库,全文加缓存减少了切块、索引和召回调优的工作;相比全文输入,检索更依赖片段选择是否准确。两边都有质量风险:长输入可能漏掉深处的细节,检索可能漏掉关键片段。
评估时要检查答案是否正确,也要检查所需证据是否进入了请求。
原文还明确说代码未经测试。模型名、参数、价格都需要核对,文中的费用函数也只估算文档输入部分,不能拿它代表整条问答链路的总账。
能拿来做什么
- 为项目文档问答做原型时,先统计真实请求的 token,再决定全文输入还是分块检索,避免只凭文件页数选方案。
- 在 Node 服务接入文档缓存时,把固定说明和文档放在前部,把问题等动态内容放在后部,并记录缓存写入、读取 token,确认请求确实发生了复用。
- 如果采用检索,准备一小组有标准答案的真实问题,特意覆盖跨章节回答和资料缺失的情况,同时检查召回片段与最终答案。
- 每次调用记录输入 token、缓存读写 token、输出 token、延迟,以及包含的文档或片段;费用或质量变化时,用这些记录定位原因。