Princeton NLP · 软件工程 Agent 框架 · Shell-First 架构范式
SWE-agent 是 Princeton NLP 团队开发的软件工程 Agent,专为自动解决 GitHub issue 设计。它在 SWE-bench 基准上取得了领先成绩,核心论文 SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering 提出了一个关键洞察:
传统 Agent 直接操作 bash shell 效率低下——模型需要处理大量噪声输出、格式不确定的返回值。SWE-agent 的创新在于:设计专用的 Agent-Computer Interface,让 LLM 与计算机的交互更简洁、更结构化,就像 HCI 让人更好用计算机一样,ACI 让 Agent 更好用计算机。
具体来说,SWE-agent 的 ACI 设计体现在:
ACI 设计 Bundle 工具 Blocklist 安全 Review-on-Submit
# 伪代码:DefaultAgent 核心循环
def run(env, problem_statement, output_dir):
self.setup(env, problem_statement, output_dir) # 安装工具 + 构造初始历史
step_output = StepOutput()
while not step_output.done:
step_output = self.step() # 单步
def step():
# 1. 历史处理 → messages
messages = self.messages # property: history → chain(history_processors)
# 2. 前向推理 + 错误处理
step_output = self.forward_with_handling(messages)
# 3. 更新历史 & 轨迹
self.add_step_to_history(step_output)
self.add_step_to_trajectory(step_output)
return step_output
def forward_with_handling(history):
# 重试循环:格式错误/被阻止的命令/bash语法错误 → 重新查询
n_format_fails = 0
while n_format_fails < max_requeries:
try:
return self.forward(history) # 模型查询 → 解析 → 执行 → 提交检查
except FormatError:
n_format_fails += 1
history = requery_with_error_template(...)
except _BlockedActionError:
n_format_fails += 1
history = requery_with_error_template(...)
except BashIncorrectSyntaxError:
n_format_fails += 1
history = requery_with_shell_check_template(...)
except CommandTimeoutError:
return autosubmit(...)
except ContextWindowExceededError:
return autosubmit(...)
except CostLimitExceededError:
return autosubmit(...)
def forward(history):
output = model.query(history) # LLM 推理
thought, action = tools.parse_actions(output) # 解析 thought + action
step = handle_action(step) # 执行 + 状态收集
return handle_submission(step) # 检查是否提交
关键设计特点:
history(发送给 LLM 的消息列表)和 trajectory(完整的执行轨迹,含所有状态)分开维护_n_consecutive_timeouts,达到阈值则终止total_execution_timeout(默认 1800s),跨步骤累计SWE-agent 的工具系统是其最独特的设计之一。它不使用传统的 Python 函数工具,而是通过 Bundle 机制将 shell 脚本包装为 LLM 可调用的工具。
每个 Bundle 是一个独立目录,包含:
# tools/edit_anthropic/config.yaml
tools:
str_replace_editor:
signature: |
str_replace_editor <command> <path> [<file_text>] [<view_range>] ...
docstring: >
Custom editing tool for viewing, creating and editing files
* view: cat -n 显示; directory 列出2层
* create: 创建新文件
* str_replace: 精确替换(old_str → new_str)
* insert: 在指定行后插入
* undo_edit: 撤销上次编辑
arguments:
- name: command
type: string
enum: ["view", "create", "str_replace", "insert", "undo_edit"]
required: true
- name: path
type: string
required: true
- name: old_str / new_str / insert_line / view_range ...
state_command: "_state_anthropic"
Bundle 的安装流程:
_upload_bundles() — 将整个目录上传到容器的 /root/tools/{name}/install.sh(如果存在)bin/ 加入 PATH_check_available_commands() — 验证所有命令可用这是 SWE-agent 最关键的 ACI 创新。传统的 bash 编辑(sed/awk)对 LLM 来说太脆弱——特殊字符、转义、多行处理都是雷区。str_replace_editor 提供了 5 个子命令:
查看文件(cat -n 格式)或目录结构。支持 view_range 参数限制行范围
创建新文件。如果文件已存在则拒绝,防止意外覆盖
精确字符串替换。old_str 必须在文件中唯一匹配。这是最安全的编辑方式
在指定行号后插入新内容。不需要匹配已有文本
撤销上次编辑。提供安全网,允许 Agent 纠正错误
隐式状态命令!每次执行后自动运行,收集 diff、cwd 等状态写入 /root/state.json
submit 命令不是一次性提交,而是多阶段审查流程:
# submit 命令执行流程
1. git add -A && git diff --cached > /root/model.patch
2. 检查 SUBMIT_STAGE(从 registry 读取)
3. 如果 STAGE < len(SUBMIT_REVIEW_MESSAGES):
- 用 diff 填充审查消息模板
- 输出审查消息(不提交!)
- SUBMIT_STAGE += 1
- exit(0) → Agent 看到 "请检查你的变更..."
4. 如果 STAGE == len(审查消息):
- 输出 <<SWE_AGENT_SUBMISSION>> 标记
- 真正提交
默认审查消息会要求 Agent:
多阶段提交 审查机制
blocklist = ["vim", "vi", "emacs", "nano", "nohup", "gdb", "less", "tail -f", "python -m venv", "make"]
blocklist_standalone = ["python", "python3", "ipython", "bash", "sh", "/bin/bash", ...]
block_unless_regex = {"radare2": r"\b(?:radare2)\b.*\s+-c\s+.*"} # 只允许非交互用法
三层过滤机制:前缀匹配 → 精确匹配 → 条件放行。这比简单黑名单更灵活。
SWE-agent 提供了 8 种解析器,是所有 Agent 框架中最丰富的。这反映了它对"模型输出 → 可执行动作"这一转化环节的极致关注。
| 解析器 | 输入格式 | 适用场景 | 特点 |
|---|---|---|---|
FunctionCallingParser |
LiteLLM tool_calls | OpenAI/Claude 等 | 官方推荐;结构化最强 |
ThoughtActionParser |
讨论 + \`\`\`代码块\`\`\` | 开源模型 | 经典格式;提取最后一个代码块 |
XMLThoughtActionParser |
<command>...</command> | XML 友好模型 | XML 标签包裹命令 |
XMLFunctionCallingParser |
<function=name><parameter=...> | 类 function calling | XML 格式的工具调用 |
JsonParser |
{"thought":"...", "command":{...}} | JSON 输出模型 | 结构化 JSON;非严格模式宽容 |
BashCodeBlockParser |
\`\`\`bash...\`\`\` | 代码生成模型 | 提取所有 bash 代码块拼接 |
SingleBashCodeBlockParser |
同上(仅一个) | 受限场景 | 强制单代码块 |
Identity |
任意 | 调试/人机模式 | 原样返回,不做解析 |
解析器架构的精妙之处:
(thought, action) 元组,后续处理完全一致command.invoke_format 格式化为最终 shell 命令_should_quote() 智能决定参数是否需要 shlex.quoteSWE-agent 的 HistoryProcessor 链是它上下文工程的核心。多个处理器可以串联,按顺序对 history 列表做变换。
这是 SWE-agent 针对 Anthropic Claude 的特别优化。它的工作原理:
# 在 history 的最后 N 条 user/tool 消息上添加 cache_control 标记
# Anthropic API 会缓存到标记位置,下次请求如果前缀相同则命中缓存
{
"role": "user",
"content": [{
"type": "text",
"text": "...",
"cache_control": {"type": "ephemeral"} ← 标记缓存断点
}]
}
last_n_messages=2 意味着缓存断点在倒数第二条 user 消息处。这样每轮对话只有新的观察和动作需要重新处理,系统提示 + 早期历史都能命中缓存。
# 论文中的经典配置:只保留最近 5 个观察
history_processors:
- type: last_n_observations
n: 5
# 实现细节:
# - 旧的观察替换为 "Old environment output: (n lines omitted)"
# - polling=1: 每步都更新(默认)
# - polling=5: 每 5 步更新一次,减少缓存失效
# - 支持 always_keep_output_for_tags / always_remove_output_for_tags 标签
注释中也指出:现代模型上下文窗口越来越大,这个处理器不再总是必需。但它展示了 SWE-agent 对 token 经济学的精细控制。
SWE-agent 的 RetryAgent 是 Agent 框架中少有的"多次尝试 + 质量评估"机制。
每次提交后,Reviewer 对解打分(多次采样取均值)。如果分数 ≥ accept_score 则接受;否则继续尝试。
reviewer_config.n_sample=5:每次评审采样 5 次reduce_by_std:减去标准差 × 系数(保守估计)failure_score_penalty:异常退出(超时/成本超限)扣分所有尝试结束后,Chooser(LLM)直接选择最优解。可选 Preselector 先筛选候选。
cost_limit 跨尝试累计Reviewer 的评分逻辑:
# Reviewer 评分 = 多次采样的均值 - 惩罚 - 标准差调整
scores = [self.interpret(answer) for answer in answers] # 从 LLM 回复提取分数
accept = mean(scores) - penalty
if reduce_by_std > 0:
accept -= std(scores) * reduce_by_std # 保守化
# TrajectoryFormatter: 将轨迹格式化为文本供 Reviewer 阅读
# 支持 filter(过滤特定动作)和 output_filter(过滤特定输出)
Action Sampler 是 SWE-agent 的另一个独特设计——在每一步,不只是问一次 LLM,而是采样多个候选动作,然后选择最好的。
采样 N 个完成 → 将所有候选汇总 → 再问一次 LLM 做最终选择
# 流程
1. model.query(history, n=2) → 2 个候选
2. 解析每个候选的 thought + action
3. 构造 "同事讨论" 消息:
"Your colleagues had the following ideas:
Thought (colleague 0): ...
Proposed Action (colleague 0): ...
Thought (colleague 1): ...
Please summarize and choose one."
4. model.query(history + discussion) → 最终动作
锦标赛式二选一:采样多个完成 → 两两比较 → 锦标赛胜者
# 流程
1. model.query(history, n=4~10) → 候选集
2. 去重 + 过滤不可解析的
3. 如果候选涉及 edit 操作,增加采样量
4. 锦标赛:
best = candidates[0]
for i in 1..n:
result = compare(best, candidates[i])
if result == "second":
best = candidates[i]
5. 返回 best
BinaryTrajectoryComparison 的比较提示词设计很有趣:
<thought1>...</thought1>
<action1>...</action1>
<thought2>...</thought2>
<action2>...</action2>
Please compare the two actions in detail.
Which action should we take?
The last line MUST be "first" or "second".
这种设计确保了模型输出可被确定性解析(interpret 函数只看最后一行的 first/second)。
SWE-agent 的环境完全由 SWE-ReX 运行时驱动,这是一个独立的 Docker 容器管理框架。
# 环境初始化流程
1. deployment.start() → 启动 Docker 容器
2. runtime.create_session() → 创建 bash session(source .bashrc)
3. set_env_variables() → 设置 LANG, PAGER 等
4. _copy_repo() → 将仓库克隆到容器
5. _reset_repository() → git checkout base_commit + git clean
6. post_startup_commands → 执行额外初始化命令
# 命令执行
communicate(input, timeout, check) →
runtime.run_in_session(BashAction(command, timeout, check))
→ 返回 output + exit_code
→ check="raise" 时,非零退出码抛出 RuntimeError 并 close()
注意 communicate() 的 check 参数有三个级别:ignore(不检查退出码)、warn(警告但不中断)、raise(抛出错误并关闭容器)。这种渐进式错误处理让 Agent 能在大多数情况下继续运行。
SWE-agent 的提示词完全由 Jinja2 模板 驱动,模板本身是 YAML 配置的一部分。
class TemplateConfig:
system_template: str # 系统提示
instance_template: str # 初始用户消息(含问题描述)
next_step_template: str # 每步观察模板
next_step_truncated_observation_template: str # 观察被截断时
next_step_no_output_template: str # 无输出时
strategy_template: str | None # 策略提示(可选)
shell_check_error_template: str # bash 语法错误提示
command_cancelled_timeout_template: str # 命令超时提示
demonstration_template: str | None # 演示模板
demonstrations: list[Path] # 演示文件路径
put_demos_in_history: bool # 演示放入历史 vs 单条消息
max_observation_length: int = 100000 # 观察截断阈值
# System prompt(极简)
"You are a helpful assistant that can interact with a computer to solve tasks."
# Instance template(核心提示)
"""
<uploaded_files>{{working_dir}}</uploaded_files>
I've uploaded a python code repository in the directory {{working_dir}}.
Consider the following PR description:
<pr_description>{{problem_statement}}</pr_description>
Can you help me implement the necessary changes?
Follow these steps:
1. Find and read code relevant to the <pr_description>
2. Create a script to reproduce the error and execute it
3. Edit the sourcecode to resolve the issue
4. Rerun your reproduce script and confirm the error is fixed
5. Think about edgecases
"""
# Next step template
"OBSERVATION:\n{{observation}}"
关键设计决策:
<pr_description>、<uploaded_files>),帮助模型定位关键信息模板变量来源极其丰富:
{
"command_docs": tools.config.command_docs, # 自动生成的工具文档
**tools.config.env_variables, # 环境变量
"problem_statement": ..., # 问题描述
"repo": env.repo.repo_name, # 仓库名
**problem_statement.get_extra_fields(), # 问题特有字段
"observation": step.observation, # 当前观察
"max_observation_length": ..., # 截断阈值
**step.state, # 环境状态(diff, cwd 等)
}
| 维度 | SWE-agent | OpenHands | Aider | Agent Zero |
|---|---|---|---|---|
| 核心架构 | Shell-First ACI | Event-Driven | Editor-First | ReAct + Self-Modify |
| 工具定义 | Bundle (shell 脚本 + YAML) | Python 函数 | 15 种 edit format | Python 函数 + 动态注册 |
| 输出解析 | 8 种解析器 | Function Calling | backtick/SEARCH-REPLACE | Function Calling |
| 环境 | Docker (SWE-ReX) | Docker (EventStream) | 本地文件系统 | Docker |
| 重试机制 | RetryAgent + Reviewer/Chooser | 无 | 无 | 无 |
| Action Sampling | 2 种 (AskColleagues / BinaryCompare) | 无 | 无 | 无 |
| 历史管理 | 7 种处理器链式组合 | Condenser 压缩 | RepoMap 上下文 | 简单截断 |
| 提交审查 | 多阶段 Review-on-Submit | 无 | 无 | 无 |
| Prompt Caching | CacheControlHistoryProcessor | 无 | 无 | 无 |
| 目标场景 | SWE-bench 基准 | 通用软件开发 | 代码编辑 | 通用自主 Agent |
| 可扩展性 | Bundle 插件系统 | Agent Hub | edit format 插件 | 技能注册 |
OpenHands 使用 Python 函数工具 + EventStream 架构,更通用但更重。SWE-agent 的 Bundle 机制让工具在容器内执行(接近生产环境),而 OpenHands 的工具在 Agent 进程内执行(需要网络通信)。SWE-agent 的 RetryAgent + Reviewer 是独特的质量保证层。
Aider 专注代码编辑,有 15 种 edit format 和 RepoMap 上下文工程。SWE-agent 的 str_replace_editor 是 Aider SEARCH-REPLACE 的简化版。但 SWE-agent 有完整的 shell 环境和重试机制,覆盖了更广的问题空间。
Agent Zero 的自修改能力(修改自己的提示词/工具)是 SWE-agent 没有的。但 SWE-agent 的 ACI 设计哲学——"让 Agent 与计算机的交互更结构化"——是更工程化的思路。Agent Zero 是生物演化,SWE-agent 是精密工程。
asyncio.run() 包装同步调用,不是真正的异步架构SWE-agent 的核心贡献是 ACI(Agent-Computer Interface) 设计哲学——不是让 Agent 适应现有的人机接口,而是为 Agent 设计专用的交互接口。这一理念影响了后续几乎所有 Agent 框架的工具设计。其 Bundle 系统、8 种解析器、RetryAgent + Reviewer、Action Sampler 等机制,构成了 Agent 工程化最完整的实践之一。
分析时间:2026-05-17 · 源码版本:swe-agent main branch · 基于 100 个 Python 文件的深度阅读