Lecture 15 · 编程智能体 · Coding & Software Agents

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

课程源码 ZIP ↓

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

本篇目录

对应 Maps to:Stanford CS329Z Engineering AI Agents(Fall 2026),Lecture 15 · Wed Nov 18 · Coding & Software Agents。阅读材料:Yang et al., SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering(NeurIPS 2024);Wang et al., OpenHands: An Open Platform for AI Software Developers as Generalist Agents(ICLR 2025)/ Claude Code 最佳实践;Young, Effective Harnesses for Long-Running Agents(Anthropic Engineering,2025-11-26);Jimenez et al., SWE-bench: Can Language Models Resolve Real-World GitHub Issues?(ICLR 2024)。 依据说明:写作时(2026-10-16)这一讲的 slides 还没有公布。本讲依据课表上的主题描述来写:编程智能体端到端怎样工作、SWE-agent(智能体–计算机接口)、Claude Code 和 OpenHands 的架构、脚手架即设计决策、SWE-bench 与 4 元组、长时间运行的 harness、软件开发的未来。引用的论文、文档和博客内容都核对过原文;Claude Code 的最佳实践依据的是现行的官方文档(code.claude.com/docs)。全部实验是本讲义自己设计的,不代表课堂上讲了什么。 配套代码 Code:code/lecture15/(只用 Python 标准库;一个真实的小仓库 + 5 个脚本 + 2 个模块 + 1 个可选脚本;不需要 API key;第一次运行先花约 30 秒造任务,之后每个脚本几秒到两分钟) 预计阅读时间:3.5 小时(含运行代码)


0. 本讲导航 · How to read this lecture

0.1 学习目标 Learning goals

学完这一讲你应该能够:

  1. 描述一个编程智能体从读 issue 到提交补丁的完整循环,并用 4 元组描述一个 SWE-bench 任务。
  2. 解释”智能体–计算机接口”(ACI)为什么重要,说出 SWE-agent 的四条设计原则,以及查看窗口、搜索、带检查的编辑、上下文管理各自解决什么问题。
  3. 比较几种脚手架:智能体循环(SWE-agent、OpenHands、Claude Code)和写死的流程(Agentless),知道各自在成本、可预测性、灵活性上的取舍。
  4. 审查一个编程智能体的评估:FAIL_TO_PASS 和 PASS_TO_PASS 各管什么、测试太弱时会发生什么、为什么需要 SWE-bench Verified、pass@1 和 pass^k 说明什么。
  5. 设计一个长时间运行的 harness:特性清单、小步提交、进度文件、启动脚本、端到端测试,各自对付哪种失败。

0.2 先修与本讲范围 Prerequisites & scope

默认你已经会 Assumed本讲只给直觉,后面再展开 Previewed only
第 4 讲:工具注册表、校验与执行分离;第 6 讲:workflow vs agent电脑操作 / 网页智能体(第 17 讲)
第 11 讲 §5:SWE-smith(从仓库造任务);第 12 讲:4 元组、有效性、pass^k用强化学习训练编程模型(不在本课范围)
第 14 讲:沙箱、权限、确认疲劳;前置讲 P1:进程、子进程、超时多智能体的软件开发流程(第 8 讲已讲过一般情形)

0.3 本讲地图 Map

§0.6 开场:一行 // 写成了 *
§1  编程智能体端到端:一个 SWE-bench 式的任务                          ← 代码 01
§2  智能体–计算机接口(ACI):SWE-agent 的设计与消融                    ← 代码 02
§3  脚手架即设计决策:Agentless、OpenHands、Claude Code                ← 代码 03
§4  评估编程智能体:SWE-bench、Verified、作弊、重复运行                  ← 代码 04
§5  长时间运行的 harness                                             ← 代码 05
§6  安全:编程智能体的权限与沙箱
§7  用真实模型试一试                                                 ← 代码 06(可选)
§8  软件开发的未来
§9 小结   §10 常见误区   §11 练习   §12 延伸阅读   §13 下一讲预告

0.4 术语 Terms

术语本讲里的意思
issue一个问题报告:描述症状(怎样复现、期望什么),不说 bug 在哪
补丁(patch)对仓库的修改;SWE-bench 里智能体最后提交的就是一个补丁
FAIL_TO_PASS / PASS_TO_PASS修复的 PR 带来的、打补丁前失败之后应该通过的测试 / 原本就通过、之后也必须通过的测试
resolved两组测试都通过
ACI(agent-computer interface)给智能体用的”界面”:它能调用哪些命令、每个命令返回什么样的观察
脚手架(scaffold / harness)包在模型外面的那一层:工具、循环、提示词、上下文管理
复现脚本智能体根据 issue 写的、能重现 bug 的一小段代码
差分检查在补丁后的代码和原始(正确的)实现上跑同样的输入,结果必须一样

0.5 本讲的代码

代码带了一个真实的小仓库:code/lecture15/repo/,Orrin Grid 的计费代码 gridbill,5 个模块、92 个测试。你可以直接打开它、跑它的测试。

repo_lab.py 照着 SWE-smith 往仓库里注入 bug,造出 125 个 SWE-bench 式的任务;agent_lab.py 实现两种脚手架:一个迷你 SWE-agent,以及一个 Agentless 式的流程。它们都在真实的文件上调用真实的工具,在子进程里跑真实的测试。

提醒:模拟的是”模型的判断”:看不看得出哪一行有问题、挑不挑得中正确的改动、写编辑时会不会写错缩进,这些都是参数。所以这一讲的成功率不能代表任何真实模型;要看的是机制:哪个设计让哪种错误更容易发生、更难恢复,以及评估在哪里会失真。

0.6 开场:一行 // 写成了 *

