跳到主要内容
Code Agents

第9章 端到端实战 — OpenHands 跑 SWE-bench Verified

完整可跑案例,用 OpenHands + Claude Sonnet 4.5 跑 SWE-bench Verified 子集,Tier 1 OpenHands SDK + Tier 2 Aider + Tier 3 Claude Code 三框架对比,失败模式分类、Cost 分析、改进思路

hands-on openhands aider claude-code swe-bench docker production

第9章 ⭐ 端到端实战 — OpenHands 跑 SWE-bench Verified

本章是模块十的实战压轴,也是第一梯队 4 大模块的收官实战。我们用 OpenHands + Claude Sonnet 4.5 跑通 SWE-bench Verified 10 题子集,然后用 Aider / Claude Code 重跑同样任务对比——你会切实感受到:模型差异 ~5%,scaffolding 差异 ~10%,prompt 与 cost 控制差异 ~30%

📑 目录


一、实战目标

跑通三件事:

  1. Tier 1:OpenHands + Claude Sonnet 4.5 跑 SWE-bench Verified 10 题子集(预期 4-5 小时)
  2. Tier 2:用 Aider CLI 跑同样 10 题对比
  3. Tier 3:用 Claude Code SDK 跑同样 10 题对比

预期产物:

  • Pass@1 三家对比表
  • Cost / Time 三家对比表
  • 失败模式 trace
  • Grafana / Markdown dashboard

二、整体架构

┌──────────────────────────────────────────────────────┐
│           SWE-bench Verified 10 题子集                 │
│  (django, flask, requests, sympy, pytest...)          │
└────────────────────┬─────────────────────────────────┘

        ┌────────────┼────────────────────┐
        │            │                    │
        ▼            ▼                    ▼
┌──────────────┐ ┌──────────────┐ ┌──────────────┐
│  Tier 1      │ │  Tier 2      │ │  Tier 3      │
│  OpenHands   │ │  Aider CLI   │ │ Claude Code  │
│  + Sonnet    │ │  + Sonnet    │ │  + Sonnet    │
└──────┬───────┘ └──────┬───────┘ └──────┬───────┘
       │                │                │
       └────────────────┼────────────────┘
                        │ Trajectory

              ┌──────────────────────┐
              │  swebench evaluator   │
              │  (Pass / Fail 自动判)│
              └──────────┬───────────┘


              ┌──────────────────────┐
              │  Markdown dashboard   │
              │  + Grafana(可选)    │
              └──────────────────────┘

三、环境准备

3.1 系统要求

  • OS:Linux(推荐 Ubuntu 22.04)/ macOS
  • Docker:必须(Sandbox 用)
  • Python:3.11+
  • 磁盘:≥ 50GB(每个 SWE-bench task 需 docker image)
  • API key:Anthropic API key(本项目用 实验室 API 中转 也可)

3.2 项目结构

mkdir code-agent-bench && cd code-agent-bench

cat > requirements.txt <<'EOF'
swebench>=2.1
openhands-ai>=0.30
aider-chat>=0.65
anthropic>=0.40
litellm>=1.50
pandas>=2.0
EOF

python3.11 -m venv .venv
source .venv/bin/activate
pip install -r requirements.txt

3.3 docker-compose.yml(LiteLLM proxy + 监控)

version: '3.9'

services:
  litellm:
    image: ghcr.io/berriai/litellm:main-latest
    container_name: litellm-proxy
    ports:
      - "4000:4000"
    environment:
      - ANTHROPIC_API_KEY=${ANTHROPIC_API_KEY}
    command: --model claude-sonnet-4-5 --port 4000
    
  prometheus:
    image: prom/prometheus
    ports:
      - "9090:9090"
    volumes:
      - ./prometheus.yml:/etc/prometheus/prometheus.yml
  
  grafana:
    image: grafana/grafana
    ports:
      - "3000:3000"
    environment:
      - GF_SECURITY_ADMIN_PASSWORD=admin

启动:

export ANTHROPIC_API_KEY=sk-ant-...
docker compose up -d

3.4 选 SWE-bench Verified 10 题

# select_tasks.py
from datasets import load_dataset

ds = load_dataset("princeton-nlp/SWE-bench_Verified", split="test")

# 选 10 题:不同 repo、不同难度
selected = []
seen_repos = set()

for task in ds:
    repo = task['repo']
    if repo in seen_repos:
        continue
    seen_repos.add(repo)
    selected.append(task['instance_id'])
    if len(selected) >= 10:
        break

print(selected)
# 输出例:
# ['django__django-11815', 'pytest-dev__pytest-8567',
#  'sympy__sympy-13971', 'matplotlib__matplotlib-23476', ...]

四、Tier 1 — OpenHands SDK 直跑

4.1 OpenHands 安装

