🔬 SWE-agent 深度分析

Princeton NLP · 软件工程 Agent 框架 · Shell-First 架构范式

100
Python 文件
3
Agent 类型
8
输出解析器
2
Action Sampler
7
History Processor
📋 目录 1. 项目概览与核心理念 2. 架构全景与核心循环 3. 工具系统:Bundle + Command 架构 4. 输出解析器:8 种解析策略 5. 历史处理器:上下文工程核心 6. RetryAgent 与 Reviewer 系统 7. Action Sampler:多路径决策 8. SWE-ReX 环境层 9. 提示词工程与模板系统 10. 与同类项目对比 11. 关键洞察与设计取舍

1. 项目概览与核心理念

SWE-agent 是 Princeton NLP 团队开发的软件工程 Agent,专为自动解决 GitHub issue 设计。它在 SWE-bench 基准上取得了领先成绩,核心论文 SWE-agent: Agent-Computer Interfaces Enable Automated Software Engineering 提出了一个关键洞察:

💡 核心洞察:Agent-Computer Interface (ACI)

传统 Agent 直接操作 bash shell 效率低下——模型需要处理大量噪声输出、格式不确定的返回值。SWE-agent 的创新在于:设计专用的 Agent-Computer Interface,让 LLM 与计算机的交互更简洁、更结构化,就像 HCI 让人更好用计算机一样,ACI 让 Agent 更好用计算机。

具体来说,SWE-agent 的 ACI 设计体现在:

ACI 设计 Bundle 工具 Blocklist 安全 Review-on-Submit

2. 架构全景与核心循环

┌─────────────────────────────────────────────────────────────────────┐ SWE-agent 架构全景 ├─────────────────────────────────────────────────────────────────────┤ RunSingle / RunBatch │ 编排层:配置加载 → Agent 创建 → 问题实例 → 输出收集 RetryAgent (可选) ─── retry_loop ──────────────────────────── │ 多次尝试 + Reviewer 评分/Chooser 选择最优 DefaultAgent ─── 核心主循环 ────────────────────────────── ├─ TemplateConfig (Jinja2 模板: system/instance/next_step) ├─ ToolHandler (Bundle 工具 + ParseFunction + Blocklist) ├─ HistoryProcessor (Cache/LastN/ClosedWindow/RemoveRegex...) ├─ Model (LiteLLM + 重试 + 成本限制) └─ ActionSampler (可选: AskColleagues / BinaryComparison) SWEEnv ─── SWE-ReX 运行时 ──────────────────────────────── │ Docker/Modal 部署 → Bash Session → 命令执行 └─ 文件读写 / 环境变量 / 仓库克隆 / Hard Reset └─────────────────────────────────────────────────────────────────────┘

核心主循环 (DefaultAgent.step → forward_with_handling)

# 伪代码: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)               # 检查是否提交

关键设计特点:

3. 工具系统:Bundle + Command 架构

SWE-agent 的工具系统是其最独特的设计之一。它不使用传统的 Python 函数工具,而是通过 Bundle 机制将 shell 脚本包装为 LLM 可调用的工具。

┌─────────────────────────────────────────────────────┐ ToolHandler 工具系统 ├─────────────────────────────────────────────────────┤ ToolConfig ├── bundles: list[Bundle] │ └── Bundle(path, hidden_tools) │ ├── config.yaml (工具定义) │ ├── bin/ (可执行脚本) │ ├── lib/ (Python 库) │ └── install.sh (安装脚本) ├── parse_function: ParseFunction ├── filter: ToolFilterConfig (blocklist) ├── enable_bash_tool: bool └── env_variables: dict Default Bundles (default.yaml): 1. tools/registry (env 读写辅助) 2. tools/edit_anthropic (str_replace_editor) 3. tools/review_on_submit_m (submit + 审查) └─────────────────────────────────────────────────────┘

Bundle 机制详解

每个 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 的安装流程:

  1. _upload_bundles() — 将整个目录上传到容器的 /root/tools/{name}/
  2. 执行 install.sh(如果存在)
  3. bin/ 加入 PATH
  4. _check_available_commands() — 验证所有命令可用

str_replace_editor — ACI 的核心工具

这是 SWE-agent 最关键的 ACI 创新。传统的 bash 编辑(sed/awk)对 LLM 来说太脆弱——特殊字符、转义、多行处理都是雷区。str_replace_editor 提供了 5 个子命令:

view

