跳转至

实验 8-3:系统提示词的自动优化(★★)

《深入理解 AI Agent》配套代码 · 第 8 章

基于人类反馈的 自动化系统提示学习:让一个 Coding Agent 读取系统提示词文件、 定位有问题的规则、生成精确修改并 真的改写 prompt 文件,从而修复 Agent 的 "过度转接" 行为。场景为 tau-bench 风格的航空客服。

1. 目的与问题

初始的航空客服 Agent 里,人工转接规则写得很含糊——"仅当请求无法在你的行动范围内 处理时才转接",并且强调"客户满意度第一,遇到不满就转人工,不要与乘客争辩政策"。

评测发现:Agent 过度转接——一遇到政策争议(要求超政策退款、要求免费、要求豁免 费用)就直接甩给人工,而不尝试向乘客解释政策。

人类专家反馈:这类争议应当 通过耐心解释政策来处理,而不是一转了之。真正需要转 人工的只有两种情况:乘客明确要求人工客服紧急安全/人身健康风险

本实验演示一条自动化闭环:人类反馈 → Coding Agent 改写系统提示词 → 重新评测验证

2. 方法与流程

初始 prompt ──评测──► 暴露"过度转接"问题
              人类反馈 ───┤
                   Coding Agent(读文件→定位规则→生成精确 search/replace 编辑→改写文件)
                          │  展示真实 diff
             自动优化后 prompt ──评测──► 边界集正确率↑ 且 保留集不退化
              对照:人工调优版 prompt ──评测──►

两组评测用例(各 5 个,控制成本、结论可复现):

  • 保留任务集 (holdout):正常请求。3 个应由 Agent 自行处理(改签 / 行李额 / 选座), 2 个本就该转接(乘客明确要人工 / 紧急安全)。用来检验优化 不会破坏既有正确行为
  • 边界案例集 (boundary):5 个政策争议(不可退票要退款 / 要求免改签费 / 小延误索赔 / 索要免费升舱 / 超额免费行李)。正确行为是 解释政策、不转接。用来检验过度转接被修复。

判据(evaluate.py):结合确定性规则 + LLM-as-judge—— - 该转的用例:正确 ⇔ 确实调用了 transfer_to_human; - 不该转的用例:正确 ⇔ 没转接,且 LLM 裁判确认它按 rubric 妥善解释了政策 / 办理了业务。

Coding Agent(coding_agent.py)不整篇重写,而是像真实编程 Agent 一样产出一组 (old_str → new_str) 精确编辑,由代码逐条做精确字符串替换落盘;匹配不上就把错误反馈 给模型重试,保证修改可审计、可出 diff。

3. 文件结构

文件 说明
demo.py 一条命令跑通完整流程(评测→改写→复评→对照→对比表)
airline_env.py 精简航空客服模拟环境:工具(含 transfer_to_human)、Agent 循环、10 个用例
coding_agent.py Coding Agent:读取并 改写 系统提示词文件,输出 diff
evaluate.py 评测器:规则 + LLM-as-judge 判定每个用例是否被正确处理
config.py LLM 客户端配置(默认 OpenAI gpt-5.6-luna,可切 Moonshot / 火山方舟;缺 Key 时 OpenRouter 兜底)
prompts/system_prompt.txt 初始系统提示词(含会诱发过度转接的规则)
prompts/system_prompt_manual.txt 对照组:人工调优版系统提示词
runtime/system_prompt_working.txt 运行时由 Coding Agent 改写的工作副本(每次运行重置)

4. 运行

pip install -r requirements.txt
cp env.example .env      # 填入 OPENAI_API_KEY(或仅设 OPENROUTER_API_KEY 走兜底)
python demo.py           # 完整运行:10 个用例 × 3 份 prompt
python demo.py --quick   # 快速演示:每组只取 2 个用例,省时省钱(推荐先跑这个)
python demo.py --help    # 查看全部命令行参数(中文说明)
python demo.py --dry-run # 离线自检:只打印配置与选中用例,不调用任何 API

命令行参数(python demo.py --help 可见完整中文说明):

