什么是 RAG?从“开卷考试”理解企业知识库问答系统
RAG 的全称是 Retrieval-Augmented Generation,也就是“检索增强生成”。 它不是单纯搜索,也不是把资料重新训练进模型,而是在大模型回答之前,先去外部知识库检索相关内容,再让模型基于这些证据生成答案。 RAG
RAG 的全称是 Retrieval-Augmented Generation,也就是“检索增强生成”。 它不是单纯搜索,也不是把资料重新训练进模型,而是在大模型回答之前,先去外部知识库检索相关内容,再让模型基于这些证据生成答案。
RAG 核心:模型负责理解和生成,知识库负责提供事实依据。
一、RAG 到底解决什么问题?
大模型很强,但它有一个天然限制:训练完成后,模型参数里的知识基本就固定了。你问它训练数据之后发生的新政策、新价格、新产品,它可能不知道;你问它公司内部制度、合同条款、客服 SOP,它更不可能凭空知道。
这时有两条路:一条是微调,把知识写进模型参数;另一条是 RAG,把知识放在外部知识库,回答时临时检索出来。企业落地更常见的是第二条,因为知识更新更快,成本更低,也更容易追溯来源。
RAG 主要解决知识冻结、私有数据缺失、答案不可追溯三类问题。
二、RAG 不是搜索,也不是微调
很多人第一次听 RAG,会把它理解成“给大模型接一个搜索 API”。这个说法只说对了一半。RAG 里确实有检索,但检索只是前半段;真正的价值在后半段:把检索到的证据组织进 Prompt,让 LLM 生成一段可读、可解释、可追溯的答案。
RAG 也不是微调。微调改变模型参数,适合让模型学会风格、格式和某类任务模式;RAG 不改变模型参数,适合动态知识、企业私有数据和需要来源引用的问答。
三、一个完整 RAG 系统分两段:离线建库,在线问答
RAG 最容易讲乱的地方,就是把离线和在线混在一起。你可以记住这句话:离线阶段负责“把知识准备好”,在线阶段负责“用户问的时候把正确知识找出来”。
1. 离线阶段:文档加载 → 切块 → 向量化 → 入库
离线阶段的目标是把原始资料变成可检索的知识库。数据源可以是 PDF、Word、Markdown、网页、数据库记录、客服工单,也可以是接口返回的结构化数据。
这一步最关键的是切块和元数据。切块决定能不能召回正确上下文;元数据决定能不能过滤权限、追踪来源、做版本更新。只把文本丢进向量库,而不保留 source、page、title、tenant_id、doc_version,后面一定会踩坑。
Chunking 需要在语义完整和检索精度之间找平衡。
2. Embedding:让文本可以被“相似度计算”
Embedding 可以理解成“把文本的意思转换成一组数字坐标”。比如“苹果手机怎么截图”和“iPhone 如何截屏”,关键词不完全一样,但语义很近,所以它们在向量空间里距离也应该很近。
向量检索不是比较字面关键词,而是比较向量距离。它的好处是能找到同义表达,坏处是它也可能召回一些“看起来语义接近但实际答不上问题”的片段,所以后面通常还需要 Rerank。
Embedding 把语义变成坐标,语义越近,向量距离越近。
向量库不仅存 embedding,还要存原文、元数据、权限和版本。
3. 在线阶段:Query 改写 → 检索 → 精排 → 生成
在线阶段发生在用户每次提问时。用户的问题通常不适合直接检索,比如“上次那个方案怎么样?”、“它怎么报销?”这类问题需要结合对话历史补全上下文。因此工程里常见第一步是 Query Rewrite,把口语化问题改写成更明确的检索问题。
RAG 在线阶段负责实时检索并生成答案。
粗排 Retrieval 负责快速召回候选 chunk,通常取 Top-20 或 Top-50;精排 Rerank 负责把真正相关的 chunk 提到前面,最终留下 Top-3 到 Top-5 拼进 Prompt。这个组合比只做向量检索更稳。
粗排负责快,精排负责准。
最终 Prompt 要包含任务约束、用户问题、检索证据和输出规则。
四、最小可用 RAG 代码长什么样?
下面这段代码不是框架魔法,而是 RAG 最核心的执行链路。实际项目里可以把每一步替换成 LangChain、LlamaIndex、OpenAI Vector Store、Milvus、ES、Cross-Encoder 或自研服务。
from typing import List, Dict
class Chunk:
def __init__(self, text: str, score: float, metadata: Dict):
self.text = text
self.score = score
self.metadata = metadata
def rewrite_query(question: str, history: List[str]) -> str:
"""把口语化问题改写成适合检索的问题。"""
if "上次" in question or "那个" in question:
return f"结合对话历史,明确用户问题:{question}"
return question
def retrieve(query: str, top_k: int = 20) -> List[Chunk]:
"""向量检索:从知识库召回候选 chunk。"""
# 实际工程中这里会调用 vector_store.similarity_search(query, top_k)
return []
def rerank(query: str, chunks: List[Chunk], top_n: int = 5) -> List[Chunk]:
"""精排:重新计算 query 与 chunk 的真实相关性。"""
return sorted(chunks, key=lambda c: c.score, reverse=True)[:top_n]
def build_prompt(question: str, chunks: List[Chunk]) -> str:
context = "\n\n".join(
f"[来源: {c.metadata.get('source')}]\n{c.text}"
for c in chunks
)
return f"""
你是企业知识库问答助手。
要求:
1. 只根据【资料】回答;
2. 资料里没有就说“不知道”;
3. 回答末尾列出来源。
【问题】
{question}
【资料】
{context}
"""
def rag_answer(question: str, history: List[str]) -> Dict:
query = rewrite_query(question, history)
candidates = retrieve(query, top_k=20)
selected = rerank(query, candidates, top_n=5)
prompt = build_prompt(question, selected)
answer = call_llm(prompt) # 替换成你的 LLM 调用
return {
"answer": answer,
"sources": [c.metadata for c in selected]
}五、生产级 RAG 不是 Demo:还要考虑权限、更新、评测和监控
Demo 版 RAG 往往只做“上传文件、切块、向量检索、生成答案”。但一到企业环境,就会出现很多真实问题:不同部门能不能互相看到资料?文档更新后旧索引怎么办?回答错了怎么定位是召回错、精排错还是模型生成错?这些才是 RAG 落地最难的部分。
生产级 RAG 需要把离线、在线、权限、评测和日志串成闭环。
一套稳定的架构通常会同时使用向量检索和关键词检索。向量检索擅长同义表达,关键词检索擅长编号、术语、代码、产品型号。两路召回后用 RRF 或业务规则融合,再进入 Rerank。
RAG 常见问题通常出在检索链路,而不一定是模型不行。
RAG 评测要拆开看:检索准不准、生成忠不忠、系统稳不稳。
六、面试怎么回答,才不像背概念?
面试里回答 RAG,不能只说“先检索再生成”。更好的回答方式是先讲它解决什么问题,再按离线和在线两个阶段把流程讲完整,最后补上工程价值:热更新、可溯源、可控权限。
可以这样总结:RAG 是一种把外部知识接入 LLM 的工程范式。离线阶段先把知识加载、清洗、切块、向量化并入库;在线阶段用户提问时,先改写 Query,再检索、精排、组装 Prompt,最后让 LLM 基于证据回答。它最大的价值是解决知识冻结、私有数据和可溯源问题。
要点速读
RAG 的全称是 Retrieval-Augmented Generation,也就是“检索增强生成”。 它不是单纯搜索
- RAG 的全称是 Retrieval-Augmented Generation,也就是“检索增强生成”
- 它不是单纯搜索
- 更多细节仍在持续更新中