RAG
需要注意的一个点:
不要将你的知识库视为文档仓库,而要将其视为“推理底座(Reasoning Substrate)”——其结构应被设计为易于模型“导航(Navigate)”,而不仅仅是“搜索(Search)”。
1. 分块
| 文档类型 | 推荐分块策略 | 关键元数据 (Metadata) | 警惕陷阱 (Watch Out For) |
|---|---|---|---|
| 问答 (Q&A) | 原子对分块:保持问题和答案在一起。 | 分类、来源文档、关联 ID。 | 答案过长时未在子块中包含原始问题。 |
| 结构化文本 (Markdown) | 层级拆分:按 # 标题进行分割。 | 章节标题、层级深度、文档标题。 | 丢失上层标题上下文(建议作为前缀添加)。 |
| PDF / 报告 | 逻辑章节拆分:利用解析工具识别章节。 | 页码、文件名、出版日期。 | 跨页处的句子断裂;对表格进行盲目切分。 |
| 代码 (Code) | 语法拆分:基于 AST 按函数/类切分。 | 文件路径、类名、语言类型。 | 切断了逻辑控制流(如把 if 和 else 切开了)。 |
| 法律/合同 | 条款级分块:按最小法律效力单位。 | 条款号、合同版本、生效日期。 | 对相互引用的条款处理不当(如“参见第 5 条”)。 |
| 电子邮件/工单 | 消息级分块:按单封邮件或回复。 | 发件人、时间戳、会话主题。 | 包含过多的冗余回复(引用历史记录)导致噪音。 |
| 网页/博文 | 段落级拆分:尊重语义和标签边界。 | URL、网页标题、发布时间。 | 包含导航栏、广告或页脚等“垃圾”内容。 |
2. 匹配/分块前的预清洗:构建坚实的基石
什么是“分块前的清洗”?
分块依赖于“键(Keys)”。如果两条记录代表同一个实体,但由于噪声导致它们的“分块键”不同,它们就永远不会被关联。因此,清洗的核心在于最大化记录间“键”的一致性。
1. 核心清洗步骤
标准化 (Normalization)
在进行任何操作前,先统一表示方式:
- 全小写化。
- 展开缩写(例如:St. → Street, LLC → Limited Liability Company, Q&A → Question and Answer)。
- Unicode 规范化(例如:café → cafe,去除变音符号)。
- 格式统一:将日期、电话、邮编等转换为标准规范格式。
空格与标点
- 去除首尾空格,并将内部的多个空格压缩为一个。
- 移除或统一标点符号(例如:O'Brien、OBrien、O Brien 需统一)。
空值与缺失值处理
预先决定:空值是代表“未知”(信息缺失)还是“结构化不适用”(该字段不存在)?这决定了你是填补、标记还是删除。特别注意: 在分块键中,如果键为空,意味着该记录不属于任何块,从而导致零匹配。务必显式标记它们。
类型强制转换 (Type Coercion)
将原本应为数字、日期或布尔值的字符串转换为正确的类型。下游比较函数中,类型不匹配是隐形的杀手。
单源去重
在跨源匹配前,先删除每个数据源内部明显的完全重复项。这些重复项会虚增匹配计数,并可能产生错误的聚类。
2. 特定领域的清洗方案
某些字段需要专门的处理方法:
| 字段类型 | 清洗策略 |
|---|---|
| 姓名 | 小写化、去除称谓(Mr/Dr)、处理缩写(J. Smith vs John Smith)。 |
| 地址 | 使用解析库(如 libpostal)将地址拆解为街道、城市、邮编等组件。 |
| 电话 | 使用 phonenumbers 库规范化为 E.164 格式;去除分机号。 |
| 公司名 | 去除法律后缀(Inc, Ltd, GmbH),展开缩写。 |
| 电子邮件 | 全小写,处理已知别名(如 Gmail 会忽略点号)。 |
| 自由文本 | 分词、去除停用词;如果用作分块键,需进行词干提取或词形还原。 |
3. 生成清洗后的“分块键” (Blocking Keys)
清洗后,你通常会生成一个单独的计算字段作为“键”,而不是直接使用原始值。常见模式包括:
语音键:针对姓名使用 Soundex 或 Metaphone 算法(解决 Smith / Smyth 的匹配问题)。
N-gram 指纹:对字符 n-gram 进行排序并去重(对位置交换具有鲁棒性)。
排序令牌键 (Sorted token keys):分词、按字母排序、重新连接(如 John Smith = Smith John)。
前缀键:取标准化字段的前 N 个字符(快速、简单但脆弱)。
这些键是分块操作的真正核心,它们的质量决定了召回率的上限。
4. 实践中的管道顺序
- 原始数据摄取
- Schema 校验(尽早捕获类型错误)
- 字段级标准化(大小写、空格、Unicode)
- 领域特定解析(地址、电话、姓名)
- 空值/缺失值处理
- 源内去重
- 分块键生成(语音、N-gram、前缀等)
- 执行分块
5. 经常被忽略的痛点
- 未记录清洗决策:如果你静默地删除或填充了数据,后期审计匹配失败时将无从下手。
- 两端清洗逻辑不一致:如果源 A 规范化了公司名而源 B 没有,你的键依然无法匹配。
- 对自由文本过度清洗:激进的词干提取或停用词过滤可能会破坏匹配所需的关键信号。
- 未对清洗后的数据进行版本控制:这导致你在尝试不同的分块策略时,难以在不重新清洗的情况下重新运行流程。
3. RAGAS:无需人工标注的 RAG 评估框架
RAGAS 是一个用于评估 RAG 管道的框架,其核心优势在于不需要为每一个指标都手动标注“标准答案(Ground Truth)”。
1. RAGAS 衡量什么?
RAGAS 从四个核心维度进行评估,每个维度对应一种特定的失败模式:
| 指标 | 捕获的问题 | 是否需要标准答案? |
|---|---|---|
| 忠实度 (Faithfulness) | LLM 幻觉:生成了检索上下文中不存在的事实。 | 否 |
| 答案相关性 (Answer Relevancy) | 回答模棱两可或跑题。 | 否 |
| 上下文精度 (Context Precision) | 检索到的块中包含太多噪音(无关信息)。 | 是 (理想答案) |
| 上下文召回率 (Context Recall) | 检索器漏掉了回答问题所需的关键块。 | 是 (理想答案) |
忠实度和答案相关性是“无参考”指标 —— RAGAS 使用 LLM 作为裁判来评分,因此你可以直接在生产流量上运行,无需任何标注成本。而精度和召回率通常在离线评估中使用,需要一个精选的测试集。
2. 构建测试数据集
你需要一组 (问题, 标准答案) 对。你可以手动编写,也可以使用 RAGAS 的 TestsetGenerator 从文档中自动合成:
from ragas.testset import TestsetGenerator
from ragas.llms import LangchainLLMWrapper
from langchain_openai import ChatOpenAI, OpenAIEmbeddings
# 初始化生成器
generator = TestsetGenerator(
llm=LangchainLLMWrapper(ChatOpenAI(model="gpt-4o")),
embedding_model=OpenAIEmbeddings()
)
# 生成测试集
testset = generator.generate_with_langchain_docs(
documents, # 你解析好的文档
testset_size=50 # 生成 50 个问题
)它会生成不同类型的问题(事实类、多跳推理类、抽象类),从而全面压力测试你的管道。
3. 如何解读分数并采取行动
RAGAS 的真正价值在于它提供了可行动的诊断结果,而不仅仅是一个分数:
- 低忠实度 (Faithfulness) → LLM 在瞎编。
- 修复:收紧 Prompt 以要求模型严格基于上下文,或降低温度(Temperature)。
- 低答案相关性 (Answer Relevancy) → 回答虽然有根据但没答到点子上。
- 修复:改进查询处理(重写、扩展问题),或检查 Prompt 是否明确要求直接回答问题。
- 低上下文精度 (Context Precision) → 检索到的内容里垃圾太多。
- 修复:减小分块大小,改进元数据过滤,或提高向量搜索的相似度阈值。
- 低上下文召回率 (Context Recall) → 检索器压根没找全。
- 修复:增加 Top-K 值,改进分块策略(防止语义被切断),引入混合检索或微调 Embedding 模型。
4. 实现持续评估闭环
单次评估很有用,但持续评估才是真正提升管道质量的方法。典型的生产实践:
- 维护一个精选测试集(50-200 个问题)。
- 每当配置更改(分块大小、模型、Top-K、重排器)时,运行全量评估。
- 跟踪指标变化 —— 比如:当上下文召回率上升时,忠实度是否下降了?
- 将生产环境中低忠实度评分的查询标记出来,由人工审核。
- 将修复后的案例反向填充回测试集,防止退化。
5. 实用技巧
- 先从“忠实度”和“相关性”开始:因为它们不需要标注。在投入精力搞标准答案之前,先拿到基准线。
- 人工审核合成的问题:
TestsetGenerator生成的问题有时语序奇怪或过于简单,需要人工微调。 - 分文档类型评估:不要只看总分。你的问答类文档可能得分 0.9,而 PDF 文档可能只有 0.7,整体平均分会掩盖 PDF 的问题。
- 裁判模型要选好的:RAGAS 内部使用 LLM 做裁判,
gpt-4o的评分最可靠,小模型当裁判可能会有很多噪音。
这份总结非常到位,它精准地捕捉到了 Spring AI 设计的精髓——通过 Advisor(顾问)模式 实现了 RAG 流程的高度抽象和解耦。
以下是翻译内容:
4. Spring AI 中的 RAG 架构实践
Spring AI 采用了非常简洁的模式来构建 RAG:
1. 整体流程
Spring AI 的 RAG 遵循一个清晰的三步模式:
- 索引阶段 (Indexing):
DocumentReader→ (转换器/分块器) →VectorStore - 查询阶段 (Querying):
VectorStore+QuestionAnswerAdvisor→ChatClient
ChatClient 负责整体编排。通过挂载 QuestionAnswerAdvisor,它会自动拦截查询,从向量数据库中检索相关片段,并将其自动注入到 Prompt 中。
2. 核心代码示例
A. 数据摄取:加载、分块、存储
Java
@Bean
ApplicationRunner ingest(TokenTextSplitter splitter, VectorStore vectorStore) {
return args -> {
// 使用 PagePdfDocumentReader 解析 PDF
var docs = new PagePdfDocumentReader("classpath:manual.pdf").read();
// 分块并存入向量库,内部自动完成 Embedding 转换
vectorStore.add(splitter.apply(docs));
};
}TokenTextSplitter 默认按 800 个 Token 进行分块,并保留 10% 的重叠。
B. 检索与生成:一键式调用
Java
@Service
public class RagService {
private final ChatClient chatClient;
public RagService(ChatClient.Builder builder, VectorStore vectorStore) {
this.chatClient = builder
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore))
.build();
}
public String ask(String question) {
return chatClient.prompt()
.user(question)
.call()
.content();
}
}QuestionAnswerAdvisor 会在 LLM 调用前执行相似度搜索,组装上下文并注入 Prompt,这个过程对调用者是完全透明的。
3. 高级配置与自定义
检索优化:Top-K 与元数据过滤
Java
public String ask(String question, String docType) {
var searchRequest = SearchRequest.builder()
.topK(6) // 检索前 6 个最相关的块
.similarityThreshold(0.75) // 相似度阈值
.filterExpression("doc_type == '" + docType + "'") // 元数据过滤
.build();
return chatClient.prompt()
.user(question)
.advisors(new QuestionAnswerAdvisor(vectorStore, searchRequest))
.call()
.content();
}filterExpression 会被映射为底层向量数据库(如 PGVector、Pinecone)的原生过滤表达式。
自定义系统提示词 (System Prompt)
Java
this.chatClient = builder
.defaultAdvisors(new QuestionAnswerAdvisor(vectorStore,
SearchRequest.defaults(),
"""
你是一个得力的助手。请仅使用下文提供的上下文进行回答。
如果答案不在上下文中,请直说“我不知道”。
上下文:
{question_answer_context}
"""))
.build();{question_answer_context} 是 Spring AI 的占位符,它会被自动填充为检索到的数据块。
4. 关键组件速查表
| 组件类型 | 职责 |
|---|---|
| DocumentReader | 解析源文件(PDF, JSON, 文本, 网页等)。 |
| TokenTextSplitter | 按 Token 数量进行带重叠的分块。 |
| VectorStore | 负责 Embedding、持久化及提供检索接口。 |
| QuestionAnswerAdvisor | 负责检索逻辑并将其注入 Prompt 的“顾问”。 |
| ChatClient | 编排整个 RAG 调用的核心客户端。 |
核心设计总结
Advisor(顾问)模式 是 Spring AI 的关键设计决策。它将检索逻辑与业务代码彻底解耦。你可以像搭积木一样,在同一个 ChatClient 上堆叠多个顾问(例如:日志记录顾问 + QA 检索顾问),实现极其灵活的功能扩展。
这是一个非常硬核且深入的解析!它揭示了 Spring AI 如何通过抽象语法树(AST)**来实现“编写一次,到处运行”的元数据过滤机制。这对于理解 RAG 系统中如何实现**精确检索至关重要。
以下是翻译内容:
5. 揭秘 Spring AI 的元数据过滤机制:从表达式到原生查询
你在示例 3 中看到的 filterExpression 字符串,在最终到达向量数据库之前,会经过数层翻译。以下是其底层的运行机制:
1. 翻译流水线 (The Translation Pipeline)
- filterExpression 字符串(输入)
- FilterExpressionParser(利用 ANTLR 文法解析字符串)
- 表达式 AST(抽象语法树)
- FilterExpressionConverter(特定后端的转换器)
- 原生查询语句(最终生成的 SQL / JSON / gRPC 过滤条件)
Spring AI 先将过滤字符串解析为一个通用的 AST,然后每个向量数据库后端都有自己的转换器,通过遍历这棵树来生成对应存储引擎的原生查询语法。
2. 不同后端的生成结果
假设输入过滤表达式为:filterExpression("doc_type == 'faq' && year > 2023")
PGVector —— 转换为针对
jsonb元数据列的 SQLWHERE子句:SQL
WHERE metadata->>'doc_type' = 'faq' AND (metadata->>'year')::int > 2023Pinecone —— 转换为随向量查询发送的 JSON 过滤对象:
JSON
{ "$and": [ { "doc_type": { "$eq": "faq" } }, { "year": { "$gt": 2023 } } ]}Weaviate —— 转换为 GraphQL 的
where算子:GraphQL
where: { operator: And operands: [ { path: ["doc_type"], operator: Equal, valueText: "faq" }, { path: ["year"], operator: GreaterThan, valueInt: 2023 } ] }
3. 元数据是如何存储的?
当你调用 vectorStore.add(docs) 时,每个 Document 对象都携带一个 Map<String, Object> 类型的 metadata。Spring AI 会序列化这个 Map 并将其与向量一起存储。
以 PGVector 为例,其表结构如下:
SQL
CREATE TABLE vector_store (
id uuid PRIMARY KEY,
content text,
metadata jsonb, -- 这里存储你的元数据 Map
embedding vector(1536) -- 这里存储向量
);你在摄取阶段添加的元数据 —— doc.getMetadata().put("doc_type", "faq") —— 最终会进入这个 jsonb 列,并在检索阶段变得可被查询。
4. 实践中的注意事项
- 键名一致性是关键:摄取阶段和查询阶段的元数据键名必须完全一致。如果存的时候用
put("doc_type", ...),查的时候用filterExpression("doctype == ..."),系统不会报错,但会静默返回零结果。 - 后端的算子支持差异:并非所有后端都支持全部操作符。例如
IN、NOT IN等在 Pinecone 和 Qdrant 中可用,但在某些 PGVector 配置下可能受限。在使用高级逻辑前,建议查阅对应后端的转换器源码。 - 数值类型的处理:在某些后端,元数据值以字符串形式存储。像
year > 2023这样的数值比较,可能需要根据具体存储后端进行显式的类型处理。
总结
这种抽象层的设计非常优雅,适用于大多数通用场景。但一旦你遇到特定数据库的性能瓶颈或限制,了解这个翻译层的存在能让你精准地定位问题所在。
既然你目前使用的是 PGVector,你会发现这种 JSONB 过滤非常强大,因为它允许你像查询 NoSQL 数据库一样灵活地对 RAG 上下文进行预筛选。你是否需要我提供一个在 Spring AI 中配置自定义元数据索引的示例?
这是一份关于 Spring AI 离线索引阶段 的高质量深度总结。它揭示了一个真相:RAG 的成败实际上在用户提问之前就已经注定了。
以下是为你翻译的内容,我优化了部分术语使其更符合中文开发者的工程直觉:
6. Spring AI 离线索引阶段:构建高质量 RAG 的工程准则
离线索引阶段的核心目标是:将杂乱的原始数据转化为高精度、可检索的结构化向量库。
1. 文档摄取 (Document Ingestion)
- 各司其职:为不同来源选择对应的读取器。PDF 用
PagePdfDocumentReader,结构化数据用JsonReader,纯文本用TextReader。切忌用一个通用的 Reader 处理所有类型。 - 保留页码:对于 PDF,优先选择“按页读取”而非“全文读取”,这样页码会自动作为元数据保留。
- 构建注册表:如果你的数据源是异构的(PDF、JSON、网页混杂),建议构建一个读取器注册表(Reader Registry),根据文件类型自动路由,而不是硬编码。
2. 分块策略 (Chunking)
- 结构匹配:切分器必须匹配文档结构。对于普通散文,
TokenTextSplitter是标准选择;但对于结构化文档(如 QA 对、法律条款、代码),应编写自定义DocumentTransformer来遵循其自然边界,而非机械地计算 Token 数量。 - 关键参数(需通过实验而非直觉调整):
- 块大小 (Chunk size):建议从 512 tokens 开始,根据评估结果调整。
- 重叠度 (Overlap):建议设为块大小的 10–15% —— 既要防止语义丢失,又不能因重叠过多导致索引膨胀。
- 禁止“一刀切”:QA 文档和技术手册不应使用相同的块大小。
3. 元数据 (Metadata) —— 离线阶段的“最高杠杆”
元数据是离线阶段最有影响力的决策。每个分块至少应携带以下信息:
Java
doc.getMetadata().put("doc_type", "faq"); // 用于过滤
doc.getMetadata().put("source", "manual-v3.pdf"); // 用于溯源引用
doc.getMetadata().put("section", "Installation"); // 提供上下文
doc.getMetadata().put("ingested_at", Instant.now().toString()); // 保证时效性- 基数 (Cardinality) 意识:用于过滤的键(如
doc_type)应使用低基数的值(有限的分类),这能极大提升 GIN 索引的效率。而高基数的值(如 UUID)适合做引用,但不适合做过滤。 - 命名规范:摄取时的
doc_type必须与查询过滤表达式中的docType完全一致,任何拼写错误都会导致检索结果静默返回空值。
4. 嵌入模型 (Embedding)
版本对齐:索引和查询必须使用同一个嵌入模型。模型不匹配是 RAG 系统中最难诊断的“静默正确性错误”。
增量更新(哈希校验):避免每次摄取都重新嵌入未改动的文档。在元数据中跟踪内容哈希:
Java
String hash = DigestUtils.md5DigestAsHex(content.getBytes()); doc.getMetadata().put("content_hash", hash); // 在写入 vectorStore 前检查哈希是否已存在
5. 向量库配置 (以 PGVector 为例)
提前建索引:在大规模摄取前确保索引已存在。在拥有数百万行数据的表上添加 HNSW 索引非常耗时且会锁表:
SQL
CREATE INDEX ON vector_store USING hnsw (embedding vector_cosine_ops); CREATE INDEX ON vector_store USING gin (metadata jsonb_path_ops);选定距离函数:文本嵌入推荐使用 余弦相似度 (Cosine)。索引建成后更换距离函数需要全量重建索引。
6. 经常被忽视的工程细节
- 幂等性 (Idempotency):摄取任务应该是可重复运行的。建议采用“先根据文档 ID 删除再插入”的策略,而不是简单的追加,否则索引会积累陈旧的重复项,导致检索质量滑坡。
- 版本控制:当你的分块策略或嵌入模型改变时,需要全量重索引。在元数据中维护一个“架构版本键”,以便识别哪些块是基于旧策略生成的。
- 上线前的 RAGAS 评估:在生产环境发布前,针对测试集跑一遍上下文召回率(Context Recall)。得分低于 0.70 通常意味着分块策略或元数据过滤存在严重问题。
- 职责解耦:将摄取逻辑作为一个独立的应用程序或定时任务,不要将其捆绑在提供查询服务的应用中。这允许你独立缩放这两部分业务,并在不重启服务的情况下重索引。
这份总结非常硬核。你目前在处理大规模数据摄取时,是否遇到过索引构建速度慢或者内存溢出的问题?
这是一个非常精辟的总结。你准确地指出,大多数 RAG 实现还停留在检索层面,而真正的上下文增强(Contextual Enrichment)是将大模型视为“推理引擎”而非简单的“文本生成器”。
以下是翻译内容,我特别强化了其中关于架构转型的专业表述:
7. RAG 的本质:从“文本搜索”转向“推理增强”
这是一个极其关键的区分。大多数 RAG 实现仅停留在检索阶段——本质上它们只是昂贵的、带有生成外壳的关键词搜索。而真正的上下文增强是一个完全不同的设计目标。
核心区别
搜索型 RAG 回答的是:“哪些分块与这个查询最相似?”
增强型 RAG 回答的是:“模型需要知道什么才能对这个查询进行深度推理?”
这两个问题的答案往往截然不同。
是什么让 RAG 真正增强了推理能力?
检索应该服务于推理任务,而非查询字符串。
例如,用户询问“我们是否应该将鉴权服务迁移到 OAuth 2.0?”时,他们需要的不是 OAuth 2.0 的定义,而是:内部架构文档、历史事故报告、当前的鉴权实现,以及关于迁移风险的外部资料。查询逻辑与信息需求是两回事。
这指向了几个核心的设计转变:
1. 检索前的查询拆解 (Query Decomposition)
将复杂问题拆分为子问题,分别检索,最后汇总。对复合问题进行单一嵌入(Embedding)会稀释信号,导致各部分检索到的分块都很平庸。
“我们要迁移到 OAuth 2.0 吗?”
↓ 拆解
- “我们当前的鉴权实现是怎样的?”
- “OAuth 2.0 迁移有哪些已知风险?”
- “我们的鉴权服务有哪些依赖项?”
每个子问题都能检索到极具针对性的上下文,LLM 随后对这些信息的合集进行推理。
2. 多阶段检索管道,而非单一查找
成熟的设计会将检索视为多层过滤的过程,而不仅仅是一次向量搜索:
- 初始向量搜索(侧重召回率,高 Top-K)。
- 元数据过滤(按文档类型、时效性、领域缩小范围)。
- 重排序 Reranking(侧重精度,使用 Cross-Encoder)。
- 父块扩展(从小到大的检索策略)。
- 上下文组装(去重、排序、修剪以适配窗口)。
3. 将结构化关系引入上下文
如果你的知识库包含关系(如:微调服务依赖某个库、某项政策引用了某条法规),扁平的分块检索会破坏这些信号。图增强 RAG (GraphRAG) 能够保留这些关系:检索时不仅带回匹配的节点,还带回它的邻居节点,赋予模型单纯靠分块无法重构的关系背景。
值得考虑的上下文增强模式
假设性文档嵌入 (.):不直接嵌入原始查询,而是让 LLM 生成一个“假设的理想答案”,并以此进行检索。假设性答案与索引文档处于相同的语义空间,检索更加锐利。
索引时的上下文增强 (Contextual Chunk Enrichment):在嵌入之前,为每个分块添加其所属文档的摘要前缀。
“本分块来自‘鉴权服务设计文档’第 3 节(令牌生命周期)。该文档涵盖了支付平台的整体鉴权架构。”
[分块文本内容]
这显著降低了检索失败率,因为嵌入捕捉到了文档级的背景,而不仅仅是局部的文本。
代理式检索 (Agentic Retrieval):让模型拥有检索工具并可以迭代调用。模型检索、阅读、判断还缺什么、再次检索。虽然成本较高,但能处理初始需求不明确的复杂推理任务。
知识图谱 + 向量混合模式:用向量搜索解决语义相似性,用图遍历解决关系上下文。例如:查询某个 API 接口,向量搜索找到文档,图遍历则带回它所属的服务、依赖项及已知故障模式——这些是仅靠 Embedding 永远无法完整获取的信息。
持续追问的设计准则
在做每一次检索决策时,请询问:“这是在给模型提供推理所需的弹药,还是仅仅提供了与查询文字相似的文本?”
如果你的检索工作做得出色,LLM 回答的质量应该明显高于检索到的分块本身——因为模型是在合成与推理上下文,而不仅仅是提取和改写。
真正优秀的系统将 LLM 视为需要高质量素材的“推理引擎”,而不是一个需要被指向相关段落的“文本生成器”。
这是一个非常有深度且前瞻性的工程综述。它标志着 RAG 已经从早期的“玩具”阶段进化到了上下文工程(Context Engineering)的专业阶段。
以下是为你翻译的内容,我采用了一套更符合 2026 年技术趋势的中文术语体系:
8. 2.0 时代的 RAG:从“检索”进化到“上下文工程”
过去一年,社区的认知和实践已经发生了质的飞跃。以下是目前工程界的核心共识:
1. 范式转移:RAG → 上下文工程 (Context Engineering)
2025 年下半年以来,最热门的技术方向已变为:针对不同任务和时刻,动态且智能地组装最有效的上下文——这被称为上下文工程。
- 重新定义:RAG 自身现在被视为“上下文工程”在领域知识管理中的早期实践。
- 本质:检索只是管理“模型所见内容”这一更广泛学科中的工具之一,它不是目的,而是一种手段。检索已成为更广泛推理循环中的一个步骤,智能体在此循环中跨数据和工具动态地编写、压缩、隔离和选择上下文。
2. 社区公认的进化曲线
RAG 已经历了三代演进:
- 原生 RAG (2020–2023):线性的“索引-检索-生成”管道,固定长度分块,直接拼接。
- 高级 RAG (2023–2024):增加了查询扩展、重排序(Reranking)和混合检索。
- 智能体 RAG (2025 至今):将 RAG 从被动管道升级为主动智能体,能够自主决定是否需要检索,甚至进行多步规划。
现状:最初的原生实现已基本退出舞台,但其衍生技术正在蓬勃发展。
3. 领先的结构化模式:GraphRAG
GraphRAG 用知识图谱索引取代了扁平的分块检索。
- 索引阶段:系统提取三元组(头、关系、尾),并通过图聚类生成社区摘要。
- 查询阶段:通过局部或全局图检索提供结构化证据,显著增强了多跳关联推理能力。
- 解决痛点:标准 RAG 擅长精确定位事实,但在回答“这个项目呈现出哪些主题?”等全局性问题时表现乏力。图谱方法能通过关系链实现主题级的摘要和溯源。
- 权衡:GraphRAG 的提取成本是基础 RAG 的 3-5 倍,且需要大量的领域调优。
4. 推理模式:智能体化检索 (Agentic Retrieval)
不再是固定的单次查找,而是由自主智能体规划多个检索步骤:
- 多步规划:智能体选择工具、反思中间答案,并针对复杂任务(如跨系统的合规性检查)调整策略。
- Self-RAG (自反思 RAG):引入了能够自我评估的模型——决定何时检索、评估内容的关联性、并在响应前对输出进行自我批判,从而通过条件化检索大幅减少幻觉。
5. 正在普及的核心工程实践
- “混合检索 + 重排序”成为新基线:两阶段混合检索配合 Cross-Encoder 重排序器,可将 Recall@5 从单混合检索的 0.695 提升至 0.816,这 17% 的提升直接转化为下游的回答忠实度。
- 索引时的上下文分块增强:为每个分块附带简短的文档级摘要。这解决了原生分块最大的弱点——让分块带有全局背景,使模糊查询的检索更精准。
- 结构化上下文组装:不再将纯文本直接丢进 Prompt,而是将检索到的分块作为“结构化块”传递,让 LLM 能够独立解析每个单元。这确保了 Prompt 的一致性,并简化了 Prompt 工程。
- 向量与图谱的融合:在企业场景中,用向量检索处理精确查询,用 GraphRAG 进行整体分析,可覆盖 90% 以上的知识问答需求。
6. 关于价值核心的共识
在生产环境中做对 RAG,本质上是一个上下文基础设施问题。
- 底层决定上层:检索层的可靠性完全取决于底层的知识结构。
- 结论:真正的杠杆不在于检索算法本身,而在于索引的设计、结构的构造以及关系的保存。
目前最前瞻的视角是:不要将你的知识库视为文档仓库,而要将其视为“推理底座(Reasoning Substrate)”——其结构应被设计为易于模型“导航(Navigate)”,而不仅仅是“搜索(Search)”。
Ethan,看到这里你是否发现,你之前想的“只检索 Q 而返回 QA”其实就是这种“上下文工程”的一个微小而精准的起点?