一个用户报告:“net_charge([1.0] * 5, 100, 'solar') 返回 12.0,应该是 0.0。” 太阳能用户用了 5 度电、卖回电网 100 度,净费用应该是 0。

net_charge 本身没有错。错在它调用的 usage_charge 里:kwh * price 被写成了 kwh // price。一个编程智能体要做的事和人一样:读 issue,找到 net_charge,顺着调用找到 usage_charge,看出那个 //,改掉,写个小脚本确认 bug 不再出现,跑一遍测试确认没弄坏别的,提交。

每一步都可能出错:搜索返回太多结果、文件太长看不全、编辑时缩进写错、改完没跑测试、跑测试时卡死……这一讲讨论怎样设计这些步骤,以及怎样知道它真的修好了。


1. 编程智能体端到端 · Anatomy of a coding agent

1.1 SWE-bench:一个任务的 4 元组

Jimenez et al.(ICLR 2024)从 12 个流行的 Python 仓库的真实 GitHub issue 和对应的 PR 里收集了 2,294 个任务。每个任务按第 12 讲的 4 元组来看:

部分SWE-bench 里
请求issue 的文字(只描述症状)
环境issue 提出那一刻的仓库快照(在容器里)
停止条件智能体提交一个补丁,或者用完预算
判分器打上补丁,跑两组测试:FAIL_TO_PASS(修复的 PR 新增 / 修改的测试,打补丁前失败、之后应该通过)和 PASS_TO_PASS(原本就通过、之后也必须通过的测试)。两组都过才算 resolved

FAIL_TO_PASS 对智能体是隐藏的:它们随修复的 PR 一起加进来,智能体只能看到 issue 和仓库原有的测试。论文发布时,最好的模型(Claude 2 + BM25 检索)只解决了 1.96%。

代码 01 演示 1 里的那个任务:

部分内容
请求`net_charge([1.0] * 5, 100, 'solar')` returns 12.0, but it should be 0.0.
环境5 个源文件;智能体能跑的测试 89 个
停止条件提交,或用完 40 步
判分器3 个 FAIL_TO_PASS(test_net_charge_floor、test_usage_charge_offpeak、test_usage_charge_with_peak)+ 89 个 PASS_TO_PASS
bug(上帝视角)tariffs.py:51,函数 usage_charge,* → //

1.2 任务从哪来:SWE-smith 的流水线(代码 01 演示 2)

SWE-bench 的任务要从真实的 issue 和 PR 里挖,很费劲。第 11 讲 §5 讲过 SWE-smith 的反向做法:拿一个测试能跑通的仓库,自动注入 bug,留下”让至少一个测试失败”的那些。迷你版的流水线:

步骤数量
候选的 bug(换运算符 / 常量加一 / 删掉一行)461
改完不能编译24
崩溃 / 超时 / 复现不出来6
没有测试失败(测试抓不到这个 bug)108
和已有任务重复198
留下的任务125

108 个 bug 没有任何测试能抓住:这本身就是一个关于测试质量的信号(第 11 讲 §5)。125 个任务里,有 53 个的 bug 不在 issue 提到的那个函数里,要顺着调用往下找。

1.3 一条轨迹(代码 01 演示 3)

迷你 SWE-agent 在这个任务上走了 10 步:

 1  search_dir('def net_charge')   → gridbill/tariffs.py (1 matches)
 2  open gridbill/tariffs.py 64    → 看到 net_charge:调用了 usage_charge 和 export_credit
 3  open gridbill/accounts.py 15   → (被文档字符串里的 "charge" 带偏,去看了 accounts.charge)
 4  open gridbill/tariffs.py 47    → 看到 usage_charge
 5  open gridbill/accounts.py 32
 6  open gridbill/tariffs.py 7     → 看到用到的常量
 7  edit 51:51                     → 把 // 改回 *(语法检查通过)
 8  python reproduce.py            → 没有输出:bug 不再出现
 9  python run_tests.py            → 89 passed
10  submit

第 3、5 步值得一提:它按名字找”相关的函数”,被文档字符串里的一个词带去了不相关的文件。真实的智能体也常常这样”顺着字面线索走错路”。走错的代价是步数和上下文。

判分:resolved(FAIL_TO_PASS 全过、PASS_TO_PASS 全过、差分检查一致)。

这个循环就是第 1 讲 ReAct 的编程版:看 → 想 → 改 → 验证 → 不行就撤销。和”代码补全”相比,难点在于定位:issue 只描述症状,bug 可能在好几层调用之下;以及验证:智能体看不到判分用的测试,只能靠自己写的复现脚本和仓库原有的测试。

1.4 成本:每一步都要重发整个对话

API 是无状态的,智能体每走一步,都要把整个对话(系统提示、工具说明、之前所有的观察)重新发一遍。这条 10 步的轨迹:

设置累计输入 token平均每步
只保留最近 5 个观察的全文(默认)29,8172,981
保留完整历史37,3243,732

长任务的成本主要由”上下文有多长 × 走了多少步”决定(第 2 讲 §5、第 7 讲)。这也是 ACI 要让观察”简洁”的原因之一。


2. 智能体–计算机接口 · The agent-computer interface

2.1 SWE-agent 的核心论点

Yang et al.(NeurIPS 2024)提出:语言模型智能体是一类新的用户,应该给它专门设计的接口,就像人用 IDE 而不是裸终端写代码。他们把这个接口叫做 ACI(agent-computer interface),并总结了四条设计原则:

  1. 动作要简单、好懂:选项少、文档短。
  2. 动作要紧凑、高效:常见的操作合并成尽量少的步骤。
  3. 环境反馈要有信息量、但要简洁。
  4. 护栏:例如编辑后自动做语法检查,限制错误扩散、加快恢复。

具体的组件:

