纯 LLM
只依赖模型训练时记住的内容
- 知识停留在训练完成时
- 不知道组织内部的私有资料
- 没有答案时仍可能自行编造
- 回答通常不能追溯来源
先查资料,再让模型回答。大模型不知道你的私有文档,也不知道训练截止后的新信息。RAG 在回答前增加一次检索:先找到相关材料,再让模型依据材料作答。
RAG(Retrieval-Augmented Generation,检索增强生成)由“检索”和“生成”两个动作组成。
用户提出问题后,系统先从资料库中找出最相关的片段,再把这些片段连同问题一起交给模型组织答案。
只依赖模型训练时记住的内容
回答前先查找相关材料
当模型需要使用训练数据之外的知识时,才需要考虑 RAG。
下面三类需求最常见。如果资料很少、长期不变,直接放进提示词通常更简单。
产品规则、业务政策和操作手册持续更新。修改资料库后,新内容就能参与回答,不需要重新训练模型。
合同、客服工单和内部文档属于组织自己的数据。模型原本不知道,但可以在回答时即时查阅。
回答需要能够回到原始材料,方便人工核对。检索结果可以和答案一起返回,说明信息来自哪里。
它们解决的问题不同,也可以组合使用。先根据资料规模和变化频率选择最简单的方案。
| 方案 | 更适合 | 不适合 |
|---|---|---|
| RAG | 大量、经常变化、需要追溯来源的知识 | 直接改变模型的语气和输出习惯 |
| 微调 | 固定格式、表达风格和特定技能 | 频繁变化的事实知识 |
| 长上下文 | 资料较少、固定,并且一次能够完整放入 | 大型资料库和需要精确召回的场景 |
RAG 由两条独立流程组成:资料新增或更新时进行离线建库;用户提问时进行在线检索与生成。
资料变化时重新运行
每次提问都会运行
两条流程必须使用同一个嵌入模型。否则文档与问题不在同一个向量空间中,无法正确比较相似度。
完整的 RAG 通常包含切块、嵌入、检索、重排和生成。先从文档进入系统后的第一步开始。
把长文档切成可检索的小段
整篇文档的颗粒度太粗,也可能无法完整放进提示词。切成几百字一段后,每一块尽量表达一个完整意思;相邻块保留少量重叠,避免语义在边界处断开。
def chunk(text, size=500, overlap=50):
chunks, i = [], 0
while i < len(text):
chunks.append(text[i:i + size])
i += size - overlap
return chunks
把文字映射到可比较的向量空间
嵌入模型把文字转换成高维向量。含义相近的文字会落在更接近的位置,语义检索也就可以转化为向量之间的距离计算。
关键:文档和问题必须使用同一个嵌入模型。
from sentence_transformers import SentenceTransformer
model = SentenceTransformer(
"all-MiniLM-L6-v2"
)
vecs = model.encode(
chunks,
normalize_embeddings=True
)
按相似度取回最相关的片段
问题先使用同一个模型生成向量,再与资料库中的向量比较。按照相似度排序后,取回前 K 个片段交给后续环节。
注意:取回太少容易遗漏,取回太多则会引入噪声。
def search(query, k=3):
q = model.encode(
[query],
normalize_embeddings=True
)[0]
scores = vecs @ q
top = scores.argsort()[-k:][::-1]
return [chunks[i] for i in top]
对候选片段进行更精细的排序
向量检索速度快,但排序相对粗略。可以先召回一批候选片段,再让重排模型同时阅读问题和片段,选出其中最相关的几条。
常见做法:先召回 20~50 条,再精排出 3~5 条。
candidates = search(query, k=30)
pairs = [(query, text) for text in candidates]
scores = reranker.predict(pairs)
ranked = sorted(
zip(scores, candidates),
reverse=True
)
context = [text for _, text in ranked[:3]]
把资料和问题一起交给模型
最后把检索到的片段和用户问题组合成提示词。应当明确要求模型只依据材料回答,并在材料没有答案时直接说明不知道。
需要可信度时:答案应当同时返回所依据的原始片段或来源。
context = "\n\n".join(retrieve(question))
prompt = f"""
只根据资料回答;资料中没有就说不知道。
资料:{context}
问题:{question}
"""
检索质量不是一个单独的配置项,而是检索结果的整体表现。下面三个因素会共同影响它,评测集则负责验证调整是否有效。
影响它的三个因素
决定检索的基本单位,以及每个片段能否保持完整语义。
决定遗漏与噪声之间的平衡,过少会漏,过多会混入无关内容。
决定如何寻找候选片段,包括向量检索、关键词检索或两者结合。
准备一组“问题及其应命中的片段”,检查正确片段是否被找回、排名是否靠前,再据此调整上面的三个因素。
RAG 能让模型使用外部资料,但不会自动解决所有知识与生成问题。先明确它的能力边界,再考虑如何落地。
不是。关键是先找出最相关的少量片段,再把这些片段放进上下文。资料较多时,不可能也不应该全部塞入。
仍然可能。检索结果错误、资料本身过时,或者模型没有遵守材料约束,都会导致错误答案。
不是。片段过多会引入噪声、增加费用并分散模型注意力。取回数量与答案质量并不等价。
两者解决的问题不同,可以配合使用。微调主要改变表达方式和行为习惯,RAG 负责提供外部事实。
不可以。它们必须位于同一个向量空间中,向量距离才有比较意义。
只实现切块、嵌入、检索和生成。先确认完整流程能够工作,不急着加入重排、混合检索等额外组件。
为每个问题标记应该命中的资料片段,用它检查系统是否找到了正确内容,而不是只看最终回答是否流畅。
依次调整块大小、取回数量和检索方式。正确片段能够稳定找回后,再优化提示词和模型回答。