Appearance
11|RAG 检索:查外部知识
上一篇的压缩解决了已有材料太长的问题。但有些材料根本不在手边——Runbook、历史案例、产品文档。
这些知识没法预先全放进上下文,量太大,而且大部分每次用不上。要用的时候得去检索。本篇讲 RAG:检索外部知识,把模型参数记忆和外部知识库结合起来。
为什么不直接塞进上下文
Runbook 可能有几百篇,历史案例上千条。全塞进上下文不现实——窗口放不下,即使放下也是噪声淹没信号(上一篇讲过)。
RAG 的思路是按需检索:用户问"Redis 延迟高怎么排查",只检索和 Redis 延迟相关的 Runbook 片段,放进上下文。模型基于这几段检索到的材料回答,而不是基于自己的参数记忆。
检索的流程
标准 RAG 流程:文档处理 → 切分 → 建索引 → 查询改写 → 检索召回 → 拼装进上下文 → 生成回答 → 输出引用。
切分是关键一步。文档不能整篇入库——整篇检索不准,相似度匹配的是整篇文档,而相关内容可能只是其中一段。切成小片,每片一个语义单元,检索时匹配到片级别。
python
def chunk_document(text: str, chunk_size: int = 500, overlap: int = 50) -> list[str]:
"""按固定大小切分,带重叠避免切断语义。"""
chunks = []
start = 0
while start < len(text):
end = start + chunk_size
chunks.append(text[start:end])
start = end - overlap # 重叠,避免句子被切断
return chunks切片粒度决定检索质量。切太粗,检索到的片里混着无关内容;切太细,语义不完整。运维 Runbook 通常按"一个故障一个排查步骤"的粒度切比较合适。具体粒度拿本项目文档测。
嵌入和索引
切分后把每片文本转成向量(embedding),存进向量索引。检索时把查询也转成向量,找最相似的几片:

python
import chromadb
client = chromadb.PersistentClient(path="knowledge/indexes")
collection = client.get_or_create_collection("runbooks")
# 建索引
for i, chunk in enumerate(chunks):
collection.add(
ids=[f"chunk-{i}"],
documents=[chunk],
metadatas=[{"source": "redis-runbook.md", "section": "延迟排查"}],
)
# 检索
results = collection.query(
query_texts=["Redis 延迟高怎么排查"],
n_results=3,
)Chroma 的 PersistentClient 是进程内本地文件存储,不是需要启动的数据库服务。早期项目用它够了,不用维护额外的服务进程。
检索质量的上限在文档质量
这是 RAG 最容易被忽视的一点:检索质量的上限不在嵌入模型,而在文档质量。
如果 Runbook 标题写成"Redis 问题"而不是"Redis 延迟高",再好的嵌入模型也检索不准——查询"Redis 延迟"和标题"Redis 问题"的语义距离,未必比"Redis 延迟"和"MySQL 慢查询"近。文档结构混乱、标题不精确、内容过期,都会让检索从源头就偏了。
多数 RAG 项目的优化收益主要来自改进文档结构和分块策略,而不是反复更换嵌入模型或调参。换模型前先确认文档质量和分块策略已经没有改进空间。
Naive RAG 和 Agentic RAG
Naive RAG 是单轮检索、固定流程:查询 → 检索 → 拼装 → 回答。适合路径明确的任务:FAQ、文档问答、事实查询。日常运维告警排查,Naive RAG 通常够用。

Agentic RAG 让模型参与检索决策:是否需要检索、如何改写查询、是否拆分子问题、是否继续检索、是否打开原文。适合复杂跨文档推理、多步证据收集。但它带来更高延迟、成本和治理要求,涉及跨服务根因分析时才更有价值。
不要一上来就用 Agentic RAG。先 Naive RAG 跑通,发现单轮检索覆盖不了的场景,再考虑让模型参与检索决策。
回答要受证据约束
检索结果拼进上下文后,模型回答必须受约束。这是 RAG 最容易出问题的地方。
RAG 不一定比纯生成少幻觉。模型看到检索到的材料后,容易过度自信地认为"这些就是全部真相",把检索到的建议当成已确认事实。没有证据约束的 RAG 回答,等于有参考资料支撑的高级编造。
约束包括几条:区分已确认事实和推断、引用片段必须可追溯、工具结果与检索结果按优先级排。
报告里写 [1] 不够,应该链接到原始文档片段或保存的检索记录。值班人员点编号就能查看"模型是根据哪段材料得出这个结论的"。没有回溯能力的证据约束只是形式合规。
检索结果里如果混入了过时文档(Runbook 变了、旧文档没删),模型会基于过时信息回答。索引更新机制不能漏——Runbook 变了、新增服务了、旧文档过期了,索引里不能还是老内容。事件驱动(文档变更触发重建)或定时轮询(每小时扫描文件修改时间)两种策略,按项目复杂度选。
第二阶段收尾
到这里,上下文设计四篇完成:会话状态管住历史、组装管线决定放什么、压缩策略处理太长、RAG 检索外部知识。agent 现在能跨多轮工作,模型面前放的是经过选择的材料。
但还有一件事没讲:拿到材料后,agent 怎么一步步做。查一步、看结果、决定下一步——这个循环本身怎么设计才能安全收敛、出错怎么办、什么时候停。这是下一阶段循环与控制设计要解决的核心问题。