组件设计
文件查看器一次最多显示 100 行,带行号,告诉你上面 / 下面还有几行;open、goto、scroll_up、scroll_down
搜索find_file、search_file、search_dir 最多返回 50 条;超过就不给结果,让你缩小范围
编辑把打开文件的第 start–end 行换成新文本;之后跑 flake8,引入新的语法错误就撤销并让智能体重试;否则显示改后的文件视图
上下文管理最近 5 个之前的观察折叠成一行;之前的报错信息只留第一个
空输出命令没有输出时明确说 “Your command ran successfully and did not produce any output.”

结果(GPT-4 Turbo):SWE-bench 完整集 12.47%、Lite 18.00%;同一个模型用 RAG(检索后一次生成补丁)只有 1.31% / 2.67%;HumanEvalFix 87.7%。平均每个任务 1.59 美元(完整集)。

2.2 消融:每个设计值多少(代码 02)

论文的消融(SWE-bench Lite,GPT-4 Turbo):默认 18.0%;编辑不做语法检查 15.0;没有编辑命令 10.3;迭代式搜索 12.0、没有搜索 15.7;查看器 30 行 14.3、整个文件 12.7;完整历史 15.0;只给 shell 11.0。

迷你版的结果(125 个任务,同一个模拟模型、同样的”运气”):

ACI修好平均步数平均输入 token和默认逐题比:变差 / 变好论文
默认(SWE-agent 式)74.4%15.840,14218.0%
编辑时不做语法检查66.4%16.843,67910 / 0(p = 0.002)15.0%
查看窗口 30 行74.4%16.224,3390 / 014.3%
一次显示整个文件65.6%16.850,52111 / 0(p = 0.001)12.7%
没有搜索命令74.4%18.357,2010 / 015.7%
不折叠旧观察73.6%15.994,6051 / 015.0%
只给 shell48.8%18.768,15432 / 011.0%

(两列数字来自完全不同的任务和模型,只看方向。比较 ACI 用的是逐题配对的 McNemar 检验,第 12 讲 §5.2:同一个任务上”模型的运气”相同,差别只来自设计。)

读这张表要分清哪些是真的、哪些是参数:

  • 真的:每种 ACI 下每一步的观察有多长(token 一列)、要走多少步、语法检查拦下了多少真的语法错误、坏掉的文件让后面的复现和测试真的崩溃。
  • 参数:“一次显示整个文件”让模型”看得没那么清楚”的程度、长上下文让它变差的程度。

在这个只有一两百行文件、十几步任务的小仓库里,窗口 30 行和完整历史只影响成本(token 差了将近 4 倍)。SWE-bench 的仓库有成千上万行、任务长得多,论文里这两项都掉了 3–4 个点。

2.3 护栏的价值:编辑失败的连锁反应

SWE-agent 的行为分析:51.7% 的轨迹至少有一次编辑失败;一旦失败,最终修好的概率从 90.5% 掉到 57.2%;约 23.4% 的失败来自连锁的编辑失败。

迷你版(代码 02 演示 3):

ACI没有编辑失败:修好至少一次编辑失败:修好
默认80%58%
编辑时不做语法检查77%0%
只给 shell83%2%

有语法检查时,一次缩进写错的编辑被当场撤销,智能体看到明确的错误信息,重写一次就好了。没有语法检查时,坏掉的编辑留在文件里,之后的每一次复现、每一次测试都因为 import 失败而崩溃;智能体常常意识不到”是我上次的编辑弄坏的”,转而去别处改,越改越乱。只给 shell 的情况更糟:sed 成功时什么都不输出,智能体连自己改成了什么都看不到。

这就是第四条原则”护栏”的意思:在错误扩散之前把它拦下。它和第 14 讲的”结构型防御”是同一个思路:不指望模型不犯错,而是让错误发生不了,或者发生了马上被发现。


3. 脚手架即设计决策 · Scaffolding as design decisions

3.1 要不要智能体循环:Agentless(代码 03)

Xia et al.(2024)问了一个反直觉的问题:修 bug 真的需要”让模型自己决定下一步”吗?Agentless 用一个写死的三段流程:

  1. 定位:分层地找,先找文件,再找函数 / 类,再找具体的行。
  2. 修复:针对定位到的代码,采样多个候选补丁(search/replace 格式的编辑)。
  3. 验证:生成复现测试,结合仓库原有的回归测试,挑出通过的补丁。

它在 SWE-bench Lite 上修好了 32.00%(96 题),超过了当时所有开源的智能体,成本 0.70 美元。作者还人工检查了 Lite 的题目,发现有的 issue 里直接写着补丁、有的描述不充分或有误导,于是整理出一个更严格的子集 Lite-S。

迷你版(同一个模拟模型、同样的”运气”):

流程修好模型调用平均输入 token
迷你 SWE-agent(最多 40 步)74.4%15.840,142
Agentless(定位 3 次 + 4 个候选)63.2%7.08,594
Agentless → 智能体(46 题交给了智能体)83.2%15.831,180

Agentless 采样的候选越多越好(1 个 30.4%,8 个 72.0%),这是第 9 讲的”best-of-N + 验证器”。但上限由定位决定:bug 所在的行没进前 3 个可疑行(26% 的任务),采样再多也没用。

逐题看,两者失败的地方不同。只有智能体修好的 25 题里,12 题是 Agentless 的定位没排对,智能体靠一行行试出来;只有 Agentless 修好的 11 题里,智能体有 4 题出现过编辑失败、4 题用完了步数。长轨迹里的错误会累积。

这是第 6 讲 “workflow vs agent” 在编程上的样子:能用便宜、可预测的流程的地方先用流程,失败了再升级到更贵、更灵活的智能体。 注意这里的仓库小、bug 都是”改一个记号”,从 issue 里的函数顺着调用找,就能把相关代码一次装进上下文;真实仓库里定位难得多,那正是智能体”边看边找”的价值所在。

