RAG Works — 直到你遇到长尾
Source: Dev.to
如果你每天使用 ChatGPT 或类似的大型语言模型,可能已经对它们产生了一定的信任。它们表达清晰、速度快,且常常表现出令人印象深刻的能力。许多工程师已经在编码帮助、文档编写或架构头脑风暴中依赖它们。
然而,迟早你会遇到瓶颈。
当你提出一个在日常工作中真正重要的问题——比如内部的、最近的或高度具体的内容——模型却突然变得模糊、错误,甚至自信地给出错误答案。这不是提示(prompting)的问题,而是结构性的局限。
本文将探讨这种现象背后的原因、当前解决方案为何只能部分缓解问题,以及为何将知识直接训练进模型权重可能成为未来的关键方向。
The real problem is not the knowledge cutoff
知识截止是大型语言模型(LLM)最显眼的限制。模型只在某个时间点之前的数据上进行训练,之后发生的任何事情对它们来说根本不存在。
然而,在实际使用中,这往往并不是最痛苦的问题。网页搜索、API 和工具往往可以缓解这一限制。
更深层次的问题是 知识的长尾。
在真实的生产环境中,最有价值的问题很少涉及公开的、文档齐全的事实。它们往往涉及内部系统、未记录的决策、专有流程以及在公共互联网中不存在的领域特定约定。
示例包括
- 为什么这个服务在看似无关的更改后开始出现故障?
- 这个架构权衡是否已经在内部讨论过?
- 我们公司如何解释某项具体的监管约束?
这些问题位于长尾,而正是大型基础模型表现最差的地方。
三种向语言模型提供知识的方式
如果去掉工具细节,实际上只有三种根本方式可以让语言模型“知道”新信息。
- 直接将知识放入提示中。
- 在推理时检索相关信息。
- 将知识训练进模型本身。
目前大多数系统几乎完全依赖前两种方式。
完整上下文:简单、昂贵且脆弱
最朴素的解决方案是把所有内容都放进提示词中。
prompt = f"""
You are an assistant with access to our internal documentation.
{internal_docs}
Question:
Why does service X fail under load?
"""
对于小文档,这种方式可行。实现简单,且不需要额外的基础设施。
然而,随着上下文规模的增长,会同时出现几个问题:
- 令牌成本线性增长。
- 延迟显著提升。
- 随着加入更多弱相关信息,推理质量下降。
这不是实现上的问题,而是 transformer 模型工作原理的直接后果。
Transformer瓶颈与上下文退化
Transformers 依赖自注意力(self‑attention),即每个 token 都会关注所有其他 token。这导致相对于输入长度的 quadratic complexity。
即使现代模型在技术上可以接受非常大的上下文窗口,仍然存在一个重要区别:
- Not crashing 与长输入,和
- Reasoning well 在长输入上的表现。
经验表明,随着上下文长度的增加,性能会下降,即使相关信息保持不变。模型仍然会生成流畅的文本,但其将正确信息片段连接起来的能力会恶化。这种现象通常被称为 context rot。
因此,单纯扩大上下文窗口并不是一个可行的长期解决方案。
RAG:通过嵌入的外部记忆
为了避免将所有内容都塞进提示,业界趋向于使用 检索增强生成(Retrieval‑Augmented Generation,RAG)。
其思路是将文档存储在外部,使用嵌入(embeddings)检索最相关的文档,然后仅将这些文档注入提示中。
一个最小的 Python 示例如下:
from langchain.vectorstores import Chroma
from langchain.embeddings import OpenAIEmbeddings
embeddings = OpenAIEmbeddings()
vector_store = Chroma.from_documents(
documents=docs,
embedding=embeddings
)
results = vector_store.similarity_search(
query="Why does the CI pipeline fail?",
k=5
)
RAG 之所以受欢迎,是因为它灵活、成本相对较低且易于部署。如今,它已成为为基于 LLM 的系统添加记忆的默认方案。
为什么 RAG 本质上受限
- 检索 ≠ 推理 – 仅选择少量相关片段并不能保证模型能够正确地将它们组合,尤其是当答案依赖于隐含关系或跨文档的多步推理时。
- 静态相似度 – 嵌入向量编码的是单一的全局相似度概念,无法适应局部领域语义。那些本不应混淆的文档往往在向量空间中靠得很近。
- 安全性问题 – 嵌入本身并不具备固有的安全性;只要付出足够的努力,就可以重建原始文本的大部分内容,使得向量数据库不适合作为隐私保护的抽象层。
这些局限表明,RAG 虽然强大,但并不完整。
天真的微调陷阱
此时,直接在内部数据上微调模型似乎很诱人。
实际上,天真的微调几乎总是会失败。直接在小规模、专业化的数据集上训练会导致模型 过拟合,失去通用推理能力,并且忘记先前学到的知识——这种现象被称为 灾难性遗忘。
其结果是模型只会记忆,却不理解。
合成数据:缺失的环节
关键的洞见在于生成能够捕获文档中知识的合成任务,而不是直接喂入原始文本。
合成知识生成
我们不再对原始文档进行训练,而是生成大量且多样的任务,这些任务描述了文档中包含的知识。这些任务可以包括问答对、解释、改写以及反事实情形。
一个简化的 Python 示例:
def generate_qa(doc):
return {
"instruction": f"Explain the key idea behind: {doc.title}",
"response": doc.summary
}
synthetic_dataset = [generate_qa(doc) for doc in internal_docs]
这种方法教授的是领域知识,而不是表层文本。令人惊讶的是,只要合成数据足够多样,即使原始数据集很小,它也能发挥作用。
Source: …
在不破坏模型的情况下进行权重训练
现代系统通过使用 参数高效微调 来避免灾难性遗忘:不再更新所有权重,而是只修改一小部分有针对性的子集。
低秩适配 (LoRA)
LoRA 在选定的层中插入低秩矩阵,使模型能够以最小的改动进行适配。
from peft import LoraConfig
lora_config = LoraConfig(
r=8, # 低秩矩阵的秩
lora_alpha=16, # 缩放因子
target_modules=["q_proj", "v_proj"] # 要适配的层
)
核心思想:进行小范围、局部的更新,以引导模型而不覆盖其已有知识。
其他参数高效技术
- 前缀调优 – 在输入序列前添加可学习的 token。
- 记忆层 – 添加外部记忆模块,用于存储任务特定信息。
所有这些方法遵循相同的原则:在保留大部分预训练权重的同时,仅学习一小套轻量级新参数,实现适应性与稳定性的平衡。
混合未来:上下文、检索与权重
没有任何一种技术能够完全取代其他技术。最有效的系统会结合这三者:
- 上下文 – 对即时指令很有用。
- 检索 – 对新鲜或经常变化的数据至关重要。
- 训练到权重 – 提供检索单独无法实现的深层、连贯的领域理解。
未来的核心设计问题不在于是否在私有知识上训练模型,而在于 哪些 知识应该存放在权重中,哪些应该在推理时处理。
Conclusion
RAG 是一种务实且强大的解决方案,它将继续成为 LLM 生态系统的一部分。然而,在对专门知识进行深度推理时,它从根本上受到限制。
随着训练技术变得更高效,将知识训练进权重将不再是研究好奇心——它将成为一种工程决策。
从长远来看,最有价值的 LLM 系统不会由它们使用的基础模型决定,而是由 它们所学的内容 和 教学的严谨程度 决定。