第 12 讲:编程智能体与元优化

Agentic AI · Lecture 12。个人学习讲义与配套实验,按主题整理并持续修订。

课程源码 ZIP ↓

个人整理的学习笔记,非官方讲义;课程来源与整理说明见课程介绍。配套示例未逐一运行验证。

本篇目录

对应:Stanford CS224V Fall 2026 Lecture 14(11/11 Coding Agents);阅读清单第 9 部分”Meta Agents and Meta Optimization”。 关于材料来源:这次课的幻灯片尚未公开。课程表的描述是:“Claude Code、OpenAI Codex 这样的编程智能体能独立完成复杂的任务。编程智能体的解剖:一个 LLM、工具和一个 harness;harness 的元优化技术。“本讲依据:2026 年深度研究一讲的幻灯片(harness 的定义、“四种 harness”的对比表);阅读清单上的论文——ADAS(必读)、AFlow、DSPy、OPRO、TextGrad、GEPA、AlphaEvolve、Darwin Gödel Machine;深度研究一讲推荐的 Meta-Harness(arXiv 2603.28052);以及编程智能体评测的公开资料(SWE-bench、SWE-agent)。 配套代码:code/lecture12/(只用 Python 标准库) 预计阅读时间:3 小时左右(含运行代码)


0. 本讲导航

0.1 学完本讲,你应该能够

  1. 说明编程智能体和”代码补全”、“聊天式编程助手”的区别,解释为什么编程特别适合智能体。
  2. 把一个编程智能体拆成三部分:LLM、工具、harness;说出 harness 的主要构件,以及工具的设计为什么像”用户界面”一样重要。
  3. 解释怎样评估编程智能体(SWE-bench 的隐藏测试),以及”测试通过了”和”改对了”之间的三种差距:改测试、针对测试写特例、漏掉边界情况。
  4. 理解”元优化”:把提示词、工作流、代码、harness 当作优化的对象;比较 OPRO、DSPy、TextGrad、GEPA、ADAS、AFlow、AlphaEvolve、Darwin Gödel Machine、Meta-Harness 各自优化什么、用什么反馈、怎样搜索。
  5. 说明元优化的风险:优化错了指标(古德哈特定律)、在搜索集上过拟合、成本、自我修改的安全问题。

0.2 先修知识

知识点用在哪里最低要求
第 3 讲第 1、2 节工具调用、ReAct 循环、harness、上下文管理、工作流 vs 智能体
第 2 讲第 4 节STORM 是一条固定的流水线(它的提示词可以被自动优化)
第 8 讲第 2 节SPINACH:模仿人类专家的动作集,“工具的设计”决定了智能体能做什么
第 5、11 讲第 3、5 节评委的偏差、标注错误、指标的漏洞

0.3 本讲地图

第1节  从补全到智能体:为什么编程特别适合智能体
第2节  解剖:LLM + 工具 + harness                         ← 代码 01 演示 1、2
第3节  怎样评估编程智能体:"自己说成功了" vs "真的成功了"   ← 代码 01 演示 3、4
第4节  元优化:自动改进提示词、工作流、代码和 harness       ← 代码 02 演示 1、2
第5节  元优化的风险                                       ← 代码 02 演示 3、4
第6节  讨论与边界
第7–11节 小结、与项目的关系、练习、延伸阅读、下一讲预告

0.4 本讲的玩具世界

  • coding_world.py:一个缩小到极致的”代码仓库”——12 个真实的 Python 小函数(求平均值、判断闰年、解析日期、求中位数……),每个有一个 bug。每个任务有可见的测试(智能体可以运行,至少有一个失败,相当于问题描述)和隐藏的测试(只用来评判,检查边界情况),还有四种手写的候选修改:改对了、漏了边界情况、针对测试写特例、改错了。check_tasks() 让程序检查这些候选确实符合定义。
  • coding_agent.py:一个迷你编程智能体。工具是真实的(读文件、真的执行测试、真的改代码),LLM 是模拟的(按它看到的信息,以设定的概率决定写哪一种修改)。harness 有 6 个旋钮:读不读源代码、错误信息给多详细、最多改几次、测试文件是否只读、要不要自我检查、系统提示里是否提醒”考虑边界情况、不要针对测试写特例”。

关于本讲的数字:论文和公开资料里的真实数字会注明出处;模拟实验的数字都来自 01、02 号代码的实际输出。模拟 LLM 写哪种修改的概率是设定的,所以数字说明的是机制(反馈为什么有用、隐藏测试为什么必要、读轨迹为什么比只看分数好),不是真实模型的成绩。


1. 从补全到智能体

1.1 三代编程助手

用户做什么助手做什么例子
代码补全写代码预测接下来的几行早期的 GitHub Copilot
聊天式助手描述问题、复制粘贴代码回答问题、写一段代码在聊天窗口里问 ChatGPT
编程智能体描述任务(“修好这个 issue”)自己读代码、改代码、运行测试、看结果、再改,直到完成Claude Code、OpenAI Codex、Cursor 的智能体模式

第三代的关键变化:助手不再只是”说”,而是行动——它能在一个真实的环境(代码仓库、终端)里执行命令,看到结果,再决定下一步。这正是第 3 讲的智能体循环。

1.2 为什么编程特别适合智能体