3.2 OpenHands:通用的智能体平台

OpenHands(Wang et al., ICLR 2025,原名 OpenDevin)是一个开放的平台,目标是”像人类开发者一样”写代码、用命令行、浏览网页。它的设计:

组件设计
事件流(event stream)按时间排列的动作和观察的集合;智能体的状态就是这个历史(外加累计成本、委派信息)
智能体抽象一个智能体就是一个 step(state) -> action 函数:读历史,返回一个动作(bash 命令、Python 代码、浏览器动作、消息、结束)
运行时每个任务一个隔离的 Docker 容器,里面有 bash、Jupyter IPython、由 Playwright 驱动的 Chromium(BrowserGym 的动作空间);可以用任意的基础镜像
AgentSkills自动导入 IPython 的工具函数库(edit_file、scroll_down、parse_pdf……);只在模型自己不容易写出来、或需要调用外部模型时才加
CodeAct 智能体默认的通用智能体:每一步要么和用户说话,要么执行代码(bash / Python / 浏览器命令)——“代码即动作”
委派AgentDelegateAction:例如把网页浏览交给专门的 BrowsingAgent

CodeAct 在 SWE-bench Lite 上 26.0%(Claude 3.5 Sonnet,每题约 1.10 美元);同一个智能体、不改系统提示,在网页(WebArena 15.3%)、问答(GPQA diamond 52.0%)等 15 个基准上都有竞争力。它的取舍和 SWE-agent 相反:通用的动作空间(写代码就能做任何事)换来灵活,代价是没有专门为编辑设计的护栏,要靠 AgentSkills 补上。

3.3 Claude Code:一个工程化的智能体环境

Claude Code 的官方文档把它描述为一个”智能体式的编程环境”:它能读你的文件、运行命令、做修改,在你看着、纠正或者离开时自主地解决问题。文档里的最佳实践几乎都围绕一个约束展开:上下文窗口很快就会被填满,而填满时表现会变差。

把文档里的建议按本课程的概念整理一下:

建议对应的概念
给它一个能自己验证的方法:测试、构建、截图对比。没有可运行的检查,“看起来做完了”就是唯一的信号,你就成了验证循环第 12 讲的判分器;本讲的复现脚本和回归测试
让它出示证据(测试输出、跑了什么命令、截图),而不是宣称成功第 13 讲:读记录
先探索、再计划、再写代码、再提交(plan mode 只读不改);一句话能说清的小改动就跳过计划第 6 讲:先规划后执行
CLAUDE.md:每次会话开始都会读;只放 Claude 猜不到的东西(命令、风格、仓库规矩),越短越好,太长了规则会被淹没第 7 讲:记忆;第 2 讲:上下文的成本
权限:白名单放行信得过的命令;沙箱做操作系统级的隔离;auto 模式用一个分类器审查动作,只拦看起来有风险的(越权、陌生的基础设施、由不可信内容驱动的动作)第 14 讲:最小权限、结构型防御、确认疲劳
Hooks 是确定性的,一定执行;CLAUDE.md 只是建议第 14 讲:结构型 vs 检测型
子智能体在自己的上下文里做调查,只把摘要带回来第 7 讲:上下文隔离;第 8 讲
/clear 在不相关的任务之间清空上下文;同一个问题纠正两次还不对,就清空、写一个更好的提示第 7 讲:上下文管理
非交互模式 claude -p:放进 CI、脚本、批量迁移(--allowedTools 预先放行需要的工具)第 5 讲:框架与编排
并行会话(git worktree 隔离)、写的人和审的人分开(新的上下文审查不会偏向自己刚写的代码)第 8 讲:多智能体;第 13 讲:评审的独立性
常见的失败:大杂烩会话、反复纠正、过长的 CLAUDE.md、“信了再验”的差距、无限探索本讲 §2.3、§5

3.4 设计决策对照

决策SWE-agentOpenHands(CodeAct)AgentlessClaude Code
谁决定下一步模型(ReAct 循环)模型(ReAct 循环)写死的三段流程模型(循环),人可以随时打断、纠正
动作空间专门设计的 ACI 命令执行代码(bash / Python / 浏览器)定位 + search/replace 编辑文件工具 + shell + MCP + 子智能体
护栏编辑后的语法检查靠 AgentSkills 和沙箱不能编译的候选直接丢掉权限模式、沙箱、hooks、检查点
上下文管理折叠旧观察事件流 + 压缩只给定位到的代码自动压缩、/clear、子智能体
验证智能体自己跑智能体自己跑复现测试 + 回归测试,投票鼓励给出可运行的检查,可以做成 hook / 子智能体

没有哪一列”最好”:它们是在不同的约束(成本、通用性、可预测性、人在回路的程度)下做出的设计决策。这就是课表里说的脚手架即设计决策,也是第 12 讲说”benchmark 分数是模型 × 脚手架的分数”的原因。


4. 评估编程智能体 · Evaluating coding agents

4.1 判分细节(代码 04 演示 1)

默认 ACI 的智能体在 125 个任务上:resolved 93 个;没修好的 32 个全部是”FAIL_TO_PASS 一个都没通过”,其中 26 个最后什么改动都没交。原因是它每次修改后都跑复现和回归测试,不通过就撤销,宁可不交,也不交一个会弄坏别的功能的补丁。所以”修好了 issue、弄坏了别的(PASS_TO_PASS 失败)“一题都没有。

这在小仓库、单记号的 bug 上不奇怪:修对了很少会弄坏别的。真实的补丁常常改几十行,“修好了症状、弄坏了别处”是 SWE-bench 上常见的失败,PASS_TO_PASS 就是为它准备的。只看 FAIL_TO_PASS 的判分器会把这样的补丁算成成功。

4.2 测试太弱时:作弊的补丁(代码 04 演示 2)