# Method A: docker(推荐)
docker pull docker.all-hands.dev/all-hands-ai/openhands:latest
docker pull docker.all-hands.dev/all-hands-ai/runtime:latest

# Method B: pip(开发用)
pip install openhands-ai

4.2 跑单个任务

# tier1_openhands.py
import asyncio
from openhands.controller.agent import Agent
from openhands.core.config import AppConfig, AgentConfig, LLMConfig, SandboxConfig
from openhands.core.main import run_controller
from openhands.events.action import MessageAction

async def run_task(task_id: str, issue_text: str, repo_path: str):
    config = AppConfig(
        agent=AgentConfig(name='CodeActAgent'),
        llm=LLMConfig(
            model='claude-sonnet-4-5',
            api_key=os.environ['ANTHROPIC_API_KEY'],
        ),
        sandbox=SandboxConfig(
            base_container_image='docker.all-hands.dev/all-hands-ai/runtime:latest',
            workspace_mount_path=repo_path,
        ),
        max_iterations=50,
        max_budget_per_task=5.0,  # USD 上限
    )
    
    initial_message = MessageAction(
        content=f"修这个 issue:\n\n{issue_text}\n\n步骤:\n"
                "1. grep 找相关文件\n"
                "2. 读相关代码\n"
                "3. 写补丁\n"
                "4. 跑测试验证\n"
                "5. 测试通过后,git diff 输出最终补丁"
    )
    
    state = await run_controller(config=config, initial_user_action=initial_message)
    
    return {
        'task_id': task_id,
        'success': state.success,
        'iterations': state.iteration_count,
        'cost_usd': state.metrics.accumulated_cost,
        'final_diff': state.final_diff,
    }

if __name__ == '__main__':
    # 跑 10 个 task
    tasks = load_swebench_tasks()  # 见 3.4
    results = []
    for task in tasks:
        result = asyncio.run(run_task(
            task_id=task['instance_id'],
            issue_text=task['problem_statement'],
            repo_path=clone_repo_at_commit(task),
        ))
        results.append(result)
    
    # 写到 jsonl
    with open('tier1_results.jsonl', 'w') as f:
        for r in results:
            f.write(json.dumps(r) + '\n')

4.3 评测

# 用 swebench-cli 评测 trajectory
python -m swebench.harness.run_evaluation \
    --predictions_path tier1_predictions.json \
    --max_workers 4 \
    --run_id tier1_test \
    --dataset_name princeton-nlp/SWE-bench_Verified

输出 tier1_test_report.json,含每 task pass/fail 信息。

4.4 预期结果(基于业界数据)

指标OpenHands + Sonnet 4.5
Pass@16-7 / 10(60-70%)
平均 cost$1.20/task
平均时间8-15 分钟/task
平均 iterations18-25

五、Tier 2 — Aider CLI 跑同题

5.1 Aider 安装

pip install aider-chat
export ANTHROPIC_API_KEY=sk-ant-...

5.2 单 task 脚本

# tier2_aider.py
import subprocess
import os
from pathlib import Path

def run_aider_task(task, repo_path):
    issue = task['problem_statement']
    
    # 在 repo_path 内调用 aider
    cmd = [
        'aider',
        '--model', 'claude-sonnet-4-5',
        '--no-auto-commits',  # 我们手动管 commit
        '--message', f"""
修这个 issue:

{issue}

请:
1. 找出问题所在
2. 写补丁
3. 不需要写测试(已有)
4. 完成后说 'DONE'
        """.strip(),
        '--yes',  # 自动接受 diff
    ]
    
    start = time.time()
    proc = subprocess.run(
        cmd,
        cwd=repo_path,
        capture_output=True,
        text=True,
        timeout=600  # 10 分钟硬上限
    )
    duration = time.time() - start
    
    # Aider 把 trace 写到 .aider.* 文件
    return {
        'task_id': task['instance_id'],
        'success': proc.returncode == 0,
        'duration_sec': duration,
        'stdout': proc.stdout[-2000:],  # 留尾巴
        'final_diff': run_git_diff(repo_path),
    }

5.3 Aider 的特殊处理

Aider 不像 OpenHands 那样有完整 SDK——主要靠 CLI。几个 tip:

  1. —map-tokens 4096:repo map 大小(影响 token cost)
  2. —no-auto-commits:让你手动控制 git
  3. —apply:直接应用 patch 不问

5.4 预期结果

指标Aider + Sonnet 4.5
Pass@15-7 / 10
平均 cost$0.80/task ⭐ 比 OpenHands 便宜
平均时间5-10 分钟/task
平均消息数8-15

观察:Aider 因为 repo map 设计,token 比 OpenHands 省 30%+,单任务 cost 也低 30%+


六、Tier 3 — Claude Code SDK 跑同题

6.1 Claude Code SDK 安装

