# RAG 基础架构与手动模拟

# 原始图示

RAG 应用架构

这张图展示了用户问题、Retriever、向量数据库、Prompt、LLM、格式化输出和对话历史之间的关系。

# RAG 是什么

RAG 是 Retrieval-Augmented Generation,也就是检索增强生成。它的核心做法是在大语言模型生成答案之前,先从外部知识库中检索和问题相关的内容,再把这些内容与用户问题一起组织成 Prompt,交给模型生成回答。

可以把普通 LLM 看成一个记忆力很强但知识有边界的人。它知道大量通用知识,但不知道企业内部文档、最新政策、实时库存、私有接口说明和刚更新的产品规则。RAG 的作用就是在回答前把这些外部知识递给模型,让模型基于资料回答。

RAG 不需要重新训练模型,因此比微调更轻量。它尤其适合知识更新频繁、资料来源明确、需要引用出处、希望快速接入私有知识的场景。

# 基础链路

一个最小 RAG 链路通常包含五个步骤:用户问题、检索、上下文拼接、Prompt 构造、模型生成。

用户问题是入口。它可能是自然语言问题,也可能经过改写、补全、分类或路由。检索器根据问题去向量库、全文索引或业务数据库中找候选资料。检索结果经过排序、过滤、压缩后形成上下文。Prompt 把系统指令、上下文、用户问题组合起来。最后 LLM 只根据这些上下文组织答案。

手动模拟 RAG 的价值在于看清每一步的职责:Retriever 只负责找证据,不负责生成;Prompt 负责约束模型,不负责存储;LLM 负责表达和归纳,不应该凭空补事实。

# 手动模拟

假设用户问:“我们公司有哪些产品?”系统可以先在产品文档中检索相关片段,例如产品名称、介绍、价格、配送方式、推荐搭配等。然后把这些片段填入 Prompt:要求模型只根据给定资料回答,如果资料中没有就说明未知。

这个过程和真正的 RAG 应用没有本质区别,只是生产环境会把手工查资料换成 Retriever,把手工粘贴上下文换成自动拼接,把单次对话换成可观测、可评估、可复用的链路。

手动模拟时要重点观察:检索片段是否真的覆盖问题、上下文是否有噪声、Prompt 是否明确禁止编造、模型是否引用了资料中的字段。如果这几件事手动都跑不通,自动化后效果也不会好。

# 源码示例

question = "我们公司有哪些产品?"

# 1. 检索候选资料
docs = retriever.invoke(question)

# 2. 拼接上下文
context = "\n\n".join(
    f"来源:{doc.metadata.get('source')}\n{doc.page_content}"
    for doc in docs
)

# 3. 构造 Prompt
prompt = f"""
请只根据上下文回答问题。
如果上下文没有答案,请说“资料中没有相关信息”。

上下文:
{context}

问题:{question}
"""

# 4. 生成答案
answer = llm.invoke(prompt)
print(answer)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25

# 拓展

可以从三个方向继续扩展:第一,把当前组件放回完整 RAG 链路中观察,不只看单点能力;第二,为每次参数变化建立固定问题集,避免凭感觉判断效果;第三,把来源、版本、权限、时间和评估结果写入 metadata 或日志,让线上问题能追溯。

这类基础能力越早规范,后面接入多查询、重排、混合检索、父文档检索和权限隔离时越省力。

# 最新版 LangChain 用法提示

最新版 LangChain 中,推荐使用 retriever.invoke(question) 获取文档,并通过 ChatPromptTemplate、模型和输出解析器组合链路。旧式 RetrievalQA 仍能在部分兼容包中看到,但新项目更建议使用 LCEL/Runnable,把检索、拼接、生成拆开,便于调试和观测。

# 常见问题

# 这类组件应该先在什么规模上验证?

先用一小批真实文档和真实问题验证,而不是直接全量入库。验证时至少观察三类结果:检索是否命中正确片段、返回片段是否包含足够上下文、最终回答是否严格基于证据。

# 只看相似度分数够不够?

不够。相似度分数只能说明向量距离,不代表答案一定正确。还要看 chunk 是否完整、metadata 是否正确、过滤条件是否过严或过松,以及 Prompt 是否要求模型只根据上下文回答。

# 参数应该怎么调?

先固定评测问题集,再一次只改一个变量,例如 chunk 大小、overlap、top_k、Embedding 模型或过滤条件。每次改动都记录召回命中率、答案准确率、延迟和成本。

# 面试题

# 1. RAG 链路里最容易被忽略的工程点是什么?

最容易被忽略的是数据处理和评估。很多问题不是 LLM 不会答,而是文档解析错误、chunk 切坏、metadata 丢失、向量版本混乱、检索过滤条件错误,导致模型拿不到正确证据。

# 2. 为什么不能只依赖 LLM 自身参数记忆?

参数记忆存在知识过期、领域知识缺失、事实边界不清和不可追踪的问题。RAG 通过检索外部证据,把回答范围约束在可追踪内容上,适合企业知识库、产品文档和实时资料问答。

# 3. 如何判断一个 RAG 系统的问题出在检索还是生成?

先直接查看检索返回的文档。如果返回内容已经不相关,问题在加载、切分、Embedding、向量库或 Retriever;如果返回内容正确但回答错误,问题多半在 Prompt、上下文压缩、引用组织或模型输出约束。

# 生产问题排查

问题 常见原因 处理方式
回答看起来合理但没有依据 Prompt 没要求基于上下文,或检索片段不相关 增加拒答策略、引用来源、检索结果抽样检查
召回结果经常跑偏 chunk 过短、Embedding 模型不适合、query 表达不完整 调整切分策略,评估 Embedding,必要时做查询改写
命中了旧内容 文档更新后没有重新入库,或缓存没有区分版本 使用文档 hash、版本号、更新时间和重建任务
多租户数据串库 metadata filter 缺失或字段设计不严谨 强制 tenant_id / dataset_id 过滤,并做权限测试
成本增长明显 重复 Embedding、top_k 过大、无缓存 增加缓存、批处理、限流和调用统计
延迟不稳定 向量库网络抖动、LLM 响应慢、检索链路串行 并行化可并行步骤,增加超时、重试和降级策略