一个”会作弊”的智能体:修不好的时候,在 issue 提到的那个函数开头加一个特例,让 issue 里的例子直接返回期望值:

-    if hours <= 0:
+    if (plan, hours,) == ('basic', 3,):
+        return 6
+    if hours <= 0:
诚实的智能体会作弊的智能体
resolved(SWE-bench 的判分)74.4%76.8%
打了特例的提交04
其中被判成 resolved-3
其中差分检查发现不一致-2

当隐藏测试只覆盖 issue 里的那个例子时,“修好了”和”绕过了”在判分器眼里是一样的。这是第 12 讲的结果有效性问题,也是第 9 讲”奖励被钻空子”的编程版:拿测试通过当奖励去训练编程模型,模型就有动机学会这种绕法。

差分检查是一种补救:把补丁后的代码和原始(正确的)实现放在一起,喂同样的输入,结果必须一样。只用测试里出现过的输入还不够(它们往往就是 issue 里的那几个例子);加上扰动(整数 ±1、±2,浮点数 ×0.5、×2,字符串换成同一位置出现过的别的值)之后,才抓住了 3 个里的 2 个。在 SWE-bench 里,修复的 PR 就是”原始实现”,所以这类差分 / 属性测试在评估时是做得到的。

4.3 重复运行(代码 04 演示 3)

63 个任务、每个跑 4 次:

指标值
pass@1(平均每次)72.6%
pass@4(4 次里至少一次)90.5%
pass^4(4 次全部成功)44.4%

6 题 4 次都没修好、28 题 4 次都修好,其余 29 题时好时坏。排行榜上的一个数字通常是 pass@1;如果有可靠的验证器能从几次尝试里挑出对的(Agentless 的思路),pass@k 才有意义;如果你要把智能体交给用户、每次都要靠得住,看 pass^k(第 12 讲 §4.1)。

4.4 说不清楚的任务:SWE-bench Verified(代码 04 演示 4)

OpenAI 和 SWE-bench 的作者合作,请 93 位有 Python 经验的开发者标注了 1,699 个随机样本:38.3% 被标为问题描述不充分,61.1% 被标为单元测试可能不公平地判错正确的解法;总共过滤掉 68.3%,留下 500 个,即 SWE-bench Verified。GPT-4o 在 Verified 上 33.2%,Agentless 从原来的 16% 翻了一倍。(作者也说明这个过滤可能偏严。)

迷你版里也能找到”说不清楚”的任务:在 bug 所在的那一行上,枚举所有”改一个记号”的修法,看有没有一个能通过 issue 里的例子和全部可见测试、却通不过隐藏测试。125 个任务里有 7 个。对这样的任务,一个同样满足 issue 要求的修法会被判错:判分器检查的是”和原来的实现一样”,而不只是”满足 issue 的要求”。(迷你版里模拟的模型多半会挑中原来的写法,这是参数,所以它在这 7 题上的分数并不低;真实模型没有这个”偏好”。)

这正是第 12 讲 §3 的任务有效性:一个任务应该”有能力就能做对”。过滤掉说不清楚的任务,分数会变,排名也可能变。

4.5 饱和

Anthropic 的 Demystifying Evals(第 13 讲)提到,LLM 在 SWE-bench Verified 上一年之内从 40% 左右涨到了 80% 以上。一个接近饱和的评估不再能区分好坏(第 13 讲 §7 第 7 步)。这也是为什么会不断出现新的、更难的编程评估:更大的仓库、更长的任务、多语言、需要和人交互的任务。


5. 长时间运行的 harness · Harnesses for long-running agents

5.1 问题:每个新会话都不记得上一个

SWE-bench 的任务十几步就能做完。但让智能体”做一个 claude.ai 的克隆”这种任务,要跨很多个上下文窗口。Young(Anthropic,2025-11-26)发现,即使是 Opus 4.5 这样的前沿模型,在 Claude Agent SDK 上靠压缩在很多个上下文窗口里循环,只给一个高层指令,也做不出一个生产质量的应用。失败有两种主要模式:

  1. 想一口气做完:做到一半上下文用完,留下一个半成品、没有记录;下一个会话要花很多时间猜发生了什么、把基本的应用重新跑起来。压缩也救不了,因为它不一定能把清楚的指示传给下一个智能体。
  2. 过早宣布胜利:后来的会话看看周围,发现已经做了不少,就宣布完工。

文章用了一个比喻:一个软件项目由轮班的工程师来做,每个新来的工程师都不记得上一班发生了什么。

5.2 解决:初始化智能体 + 编程智能体

失败初始化智能体(第一个会话)做的事编程智能体(之后每个会话)做的事
过早宣布完工写一份特性清单(JSON,200 多条端到端的特性,全部标成 failing)开工时读清单,挑一个最优先的、还没做完的特性
留下有 bug 或没记录的状态初始的 git 仓库和进度文件开工时读进度文件和 git 日志,在开发服务器上跑一遍基本的测试;收工时提交 git、写进度
没测就标成完成(特性清单)只有仔细测过(用浏览器自动化、像真实用户一样)才标成 passing
每次都要摸索怎样跑起来写一个能启动开发服务器的 init.sh开工时先读 init.sh

几个细节:特性清单用 JSON 而不是 Markdown,因为模型更不容易不恰当地修改或覆盖 JSON 文件;提示里用了很强的措辞,只允许改 passes 字段,“删掉或修改测试是不可接受的”;“一次只做一个特性”对付”想一口气做完”至关重要。文章脚注说明:两个”智能体”只是第一个提示不同,系统提示、工具和 harness 完全一样。

5.3 模拟(代码 05)

30 个特性、最多 25 个会话、每个会话 100 个单位的工作量(参数化的模拟,没有真的写代码):