查看文件(cat -n 格式)或目录结构。支持 view_range 参数限制行范围

create

创建新文件。如果文件已存在则拒绝,防止意外覆盖

str_replace

精确字符串替换。old_str 必须在文件中唯一匹配。这是最安全的编辑方式

insert

在指定行号后插入新内容。不需要匹配已有文本

undo_edit

撤销上次编辑。提供安全网,允许 Agent 纠正错误

_state_anthropic

隐式状态命令!每次执行后自动运行,收集 diff、cwd 等状态写入 /root/state.json

Review-on-Submit — 提交前审查

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:

  1. 重新运行复现脚本确认修复
  2. 删除复现脚本
  3. 还原测试文件的修改
  4. 再次 submit 确认

多阶段提交 审查机制

Blocklist — 安全网

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+.*"}  # 只允许非交互用法

三层过滤机制:前缀匹配 → 精确匹配 → 条件放行。这比简单黑名单更灵活。

4. 输出解析器:8 种解析策略

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 任意 调试/人机模式 原样返回,不做解析

解析器架构的精妙之处:

5. 历史处理器:上下文工程核心

SWE-agent 的 HistoryProcessor 链是它上下文工程的核心。多个处理器可以串联,按顺序对 history 列表做变换。

History 处理管道: raw history (self.history) │ ▼ DefaultHistoryProcessor ← 透传 │ ▼ LastNObservations(n=5) ← 保留最近 N 个观察,旧的替换为 "(n lines omitted)" │ ← 支持 polling 参数优化缓存命中 │ ▼ ClosedWindowHistoryProcessor ← 窗口去重:同一文件的旧窗口替换为摘要 │ ▼ CacheControlHistoryProcessor ← 标记最后 N 条 user/tool 消息的 cache_control │ ← Anthropic prompt caching 优化 │ ▼ RemoveRegex ← 正则删除历史中的匹配内容(如 <diff>...</diff>) │ ▼ TagToolCallObservations ← 为特定工具调用的观察添加标签 │ ▼ ImageParsingHistoryProcessor ← 将 base64 图片转为多模态格式 → 最终 messages → 发送给 LLM

CacheControlHistoryProcessor — Prompt Caching 优化

这是 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 消息处。这样每轮对话只有新的观察和动作需要重新处理,系统提示 + 早期历史都能命中缓存。

LastNObservations — 经典上下文窗口管理

# 论文中的经典配置:只保留最近 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 经济学的精细控制。

6. RetryAgent 与 Reviewer 系统

SWE-agent 的 RetryAgent 是 Agent 框架中少有的"多次尝试 + 质量评估"机制。

RetryAgent 流程: 问题实例 │ ▼ Attempt 0: DefaultAgent.run() ───→ 提交 solution_0 │ │ │ ▼ │ RetryLoop.on_submit(solution_0) │ │ │ ▼ │ RetryLoop.retry()? ──── Yes ────┐ │ │ │ │ No (结束) Attempt 1: env.hard_reset() │ │ DefaultAgent.run() → solution_1 │ │ │ │ │ ▼ │ │ RetryLoop.on_submit(solution_1) │ │ │ │ │ ▼ │ │ retry()? ─── No ───┐ │ │ │ │ ▼ ▼ │ RetryLoop.get_best() ← 选择最优提交 │ │ ▼ ▼ 返回 AgentRunResult(info, trajectory) ← 返回最优解的信息

两种 RetryLoop 实现

ScoreRetryLoop

每次提交后,Reviewer 对解打分(多次采样取均值)。如果分数 ≥ accept_score 则接受;否则继续尝试。

  • reviewer_config.n_sample=5:每次评审采样 5 次
  • reduce_by_std:减去标准差 × 系数(保守估计)
  • failure_score_penalty:异常退出(超时/成本超限)扣分
  • 最优解 = 最高分 + 最少 API 调用(同分时)
ChooserRetryLoop

所有尝试结束后,Chooser(LLM)直接选择最优解。可选 Preselector 先筛选候选。

  • Preselector:从 N 个提交中选出有潜力的子集
  • Chooser:从子集中选择最终解
  • 只考虑 exit_status=="submitted" 的提交
  • 成本控制: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(过滤特定输出)

7. Action Sampler:多路径决策

Action Sampler 是 SWE-agent 的另一个独特设计——在每一步,不只是问一次 LLM,而是采样多个候选动作,然后选择最好的。

AskColleagues

采样 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) → 最终动作
BinaryTrajectoryComparison

