AI Agent 上下文消融实验实录
移除哪块"上下文"最致命?—— 一次 AI Agent 上下文消融实验实录
我们用一个真实的跨国预算分析任务,把 Agent 上下文中的四个组件逐一"摘除",
观察它何时崩溃、何时变慢、以及最可怕的一种情况——看起来成功了,答案却是错的。
一、引言:为什么上下文是 Agent 的生命线
和传统的一次性问答不同,AI Agent 是在一个循环里工作的:理解任务 → 调用工具 → 观察结果 → 再决策 → 再调用……直到产出最终答案。驱动这个循环的,不只是模型本身,更是喂给模型的那一长串上下文:
- 历史工具调用(我做过什么)
- 推理过程(我当时的思考)
- 工具调用命令(我要调用哪个工具、传什么参数)
- 工具返回结果(刚才那一调用回传了什么)
这些组件各自值多少钱?哪个是"命根子"、哪个是"可选项"?回答这个问题不能靠猜,要靠消融实验(Ablation Study)——把组件一个一个拿掉,看 Agent 的表现如何变化。
本文基于《深入理解 AI Agent》第 1 章"实验 1-1 ★★:上下文的关键作用"的代码与思路,在 DeepSeek 模型上跑了一轮完整消融实验,下面是全部数据与解读。
二、实验设计
2.1 任务
一个跨国预算分析任务("Multinational Budget"),要求 Agent:
- 解析一份 PDF 财务报告;
- 通过货币换算工具把多币种数据统一为美元;
- 计算全球总支出、各区域占比;
- 按 8% 的比例做全员预算削减后,重新计算各项数字。
这是一个典型的多步 Agent 任务:外部数据获取(PDF)+ 工具计算 + 顺序依赖的推导,任何一步断掉都会影响最终结果。
2.2 模型与配置
- 模型:DeepSeek(默认模型)
- 基线与四个消融臂共 5 种上下文配置:
| 配置 | 拿掉了什么 | 一句话含义 |
|---|---|---|
full(基线) |
什么都不拿 | 完整上下文 |
no_history |
历史工具调用 | Agent 记不住自己做过什么 |
no_reasoning |
推理过程 | Agent 不再"想一步做一步" |
no_tool_calls |
工具调用命令 | Agent 无法调用任何外部工具 |
no_tool_results |
工具返回结果 | Agent 发出调用,却看不到返回值("盲操作") |
2.3 观察指标
- 执行时间(execution_time):跑完(或放弃)花了多少秒
- 迭代轮数(iterations):Agent 与模型来回了几轮
- 工具调用次数(num_tool_calls):实际调用了多少次工具
- 是否产出终态回答(has_final_answer / completed):有没有给出最终答案
⚠️ 一个重要的方法论提醒:本套件的
success字段只表示"Agent 给出了一个终态回复",并不代表任务做对了。任务正确性必须用任务级的评分标准(rubric)单独核对。这一点在第四组实验里会变成血淋淋的教训。
三、结果一览
| 配置 | 执行时间 | 迭代轮数 | 工具调用 | 终态回答 | 最终答案正确性 |
|---|---|---|---|---|---|
| 完整上下文(基线) | 23.74s | 3 | 6 | ✅ | ✅ 正确($11,990,955.43) |
| ① 无历史工具调用 | 41.69s | 10 | 50 | ❌ 未完成 | — |
| ② 无推理过程 | 23.49s | 4 | 6 | ✅ | ✅ 正确(同上) |
| ③ 无工具调用命令 | 62.67s | 1 | 0 | ❌ 未完成 | — |
| ④ 无工具返回结果 | 83.03s | 6 | 10 | ⚠️ "完成" | ❌ 错误($11,951,000) |
(完整原始数据见文末仓库中的 ablation_results.json)
四、逐臂解读
4.1 ① 无历史记录:失忆症的代价是"重复劳动"
去掉历史工具调用后,Agent 变成了一个失忆症患者:它不记得自己刚刚解析过 PDF、也不记得已经查过汇率,于是:
- 工具调用从基线的 6 次暴涨到 50 次(超过 8 倍);
- 迭代了 10 轮仍然没有跑完,最终没有产出任何终态回答;
- 用时从 23.74s 膨胀到 41.69s。
解读:历史记录的价值不在"聪明",而在"不犯蠢"。没有它,Agent 每一步都从零开始,多步依赖任务(先用 PDF 结果、再算汇率、再汇总)会陷入无休止的重做循环。它是多步任务的连贯性保障。
4.2 ② 无推理过程:本任务上几乎零影响
去掉推理过程后,Agent 表现与基线几乎完全一致:
- 用时 23.49s(基线 23.74s);
- 工具调用同为 6 次;
- 最终答案与基线一字不差($11,990,955.43)。
解读:对这个任务来说,模型的"推理文本"是冗余的——不写思考过程,答案照样对,速度还略快。这提示我们:推理过程不是免费的午餐,也不是万灵药。当然,这只是一个任务、一个模型上的结论;在数学证明、长链条规划等复杂推理任务上,推理过程的价值可能会完全不同。
4.3 ③ 无工具调用命令:直接瘫痪
拿掉工具调用后,Agent 变成了一个"只能空谈的咨询师":0 次工具调用,只迭代了 1 轮就结束了,没有产出任何答案(用时 62.67s 主要耗在了单轮的长回复上)。
解读:这是最不意外的一组。这类任务的数据(PDF 内容、汇率)完全来自外部工具,砍掉工具等于砍掉了任务的地基。工具是硬依赖,没有商量的余地。
4.4 ④ 无工具返回结果:最危险的"成功" ⚠️
这是整场实验里最值得警惕的一组。Agent 可以发出工具调用,但看不到任何返回值——像一个蒙着眼睛开车的人:
- 它花了 83.03s(全场最慢),迭代 6 轮、调用 10 次工具;
- 协议层面它"完成了":
completed=true、success=true、有终态回答; - 但答案错了:它给出总支出 11,951,000,而正确答案是 **11,990,955.43**,少了约 4 万美元(相对误差约 0.33%);
- 各地区占比也全部跑偏(如美国 20.92% vs 正确的 20.85%,日本 20.99% vs 正确的 21.20%)。
解读:看不见结果,Agent 就会用"猜"来填补空白——它甚至会在盲区里重复调用(10 次 vs 基线的 6 次)来试图"蒙对"。最可怕的地方在于:这是一个表面健康的失败。如果你只看 success 标志,你会以为一切正常,然后把这个差 4 万美元的数字写进预算报告。
这正是实验报告里那句警告的含义:
"A terminal response is not evidence that the financial task was completed. Task-level claims require a separate evaluator."
协议完成 ≠ 任务完成。success 会撒谎,rubric 不会。
五、关键启示
启示 1:验收 Agent,必须用任务级评分标准
success=true 只证明"Agent 说了句结束语"。要判断它到底做没做对,必须有一个任务特定的 rubric——例如把最终答案与预期数值比对(本仓库的 run_experiment_1_1.py 就是这么做的:它把模型的回答与预计算的标准答案做数值核对)。第四组实验就是"无 rubric 验收"的完美反面教材。
启示 2:上下文裁剪的经济学——要省钱,先砍谁?
上下文越长越贵,生产环境里大家总想"裁剪"上下文省 token。这组实验给出的优先级建议是:
- 最不能砍:工具调用 + 工具返回结果。砍前者直接瘫痪,砍后者产出错误答案——省下的钱不够一次错误决策的损失。
- 谨慎保留:历史工具调用。砍掉它不会马上出错,但会让成本失控(50 次调用 vs 6 次),效率损失本身就是成本。
- 优先候选:推理过程。在本任务上几乎零损耗。若真要做上下文压缩,考虑对推理文本做摘要/截断,而不是直接删除历史与工具结果——删除是"截肢",摘要才是"减肥"。
启示 3:正确性比"跑完"更难测量,也更值钱
四组消融里,最贵的一组(83s、10 次调用)反而是错误的一组。"慢但错"比"快但没做完"危害更大,因为它会混入正常结果、污染下游决策。评估体系必须把"正确性"和"完成度"分开统计。
六、局限与后续工作
这份实验必须诚实地标注边界:
- 单任务、单模型、单次运行:结论来自一个任务(跨国预算)和一个模型(DeepSeek)的一次运行。LLM 输出有随机性,严谨起见应当多次重复取均值;
- 推理过程"无用"的结论不能外推:本任务的推理依赖较浅;在复杂推理任务上,
no_reasoning可能表现出完全不同的结果; - 缺少任务级 rubric 的自动评判:本套件记录了行为数据,但正确性核对目前依赖人工/独立脚本(
run_experiment_1_1.py提供了数值 rubric 的参考实现)。
后续可以做的方向:多模型横向对比、多任务重复实验、推理文本压缩(摘要 vs 截断)的消融、以及把 rubric 自动评分接入主流程。
七、复现指南
想亲手跑一遍?三步即可:
# 1. 获取代码
git clone https://github.com/Hanserwei/ai-agent-context-ablation.git
cd ai-agent-context-ablation
# 2. 配置 API Key
pip install -r requirements.txt
cp env.example .env
# 编辑 .env,填入任一 provider 的 API Key(如 DEEPSEEK_API_KEY)
# 3. 运行消融实验
python main.py --mode ablation
运行后会在当前目录生成:
ablation_results.json—— 每组配置的原始数据(时间、迭代、调用次数、终态回答)ablation_study_report.md—— 自动生成的英文实验报告ablation_study_results.png—— 可视化对比图
用 run_experiment_1_1.py 可以跑带任务级数值 rubric 的严谨版本(它会持久化每一次请求/响应,并核对最终答案是否正确)。
八、结语
一个看似简单的消融实验,暴露了 Agent 工程里最容易被忽视的真相:上下文组件之间是不平等的。工具调用与工具结果决定 Agent"能不能做对",历史记录决定"做得贵不贵",而推理过程在某些任务上只是锦上添花。
以及那条最值得写进团队检查清单的教训:永远不要用"它跑完了"替代"它做对了"来验收一个 Agent。
代码仓库
本文所有实验代码与完整结果数据均已开源:
📦 https://github.com/Hanserwei/ai-agent-context-ablation
(代码来源:https://github.com/bojieli/ai-agent-book/tree/main ,chapter1/context 目录,实验 1-1:上下文的关键作用)