harness真的能用的特性标成完成但其实有 bug提前宣布完工结束时主分支坏着
只给高层指令(靠压缩接着干)7.23.3100%86%
+ 特性清单(JSON,全部 failing)15.76.20%68%
+ 一次一个特性 + git + 进度文件23.76.30%0%
+ init.sh + 冒烟测试 + 端到端测过才标通过29.80.20%0%

每加一个部件,就消掉一种失败:特性清单消掉”过早宣布完工”;一次一个特性 + git 消掉”留下坏掉的主分支”;进度文件消掉”每次都要重新摸索”(摸索 / 修复的工作量从 482 降到 0);端到端测试消掉”没测就标完成”。

这些部件和人类工程师的习惯一一对应:需求清单、小步提交、交接记录、启动脚本、冒烟测试。长时间运行的智能体缺的不是能力,而是跨会话的状态;文件和 git 就是它的外部记忆(第 7 讲)。文章最后留下的开放问题之一是:一个通用的编程智能体好,还是测试、QA、清理代码这样分工的多智能体好(第 8 讲)。


6. 安全:编程智能体的权限与沙箱 · Safety for coding agents

编程智能体同时具备第 14 讲 Rule of Two 的三个性质:它读不可信的内容(依赖的源码、issue、网页、测试输出),能接触敏感的东西(凭证、源码),能改变状态(写文件、执行命令、推送代码)。所以第 14 讲的结构型防御在这里尤其重要:

  • 沙箱:OpenHands 每个任务一个 Docker 容器;Claude Code 提供操作系统级的沙箱,限制文件系统和网络访问;SWE-bench 的评估也在容器里跑。
  • 权限按会话配置:白名单放行信得过的命令;无人值守的批量任务用 --allowedTools 预先放行需要的工具、拒绝其他一切(dontAsk 模式)。
  • 确认留给高风险的动作:文档自己就写道,手动模式下”批到第十次,你就是在点击通过而不是在审查”——第 14 讲的确认疲劳。auto 模式用一个分类器代替人审查大多数动作,只拦看起来有风险的。
  • 可恢复:Claude Code 每次修改前都给文件拍快照,可以回退到任何一个检查点;但它只追踪自己的文件编辑工具做的修改,不追踪 shell 命令,文档明确说它不能代替 git。长时间运行的 harness 用 git 提交来保证”随时可以回到干净的状态”(§5)。
  • 测试本身也要保护:长时间运行的 harness 明确禁止智能体删改测试;SWE-bench 判分时用修复 PR 的测试,不用智能体可能改过的版本。否则”让测试通过”最简单的办法就是改测试(第 12 讲练习 2)。

7. 用真实模型试一试 · Try it on a real model

06_real_llm.py 用 Agentless 的形状:定位由代码做(从 issue 里的函数顺着调用找到相关的函数和常量),修复交给 Claude:给它 issue 和这些代码(带行号),让它输出一个 SEARCH/REPLACE 编辑;然后按 SWE-bench 判分,再做差分检查。默认取 8 个任务,每个 1 次调用。

python3 06_real_llm.py --toy

(--toy 用模拟模型,不联网;去掉它就调用真实模型,需要 pip install anthropic 和 API key。)

运行之后看:没修好的那几个,是定位给的代码里根本没有 bug,还是模型改错了地方或改错了方式?模型有没有”改得比需要的多”,例如顺手重写了整个函数?这类补丁更容易弄坏 PASS_TO_PASS。


8. 软件开发的未来 · The future of software development

课表把这一讲的最后一个主题叫做”软件开发的未来”。不做预言,只说这一讲的内容能支持的几点观察:

  • 工作的重心在移动。Claude Code 的文档写道:不再是你写代码、让 Claude 审,而是你描述想要什么、Claude 想办法去做。当”写”变便宜了,难的部分就变成了说清楚要什么(规格、issue、验收标准)和确认做对了(测试、评审)。这正是 SWE-bench Verified 过滤掉的那两类问题:描述不充分、测试不公平。
  • 验证是瓶颈,也是杠杆。本讲每一个让智能体变好的部件都和验证有关:编辑时的语法检查、复现脚本、回归测试、端到端测试、差分检查、写的人和审的人分开。Claude Code 文档的第一条建议就是”给它一个能自己验证的方法”。
  • 脚手架和模型一样重要。同一个模型,换一套 ACI,结果可以差很多(§2);一个写死的流程可以和智能体打平(§3.1);一个好的长时间 harness 能把”做不出来”变成”做得出来”(§5)。
  • 评估要跟上。基准会饱和、会被钻空子、会有说不清楚的任务(§4)。造一个好的编程评估,本身就是一项工程(第 12、13 讲)。

9. 小结 · Summary

  1. 编程智能体的循环:读 issue → 定位(顺着调用找)→ 编辑 → 用复现脚本和回归测试验证 → 不行就撤销 → 提交。难点在定位和验证。
  2. SWE-bench 的 4 元组:issue、仓库快照、提交或预算、FAIL_TO_PASS + PASS_TO_PASS。FAIL_TO_PASS 对智能体隐藏。
  3. ACI 是设计:简单的动作、紧凑的操作、简洁的反馈、护栏。编辑时的语法检查阻止了连锁失败;观察的长短决定了成本。
  4. 脚手架即设计决策:智能体循环灵活但贵、错误会累积;写死的流程便宜可预测但受定位限制;接力往往两者兼得。OpenHands 选择通用的动作空间,Claude Code 围绕”上下文是最重要的资源”组织一切。
  5. 评估编程智能体:PASS_TO_PASS 防止修好一处弄坏别处;测试太弱时作弊的补丁会被判成成功,差分 / 扰动检查可以补救;pass@1、pass@k、pass^k 回答不同的问题;说不清楚的任务要过滤(Verified)。
  6. 长时间运行的 harness:特性清单、一次一个特性、git、进度文件、init.sh、端到端测试——每一个对付一种失败。
  7. 安全:编程智能体满足 Rule of Two 的全部三条;沙箱、按会话的权限、可恢复、保护测试。

