03 上下文工程与 RAG
学习目标:能构建企业知识库 Agent(检索/Rerank/引用溯源/评测),并能为长任务 Agent 设计上下文预算与压缩策略。 重点度:必会(12 分) 前置要求:Ch2 LLM 应用基础、Python、基础向量运算概念
概述#
Agent 不能只依赖模型参数里的知识——模型训练截止后的事情、企业内部数据、用户私有上下文,都得靠运行时检索。这就是 RAG(Retrieval-Augmented Generation)解决的问题。
但 2026 的现实是:RAG 已经不够用了。长任务 Agent 跑着跑着上下文窗口就爆了——一次自主任务可能烧几十万 token,单纯堆 RAG 解决不了"运行时上下文预算管理"的问题。
所以本章把 Context 工程 提为一等公民,与 RAG 并列。两者边界清晰:
- RAG 是"知识获取":把外部知识塞进上下文
- Context 工程是"运行时上下文预算管理":决定什么进上下文、什么不进、什么时候压缩、什么时候丢弃
Part 1:RAG#
RAG 全链路#
一次完整的 RAG 流程:
用户问题
↓
[1. Query Rewrite] ← 改写查询,提升召回
↓
[2. Hybrid Search] ← 向量检索 + 关键词检索
↓
[3. Rerank] ← 重排,把最相关的推到前面
↓
[4. Context Filter] ← 截断/过滤,只保留 top-k
↓
[5. Prompt Assembly] ← 拼到 system/user prompt 里
↓
[6. LLM 生成] ← 引用溯源
↓
[7. RAG Eval] ← 评测检索质量与生成忠实度文档解析与 Chunking#
文档解析#
| 格式 | 工具 | 注意点 |
|---|---|---|
| PyMuPDF / unstructured / Marker | 扫描件需 OCR;表格/公式易丢失 | |
| Word/Excel | python-docx / openpyxl | 保留段落结构,不要按行粗暴切 |
| HTML | BeautifulSoup / Trafilatura | 去广告/导航/页脚,保留正文 |
| Markdown | 直接按 heading 切分 | 天然结构化,最友好 |
实战坑:PDF 解析是 RAG 工程里最耗时的一块。生产场景建议用 Marker / Docling 这类专门做 PDF → Markdown 的工具,比 PyMuPDF 直接抽文本结构保留好得多。
Chunking(切片)#
Chunking 决定了检索粒度。切太大召回噪声多,切太小语义不完整。
常用策略:
- 固定长度切分:按 token 数(如 500 token/chunk,overlap 50)—— 简单但破坏语义
- 按结构切分:按段落/标题切—— 保留语义但 chunk 大小不均
- 递归切分(RecursiveCharacterTextSplitter):先按段落切,太大再按句子切—— LangChain 默认
- 语义切分(SemanticChunker):用 embedding 相似度判断边界,相似度骤降处切分 —— 效果好但成本高
关键参数:
chunk_size:500-1500 token 是常见区间overlap:50-200 token,避免边界信息丢失metadata:每个 chunk 必须带 source/page/section 等元数据,用于引用溯源和权限过滤
Embedding 与 Vector DB#
Embedding 模型#
把文本转成向量,相似度通过余弦距离计算。
| 模型 | 维度 | 特点 |
|---|---|---|
| OpenAI text-embedding-3-large | 3072 | 通用强,国内访问受限 |
| BGE-M3 | 1024 | 中文优秀,开源,支持稠密+稀疏+多向量 |
| Qwen3-Embedding | 多种 | 阿里出品,中文友好 |
| Cohere embed-v3 | 1024 | 多语言强 |
实战策略:国内首选 BGE-M3 / Qwen3-Embedding,闭源场景可考虑 OpenAI/Cohere。
Vector DB 选型#
| 类型 | 代表 | 适用场景 |
|---|---|---|
| 专用向量库 | Milvus / Qdrant / Weaviate | 大规模(千万级 chunk) |
| 数据库扩展 | pgvector / Redis Vector | 中小规模,已有 PG/Redis |
| 全托管 | Pinecone / 腾讯 VectorDB | 不想运维 |
| 内存型 | FAISS | 单机百万级,无持久化需求 |
关键能力对比:
- HNSW 索引:主流近似最近邻算法,查询快但内存占用高
- IVF + PQ:内存占用低但精度损失
- Filter:metadata 过滤(如"只检索部门=HR 的文档")—— 生产必备,选型时务必验证
Hybrid Search 与 Rerank#
为什么需要 Hybrid Search#
纯向量检索的弱点:
- 关键词失效:人名、产品型号、专有名词,向量检索可能召回不到
- 短查询过泛:"Q3 财报"三个字,向量空间相似的东西太多
解决方案:向量检索 + 关键词检索(BM25)并行,结果融合。
RRF 融合#
Hybrid Search = Vector Search(top 50) ∪ BM25 Search(top 50)
→ RRF 融合排序 → top 20RRF(Reciprocal Rank Fusion):score = Σ 1/(rank_i + k),简单有效。
Rerank#
Rerank 模型对 hybrid search 召回的 top-N 重新打分,把真正相关的推到前面。
Query: "Q3 财报里提到哪些风险因素?"
Hybrid 召回 top-20:
chunk_1: Q3 财报封面 ← 向量相似但答非所问
chunk_2: Q3 风险因素章节 ← 真正想要的
chunk_3: Q2 风险因素章节 ← 历史内容
...
Rerank 后:
chunk_2 → chunk_3 → chunk_1 → ...Rerank 模型:BGE-Reranker-v2-m3(开源,中文好)、Cohere Rerank(商用强)。
Query Rewrite#
用户的原始查询往往不是最优检索 query:
- 口语化:"那个东西怎么用" → 改写为"产品 X 使用说明"
- 指代不清:"他说的那个文件" → 需要结合上下文消解
- 多跳问题:"对比 A 和 B 公司 Q3 营收" → 拆成两次检索
HyDE(Hypothetical Document Embedding)#
让 LLM 先编一个"假想答案",用假想答案的 embedding 去检索——因为"答案"比"问题"在向量空间更接近真实文档。
Query: "Q3 财报风险因素"
HyDE 生成: "Q3 财报中提到的风险因素包括:原材料价格波动、汇率风险..."
用 HyDE 生成内容做 embedding 检索 → 召回更准Multi-Query#
LLM 把一个问题改写成多个不同角度的子查询,并行检索后融合。
引用溯源与权限感知检索#
引用溯源#
生产 RAG 系统必须给每个回答附带引用,让用户能验证:
回答: "Q3 营收同比增长 12%[1], 主要增长来自云业务[2]"
引用:
[1] 2026 Q3 财报.pdf, p.3, "本季度营收同比增长 12%"
[2] 2026 Q3 财报.pdf, p.5, "云业务收入贡献主要增长"实现:让模型在每个事实陈述后输出 [cite_id], cite_id 关联到具体 chunk 的 metadata。
权限感知检索(Permission-Aware Retrieval)#
企业场景必须做:
- 检索时按用户权限过滤 metadata(部门/级别/项目组)
- 不要"先检索后过滤"——会丢失相关结果;要"检索时就过滤",Vector DB 的 filter 能力在这里用上
- 引用也按权限显示——用户能看到答案但看不到机密源文件名
RAG Eval#
评测维度#
| 维度 | 含义 | 评测方法 |
|---|---|---|
| 检索召回 | 相关文档是否被召回 | 标注 golden chunks,看 Recall@k |
| 检索精度 | 召回的文档是否相关 | Precision@k |
| 生成忠实度 | 回答是否基于检索内容(防幻觉) | LLM-as-a-Judge / faithfulness 评分 |
| 答案正确性 | 回答是否真的回答了问题 | 人工标注 / LLM-as-a-Judge |
| 引用正确性 | 引用是否准确指向源 | 标注 golden citation |
工具#
- Ragas:开源 RAG 评测框架,覆盖 faithfulness / answer_relevancy / context_precision / context_recall
- TruLens:可观测 + 评测,能记录每次 RAG 调用并评分
- DeepEval:单元测试风格的 RAG 评测
Agentic RAG 与 GraphRAG#
Agentic RAG#
传统 RAG 是"一次检索 → 生成"。Agentic RAG 让 Agent 自己决定:
- 要不要检索?(也许模型已经知道答案)
- 检索几次?(多跳问题需要迭代检索)
- 检索结果不够好,要不要改写 query 再检一次?
- 要不要检索别的源(SQL DB / Web)?
用户: "对比 OpenClaw 和 WorkBuddy 的安全模型"
Agent 决策:
Round 1: 检索 "OpenClaw 安全模型" → 召回 5 篇
Round 2: 检索 "WorkBuddy 安全模型" → 召回 3 篇
Round 3: 信息不够,检索 "Confused Deputy MCP" → 补充背景
最终: 整合三轮结果生成对比GraphRAG#
传统 RAG 是"扁平 chunk 检索",处理跨文档关系弱(如"找出和 X 有合作的所有公司")。GraphRAG 在 chunk 之上构建知识图谱:
- 抽取实体 + 关系 → 建图
- 检索时既找相关 chunk,也找图谱上的关联节点
- 适合多跳推理、全局总结类问题
何时用 GraphRAG:跨文档关系查询、全局总结、多跳推理。简单问答用 GraphRAG 是过度设计。
Part 2:Context 工程(2026 一等公民)#
为什么 Context 工程独立成章#
| RAG | Context 工程 |
|---|---|
| 解决"知识从哪来" | 解决"运行时上下文怎么管" |
| 离线建索引,在线检索 | 在线动态决策 |
| 关注召回/精度 | 关注 token 预算、压缩、丢弃 |
| 一次性的 prompt 增强 | 持续滚动的上下文管理 |
Agent 跑长任务时,上下文会持续膨胀:每一步的思考、每次工具调用、每次检索结果都往上下文塞。10 步后上下文可能已经 50K token,20 步后 200K,模型窗口再大也撑不住——这就是 Context 工程要解决的核心问题。
Context 窗口预算#
核心问题:什么进上下文、什么不进#
[Agent 一次决策的上下文构成]
System Prompt ─── 必留 (规则/角色)
Conversation ─── 必留 (用户对话)
Tool List ─── 必留 (工具 schema)
Tool Call History ─── 选择性保留 (近期留,早期压缩)
RAG Results ─── 选择性保留 (用完即丢)
Scratchpad ─── 选择性保留 (工作笔记)
File Content ─── 几乎不留 (太长,用 reference 替代)预算分配#
假设上下文窗口 200K token:
System Prompt: 2K (固定)
Conversation: 20K (用户对话历史)
Tool List: 5K (工具 schema)
Tool Call History: 30K (近期工具调用)
RAG Results: 20K (当前任务的检索结果)
Scratchpad: 10K (工作笔记)
─────────────────────
已用: 87K
剩余给模型生成: 113K (output)实际工程里要把预算做成动态的——简单任务少给点、复杂任务多给点。
Context Compression(上下文压缩)#
摘要压缩#
对长历史记录做摘要,把 N 轮对话压缩成几句话:
原始:
user: 帮我分析这个 repo
assistant: 好的,我先看 README... (3K token)
assistant: 然后看了 package.json... (2K token)
assistant: 接着分析 src 目录... (5K token)
...总共 30K token
压缩后:
assistant: [已完成 repo 分析,识别为 Node.js 项目,
使用 React+Express,主要模块包括 auth/payment/user]
(200 token)何时 summarize、何时丢弃#
- summarize:内容可能后续还要用,但不需要原细节 → 工具调用历史、RAG 结果
- 丢弃:内容用完即弃 → 中间计算结果、临时 scratchpad
- 保留原文:必须精确的 → 用户原话、合同条款、代码 diff
⚠️ 实战坑:不要无脑 summarize。如果任务涉及"对比第 3 步和第 7 步的输出",summarize 掉就找不回了。要按"未来是否需要细节"判断。
Compaction(长任务上下文滚动压缩)#
Compaction 是 Claude Code / Cursor 等长任务 Agent 的核心技术:当上下文逼近窗口上限时,自动触发压缩,让任务能继续跑下去。
基本流程#
[Agent 跑到第 25 步, 上下文 180K/200K]
↓
触发 Compaction:
1. 把最早的 N 步压缩成 summary
2. 保留近 M 步原文
3. 保留关键状态: 当前任务/已完成步骤/未完成步骤
4. 上下文降到 80K
↓
[Agent 继续跑, 25 → 50 步]
↓
再次触发 Compaction
↓
[Agent 能跑到 100+ 步而不爆窗口]关键设计#
- 触发阈值:上下文用量超过 70-80% 时触发,留余量给后续步骤
- 保留窗口:最近 N 步原文必须保留,否则模型会"失忆"
- 关键状态外置:当前任务/进度/已做决策不应放上下文,应外置到 workspace 文件(见 Ch7)
- summary 要可恢复:summary 本身要包含足够信息,让模型能"知道自己做过什么"
Subagent 隔离 context#
主 Agent 上下文爆炸时,把子任务派给 subagent:
主 Agent (上下文 50K, 剩余预算紧张):
"我需要分析这个 100 文件的 repo"
↓
派 subagent:
Subagent 接收: "分析 X repo,输出主要模块清单"
Subagent 自己加载 100 文件 (自己上下文爆了也不影响主)
Subagent 返回: "main 模块/auth/payment/user..." (500 token)
↓
主 Agent 上下文只增加了 500 tokenClaude Code 的 subagent、Codex 的 parallel agent、WorkBuddy 的多 agent 并行,本质都是这个机制。
何时用 subagent:
- 子任务需要大量上下文,但输出可以浓缩
- 子任务可以并行(多个独立分析)
- 子任务失败不该污染主上下文
阶段产出#
完成本章后应该能:
- 构建完整 RAG 流程:解析 → chunking → embedding → hybrid search → rerank → 引用溯源
- 实现权限感知检索(metadata filter)
- 用 Ragas/TruLens 做 RAG Eval(召回/精度/忠实度/引用正确性)
- 理解 Agentic RAG / GraphRAG 的适用场景
- 为长任务 Agent 设计上下文预算分配策略
- 实现 Context Compression(摘要压缩 + 选择性丢弃)
- 实现 Compaction(阈值触发 + 保留窗口 + 关键状态外置)
- 用 Subagent 隔离 context,避免主上下文爆炸
下一章 Ch4 进入 Tool Calling 与 MCP——Agent 怎么调用工具、怎么操作浏览器/GUI、MCP 与 A2A 的边界。这是 Agent 真正"能动手"的关键。