Context Engineering 实战 01|Context Engineering ≠ Prompt Engineering 2.0:到底在"工程"什么
AIReview 系统的 Code Review Agent 上线一个月,效果一般。eval 通过率 62%——能抓住明显 bug,但对业务逻辑问题基本无感。
团队开始优化 prompt。
第一周,改措辞。把"请检查代码质量"改成"按以下维度逐项检查:正确性、安全性、性能、可维护性"。加了 few-shot 示例,展示"好的 review 长什么样"。从 800 token 到 2400 token,通过率 62% → 71%。
第二周,继续加内容。团队的 review 规则库有 200 条规则——安全类 38 条、性能类 27 条、风格类 45 条、业务逻辑类 52 条、其他 38 条。“反正 context window 放得下,全塞进去让模型参考”。Prompt 从 2400 涨到 6200 token。
通过率跌到 55%。加了 200 条正确的规则,比不加还差。
优化手段 prompt 大小 eval 通过率
────────────── ────────── ──────────
初版 800 tokens 62%
措辞优化(一周) 2400 tokens 71%
全量规则注入(两周) 6200 tokens 55% ← 更多规则,反而更差
200 条规则都是对的——每条都是团队从真实 code review 里沉淀出来的。全塞进去为什么效果还退步了?
排查:问题不在"写了什么",在"模型看到了什么"
做了一件事:手动检查 AI 输出的 review 意见,逐条比对它实际"遵循了哪些规则"。
200 条规则注入后,AI 的行为分布:
规则遵循情况 数量 占比
────────── ──── ────
正确遵循,且跟当前代码相关 23条 12%
正确遵循,但跟当前代码无关 31条 16% ← 看到规则就乱套
完全忽略 146条 73%
73% 的规则被完全忽略。16% 被强行套用到不相关的代码上。只有 12% 被正确使用。
“强行套用"是什么意思?比如一条规则是"数据库查询必须有超时设置”——AI 把它套用到一个纯前端组件的 review 上,硬说"这里应该加超时设置"。还有一条"并发操作需要加锁"——AI 在一个同步的配置解析函数里也建议加锁。
AI 不是不懂规则——它在 6000 token 的规则海洋里失去了判断力。该看的没看到,不该用的却用了。
当时团队有人说了一句话:“它不像是没读懂 prompt,更像是什么都看了但什么都没看仔细。”
这句话是突破口。
对照实验:同一个 prompt,不同的 context 组装
为了验证"问题在 context 组装而不是 prompt 措辞",做了一个对照实验。
两组用完全相同的 system prompt 措辞、角色定义、输出格式要求。唯一区别是规则注入方式:
- A 组(静态全量注入): 200 条规则全部写进 context,每次 review 都看到同样的 200 条
- B 组(动态选择注入): 先分析当前 diff 的文件类型和变更内容,从 200 条中选 10-15 条相关规则注入
100 条测试用例,结果:
指标 A组(全量注入) B组(动态选择) 差异
────── ───────── ───────── ────
规则遵循率 45% 83% +84%
误报率(套用不相关规则) 22% 4% -82%
context token 消耗 6000 500 -92%
eval 综合通过率 55% 86% +56%
Prompt 一个字没改。只改了"模型看到哪些规则",通过率 55% → 86%。
更值得注意的是误报率:22% → 4%。全量注入时那些"硬套规则"的行为几乎消失了——因为模型只看到了跟当前代码相关的规则,没有多余的规则可以乱套。
这就是 Prompt Engineering 和 Context Engineering 的分界线。Prompt Engineering 在优化"你怎么说",Context Engineering 在优化"模型看到什么"。 前者是语言问题,后者是信息工程问题。
为什么 200 条不如 15 条:注意力的零和博弈
Transformer 的 attention 不是无限的——它是零和博弈。
每个 token 生成时,模型要对 context 里的所有 token 做注意力分配。Softmax 归一化后总和是 1。context 越长,每条信息分到的注意力越少。
200 条规则(6000 tokens):
每条规则的平均注意力 ≈ 1/200 = 0.5%
15 条规则(500 tokens):
每条规则的平均注意力 ≈ 1/15 = 6.7%
差距:13 倍
而且实际的注意力分布不是均匀的——是 U 形的。开头和结尾的信息拿到更多注意力,中间大段信息落入"注意力凹陷区"。
注意力权重 ↑
│ █ █
│ █ █ █ █
│ █ █ █ █ █ █
│ █ █ █ █ █ █ █ █
│ █ █ █ █ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ █ █ █ █
│ █ █ █ █ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ ░ █ █ █ █
├───────────────────────────────────────────
context 开头 中段 结尾
200 条规则全量注入时,大部分规则挤在中间——注意力凹陷区。模型"看到了"这些规则,但没有给它们足够的注意力权重来真正遵循。
这解释了之前发现的 73% 忽略率:不是 prompt 没写清"要遵守规则",是这些规则在 context 里的物理位置决定了它们拿不到足够的注意力。
这也是为什么误报率会上升——中间凹陷区的规则被部分激活,模型"隐约记得有这么条规则"但不清楚具体适用条件,就粗暴地套用了。
三轮优化实录
理解了注意力机制后,优化方向从改措辞变成了改 context 的组装方式。三天做了三轮。
第一轮:按文件类型粗筛(200 → 60 条)
def filter_by_file_type(rules, file_path):
ext = Path(file_path).suffix
tag_map = {
".py": ["python", "general"],
".ts": ["typescript", "general"],
".sql": ["sql", "database", "general"],
}
tags = tag_map.get(ext, ["general"])
return [r for r in rules if r.category in tags]
review 一个 .py 文件时,TypeScript 规则、SQL 规则自动排除。200 条 → 60 条。
结果:55% → 68%。有提升,但 60 条还是太多。
第二轮:按变更内容语义匹配(60 → 15 条)
def semantic_rank(rules, diff_text, top_k=15):
diff_emb = embed(diff_text)
scored = []
for rule in rules:
sim = cosine_similarity(diff_emb, rule.embedding)
scored.append((rule, sim))
scored.sort(key=lambda x: x[1], reverse=True)
return [r for r, _ in scored[:top_k]]
对 diff 内容做 embedding,跟每条规则的 embedding 算相似度,选 top-15。
结果:68% → 82%。跃升 14 个点——这一轮贡献最大。
第三轮:利用 U 形曲线排列规则位置
高优先级规则放 context 开头(注意力高区),次优先级放结尾(第二高区),普通规则放中间。
def arrange_by_attention(rules):
by_priority = sorted(rules, key=lambda r: r.priority, reverse=True)
high = by_priority[:5] # 最重要 → 开头
low = by_priority[5:10] # 次重要 → 结尾
medium = by_priority[10:] # 一般 → 中间
return high + medium + low # 注意力 U 形分布
结果:82% → 86%。提升幅度小了,但这 4 个点纯粹来自规则的物理排列,一行 prompt 都没改。
优化轮次 动作 通过率 token 消耗
──────── ────────── ────── ─────────
基线 全量注入 200 条 55% 6000
第一轮 按文件类型筛选到 60 条 68% 1800
第二轮 语义匹配选 15 条 82% 500
第三轮 U 形排列 86% 500
三轮下来,token 消耗 6000 → 500(-92%),通过率 55% → 86%(+56%)。Prompt 措辞一字未改。
模型看到的"全部信息"比你以为的大
优化 context 时,大部分人只盯着自己写的 system prompt。但模型的 context window 里远不止这些。
一次 AIReview 的 review 请求,实际的 context 构成:
组成部分 token 占用 你的控制程度
──────── ───────── ──────────
System Prompt 2400 完全控制
Review 规则(动态注入) 500 完全控制
待 review 的 diff 3000-15000 部分控制(可截取关键段)
工具定义(8 个工具) 900 容易忽视 ←
对话历史 2000-8000 需要主动管理
──────────────────────────────────────────────
总计 8800-26800
两个容易被忽视的开销:
工具定义占 900 token。 AIReview 有 8 个工具(search_code、read_file、run_test 等),每个工具的 description + parameter schema 大约 100-150 token。这 900 token 是隐性固定开销,每次请求都在,你的规则和 diff 要跟它们竞争注意力。
对话历史是会膨胀的。 如果不是第一轮 review——比如模型已经检查了 3 个文件——之前的对话可能积累了 5000-8000 token。你花力气把规则从 6000 压到 500,但对话历史悄悄加回来 5000,总体 context 又膨胀了,注意力又被稀释。
这就是 Context Engineering 的范畴:你要管理的不是自己写的那一段 prompt 文本,而是模型每次推理时实际看到的全部信息。 System prompt、动态注入的规则、检索到的外部知识、工具定义、对话历史——五个来源,任何一块膨胀,都会稀释其他部分的注意力。
什么时候 Prompt Engineering 就够了
不是所有场景都需要 Context Engineering。判断方法很简单:
如果你的系统只有一种输入模式,一个固定 prompt 能覆盖所有情况——Prompt Engineering 够用。
翻译、单次分类、简单文本处理——这些任务输入类型单一、不需要外部知识、不需要多轮对话、不用工具。一个写好的 prompt 就是最终答案。
一旦你的系统有以下任何一个特征,就进入了 Context Engineering 的领域:
特征 为什么 PE 不够
───────── ──────────
输入类型多样(>3 种) 不同输入需要不同的规则和示例
需要外部知识(RAG / 规则库) 检索质量直接决定输出质量
多轮对话(>5 轮) 对话历史的膨胀和遗忘需要主动管理
工具数量 > 3 工具 description 吃 token,选择准确率受描述质量影响
输出要求随场景变化 需要动态切换约束和格式
AIReview 全中——多种文件类型、200 条规则库、多轮 review 循环、8 个工具。在这种系统上继续打磨 prompt 措辞,就是在局部最优解上死磕。
PE 和 CE 的一个直觉区分
如果你做的事情只需要打开一个文本文件就能完成——那是 Prompt Engineering。
如果你需要写代码来决定模型看到什么——写检索逻辑、写过滤规则、写排序算法、写组装 pipeline——那就是 Context Engineering。
PE 的工程量在编辑器里,CE 的工程量在代码里。
AIReview 这个案例的具体对比:
Prompt Engineering 的工作量:
打开 system_prompt.txt
把"检查代码"改成"逐行检查以下维度:..."
加 2 个 few-shot 示例
跑 eval 看效果
→ 一周,通过率 +9%(62% → 71%)
Context Engineering 的工作量:
写规则的 embedding 索引(embed_rules.py)
写按文件类型过滤的逻辑(filter.py)
写语义匹配的排序逻辑(ranker.py)
写 context 组装 pipeline(assembler.py)
跑 eval 看效果
→ 三天,通过率 +31%(55% → 86%)
一周 +9% vs 三天 +31%。不是 PE 没用——是在 AIReview 这类系统上,信息选择的杠杆远大于措辞优化。
Prompt 是你写的文本,Context 是模型做决策时看到的一切——后者才是工程的对象。别再优化措辞了,去优化信息供给。
767

被折叠的 条评论
为什么被折叠?