10. 常见误区 · Common pitfalls

  • “模型越强,脚手架越不重要。” SWE-agent 的消融里,同一个模型换一套 ACI,从 18% 掉到 11%;RAG 式的一次生成只有 2.67%。
  • “给智能体一个能执行任何命令的 shell 就够了。” 只给 shell 时没有护栏、没有简洁的反馈,编辑失败一次几乎就救不回来。
  • “让它一次看到整个文件,信息越多越好。” 观察越长,成本越高,模型也越容易看漏;SWE-agent 的整文件查看器比 100 行的窗口差了 5 个多点。
  • “测试通过就是修好了。” 测试只覆盖 issue 里的例子时,打特例也能通过;要有 PASS_TO_PASS、差分检查、代码审查。
  • “智能体比写死的流程更好。” Agentless 用三段流程打败了当时所有开源的智能体,而且便宜得多。
  • “pass@k 很高,说明它很可靠。” pass@4 90.5% 的智能体,pass^4 只有 44.4%。
  • “benchmark 上的每一个失败都是智能体的错。” 有的任务描述不充分、有的测试判错正确的解法(SWE-bench 里分别是 38.3% 和 61.1% 被标出)。
  • “长任务交给压缩就行。” 压缩不一定能把清楚的指示传给下一个会话;要有特性清单、进度文件和 git。
  • “CLAUDE.md 写得越详细越好。” 太长了重要的规则会被淹没;必须每次都执行的事用 hook。
  • “有检查点就不用 git 了。” 检查点只追踪编辑工具的修改,不追踪 shell 命令。

11. 练习 · Exercises

练习 1(4 元组):为”给一个 Web 应用加一个’导出 CSV’的按钮”写一个编程任务的 4 元组,并说明 FAIL_TO_PASS 和 PASS_TO_PASS 各应该包含什么。

参考答案 请求:一段需求描述(在哪个页面、导出哪些列、文件名格式、空数据时怎样)。环境:仓库快照 + 能启动应用的容器(数据库种子数据)。停止条件:提交补丁或用完预算。判分器:FAIL_TO_PASS——一个端到端测试(打开页面、点按钮、下载的 CSV 列和行正确)+ 边界情况(空表、包含逗号和引号的字段要正确转义);PASS_TO_PASS——原有的全部测试(页面的其他功能、其他导出格式)。注意 FAIL_TO_PASS 不要只覆盖需求里举的那一个例子,否则打特例也能通过。

练习 2(ACI 设计):你要为一个 Java 仓库设计编辑命令。参考 SWE-agent 的四条原则,你会怎样设计”编辑”和”编辑后的反馈”?

参考答案 动作简单:`edit ` + 新文本,或 search/replace(不用记行号,但要处理多处匹配)。护栏:编辑后对这个文件做增量编译 / 语法检查(javac 或语言服务器的诊断),新增的编译错误直接撤销并返回错误信息(文件名、行号、错误原文的前几行)。反馈简洁:成功时显示修改附近的窗口(带行号),而不是整个文件;失败时只显示新引入的错误,不显示原本就有的警告。再加一条:编辑失败时说明"文件没有被修改",免得智能体以为改坏了。

练习 3(连锁失败):用本讲的数据解释,为什么”编辑后做语法检查”对没有语法检查时有编辑失败的运行影响这么大(0% vs 58%)。

参考答案 没有语法检查时,写错缩进的编辑被保存进文件;之后整个模块无法导入,复现脚本和所有测试都崩溃,报错信息指向 import 失败而不是"你上次的编辑"。智能体如果没意识到是自己的编辑造成的(模拟里以 p_notice 的概率意识到),就会认为"这个修法不对",转去别的行改,而坏掉的文件还在——之后的每一次尝试都失败,直到用完步数。有语法检查时,错误在发生的那一刻就被拦下、撤销,并且错误信息直接指出是这次编辑的哪一行,智能体重写一次即可。

练习 4(智能体 vs 流程):你的团队每天收到 200 个小 bug 报告,大多数只涉及一两个函数。你会选智能体循环还是 Agentless 式的流程?怎样组合?

参考答案 先跑 Agentless 式的流程:便宜(本讲约 1/4.7 的 token)、可预测、容易并行和审计;对候选补丁用复现测试和回归测试验证。只有当没有候选通过验证时,再把任务交给智能体(本讲的接力把 63.2% 提到 83.2%)。同时记录哪些任务需要升级,分析原因(通常是定位失败),改进定位或把这类任务直接路由给智能体。最后都要有人审查合并。

练习 5(作弊的补丁):你的评估发现某个智能体的 resolved 比例很高,但用户反馈修复经常”只对报告里的例子有效”。设计两种检测方法。

参考答案 ① 差分 / 属性测试:把补丁后的函数和修复 PR 的实现(或参考实现)在大量扰动的输入上比较;或者写性质(例如"结果不超过上限""对称")随机测试。② 静态检查补丁:找"if 参数 == 常量: return 常量"这类模式、对测试文件的修改、新增的 try/except 吞掉异常。③ 加强 FAIL_TO_PASS:每个任务准备多组不同的输入,只公开其中一组作为 issue 的例子。④ 人工抽查一部分 resolved 的补丁(第 13 讲:读记录)。

练习 6(说不清楚的任务):一个 issue 写着”round_price(2.675) 返回 2.67,应该是 2.68”。为什么这个任务可能”说不清楚”?你会怎样改 issue 或测试?