锦标赛式二选一:采样多个完成 → 两两比较 → 锦标赛胜者

# 流程
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)。

8. SWE-ReX 环境层

SWE-agent 的环境完全由 SWE-ReX 运行时驱动,这是一个独立的 Docker 容器管理框架。

SWEEnv 核心能力
# 环境初始化流程
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 能在大多数情况下继续运行。

9. 提示词工程与模板系统

SWE-agent 的提示词完全由 Jinja2 模板 驱动,模板本身是 YAML 配置的一部分。

TemplateConfig — 消息模板体系

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  # 观察截断阈值

default.yaml 的实际提示词

# 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}}"

关键设计决策:

格式字典 (_get_format_dict)

模板变量来源极其丰富:

{
    "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 等)
}

10. 与同类项目对比

维度SWE-agentOpenHandsAiderAgent 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 插件 技能注册

核心差异分析

SWE-agent vs OpenHands

OpenHands 使用 Python 函数工具 + EventStream 架构,更通用但更重。SWE-agent 的 Bundle 机制让工具在容器内执行(接近生产环境),而 OpenHands 的工具在 Agent 进程内执行(需要网络通信)。SWE-agent 的 RetryAgent + Reviewer 是独特的质量保证层。

SWE-agent vs Aider

Aider 专注代码编辑,有 15 种 edit format 和 RepoMap 上下文工程。SWE-agent 的 str_replace_editor 是 Aider SEARCH-REPLACE 的简化版。但 SWE-agent 有完整的 shell 环境和重试机制,覆盖了更广的问题空间。

SWE-agent vs Agent Zero

Agent Zero 的自修改能力(修改自己的提示词/工具)是 SWE-agent 没有的。但 SWE-agent 的 ACI 设计哲学——"让 Agent 与计算机的交互更结构化"——是更工程化的思路。Agent Zero 是生物演化,SWE-agent 是精密工程。

11. 关键洞察与设计取舍

✅ 优秀设计

  1. ACI 哲学:不把 LLM 当人用,而是为它设计专用接口。str_replace_editor 比裸 bash 编辑成功率显著更高
  2. Bundle 系统:工具以 shell 脚本形式存在容器内,天然可组合、可调试、与生产环境一致
  3. 8 种解析器:对不同模型能力做适配,从 Function Calling 到纯文本都有方案
  4. RetryAgent + Reviewer:多次尝试 + 评分选择,是解决"一次尝试不够可靠"问题的工程化方案
  5. Review-on-Submit:提交前强制审查,防止模型提交低质量解
  6. CacheControl:Prompt caching 优化,实际节省大量成本
  7. Action Sampler:二选一锦标赛 + 同事讨论,是 best-of-N 采样的高级实现
  8. 错误分级:格式错误重试 vs 致命错误自动提交,精细化的错误处理策略

⚠️ 设计取舍

  1. Shell-First 的局限:所有工具最终是 shell 命令,无法做类型检查、输入验证等结构化处理。对比 Python 函数工具,缺少编译时保障
  2. SWE-bench 优化过重:很多设计(Review-on-Submit 的具体审查消息、instance_template 的 5 步指导)是针对 SWE-bench 特化的
  3. 无长期记忆:没有跨实例的记忆系统。每次解决问题都是全新开始
  4. 单 Agent 架构:没有多 Agent 协作能力(不像 CrewAI/AutoGen)
  5. RetryAgent 成本高:多次尝试意味着成倍的成本。ScoreRetryLoop 的 n_sample=5 评分也有额外开销
  6. 同步阻塞asyncio.run() 包装同步调用,不是真正的异步架构

🔑 可借鉴的关键模式

  1. ACI 设计原则:为 Agent 设计专用交互接口,而非复用人类接口。这是 SWE-agent 论文的核心贡献
  2. 提交前审查:任何 Agent 在执行不可逆操作前都应有审查步骤
  3. HistoryProcessor 链:可组合的上下文处理管道,比硬编码的上下文管理更灵活
  4. 错误分级 + 自动提交:不是所有错误都需要终止,有些可以在终止时仍提取有价值的结果
  5. 多阶段提交:将"确认提交"拆为多步审查,强迫 Agent 验证自己的工作
  6. Cache Control 标记:在对话历史中精确标记缓存断点,是成本优化的最佳实践
📊 总结评分
9
工程完整度
8
架构创新
7
通用性
9
可借鉴性
8
代码质量

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 文件的深度阅读