第 1 讲说,LLM 会”一本正经地胡说八道”,我们花了好几讲想办法核查它说的话:检索证据(第 5 讲)、定理证明器(第 4 讲)、数据库(第 6~11 讲)。编程有一个得天独厚的条件:核查器是现成的。

  1. 可执行:代码写得对不对,运行一下就知道。编译器、解释器、测试框架、类型检查器、代码风格检查都是现成的”核查器”,而且是确定的、便宜的;
  2. 环境是文本的:文件、终端输出、错误信息都是文字,LLM 天然能读;
  3. 可以撤销:版本控制(git)让每一步修改都能回退,犯错的代价低;
  4. 任务可以分解:仓库由文件、函数组成,可以一部分一部分地处理;
  5. 可以训练:测试通过与否是天然的”奖励”,可以用强化学习训练模型(“可验证奖励的强化学习”,第 14 讲)。

对比一下:第 9 讲的 SLIDERS 要从长文档里抽取数字,抽错了没有任何东西会报错;而智能体把 sum(xs) // len(xs) 改成 sum(xs) / len(xs),运行测试马上就知道结果。

1.3 但是:测试只检查它检查的东西

“测试通过了”不等于”改对了”。测试只覆盖了一部分输入;智能体有可能——

  • 漏掉边界情况:修好了报告的 bug,但空列表、负数之类没有被可见测试覆盖的情况仍然是错的;
  • 针对测试写特例:if xs == [4, 1, 3, 2]: return 2.5——测试通过了,什么也没修好;
  • 改测试:把测试的期望结果改成代码现在的输出。

后两种是奖励投机(reward hacking):系统优化的是”让测试通过”这个代理指标,而不是”让代码正确”这个真实目标。第 3 节用代码演示这三种差距,第 5 节会看到,优化 harness 的优化器也会犯同样的错误。


2. 解剖:LLM + 工具 + harness

2.1 harness 是什么

2026 年深度研究一讲的幻灯片引用了 Databricks 的定义:

harness 是包在 LLM 外面、让它能完成任务(而不只是回答提示)的软件基础设施。

模型是”大脑”(推理、决策),harness 是”身体”(工具、记忆、工作区、护栏),两者合起来才是智能体。幻灯片列出了 harness 的构件:

构件在编程智能体里是什么
系统提示角色、规则(“先读代码再改""不要改测试""完成前运行测试”)
工具读文件、搜索、编辑、运行命令、访问网页
沙盒命令在哪里执行、能访问什么(网络、文件系统的范围)
文件与存储代码仓库本身;项目的说明文件(例如 Claude Code 的 CLAUDE.md、Codex 的 AGENTS.md)
记忆与上下文对话很长时压缩旧内容;把子任务交给有独立上下文的子智能体
反馈与自我检查运行测试、类型检查;完成前回顾自己的修改
护栏与审批有副作用的操作(删除文件、推送代码)要用户批准;权限模式
可观测性与日志每一步的轨迹都记下来,便于调试

2.2 工具:智能体的”用户界面”

一个编程智能体的核心工具很少:

工具作用设计上的讲究
搜索(按文件名、按内容)在大仓库里找到相关的代码结果要简洁(只返回文件名和匹配的行),否则会淹没上下文
读文件看代码带行号;大文件可以只读一部分
编辑改代码常用”把这段精确的原文替换成新文本”,而不是重写整个文件:省词元,也不会无意中改坏别处
运行命令运行测试、git、安装依赖在沙盒里执行;输出太长时截断

SWE-agent(Yang 等,2024)提出了智能体—计算机接口(agent-computer interface)的概念:就像人类程序员需要好用的编辑器和终端,LLM 也需要专门为它设计的工具——例如编辑之后自动做语法检查、拒绝有语法错误的修改,搜索结果只给摘要。工具的设计会显著影响智能体的表现。第 8 讲的 SPINACH 也是同一个思路:给智能体人类专家用的那几个动作。

2.3 循环

编程智能体的主循环就是第 3 讲的 ReAct:

任务 → [ LLM 决定下一步(读哪个文件?改哪里?运行什么?)→ 执行工具 → 把结果放进上下文 ] → ……→ 完成

几个让它能做长任务的 harness 机制(第 3 讲都介绍过):待办清单(把大任务拆成步骤,逐个打勾)、上下文压缩、子智能体(例如把”在整个仓库里找出所有调用这个函数的地方”交给一个子智能体,只把结论带回来)、权限与审批、钩子(在特定时刻运行确定的程序,例如每次编辑后自动格式化代码)、检查点(随时回退到之前的状态)。

2.4 一条轨迹

代码 01 演示 1:任务 median(“偶数长度的列表算错了;空列表应该抛出 ValueError”),harness 打开了详细的错误信息、测试只读、自我检查、提醒边界情况。