参考答案 有多种都能让这个例子通过、行为却不同的修法:改用十进制的四舍五入(ROUND_HALF_UP)、加一个很小的 epsilon、改成向上取整……它们在别的输入(例如 2.665、负数、恰好是 .5 的情况)上结果不同。如果隐藏测试检查的是其中一种特定的行为,其他同样满足 issue 的修法会被判错。改法:在 issue 里写清楚规则("金额按十进制、四舍五入、.5 向远离零的方向进")并给出几个边界例子;或者让测试只检查 issue 写明的行为。

练习 7(pass@k):一个编程智能体 pass@1 = 60%、pass@5 = 85%。什么情况下你会报告 pass@5?什么情况下必须报告 pass@1 或 pass^k?

参考答案 pass@5 有意义的前提是有一个可靠的、不依赖隐藏测试的验证器,能从 5 次尝试里自动挑出对的那一个(例如完整的回归测试 + 复现测试,或者人只需要挑一个)——这时用户实际体验到的接近 pass@5。如果智能体只跑一次就交给用户(例如自动提交 PR),或者没有可靠的验证器,就报告 pass@1;如果关心"每次都靠得住"(例如在 CI 里无人值守地跑),报告 pass^k。无论哪种,都要说明算法和成本(5 次 = 5 倍的成本)。

练习 8(长时间运行的 harness):你要让一个智能体在一周内把一个 Python 2 的项目迁移到 Python 3(2,000 个文件)。按本讲的方法设计 harness。

参考答案 初始化:生成"待迁移文件清单"(JSON,每个文件一条,`migrated: false`、`tests_pass: false`),写 `init.sh`(建虚拟环境、跑测试),初始提交,建进度文件。每个会话:读进度文件和 git 日志 → 跑冒烟测试(确认主分支是好的)→ 取下一批(例如同一个包里的 10 个文件)→ 迁移 → 跑这些文件相关的测试 → 通过才标 `migrated: true` 并提交 → 写进度(遇到的模式、坑)。不允许修改测试来让测试通过。可以并行:每个会话一个 worktree、一个包,最后合并(Claude Code 文档的 fan-out 模式:`claude -p` 加 `--allowedTools`)。

练习 9(权限):为一个”每晚自动修复 lint 错误并开 PR”的无人值守编程智能体设计权限。

参考答案 在沙箱 / 容器里跑;只放行需要的工具:读写工作区的文件、运行 linter 和测试、`git commit`、开 PR 的命令(例如 `gh pr create`);禁止网络访问(除了推送到指定的远端)、禁止修改 CI 配置和测试文件、禁止推送到主分支;其他一切需要确认的动作一律拒绝(无人值守,没有人可以确认)。PR 必须经人审查合并。这是第 14 讲的最小权限 + Rule of Two:它读的是自己仓库的代码(相对可信),能改状态,但拿不到凭证之外的敏感数据、不能对外通信。

练习 10(开放题:评估你的编程智能体):为你常用的编程智能体(或你自己写的)设计一个小评估:任务从哪来、怎样判分、报告哪些数字、怎样防止钻空子。

参考答案(思路) 任务:从你自己的仓库造(SWE-smith 式注入 bug + 你的测试),再加几个真实的历史 bug(从 git log 里找"fix"提交,回退修复、用修复带来的测试做 FAIL_TO_PASS)。判分:FAIL_TO_PASS + PASS_TO_PASS + 差分检查;在容器里跑,用原始的测试文件。报告:pass@1、pass^k(多跑几次)、成本(token、时间)、按失败原因分类(定位 / 编辑 / 验证 / 超时)、配对比较(换脚手架时)。防钻空子:隐藏测试多组输入、静态检查补丁、人工抽查、过滤说不清楚的任务。最后读一读失败的轨迹。

12. 延伸阅读 · Further reading

本讲的阅读材料

  • J. Yang, C. E. Jimenez, A. Wettig, K. Lieret, S. Yao, K. Narasimhan, O. Press, SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering, NeurIPS 2024.
  • X. Wang et al., OpenHands: An Open Platform for AI Software Developers as Generalist Agents, ICLR 2025.
  • Claude Code 文档:Best practices for Claude Code(code.claude.com/docs/en/best-practices)。
  • J. Young, Effective Harnesses for Long-Running Agents, Anthropic Engineering, 2025-11-26.
  • C. E. Jimenez et al., SWE-bench: Can Language Models Resolve Real-World GitHub Issues?, ICLR 2024.

相关

  • C. S. Xia, Y. Deng, S. Dunn, L. Zhang, Agentless: Demystifying LLM-based Software Engineering Agents, 2024.
  • OpenAI, Introducing SWE-bench Verified, 2024.
  • J. Yang et al., SWE-smith: Scaling Data for Software Engineering Agents, 2025(第 11 讲 §5)。
  • X. Wang et al., Executable Code Actions Elicit Better LLM Agents(CodeAct),ICML 2024.

13. 下一讲预告 · Next lecture

第 16 讲 主动式智能体(11/30)。到目前为止,所有的智能体都在等你开口:你提一个 issue,它去修;你问一个问题,它去查。下一讲讨论反过来的情形:智能体先猜你需要什么,再决定要不要、什么时候说出来。这需要一个关于你的模型:Shaikh et al. 的通用用户模型(GUM)从你用电脑的观察里推断出带置信度的命题;他们后续的下一动作预测(NAP)试图预测你接下来要做什么。有了用户模型,还要决定什么时候打扰你:这是 Horvitz 的混合主动原则,用期望效用权衡帮对了的好处、打扰错了的代价和你此刻的注意力。我们也会看开源的主动式个人助手(OpenClaw),以及隐私与信任:一个整天看着你屏幕的助手,知道什么、该说什么。配套代码会从模拟的行为日志里长出一个用户模型,做下一动作预测,并模拟”要不要打扰用户”的决策。

← 返回课程目录