参数 作用 默认
--quick 每组只取 2 个用例,省时省钱 关闭
--limit N 每组最多评测 N 个用例(覆盖 --quick 不限
--group {holdout,boundary,both} 选择评测的任务集 both
--rounds N Coding Agent 自动改写提示词的最大重试轮数 3
--model NAME 覆盖 LLM 模型名(等价于 LLM_MODEL config.py
--provider {openai,moonshot,ark} 覆盖 LLM 提供商(等价于 LLM_PROVIDER openai
--output PATH 把优化前后 + 人工对照的对比结果写成 JSON 不写
--dry-run 离线:只打印解析后的配置与用例数,不调用 API 关闭

默认模型 gpt-5.6-luna(读取 OPENAI_API_KEY;缺失时若有 OPENROUTER_API_KEY 则自动改走 OpenRouter 并映射到 openai/gpt-5.6-luna),温度 0(结果可复现)。完整运行跑 10 个用例 × 3 份 prompt,约几十次 API 调用、数分钟;--quick(或 --limit N 指定每组 用例数)会显著缩短耗时,用于快速验证闭环。命令行参数优先级高于环境变量:--model / --provider 会覆盖 .env 中的 LLM_MODEL / LLM_PROVIDER。加 --output output/run.json 可把对比表落盘为 JSON(output/ 已被 .gitignore 忽略),便于复现与二次分析。若未设置 对应 API Key,程序会打印清晰的中文错误提示并退出,而不是抛出堆栈。

优化后的工作副本会写入 runtime/system_prompt_working.txt(每次运行自动重置,属生成 工件,已被 .gitignore 忽略)。

5. 真实运行结论

下表为一次真实运行(gpt-5.6-luna,完整 10 用例)的结果:

正确率对比(保留任务集 = 既有正确行为不能退化;边界案例集 = 过度转接应改善)
==========================================================================
系统提示词版本                   保留任务集(holdout)      边界案例集(boundary)
--------------------------------------------------------------------------
初始 prompt(优化前)          5/5 (100%)            0/5 (0%)
自动优化后 prompt            5/5 (100%)            1/5 (20%)
人工调优版(对照)               5/5 (100%)            2/5 (40%)
==========================================================================

【结论】
  · 边界案例集正确率:0/5 → 1/5 (提升 ✓)
  · 保留任务集正确率:5 → 5 (未退化 ✓)
  • 边界案例集:优化前 5 个用例全部"一转了之"(过度转接 5/5),优化后 转接彻底消失 (边界集转接数 5 → 0),过度转接问题被修复;正确率从 0/5 升到 1/5——B5(超额免费行李) 由主动解释重量计费政策而被判合格,B1/B2/B3/B4 虽不再转接,但模型倾向先向乘客索要订单号、 未充分把政策解释到位,被严格的裁判判为"处理不当",属于真实的边界表现。
  • 保留任务集:优化前后均 5/5,既有正确行为(含 H4/H5"该转的仍会转")未退化
  • 自动优化后的效果接近 人工调优版对照组(自动边界 1/5、人工 2/5,保留均 5/5);人工版在 B2(要求免改签费)上多解释到位一例,差距在裁判的 ±1 个用例波动范围内。

注:具体数字可能随模型版本与采样有 ±1 个用例的波动,但"边界集过度转接被修复、保留集不退化、 且与人工调优接近"的结论稳定可复现。上表为 gpt-5.6-luna 的一次真实运行(因该模型仅支持默认 温度,本次以 LLM_TEMPERATURE=1 运行)。

Coding Agent 对 system_prompt.txt 的真实改写(diff 节选)会在 python demo.py 的 【步骤 2】中打印,核心是把第 3、4 条转接规则收紧为"仅乘客明确要求人工 / 紧急安全才转", 并新增负面规则"绝不因政策争议或乘客不满而转接,应先解释政策再给替代方案"。

补充:模型 ↔ 脚手架此消彼长(强模型 vs 弱模型的两次真实运行)

一个自然的问题:模型更强,这套"自动改 prompt"的脚手架是不是就不那么必要了? 为此我们把完全相同的闭环(10 用例 × 3 份 prompt × 最多 3 轮改写)在一个较弱模型 gpt-4o-mini(OpenAI 直连,LLM_TEMPERATURE=0)上又真实跑了一次,与上文 gpt-5.6-luna 的运行对照。边界案例集(过度转接应改善)的优化前 → 自动优化后变化:

模型 保留集(holdout) 边界集 优化前 → 自动优化后 自动优化增益 人工调优版(对照)
gpt-4o-mini(较弱) 5/5 → 5/5(未退化) 1/5 → 3/5 +2 个用例 3/5
gpt-5.6-luna(较强) 5/5 → 5/5(未退化) 0/5 → 1/5 +1 个用例 2/5
  • 方向上,较弱的 gpt-4o-mini 从这套自动优化脚手架里拿到的增益更大(边界集 +2 个用例, 1/5→3/5),强于 gpt-5.6-luna 的 +1(0/5→1/5);且优化后 gpt-4o-mini 已追平其人工调优版 (均 3/5)。这与"弱模型更依赖脚手架、强模型对同一条基线 prompt 的容错更高"的直觉一致。
  • 但需诚实标注两点,避免过度解读:(1) 两者增益之差仅 1 个用例,正落在本实验反复申明的 ±1 个用例裁判波动带内;(2) 两次运行采样条件不同——弱模型 temperature=0(确定性), 强模型因仅支持默认温度而 temperature=1(有采样噪声)。因此这是一次方向性、非结论性的 对照:可以说"弱模型从脚手架获益不少于强模型",但不宜据这 1 个用例的差断言强模型"就不需要" 自动优化。真正稳健的结论仍是两模型共有的那条——边界集过度转接被修复、保留集不退化、 且自动优化逼近人工调优

6. 如何适配 / 扩展与局限

  • 换模型 / 供应商LLM_PROVIDER 可切换 openai / moonshot / ark(均兼容 OpenAI 接口), LLM_MODEL 覆盖模型名,LLM_TEMPERATURE 调采样温度(默认 0,见 config.py / env.example)。
  • 换任务 / 输入:评测用例集在 airline_env.pyCASES(分 holdout / boundary 两组); 驱动优化的人类反馈是 demo.py 顶部的 HUMAN_FEEDBACK;初始与人工对照 prompt 在 prompts/ 下—— 改这几处即可把闭环套到你自己的场景。
  • 局限:环境为教学用途的精简模拟,工具返回固定 mock 数据;重点在"人类反馈驱动提示词自动优化" 这一闭环,而非完整复刻 tau-bench。具体正确率会随模型版本与采样有 ±1 个用例的波动。