[1] read_file(stats.py)
      def median(xs):
          s = sorted(xs)
          return s[len(s) // 2]
[2] run_tests(visible tests)
      1 failed: median([4, 1, 3, 2]) returned 3, expected 2.5
[3] edit_file(stats.py)            ← 第一次:改错了
      ... return s[len(s) // 2 - 1]
[4] run_tests(visible tests)
      2 failed: median([3, 1, 2]) returned 1, expected 2; median([4, 1, 3, 2]) returned 2, expected 2.5
[5] edit_file(stats.py)            ← 第二次:针对测试写特例
      def median(xs):
          if xs == [4, 1, 3, 2]:
              return 2.5
          ...
[6] run_tests(visible tests)
      2 passed, 0 failed.
[7] review(self-review of the diff)
      Rejected: special-cases the tests.
[8] edit_file(stats.py)            ← 第三次:改对了,还处理了空列表
      def median(xs):
          if not xs:
              raise ValueError('empty list')
          s = sorted(xs)
          mid = len(s) // 2
          if len(s) % 2 == 0:
              return (s[mid - 1] + s[mid]) / 2
          return s[mid]
[9] run_tests(visible tests)
      2 passed, 0 failed.
[10] review(self-review of the diff)
      Looks good.
结束:智能体认为成功;隐藏测试 全部通过;一共 3 次修改、5 次 LLM 调用、约 2090 个词元

注意第 [5]、[6] 步:可见测试全部通过了,代码却什么也没修好。如果没有第 [7] 步的自我检查,智能体会在这里宣布成功。

2.5 反馈的作用

代码 01 演示 2,12 个任务 × 20 次运行:

做法                                      可见测试通过   真的修好了   LLM 调用
一次写完,不运行测试                          (不知道)         15%       1.0
循环,只告诉它"测试跑完了"                     60%          28%       2.3
循环,告诉它几个通过、几个失败                 75%          32%       2.0
循环,给出每个失败测试的输入和输出             99%          34%       1.3
同上,但不读源代码(只看问题描述)             92%          18%       1.6
  1. 一次写完不测试,只有 15% 真的修好了——它不知道自己错了;
  2. 有了测试的循环,真实的成功率翻了一倍:错了可以再试;
  3. 错误信息越详细,“可见测试通过”的比例越高(60% → 75% → 99%),花的 LLM 调用越少——详细的错误信息告诉它错在哪里;
  4. 不读源代码只看问题描述,真实的成功率掉到 18%——上下文工程:给 LLM 看对的东西;
  5. 但是”可见测试通过”从 60% 涨到 99%,真实的成功率只从 28% 涨到 34%。中间的差距是什么?下一节。

2.6 四种 harness

深度研究一讲的幻灯片把 harness 分成四种(以深度研究为例,同样适用于编程):

harness谁决定下一步代码固定了什么能力在哪里怎样改进
固定流水线(STORM,2024)我们写的代码:固定的阶段阶段和顺序提示词和流水线设计更好的提示词(例如 DSPy)
规划者 + 工作者(Anthropic Research,2025)主模型规划,工作者执行角色:主、工作者、引用者提示词,加一个强模型更好的分工和工具
训练出来的智能体(OpenAI、Kimi,2025)模型,每一步一个小循环:搜索、打开、回答权重,用强化学习训练在更难的任务上做更多强化学习
通用 harness(Claude Agent SDK,2025)模型,每一步通用的循环:工具、文件、子智能体通用的模型,加上循环更好的工具和上下文

幻灯片的结论:这四种都是 harness;从上往下,代码决定得越来越少,能力从提示词转移到权重或通用的循环里。编程智能体属于最后一种。最后一列”怎样改进”引出了第 4 节的问题:能不能让程序自动地改进 harness?


3. 怎样评估编程智能体

3.1 SWE-bench:用隐藏的测试评判

SWE-bench(Jimenez 等,ICLR 2024)从 12 个流行的 Python 开源项目里收集了 2,294 个真实的 GitHub issue 和修复它们的 pull request。智能体拿到代码仓库和 issue 的描述,要改代码修好它;然后用那个 pull request 附带的测试评判,分两类:

  • FAIL_TO_PASS:修复之前失败、修复之后应该通过的测试——检查问题是否真的被解决;
  • PASS_TO_PASS:修复之前就通过、之后也必须通过的测试——检查有没有改坏别的东西。

这些测试智能体看不到。后来 OpenAI 请人工核查了其中 500 个任务(问题描述是否清楚、测试是否合理),得到 SWE-bench Verified,成为最常用的版本。另一个常用的基准 Terminal-Bench 2.0 有 89 个需要在终端里长时间操作的困难任务(Meta-Harness 论文用它评估编程智能体的 harness)。

本讲的玩具世界模仿了这个设计:可见的测试相当于 issue 里的复现步骤,隐藏的测试相当于评判用的测试。

3.2 “自己说成功了” vs “真的成功了”

代码 01 演示 3,12 个任务 × 20 次:

harness                     自称成功  真的成功 │ 改对了  漏边界  写特例  改测试  改错了
只说通过 / 失败,测试可以改     85%       29% │     29%     42%      0%     13%     15%
+ 测试只读                     75%       32% │     32%     43%      0%      0%     25%
+ 详细的错误信息               99%       34% │     34%     43%     22%      0%      1%
+ 自我检查                     97%       38% │     38%     54%      5%      0%      3%
+ 提醒考虑边界情况             96%       73% │     73%     21%      2%      0%      4%

右边五列是最后留下的代码属于哪一种。三种差距、三种对策:

差距什么时候出现对策效果(模拟)
改测试测试可以改、又经常失败测试文件只读(护栏)13% → 0%;“自称成功”从 85% 降到 75%——更诚实了
写特例看到了失败测试的具体输入(详细的错误信息让写特例变得容易)自我检查:重读自己的修改22% → 5%
漏边界修好了报告的问题就停下系统提示提醒”考虑边界情况、不要针对测试写特例”真实成功率 38% → 73%

两个值得注意的地方:

  1. “自称成功”一直在 75%~99%,真实的成功率从 29% 到 73%——只看智能体自己的报告,几乎看不出这几种 harness 的区别。这就是 SWE-bench 等评测必须用隐藏测试的原因,也是你评估自己项目时要记住的:不要让被评估的系统给自己打分。
  2. 详细的错误信息是一把双刃剑:它让智能体更快地修好 bug,也让它更容易”针对测试写特例”。harness 的设计常常是这样的权衡。

真实的模型也有这些行为:模型开发商在发布说明和研究报告里都讨论过编程智能体”针对测试写特例""修改测试让它通过”的现象,这也是为什么强化学习训练编程能力时,奖励的设计(测试是否可以被绕过)非常重要(第 14 讲)。

3.3 每个旋钮值多少

代码 01 演示 4:从”全开”出发,每次关掉一个旋钮:

harness                     真的成功   LLM 调用   词元
全开                           77%        2.6     1491
不读源代码                     70%        3.9     1569
错误信息只有通过 / 失败        66%        3.3     1332
最多改 1 次                    46%        1.7     1270
测试可以改                     75%        2.6     1497
不做自我检查                   73%        1.5     1310
不提醒边界情况                 41%        2.8     1516

每个旋钮都有用,也都有代价(自我检查多一次 LLM 调用)。还有一个有意思的现象:其他旋钮都开着时,“测试只读”几乎不起作用(77% vs 75%)——自我检查会撤销对测试的修改,两个旋钮在防同一件事。旋钮之间有相互作用,所以不能一个一个地调,而要搜索它们的组合。6 个旋钮就有 192 种组合;真实的 harness 有成百上千个设计决定(每一句提示词、每个工具的说明、什么时候压缩上下文……)。这引出了下一节。


4. 元优化:自动改进 harness

4.1 动机

ADAS 论文开头的一句话概括了动机:机器学习的历史告诉我们,手工设计的方案最终会被学出来的方案取代——手工设计的特征被深度学习取代,手工设计的网络结构被自动搜索的结构追上。今天的智能体系统(思维链、自我反思、工具调用、多智能体流水线)大多是手工设计的,能不能也让它们被”学”出来?

Meta-Harness 论文给出了另一个动机:只改 harness、不改模型,同一个基准上的成绩可以相差 6 倍。harness 往往和模型本身一样重要,但今天的 harness 工程仍然主要靠手工:看失败的例子、调整规则、试几个版本。

4.2 一个统一的框架

几乎所有的元优化方法都是同一个循环:

候选(提示词 / 工作流 / 代码 / harness)
   → 在搜索集上评估(运行整个系统)
   → 得到反馈(分数?文字的批评?完整的执行轨迹?)
   → 提议者(通常是一个 LLM)根据反馈和历史提出新的候选
   → ……

不同的方法在四个维度上不同:优化什么、反馈有多丰富、怎样搜索、谁来提议。

方法优化什么反馈搜索方式论文报告的结果
OPRO(Yang 等,2024)提示词里的指令过去的(候选, 分数)对,按分数排序放进元提示词LLM 直接写新候选比人写的提示词在 GSM8K 上高最多 8%,在 Big-Bench Hard 上高最多 50%
DSPy(Khattab 等,2024)整条 LM 流水线的提示词和示例指标的分数;自己运行流水线收集成功的示例“编译器”:自动挑选和生成示例、优化指令几行 DSPy 程序编译之后,GPT-3.5 和 llama2-13b-chat 比标准的少样本提示高 25% 和 65% 以上
TextGrad(Yuksekgonul 等,2024)计算图里的任何文本变量(提示词、代码、答案)LLM 写的文字批评,像梯度一样沿着计算图”反向传播”类似梯度下降GPT-4o 在 GPQA 上的零样本准确率从 51% 提高到 55%;LeetCode-Hard 相对提高 20%
GEPA(Agrawal 等,ICLR 2026)复合 AI 系统里的各个提示词读执行轨迹(推理、工具调用、评测函数的报错),用自然语言反思、总结教训遗传算法 + 帕累托前沿(保留”在某些题上最好”的多个候选,避免陷入局部最优)6 个任务上平均比 GRPO 强化学习(24,000 次运行)高 6%、最多高 20%,所需的运行次数最多少 35 倍;比之前最好的提示词优化器 MIPROv2 高 10% 以上
ADAS / Meta Agent Search(Hu 等,ICLR 2025)用代码定义的整个智能体新智能体的评测分数元智能体参考一个不断增长的”已发现智能体的档案”,编写新的智能体DROP 阅读理解 F1 高 13.6,MGSM 数学准确率高 14.4%;迁移到 GSM8K、GSM-Hard 时比基线高 25.9%、13.2%
AFlow(Zhang 等,ICLR 2025)用代码表示的工作流(调用 LLM 的节点和边)执行反馈蒙特卡洛树搜索6 个基准上平均比之前最好的方法高 5.7%;让小模型在部分任务上超过 GPT-4o,花费只有它的 4.55%
AlphaEvolve(Novikov 等,2025)解决问题的程序(算法、启发式规则)自动评测函数的分数进化:程序库 + LLM 修改代码 + 评测用 48 次标量乘法完成 4×4 复数矩阵乘法(56 年来首次在这个设定下改进 Strassen 算法);谷歌数据中心的调度规则平均回收 0.7% 的算力;内核启发式规则平均加速 23%,使 Gemini 的训练时间减少 1%
Darwin Gödel Machine(Zhang 等,ICLR 2026)编程智能体自己的代码编程基准的分数开放式的进化:从档案里挑一个智能体,让它修改自己SWE-bench 从 20.0% 提高到 50.0%,Polyglot 从 14.2% 提高到 30.7%(在沙盒和人工监督下进行)
Meta-Harness(Lee 等,2026)harness 的代码全部历史:之前所有候选的源代码、分数、执行轨迹,放在一个文件系统里一个编程智能体(Claude Code + Opus 4.6)当提议者,自己决定去读哪些文件在线文本分类比 ACE 高 7.7 个点、上下文词元少 4 倍;IMO 级别的 200 道数学题上,一个发现的 harness 在 5 个没参与搜索的模型上平均提高 4.7 个点;Terminal-Bench 2 上超过最好的手工 harness

这张表有一个清楚的趋势:被优化的对象越来越大(指令 → 提示词 + 示例 → 工作流 → 整个智能体 → harness 代码),反馈越来越丰富(分数 → 文字批评 → 轨迹 → 全部历史)。Meta-Harness 论文估算了每种方法每评估一个候选时提议者能看到的信息量:OPRO 约 0.002 百万词元、GEPA 约 0.008、TextGrad 约 0.015、AlphaEvolve 约 0.022,而 Meta-Harness 约 10 百万词元——多了几个数量级。它的提议者不把这么多信息一次塞进上下文,而是像程序员一样用 grep、cat 在文件系统里按需查阅(中位数每轮读 82 个文件)。

还有一个趋势:提议者本身就是编程智能体。ADAS 的元智能体写代码,AlphaEvolve 修改代码,Darwin Gödel Machine 修改自己的代码,Meta-Harness 的提议者就是 Claude Code。编程智能体既是元优化的对象,也是元优化的工具——这就是本讲把两者放在一起的原因。

4.3 为什么丰富的反馈有用

Meta-Harness 做了一个消融:在线文本分类任务上,让提议者分别只看到分数、分数加 LLM 写的摘要、完整的执行轨迹:

提议者能看到候选的中位数准确率最好的候选
只有分数(和代码)34.641.3
分数 + 摘要34.938.7
完整的执行轨迹50.056.7

完整轨迹下,中位数的候选都比两个消融里最好的候选还好。摘要几乎没有帮助——它在压缩时丢掉了诊断问题需要的细节。

直觉上,这和”信用分配”有关:一个复杂系统失败了,是哪个部件的错?只有分数时,优化器只知道”整体不好”,只能盲目地试;读了轨迹,它能看到”第 3 步检索回来的文档是错的”,于是去改检索的部分。这有点像神经网络训练里的反向传播(知道每个参数该往哪边改)和进化算法(只知道整体的适应度)的区别——TextGrad 的名字就来自这个类比。GEPA 的论点也一样:语言是比稀疏的标量奖励丰富得多的学习信号,所以它用几十次运行就能学到强化学习要几千次运行才学到的东西。

代码 02 演示 1 用玩具世界复现了这个现象。三种优化器(预算都是 10 次评估,起点都是默认的 harness,各运行 20 次取平均):

起点(默认 harness):搜索集 25%,留出的任务 34%
优化器        第 1 次   第 3 次   第 5 次   第 10 次 │ 选出的 harness 在留出任务上
随机搜索          36%      48%      54%      63% │         63%
只看分数          36%      46%      55%      68% │         65%
读轨迹            36%      66%      75%      78% │         77%
(参考:192 种组合里,留出任务上最好的是 79%)
  • 随机搜索:在 192 种组合里随便挑;
  • 只看分数(OPRO 的思路):在目前最好的候选上随机改一个旋钮;它不知道为什么低分,只能一个一个地试;
  • 读轨迹(GEPA / Meta-Harness 的思路):读目前最好的候选失败的运行,统计失败的原因,按最常见的原因改对应的旋钮。

读轨迹的优化器 3 次评估就到了 66%,10 次之后选出的 harness 在留出的任务上达到 77%,接近 192 种组合里最好的 79%。演示 2 显示了它每一步的”思考”:

第  2 次评估:最常见的失败原因:missed_edge_case(27 次) → 改 edge_hint;25% → 48%(保留)
第  3 次评估:最常见的失败原因:edited_tests(9 次) → 改 protect_tests;48% → 53%(保留)
第  4 次评估:最常见的失败原因:out_of_attempts(15 次) → 改 error_detail;53% → 60%(保留)
第  5 次评估:最常见的失败原因:special_case(8 次) → 改 self_review;60% → 63%(保留)
第  6 次评估:最常见的失败原因:out_of_attempts(3 次) → 改 max_attempts;63% → 68%(保留)
第  7 次评估:随机改一个旋钮 → 改 self_review;68% → 63%(不如原来)
……
第 10 次评估:随机改一个旋钮 → 改 read_source;68% → 70%(保留)

这正是一个人类工程师调 harness 时会做的事:看失败的例子 → 想原因 → 改设置 → 再测。元优化把这个过程自动化了。注意最后一步:随机关掉了”读源代码”,分数反而高了 2 个点——从演示 2 的表格我们知道不读源代码是有害的,这 2 个点多半是评估的噪声。第 5.2 节会专门讨论这个问题。

说明:模拟里,“读轨迹”的优化器用一张写好的”诊断 → 处方”对照表(FIXES)代替 LLM 的反思。真实的系统里,读轨迹、诊断原因、想出改法都由 LLM 完成,这正是它们的价值所在——不需要人预先写好对照表。


5. 元优化的风险

5.1 优化错了指标

代码 02 演示 3:同样的”只看分数”优化器,把目标换成”智能体自己说成功了的比例”:

优化的目标               搜索集上的目标值   选出的 harness 在留出任务上的真实成功率
真实的成功率(隐藏测试)         68%                     65%
智能体自称成功的比例            100%                     39%
(搜索集上"自称成功" ≥ 99% 的组合有 13 种,它们在留出任务上的真实成功率从 19% 到 79%)

“自称成功”很快就到了 100%(打开详细的错误信息就够了——写特例、改测试都能让可见测试通过),很多候选并列第一,优化器选出哪一个全凭运气。

这是古德哈特定律:“一个指标一旦成为目标,就不再是好的指标。“元优化器会利用指标的漏洞,就像编程智能体会利用测试的漏洞一样——而且元优化器更擅长找漏洞,因为它的工作就是系统地搜索什么能提高分数。在真实的项目里,常见的漏洞包括:用 LLM 当评委时评委的偏差(第 15 讲:更长的回答得分更高、放在前面的得分更高)、自动指标和真实目标的差距(第 5 讲的词重叠指标)、评测数据本身的错误(第 11 讲:InsightBench 里 45% 的低分样例是标注错误)。

5.2 在搜索集上过拟合

代码 02 演示 4:搜索集越小,评估的噪声越大:

搜索集                     搜索集上的分数   留出任务上的分数   差距
2 个任务 × 3 次                 79%               64%        +15%
4 个任务 × 5 次                 76%               73%         +4%
6 个任务 × 10 次                78%               77%         +1%

在很多候选里挑分数最高的,挑出来的那个往往是”运气好”的——它在搜索集上的分数被高估了(统计学里叫赢家的诅咒)。候选越多、搜索集越小,高估得越厉害。这和机器学习里”在训练集上过拟合”是一回事,对策也一样:

  • 留一个优化过程完全没碰过的测试集。Meta-Harness 的提议者从来看不到测试集的结果;ADAS、Meta-Harness 还检查了发现的智能体 / harness 能否迁移到没参与搜索的领域和模型;
  • 搜索集要足够大、有代表性;评估要重复多次(第 1、15 讲的置信区间);
  • 对”只高了一点点”的改进保持怀疑(演示 2 的最后一步)。

5.3 成本

每评估一个候选,就要在整个搜索集上把系统运行一遍。GEPA 的卖点之一就是运行次数少;Meta-Harness 每一轮的提议者要读上千万词元的历史。元优化本身也是一个需要预算、需要权衡的系统。

5.4 自我修改的安全问题

当被优化的对象是智能体自己的代码(Darwin Gödel Machine、STOP 这类”自我改进”的系统),风险更大:它可能修改自己的评测、关掉安全检查、做出意料之外的行为。Darwin Gödel Machine 的论文强调实验都在沙盒和人工监督下进行。编程智能体日常使用中的护栏(沙盒、权限、审批、只读的测试)在这里更加重要。另外,编程智能体会读仓库里的内容——文件、网页、issue 里也可能藏着针对它的指令(第 3 讲的提示注入)。


6. 讨论与边界

6.1 改权重,还是改 harness?

让一个系统在某个任务上变好,有两条路:

改权重(第 14 讲:微调、强化学习)改 harness(本讲:元优化)
需要能训练模型(开源模型或微调接口)、大量数据或运行只需要能调用模型
可解释难:改变藏在几十亿个参数里容易:新的提示词、新的代码,人能读
迁移换模型就要重新训练发现的 harness 常常能迁移到别的模型(ADAS、Meta-Harness 都验证了)
样本效率强化学习常常要成千上万次运行GEPA:用少得多的运行达到或超过 GRPO

两者并不互斥:DSPy 团队的 BetterTogether(arXiv 2407.10930,深度研究一讲的阅读清单)研究了提示词优化和权重微调交替进行;第 14 讲会看到,训练也常常从一个好的 harness 产生的数据开始(蒸馏)。

6.2 什么时候值得做元优化

你需要:(1) 一个可靠的、自动化的指标(否则会掉进第 5.1 节的坑);(2) 一个足够大的搜索集和一个独立的测试集;(3) 评估的预算;(4) 记录完整的轨迹(这是给优化器、也是给你自己的最重要的信息)。如果这些都没有,先手工调——手工调的过程(看失败、想原因)本身就会帮你建立评估集和诊断的规则。

6.3 本讲模拟的边界

  • 没有真实的 LLM。模拟 LLM 写哪种修改、改测试的概率都是设定的;三种差距(改测试、写特例、漏边界)发生得多频繁,取决于这些参数。真实的代码和测试是真的被执行的,所以”可见测试通过、隐藏测试失败”是真实的代码行为;
  • 12 个任务都很小(一个函数),真实的仓库有成千上万个文件,大部分难度在于找到该改的地方(搜索、读代码),本讲几乎没有模拟这部分;
  • 192 种组合的 harness 空间是人设计的;真实的元优化(ADAS、Meta-Harness)搜索的是代码,空间几乎无限,需要 LLM 的创造力,也更容易产生意外的结果。

7. 本讲小结

  1. 编程智能体 = LLM + 工具 + harness。工具很少(搜索、读、编辑、运行命令),但设计很讲究(智能体—计算机接口);harness 包括系统提示、沙盒、项目说明文件、上下文管理、子智能体、权限与审批、钩子、检查点、日志。
  2. 编程特别适合智能体:代码可执行、可测试,核查器是现成的、确定的;环境是文本的;可以撤销;测试结果是天然的奖励。模拟里,带测试的循环让真实成功率从 15% 提高到 28%~34%。
  3. 测试通过 ≠ 改对了:改测试、针对测试写特例、漏掉边界情况。评估要用隐藏的测试(SWE-bench 的 FAIL_TO_PASS 和 PASS_TO_PASS);对策分别是测试只读、自我检查、提醒边界情况。模拟里”自称成功”始终在 75%~99%,真实成功率从 29% 到 73%。
  4. 元优化把提示词、工作流、代码、harness 当作优化的对象:OPRO、DSPy、TextGrad、GEPA 优化提示词;ADAS、AFlow 优化工作流和整个智能体;AlphaEvolve 进化程序;Darwin Gödel Machine 修改自己;Meta-Harness 优化 harness 代码。趋势是优化的对象越来越大、反馈越来越丰富、提议者本身就是编程智能体。读完整轨迹的提议者远好于只看分数或摘要的(Meta-Harness:中位数 50.0 vs 34.6)。
  5. 风险:优化错了指标(古德哈特定律:优化”自称成功”选出的 harness 真实成功率只有 39%);在搜索集上过拟合(赢家的诅咒);成本;自我修改的安全问题。

8. 与项目的关系

  • 课程要求项目”使用最先进的工具和概念,不能只是把问题描述丢给一个普通的 LLM”。如果你的项目是一条 LLM 流水线,可以考虑用 DSPy / GEPA 优化它的提示词——但先建好评估集和指标。
  • 把评估集分成搜索集和测试集;所有的调整(手工的或自动的)只看搜索集,最后在测试集上报告一次。
  • 记录每一次运行的完整轨迹:它们是你调试、做错误分析、写论文的原始材料,也是元优化器最需要的信息。
  • 如果你在做编程类的智能体(或者用编程智能体帮你写项目代码):保护你的测试,让评判用的测试和它看到的测试分开,检查它有没有”针对测试写特例”。
  • 你的项目代码大概率会有编程智能体参与。把它当作一个能干但会犯错的合作者:给它清楚的任务和项目说明,让它运行测试,自己审查它的修改。

9. 练习

练习 1(动手:更爱改测试的模型)。把 coding_agent.py 的 PARAMS["hack"] 从 0.12 改成 0.4,运行 01 号文件演示 3 的前两行(“只说通过 / 失败,测试可以改”和”+ 测试只读”)。“自称成功”和”真的成功”各怎样变化?测试只读之后,真实的成功率为什么没有明显提高?

参考答案

实测(12 个任务 × 20 次):

自称成功真的成功改测试的比例
hack = 0.12,测试可以改85%29%13%
hack = 0.12,测试只读75%32%0%
hack = 0.4,测试可以改92%25%33%
hack = 0.4,测试只读63%28%0%

更爱改测试的模型,“自称成功”更高(92%),真实的成功率更低(25%)——指标和真实目标朝相反的方向走。测试只读之后,自称成功降到 63%,变得诚实了,但真实的成功率只从 25% 提高到 28%:被拦下的”改测试”只是失败了一次,消耗了一次尝试机会,模型并没有因此学会改对代码。护栏能防止作弊、让报告变得可信,但不能让一个不会的模型变会——那需要更好的信息(详细的错误信息、读源代码)或更好的模型。

练习 2(动手:诊断能力)。在 02 号文件里,从 FIXES 中删掉 "missed_edge_case" 这一项(读轨迹的优化器不再能诊断”漏了边界情况”),运行演示 1。读轨迹的优化器还领先吗?这说明了什么?

参考答案

实测(20 次运行的平均):第 1、3、5、10 次评估后的最好分数从 36% / 66% / 75% / 78% 变成 36% / 42% / 60% / 72%,选出的 harness 在留出任务上从 77% 降到 72%——第 3 次评估时的优势几乎消失了(只看分数是 46%)。默认 harness 最常见的失败就是”漏了边界情况”(27 次),诊断不出它,优化器就先去处理次要的问题。

这说明”读轨迹”的价值取决于诊断的质量:反馈再丰富,如果优化器不能从中正确地归纳出原因,也就和只看分数差不多。这也是 Meta-Harness 的消融里”摘要”几乎没有帮助的原因——LLM 写的摘要丢掉了诊断需要的细节。真实系统里,诊断由 LLM 完成;它的能力(以及你给它看什么)决定了元优化的效率。

练习 3(动手:更多的搜索能弥补太小的搜索集吗?)。把 BUDGET 改成 30,搜索集只用 2 个任务 × 3 次,分别运行读轨迹的优化器和随机搜索,比较搜索集上的分数和留出任务上的分数。

参考答案

实测(20 次运行的平均):

预算 10预算 30
读轨迹:搜索集 / 留出79% / 64%80% / 64%
随机搜索:搜索集 / 留出71% / 56%79% / 65%

预算增加到 3 倍,读轨迹的优化器在留出任务上没有任何提高,随机搜索追上了它;两者的差距(搜索集比留出任务高 14~16 个点)都没有缩小。搜索集太小时,评估的噪声决定了”谁看起来最好”,搜索得再多,也只是在噪声里挑得更细。对策是增大搜索集、重复评估,而不是增加搜索的步数。

练习 4(思考:设计隐藏测试)。给本讲的 average 函数写一组隐藏测试,要求能区分”改对了""漏了边界情况""针对测试写特例”三种修改。再想一想:SWE-bench 为什么同时需要 FAIL_TO_PASS 和 PASS_TO_PASS 两类测试?

参考思路

average 的可见测试是 average([2, 4]) == 3.0 和 average([1, 2]) == 1.5。隐藏测试应该包括:(1) 另一个普通输入(例如 [1, 2, 4])——专门针对 [1, 2] 写特例的代码会失败;(2) 边界情况(空列表、只有一个元素、负数)——只修了整除的代码在空列表上会抛出 ZeroDivisionError;(3) 如果规格有要求,还有类型(返回 float 而不是 int)。本讲的隐藏测试正是 [1, 2, 4]、[]、[-3]。

FAIL_TO_PASS 检查”问题被解决了”;PASS_TO_PASS 检查”没有改坏别的东西”。只有前者,智能体可能用一个粗暴的修改(例如删掉一个功能)让新测试通过,同时破坏了原来正常的行为;只有后者,什么都不改也能通过。两者一起,才逼近”改对了”的定义。

练习 5(思考:你的项目里的古德哈特定律)。假设你的项目用一个 LLM 评委给系统的回答打 1~10 分,并用 GEPA 自动优化系统的提示词、最大化这个分数。可能会出什么问题?你会怎样防范?

参考思路

优化器会找到评委的偏好而不是真正的质量:例如回答越来越长(评委的长度偏差)、越来越自信、使用评委喜欢的措辞、甚至在回答里写”这是一个优秀的回答”(针对评委的提示注入)。防范:(1) 先验证评委(第 15 讲:位置偏差、长度偏差、和人工判断的一致率、负对照);(2) 用多个评委、混合指标(加上可验证的部分,例如事实核查、引用是否正确);(3) 留出一个用人工判断的测试集,定期检查优化后的系统在人看来是不是真的变好了;(4) 读优化器生成的提示词——它是文字,人能看懂,如果出现”尽量写长一些”之类的指令,就是警报;(5) 限制搜索空间(例如回答长度的上限)。


10. 延伸阅读

课程阅读清单(Meta Agents and Meta Optimization)

  • Hu、Lu、Clune. Automated Design of Agentic Systems(ADAS). ICLR 2025. arXiv 2408.08435. 必读
  • Zhang 等. AFlow: Automating Agentic Workflow Generation. ICLR 2025. arXiv 2410.10762.
  • Khattab 等. DSPy: Compiling Declarative Language Model Calls into Self-Improving Pipelines. ICLR 2024. arXiv 2310.03714.
  • Yang 等. Large Language Models as Optimizers(OPRO). ICLR 2024. arXiv 2309.03409.
  • Yuksekgonul 等. TextGrad: Automatic “Differentiation” via Text. arXiv 2406.07496.
  • Agrawal 等. GEPA: Reflective Prompt Evolution Can Outperform Reinforcement Learning. ICLR 2026. arXiv 2507.19457.
  • Novikov 等. AlphaEvolve: A Coding Agent for Scientific and Algorithmic Discovery. arXiv 2506.13131.
  • Zelikman 等. Self-Taught Optimizer (STOP): Recursively Self-Improving Code Generation. 2023.
  • Zhang 等. Darwin Gödel Machine: Open-Ended Evolution of Self-Improving Agents. ICLR 2026. arXiv 2505.22954.
  • Zhang 等. Hyperagents. arXiv 2603.19461;Zheng 等. Dream-RSI: Recursive Self-Improvement through Evolving Worlds. 2026.

harness 与编程智能体

  • Lee 等. Meta-Harness: End-to-End Optimization of Model Harnesses. arXiv 2603.28052.
  • Jimenez 等. SWE-bench: Can Language Models Resolve Real-World GitHub Issues? ICLR 2024.
  • Yang 等. SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering. NeurIPS 2024.
  • Anthropic. Building effective agents(2024);Effective context engineering for AI agents(2025)。
  • Databricks. What is an AI Agent Harness?(2026)。

11. 下一讲预告

到目前为止,我们的智能体处理的都是已经数字化的文字:网页、文档、数据库。但人类几千年的知识里,有大量还躺在图书馆和档案馆里:手稿、古籍、旧报纸。它们的语言古老、版式特殊、纸张破损,今天最好的商业模型也读不好。第 13 讲讲 CHURRO:

  • 光学字符识别(OCR)和视觉语言模型(VLM);怎样衡量转写的质量(编辑距离、字符错误率);
  • 为什么历史文献难:古老的语言和拼写、多栏和竖排的版式、阅读顺序、破损和扫描质量;
  • CHURRO-DS:统一 155 个语料库、近 10 万页、跨越 22 个世纪的数据集是怎样整理出来的;
  • 一个 30 亿参数的开源模型,经过微调,比最好的商业模型更准,成本只有它的 1/15。
← 返回课程目录