2 minutes
上下文工程:RAG 与长上下文
如果说 prompt 工程是"怎么说话",上下文工程就是"让模型能看到什么"。这是 2025–2026 年 AI 工程师的核心能力——因为模型能力本身已经够强,前 80% 的产出差距来自上下文喂得对不对。
4.1 三种获取"知识"的方式
模型要"知道事情",只有三条路:
┌─────────────────────────────────────────────────────────────┐
│ 1. Pretrain :训练时就背在脑子里了 │
│ 2. Context :你这次对话贴进来,模型看到了 │
│ 3. Tool Call :模型调用工具(搜索、读文件、grep)拿到结果 │
└─────────────────────────────────────────────────────────────┘
三者构成一个预算三角:pretrain 是免费的但有时效与范围;context 是最贵但最准确;tool call 介于两者之间,但要模型"会调用"。
关键洞察:所有"AI 编程工具"——Cursor、Claude Code、Copilot Workspace、OpenCode——做的核心事都是这三种方式的自动调度。
4.2 RAG:检索增强生成
RAG 解决一个矛盾:你想让模型回答某个文档里的事,但塞全文进 context 太贵太脏。做法是先检索 → 拿到相关片段 → 把片段塞进 context。
# RAG 的极简伪代码
def answer(query: str, knowledge_base: list[str]) -> str:
# 1. 把知识库切成 chunk 并预计算 embedding
chunks = [chunk for doc in knowledge_base for chunk in split(doc, size=512)]
chunk_embeddings = embed(chunks)
# 2. 用 query 检索 top-k 相关 chunk
query_emb = embed(query)
top_k = cosine_topk(query_emb, chunk_embeddings, k=5)
# 3. 把片段塞进 prompt
return client.messages.create(
model="claude-...",
messages=[{
"role": "user",
"content": f"基于以下资料回答:\n\n{top_k}\n\n问题:{query}"
}]
)
RAG 的工程陷阱
| 陷阱 | 现象 | 解法 |
|---|---|---|
| 切分粒度太粗 | 命中文章却错过段落 | 按 “语义边界"切(章节、heading)而非定长 |
| Chunk 没元信息 | 命中旧文档以为是新规章 | 给每个 chunk 加 source / version / updated_at |
| 检索召回低 | 用户问的就是答案却没召回 | 加 hybrid search(向量 + BM25 关键词) |
| 答非所求 | 模型把无关片段当依据 | 给片段加 confidence,低分不加进 context |
4.3 长上下文:直接塞满整个项目
2026 年的城市主流模型普遍 200k–1M token 上下文,看起来"塞全部源文件"可以了——但事情没那么简单。
Lost-in-the-middle 效应
研究人员发现,模型对开头和结尾的注意力高,对中间的注意力低。这意味着:
❌ 把整个 repo 塞前面 → 模型"问题"被挤到结尾时已疲软
⚠️ 把全部搜索结果铺开 → 中间的 20 条几乎被忽略
✅ 把最相关的放头尾,无关的不塞
实操:在 Claude Code / OpenCode 等工具里,你不需要手动管这个——它们有截断 / 重排序策略。但写自己的 harness 时务必考虑。
项目正常大小 vs 上下文窗口
项目 代码 token 上下文够吗 现实策略
─────────────────────────────────────────────────────
单功能 demo <10k 一发塞完 ✅ 直接全塞
单人一周项目 30–100k 边缘 🟧 检索后塞
中型团队项目 200k+ 不够 ❌ 走 RAG / MCP
大型工程 >1M 远远不够 ❌ 走 Agent 分治
4.4 三种长上下文实战策略
策略 1:压缩 + 重排
让模型先总结再回答:
PASS 1(总结):读下面 [50 个文件],每文件给我 50 字总结,按功能分组。
PASS 2(回答):基于上一步的总结 + 你需要的原文件(自己去 grep),回答问题。
代价:双步成本翻倍,但有显著准确度收益。
策略 2:渐进式加载
让 Agent 拿目录树 → 拿几个文件 → 在 scope 内展开。这是 Claude Code、Codex 的核心机制:
# OpenCode 风格的渐进加载策略
async def load_repo(path):
files = await list_all_markdown(path, depth=2) # 拿个目录树
summary = await summarize_files(files, k=10) # 让 AI 看 10 篇
candidate = await ai_pick_files(summary, query) # AI 自己挑
return await read_files(candidate) # 才真正全文加载
策略 3:分布式 Agent(第五篇实战)
如果项目太大(>1M token),单个上下文塞不下,让多个 Agent 分而治之——每个 Agent 拿一个模块、调外部协调 Agent。这一节在 28 实战:多 Agent 协作 展开。
4.5 上下文工程的五个原则
把"让模型看到什么"当一种投资组合:
- 越靠近 → 越贵:根目录的 README.md 放 context 比放 RAG 检索召回要直接
- 越小越好:你能给 5 个文件就别给 50 个;让模型主动去 grep
- 可解释优先:模型必须能"说出"它看到了什么——不做"静默吞掉”
- 结构 > 散文:项目结构、front matter、明确分隔符比自然语言叙述更省 token
- 存量复用 > 重复生成:把"反复被查的"沉淀成 MCP 资源 或 Skill
4.6 一道实操:手动模拟 RAG
任何能用 Python 的环境都能做:
pip install openai pyarrow # 一个 embedding + 一个本地 chunk
export OPENAI_API_KEY=...
python -c "
import openai, numpy as np
docs = ['用户登录用 JWT', '订单表是 orders', '限速中间件叫 rate_limit', 'JWT 30 分钟过期']
emb = openai.embeddings.create(model='text-embedding-3-small', input=docs).data[0].embedding
print(len(emb))
"
再实现一下 cosine_topk + 选 top 2 + 拼进 prompt。整个流程会让你瞬间明白 RAG 不是黑魔法,是一行加权和。
4.7 小结
- 三条知识路径:pretrain / context / tool call
- RAG = 检索 + 塞片段,长上下文 ≠ 万灵药
- 五原则:靠近越贵 → 越小越好 → 可解释 → 结构化 → 沉淀复用
下一篇:《05 主流 AI 编程工具全景》——当别人在用工具时,你要明白它们各自擅长什么。
Summary: 三种知识路径 + RAG/长上下文权衡 + 五原则,构建给模型"喂"上下文的投资组合。