# 安装 Claude Code CLI(2026 起 SDK 公开)
npm install -g @anthropic-ai/claude-code
# 或用 pip 版(若有)
pip install claude-code-sdk

6.2 单 task 脚本

# tier3_claude_code.py
from claude_agent_sdk import query, ClaudeAgentOptions
import asyncio

async def run_claude_code_task(task, repo_path):
    issue = task['problem_statement']
    
    options = ClaudeAgentOptions(
        cwd=repo_path,
        model='claude-sonnet-4-5',
        permission_mode='acceptEdits',
        allowed_tools=['Read', 'Edit', 'Bash', 'Grep', 'Glob'],
        max_turns=50,
    )
    
    full_prompt = f"""
你是一个 Code Agent,任务是修一个 issue。

Issue:
{issue}

要求:
1. grep / read 找相关文件
2. 写补丁(用 Edit tool)
3. 跑 pytest 验证
4. 测试通过后,输出最终 diff,以 'DONE' 结束
"""
    
    messages = []
    async for msg in query(prompt=full_prompt, options=options):
        messages.append(msg)
    
    # 解析 trajectory
    final_response = messages[-1]
    return {
        'task_id': task['instance_id'],
        'success': 'DONE' in str(final_response),
        'turns': len(messages),
        'cost_usd': sum(m.cost for m in messages if hasattr(m, 'cost')),
        'final_diff': run_git_diff(repo_path),
    }

6.3 Claude Code 优势

  • 本身就是 Anthropic 设计 ——对 Claude 模型最 native
  • subagent 支持:可启 sub-agent(本项目用了大量 subagent 写文档)
  • MCP 集成:可挂 GitHub MCP / 自家工具

6.4 预期结果

指标Claude Code + Sonnet 4.5
Pass@17-8 / 10 ⭐ 最高
平均 cost$1.50/task
平均时间6-12 分钟/task
平均 turns12-20

观察:Claude Code 的 prompt + tool 设计专为 Claude 优化,通过率比 OpenHands 略高,但 cost 也略高(更大上下文 / 更多 turn)。


七、对比结果

(以下为预期数据,实际跑会有 ±15% 波动)

指标OpenHandsAiderClaude Code
Pass@16.5/10 (65%)6/10 (60%)7.5/10 (75%)
平均 cost/task$1.20$0.80$1.50
平均时间/task12 min8 min9 min
总 cost(10 题)$12$8$15
总时间~120 min~80 min~90 min
易用性中(SDK)极高(CLI)高(CLI+SDK)
可定制性极高
Sandbox 隔离弱(本地)中(权限可控)

7.1 三家定位

  • OpenHands:最强 SDK / 最强 Sandbox ——研究 / 自训用
  • Aider:最快 / 最省钱 / 最易用 ——日常 CLI 用
  • Claude Code:最高准确率 ——重要任务用 ⭐

八、失败模式分析

跑完 10 题后,典型失败模式:

8.1 类型 1:测试找不到 / 装不上(2-3 题)

Error: pytest could not collect any tests
ImportError: No module named 'tox'

根因:repo 依赖装不全 / 特殊 fixtures 缺失。

修复:

  • 给 OpenHands 加 hint: “先 pip install -e .
  • 升级 docker image 到含完整依赖的版本

8.2 类型 2:改错文件(1-2 题)

agent 改了一个类似名但不相关的文件(常见于 sympy 这种数学库的同名函数)。

修复:加 grep 精确匹配 + read 时验证。

8.3 类型 3:测试 hack(警惕,1 题)

agent 改了 test 让它过(即 reward hacking)。

修复:diff 审计 — 测试改动远多于源码 → 拒绝 patch。

8.4 类型 4:死循环(0-1 题)

agent 反复改一个文件,测试反复失败。OpenHands 默认 50 step 上限触发后终止。

修复:loop 检测 + 降级模型 / 人介入。

8.5 类型 5:超 cost(0-1 题)

复杂任务 token 爆炸。$5 cap 触发。

修复:加 soft cap(到 $3 警告)+ 优化 prompt。


九、Cost / Reliability Dashboard

9.1 Markdown dashboard(简版)

# dashboard.py
import json
import pandas as pd

def load_results(path):
    with open(path) as f:
        return [json.loads(line) for line in f]

t1 = pd.DataFrame(load_results('tier1_results.jsonl'))
t2 = pd.DataFrame(load_results('tier2_results.jsonl'))
t3 = pd.DataFrame(load_results('tier3_results.jsonl'))

def summary(df, name):
    return {
        'framework': name,
        'pass_rate': df['success'].mean(),
        'avg_cost': df['cost_usd'].mean(),
        'avg_time': df['duration_sec'].mean() / 60,
        'total_cost': df['cost_usd'].sum(),
    }

results = pd.DataFrame([
    summary(t1, 'OpenHands'),
    summary(t2, 'Aider'),
    summary(t3, 'Claude Code'),
])

print(results.to_markdown(index=False))

输出例:

| framework   |   pass_rate |   avg_cost |   avg_time |   total_cost |
|:------------|------------:|-----------:|-----------:|-------------:|
| OpenHands   |        0.65 |       1.20 |       12   |        12.0  |
| Aider       |        0.60 |       0.80 |        8   |         8.0  |
| Claude Code |        0.75 |       1.50 |        9   |        15.0  |

9.2 Grafana(可选)

跟模块六/七类似,把指标推到 Prometheus → Grafana 面板:

from prometheus_client import Counter, Histogram

PASS_COUNTER = Counter('agent_task_passes', 'Passes', ['framework'])
COST_HIST = Histogram('agent_task_cost_usd', 'Cost', ['framework'])
TIME_HIST = Histogram('agent_task_duration_seconds', 'Duration', ['framework'])

# 在每次 task 完成后
PASS_COUNTER.labels(framework='openhands').inc(int(success))
COST_HIST.labels(framework='openhands').observe(cost_usd)
TIME_HIST.labels(framework='openhands').observe(duration_sec)

Grafana 面板配置:

  • pass rate over time
  • cost distribution per framework
  • duration heatmap

十、改进思路 + 自己 fork

跑完 baseline 后,怎么往上做改进?

10.1 加 Verifier(SWE-Gym 思路)

跑测试前用 LLM judge 预判:“这个 patch 看起来对吗?”——筛掉明显错的 candidate,降 cost。

10.2 加搜索(SWE-Search 思路)

每个 step 生成 5 个候选 action,LLM 评分,选最优 → MCTS 扩展。准确率 +5-10%,cost ×3-5

10.3 加 Multi-agent(MAGIS 思路)

分 Manager / Coder / Reviewer / Tester 四 agent,对超复杂任务有效。但对 SWE-bench 这种”中等难度”反而 overhead 大。

10.4 加 RL fine-tune

用 SWE-Gym + GRPO 训自己的 7B-32B 模型,在内部 repo 上比 frontier LLM 强 10-30%。

10.5 加私有索引

公司内部 repo + 文档 + Wiki embedding 索引,agent 调用 → 跨 repo 知识。这是 Sourcegraph Cody 等企业产品的差异化。

10.6 改 ACI

OpenHands 的 ACI 已很好,但你可以领域定制:

  • run_unittest_only(file)(只跑某个 test 文件)
  • git_blame(file, line)(看某行历史)
  • ast_parse(file)(返回 AST)

业界实战:每加一个领域专用 tool,该领域任务 +5-10% pass rate。


🎉 恭喜完成模块十 Code Agents 教程!

你已掌握:

  • ✅ Code Agent 范式跃迁与三层栈
  • ✅ 8 大商业产品 + 8 大开源框架对比
  • ✅ SWE-Agent ACI / OpenHands / CodeR / MAGIS / Aider repo map 等核心论文
  • ✅ SWE-bench / Verified / Multimodal / Live / Multi-SWE-bench 全家桶评测
  • ✅ LSP / Tree-sitter / VS Code Extension / Cursor 内核架构
  • ✅ SWE-RL / SWE-Gym 训练栈
  • ✅ Sandboxing / PR Review / Cost / Failure / Audit / Prompt Injection / License 生产化清单
  • ✅ OpenHands + Aider + Claude Code 三框架端到端实战 + 改进思路

第一梯队 4 大模块全部完工 🏆

模块主题章节数状态
模块七Agentic RL9 章 + 路线
模块八Agent Evaluation & Benchmarks9 章 + 路线
模块九Computer / Browser Use Agents9 章 + 路线
模块十Code Agents9 章 + 路线

✅ 自我检验清单

  • 跑通 OpenHands + Sonnet 4.5 1 个 SWE-bench Verified 任务
  • 跑通 Aider 同任务对比
  • 跑通 Claude Code SDK 同任务对比
  • 三框架对比表(Pass / Cost / Time)
  • 至少 3 类失败模式 trace 记录
  • dashboard 输出
  • 思考过 1-2 个改进方向并实现一个

📚 参考资料

框架

评测

LLM Proxy / 监控

改进思路

  • SWE-Search (arXiv 2410.20285)
  • MAGIS (arXiv 2403.17927)
  • SWE-RL (arXiv 2504.21798)

第一梯队 4 大模块完工 🎊。下一步 第二梯队(中型 4-6 章/模块):

  • Multi-Modal Agents —— 视觉 + 语音 + 视频 agent
  • Agent Safety / Red Teaming —— alignment、jailbreak、constitutional AI
  • Agent Cost / Token Optimization —— prompt caching、compression、模型路由