SWE-agent: Princeton's autonomous software engineering agent that fixes real GitHub issues
Princeton's autonomous software engineering agent for real GitHub issues — SWE-agent achieves 12.5% on SWE-bench by treating repos as interactive environments.
The Problem: Language Models Cannot Navigate Code Repositories
A language model can write a sorting function from scratch. It can explain the difference between a B-tree and a hash index. But give it a real GitHub issue — a bug report with stack traces, reproduction steps, and 50,000 files in a Django or scikit-learn repository — and it freezes. The model does not know where the relevant code lives. It does not know how to run the test suite. It cannot iterate: write a fix, run the tests, see the failure, adjust, repeat.
The root cause is architectural. Standard LLM (Large Language Model) inference is stateless: you send a prompt, you get a completion, the conversation ends. Software engineering is the opposite of stateless. It requires navigating a directory tree, reading files, editing lines, running commands, parsing error output, and looping back to edit again. A single bug fix can require 10 to 20 edit-test cycles across 5 to 8 files.
Before SWE-agent, the state of the art on SWE-bench — a benchmark of 2,294 real GitHub issues from 12 popular Python repositories — was 3.8% resolved. That means 96.2% of bugs went unfixed by any automated system.
| Metric | Before SWE-agent | After SWE-agent (GPT-4 Turbo) |
|---|---|---|
| SWE-bench Full resolve rate | 3.8% (best prior) | 12.47% |
| SWE-bench Lite resolve rate | 4.3% (best prior) | 18.00% |
| Average cost per resolved issue | N/A (no working pipeline) | $1.59 |
| Average steps per resolved issue | N/A | 12 |
| HumanEvalFix Python | 47.0% (GPT-4 baseline) | 87.7% |
| HumanEvalFix JavaScript | 48.2% (GPT-4 baseline) | 89.7% |
| HumanEvalFix Java | 50.0% (GPT-4 baseline) | 87.9% |
Why this matters: The gap between what LLMs can do in isolation and what they can do in a real codebase is the single largest barrier to automated software engineering. SWE-agent is the first system to bridge that gap by treating the code repository as an interactive environment rather than a static context dump.
The Investigation: Why Static Retrieval Fails
The obvious approach to automated bug fixing is retrieval-augmented generation (RAG): find the relevant files, dump them into the model’s context, and ask for a patch. Every team that tried this before SWE-agent hit the same wall.
Finding 1: Context windows are not large enough for real repositories.
A Django repository contains 2,000+ files totaling over 500,000 lines of code. Even with a 200K-token context window, you cannot fit the entire codebase. You must select a subset. But the subset selection problem is itself a software engineering task: the bug could be in any file, and the fix often touches files the bug report never mentions.
What this means: RAG-based approaches (Retrieval-Augmented Generation, where relevant code snippets are retrieved via embedding similarity and injected into the prompt) achieve 1.31% on SWE-bench with GPT-4 Turbo. They retrieve the wrong files because embedding similarity does not capture the causal chain from a bug to its fix.
Finding 2: One-shot patches fail because the model cannot verify its work.
Even when the model generates a syntactically correct patch, it has no way to run the test suite and confirm the fix works. The patch might introduce a regression, break a different test, or fail to address the root cause. Without execution feedback, the model is flying blind.
What this means: the 47.0% GPT-4 baseline on HumanEvalFix drops to near zero on real repositories because HumanEval functions are self-contained (no imports, no dependencies, no test suite). Real bugs require iterative refinement.
Finding 3: Shell access alone is not enough.
Giving the model a raw bash shell and asking it to fix bugs achieves 11.0% on SWE-bench Lite — better than RAG, but still far below what is possible. The problem is that bash commands are designed for humans, not LLMs. A human knows that grep -rn "exception" src/ returns too many results and adds | head -20. An LLM does not. A human knows to run python -m pytest tests/test_foo.py -x to stop at the first failure. An LLM will try pytest on the entire suite, wait 10 minutes, and time out.
What this means: the interface between the LLM and the computer is the bottleneck. The commands must be redesigned for LLM consumption — simpler, more structured, with built-in guardrails.
The Solution: Agent-Computer Interfaces (ACI)
SWE-agent introduces the concept of an Agent-Computer Interface (ACI): a purpose-built abstraction layer between the language model and the operating system that makes software engineering tasks tractable for LLMs.
┌─────────────────────────────────────────────────────────────┐
│ SWE-agent Architecture │
│ │
│ ┌─────────────┐ ┌──────────────────┐ ┌───────────┐ │
│ │ LM Model │ │ Agent Loop │ │ Docker │ │
│ │ (GPT-4, │◄──►│ (ReAct: Thought │◄──►│ Container │ │
│ │ Claude, │ │ → Action → │ │ (Sandbox) │ │
│ │ etc.) │ │ Observation) │ │ │ │
│ └─────────────┘ └──────────────────┘ └───────────┘ │
│ │ │ │
│ ▼ ▼ │
│ ┌──────────────────────────────────┐ │
│ │ Agent-Computer Interface │ │
│ │ ┌─────────┐ ┌────────┐ ┌──────┐ │ │
│ │ │ Search │ │ File │ │ Edit │ │ │
│ │ │Commands │ │ Viewer │ │ w/ │ │ │
│ │ │(find, │ │(open, │ │Linter│ │ │
│ │ │ search) │ │ scroll)│ │ │ │ │
│ │ └─────────┘ └────────┘ └──────┘ │ │
│ │ ┌─────────┐ ┌──────────────────┐ │ │
│ │ │ Context │ │ Prompt Templates │ │ │
│ │ │Manager │ │ (Jinja2) │ │ │
│ │ └─────────┘ └──────────────────┘ │ │
│ └──────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
Here is what each piece does:
-
LM Model: The underlying language model (GPT-4 Turbo, Claude 3 Opus, or any model supported by the framework). The model generates structured outputs containing a “thought” (reasoning) and an “action” (command to execute).
-
Agent Loop: A ReAct-style loop (Reasoning + Acting, from Yao et al., 2022). At each step, the model receives the full conversation history, generates a thought and an action, the action executes in the environment, and the observation is appended to the history.
-
Docker Container: Every run executes inside a disposable Docker container. The repository is cloned at a specific commit, dependencies are installed, and the agent works in an isolated, reproducible environment. No state leaks between runs.
-
Search Commands:
find_file,search_file, andsearch_dirlet the agent locate relevant code by filename or content. Results are capped at 50 entries to prevent context flooding. Each result shows the file path and a one-line snippet. -
File Viewer: The
opencommand displays a file 100 lines at a time with line numbers, total line count, and scroll indicators. The agent canscroll_down,scroll_up, orgotoa specific line. This prevents the model from dumping entire files into context. -
Edit with Linter: The
editcommand replaces a range of lines with new content. After every edit, the system runsflake8on the modified file. If the edit introduces a syntax error (undefined variable F821, syntax error E999), the edit is rejected and the error is returned to the agent. -
Context Manager: Observations older than the last 5 turns are collapsed into a single placeholder line (“Old output omitted (101 lines)”). This keeps the context window focused on recent actions and prevents token overflow on long trajectories.
-
Prompt Templates: All prompts are defined as Jinja2 templates. The system template describes the environment and available commands. The instance template contains the issue description. The next-step template formats each observation. This separation lets you swap models or tasks without changing code.
Production-Grade Code Walkthrough
The core agent loop lives in sweagent/agent/agents.py. Here is the simplified forward pass that drives every interaction:
# sweagent/agent/agents.py (simplified core loop)
from typing import Optional
from dataclasses import dataclass, field
@dataclass
class StepOutput:
"""Output from a single agent step."""
thought: str
action: str
observation: str
done: bool = False
info: dict = field(default_factory=dict)
class DefaultAgent:
"""The standard SWE-agent loop: think, act, observe, repeat."""
def __init__(self, model, tools, templates, max_requeries: int = 3):
self.model = model
self.tools = tools
self.templates = templates
self.max_requeries = max_requeries
self.history: list[dict] = []
self.trajectory: list[StepOutput] = []
def forward(self, history: list[dict]) -> tuple[str, str]:
"""Query the LM and parse the response into thought + action."""
response = self.model.query(history)
thought, action = self.tools.parse_actions(response)
return thought, action
def forward_with_handling(self, history: list[dict]) -> StepOutput:
"""Wrap forward() with error recovery: retry on format errors,
blocked actions, bash syntax errors, and timeouts."""
for attempt in range(self.max_requeries):
try:
thought, action = self.forward(history)
if self.tools.is_blocked(action):
raise BlockedActionError(f"Command blocked: {action}")
observation = self.env.communicate(action)
if self.tools.is_submit(action):
return StepOutput(
thought=thought, action=action,
observation=observation, done=True
)
return StepOutput(
thought=thought, action=action,
observation=observation
)
except (FormatError, BlockedActionError,
BashSyntaxError, TimeoutError) as e:
error_template = self._get_error_template(e)
history.append({"role": "user", "content": error_template})
continue
raise MaxRequeryError(f"Failed after {self.max_requeries} attempts")
def step(self) -> StepOutput:
"""Execute one agent step with full bookkeeping."""
messages = self.history_processors(self.history)
output = self.forward_with_handling(messages)
self.history.append({
"role": "assistant",
"content": f"{output.thought}\n```\n{output.action}\n```"
})
self.history.append({
"role": "user",
"content": f"Observation: {output.observation}"
})
self.trajectory.append(output)
return output
def run(self, env, problem_statement: str) -> StepOutput:
"""Main entry point: setup, then loop until done."""
self.setup(env, problem_statement)
while True:
output = self.step()
if output.done:
return output
The environment layer in sweagent/environment/swe_env.py manages the Docker container and shell session:
# sweagent/environment/swe_env.py (simplified)
import subprocess
import tempfile
from pathlib import Path
class SWEEnv:
"""Sandboxed execution environment inside a Docker container."""
def __init__(self, deployment, repo_config):
self.deployment = deployment # SWE-ReX deployment abstraction
self.repo = repo_config
self.session: Optional[subprocess.Popen] = None
def start(self):
"""Start the Docker container and clone the repository."""
self.deployment.start()
self.deployment.run(f"git clone {self.repo.url} /workspace/repo")
self.deployment.run(f"cd /workspace/repo && git checkout {self.repo.base_commit}")
self.deployment.run("cd /workspace/repo && pip install -e .")
self.session = self.deployment.start_shell_session()
def communicate(self, input: str, timeout: int = 30) -> str:
"""Send a command to the running shell session and return output."""
if not self.session:
raise RuntimeError("Session not started. Call start() first.")
self.session.stdin.write(input + "\n")
self.session.stdin.flush()
try:
output = self.session.stdout.read(timeout=timeout)
except TimeoutError:
self.interrupt_session()
return f"Command timed out after {timeout}s"
return output
def read_file(self, path: str) -> str:
"""Read a file from inside the container."""
return self.deployment.read_file(path)
def write_file(self, path: str, content: str):
"""Write a file inside the container."""
self.deployment.write_file(path, content)
def close(self):
"""Tear down the container."""
if self.session:
self.session.terminate()
self.deployment.close()
Setup Instructions
# Clone the repository
git clone https://github.com/princeton-nlp/SWE-agent.git
cd SWE-agent
# Create a virtual environment and install dependencies
python -m venv .venv
source .venv/bin/activate
pip install -e .
# Set your API key
export OPENAI_API_KEY="sk-..." # or ANTHROPIC_API_KEY
# Run SWE-agent on a single GitHub issue
swe-agent run \
--model_name gpt-4-turbo \
--repo_url https://github.com/django/django \
--base_commit abc123 \
--issue "Fix AttributeError when accessing ManyToManyField through a related_name"
# Run on a SWE-bench instance
swe-agent run --instance_id django__django-16379
# Run in batch mode on multiple instances
swe-agent batch run --instances instances.json --output_dir ./results
How to Use Effectively
Step 1: Configure the Model and Tools
Every SWE-agent run starts with a configuration file that defines the model, tools, and prompt templates. Create a config.yaml:
# config.yaml
model:
name: gpt-4-turbo
per_instance_cost_limit: 4.0 # $4 max per issue
total_cost_limit: 40.0
tools:
commands:
- name: search_file
code: "grep -rn {term} {path} | head -50"
- name: find_file
code: "find {path} -name {file_name} -type f | head -50"
- name: open
code: "cat -n {path} | head -{window_size}"
- name: edit
code: "sed -i '{start},{end}c\\{text}' {path}"
blocklist:
- "rm -rf /"
- "sudo"
- "> /dev/"
lint_command: "python -m flake8 --select=F821,E999 {path}"
templates:
system_template: |
You are a software engineer working in a Linux terminal.
You have access to the following commands:
{{command_docs}}
Always output your thought first, then your command in a code block.
instance_template: |
We are working on the repository {{repo_name}}.
Issue: {{issue_description}}
next_step_template: "Observation: {{observation}}"
Step 2: Run the Agent on a Single Issue
swe-agent run \
--config config.yaml \
--repo_url https://github.com/django/django \
--base_commit 2a3b4c5 \
--issue_file issue.md
The agent will:
- Clone the repository at the specified commit
- Install dependencies
- Start the ReAct loop
- Search for relevant files
- Read and edit code
- Run tests to verify
- Submit a patch when done
Step 3: Review the Trajectory
After the run, SWE-agent saves a trajectory file (JSON) containing every thought, action, and observation. Use this to understand what the agent did:
# View the trajectory
swe-agent trajectory show --traj_file trajectory.json
# Extract the final patch
swe-agent trajectory patch --traj_file trajectory.json > fix.patch
# Replay the trajectory step by step
swe-agent trajectory replay --traj_file trajectory.json
Step 4: Run Batch Evaluation
For benchmarking or regression testing, run on multiple issues at once:
# Create an instances file
cat > instances.json << 'EOF'
[
{"instance_id": "django__django-16379", "repo": "django/django", "base_commit": "abc123", "issue": "..."},
{"instance_id": "sympy__sympy-24152", "repo": "sympy/sympy", "base_commit": "def456", "issue": "..."}
]
EOF
# Run batch
swe-agent batch run \
--instances instances.json \
--output_dir ./results \
--num_workers 4
Step 5: Customize the ACI for Your Repository
The real power of SWE-agent is that you can add custom commands tailored to your project:
# custom_config.yaml
tools:
commands:
- name: run_tests
code: "cd /workspace/repo && python -m pytest {test_path} -x --tb=short 2>&1 | tail -100"
- name: build_project
code: "cd /workspace/repo && python setup.py build 2>&1 | tail -50"
- name: check_coverage
code: "cd /workspace/repo && coverage run -m pytest {test_path} && coverage report"
- name: lint_changed
code: "cd /workspace/repo && git diff --name-only HEAD | xargs flake8 2>&1"
Use Cases
Use Case 1: Automated Bug Triage for Open-Source Repositories
When you would use this: You maintain a popular open-source project and receive 20+ bug reports per week. You need to triage which issues are real, reproduce them, and generate initial patches for review.
Why SWE-agent fits: SWE-agent can clone the repository at the commit where the bug was reported, reproduce the issue, search the codebase for the root cause, and generate a patch — all without human intervention. The trajectory file gives you a complete audit trail of what the agent tried and why.
Use Case 2: Regression Test Verification
When you would use this: A CI (Continuous Integration) pipeline fails on a pull request. You need to determine whether the failure is a pre-existing issue or was introduced by the PR.
Why SWE-agent fits: Run SWE-agent on the base commit and the PR commit. If the agent can fix the issue on the base commit but not on the PR commit, the regression is likely in the PR. The agent’s trajectory shows exactly which files and lines it examined.
Use Case 3: Security Patch Automation
When you would use this: A CVE (Common Vulnerabilities and Exposures) is published for a dependency your project uses. You need to patch it across multiple repositories.
Why SWE-agent fits: SWE-agent can search for the vulnerable pattern across all repositories, apply the fix, and run the test suite to verify no regressions. The Docker sandbox ensures the agent cannot accidentally deploy a broken patch.
Use Case 4: Onboarding New Contributors
When you would use this: A new developer joins your team and needs to understand the codebase. They have a list of “good first issues” but do not know where to start.
Why SWE-agent fits: Run SWE-agent on a good-first-issue and review the trajectory. The agent’s search patterns, file navigation, and edit decisions serve as a live tutorial for how to approach bugs in that codebase. The trajectory shows exactly which files are relevant and why.
Use Case 5: Benchmarking Model Performance on Software Engineering
When you would use this: You are evaluating which LLM to use for your engineering team. You need a standardized, reproducible benchmark that measures real software engineering ability, not just code generation.
Why SWE-agent fits: SWE-agent provides a consistent scaffolding layer across models. Run the same SWE-bench instances with GPT-4, Claude 3, and your candidate model. The ACI is model-agnostic, so differences in resolve rate reflect the model’s engineering ability, not the tooling.
Cheat Sheet
| Aspect | Detail |
|---|---|
| Repository | github.com/princeton-nlp/SWE-agent |
| License | MIT |
| Language | Python (94.8%) |
| GPU Requirements | None (API-based models) |
| Setup Time | 5 minutes (pip install + API key) |
| Key Features | Agent-Computer Interface (ACI), ReAct loop, Docker sandbox, linting guardrails, configurable tools, trajectory replay |
| Supported Models | GPT-4 Turbo, GPT-4o, Claude 3 Opus, Claude 3.5 Sonnet, Claude 3.7 Sonnet, any OpenAI-compatible API |
| Benchmark (SWE-bench Full) | 12.47% with GPT-4 Turbo |
| Benchmark (SWE-bench Lite) | 18.00% with GPT-4 Turbo |
| Average Cost Per Run | $1.59 (successful), $2.52 (unsuccessful) |
| Average Steps Per Run | 12 (successful), 21 (unsuccessful) |
| Common Gotchas | Docker must be installed and running; API keys must have sufficient rate limits; long-running trajectories can exceed cost limits; linting may reject valid edits in non-Python files; the agent cannot install system packages without explicit permission |
| Successor Project | mini-swe-agent (100 lines, 65%+ on SWE-bench verified) |
| Paper | arXiv:2405.15793 (NeurIPS 2024) |
Vibe Coding Projects
Project 1: Build a Custom ACI for Your Team’s Monorepo
What it does: Create a custom SWE-agent configuration with commands tailored to your monorepo’s build system, test framework, and deployment pipeline. Add commands for running specific test suites, building Docker images, and checking lint rules.
What you will learn: How the ACI abstraction works, how to design LM-friendly commands, how to balance command expressiveness with simplicity, and how to handle project-specific error messages.
Effort: 4-6 hours. You need to understand your monorepo’s build system and test framework, then write YAML command definitions and test them against real issues.
Project 2: Run a SWE-bench Evaluation on Your Own Model
What it does: Set up the SWE-bench evaluation pipeline and run it with a model of your choice (open-source or proprietary). Compare your model’s resolve rate against the published baselines.
What you will learn: How SWE-bench instances are structured, how to parse trajectory files, how to compute resolve rates, and how to analyze failure modes. You will also learn the practical challenges of running 2,294 evaluation instances (cost, rate limits, time).
Effort: 8-12 hours for setup and a small-scale run (100 instances). Full SWE-bench evaluation requires $400-$1,000 in API costs and 24-48 hours of wall-clock time.
Project 3: Extend SWE-agent with a Custom Tool
What it does: Add a new command to the SWE-agent toolset — for example, a git_blame command that shows who last modified each line, or a run_migration command that applies database migrations before running tests.
What you will learn: The tool registration system, the parsing pipeline, how to write command documentation that LMs can understand, and how to handle edge cases (empty output, timeouts, errors).
Effort: 2-3 hours. The tool system is well-documented and the codebase is modular. Most of the work is writing the YAML definition and testing edge cases.
Problems Solved Efficiently
| Problem Type | Why SWE-agent Fits | When to Look Elsewhere |
|---|---|---|
| Bug fixing in Python repositories | The ACI is designed for Python projects with pytest test suites. The linting guardrail catches syntax errors. | Non-Python repositories (JavaScript, Go, Rust) lack linting guardrails. The file viewer and search commands work, but edit validation is Python-specific. |
| Reproducing and diagnosing test failures | The agent can run specific tests, parse output, and iterate on fixes. The Docker sandbox ensures clean state. | Performance optimization or refactoring tasks. The agent has no concept of code quality beyond correctness. |
| Triaging large backlogs of issues | Batch mode runs multiple issues in parallel. The cost per issue is predictable ($1-$4). | Issues that require external context (documentation, user forums, Slack conversations). The agent only sees the issue text and the codebase. |
| Evaluating model engineering ability | The standardized ACI provides a fair comparison across models. Trajectories are fully reproducible. | Production deployment. SWE-agent generates patches, not deployable code. You still need human review. |
| Teaching codebase navigation to new developers | Trajectories show exactly which files the agent examined and why. | Real-time pair programming. SWE-agent is not interactive — it runs to completion and then reports results. |
Architectural Tradeoffs
What we gained:
-
12.47% resolve rate on SWE-bench Full — a 3.3x improvement over the prior state of the art. The ACI design alone accounts for a 64% relative improvement over raw shell access (18.0% vs 11.0% on SWE-bench Lite).
-
Model-agnostic scaffolding. The same ACI that achieves 12.47% with GPT-4 Turbo achieves 10.46% with Claude 3 Opus. You can swap models without changing the agent logic.
-
Reproducible evaluation. Every run starts from a fixed commit in a fresh Docker container. No state leaks, no non-determinism from the environment. Two runs with the same model and same issue produce the same trajectory.
-
Cost predictability. The per-instance cost limit ($4 default) prevents runaway spending. 93% of resolved instances submit before exhausting the budget, compared to 69% of all instances. The system fails fast on issues it cannot solve.
What we sacrificed:
-
Python-only linting. The
flake8guardrail only works for Python files. For JavaScript, Go, or Rust repositories, the edit command has no validation. The agent can introduce syntax errors and not realize it until the test suite fails. -
No multi-file editing. The
editcommand operates on one file at a time. A fix that requires coordinated changes across 5 files takes 5 separate edit steps, each with its own lint check. The agent cannot reason about cross-file changes as a single operation. -
Stateless per-run design. Each run starts from scratch. The agent cannot learn from previous runs on the same repository. If it fixes issue A and then encounters issue B in the same codebase, it has no memory of the first fix.
-
Docker dependency. Every run requires Docker. This adds 5-15 seconds of overhead per run for container startup. It also means SWE-agent cannot run on systems without Docker (some CI runners, restricted environments).
-
No interactive debugging. The agent cannot set breakpoints, inspect variables at runtime, or step through code. It relies entirely on static analysis and test output. This limits its ability to diagnose subtle runtime bugs.
The real lesson: The ACI is the product, not the model. SWE-agent’s 3.3x improvement over prior work came from redesigning the interface between the LM and the computer, not from a better model. The same principle applies to any agent system: the interface design matters more than the model choice. A mediocre model with a great interface will outperform a great model with a terrible interface every time.
Course-Style Deep Dive
How SWE-agent Works Under the Hood
SWE-agent is built on the ReAct framework (Reasoning + Acting, Yao et al., 2022). At each step, the model generates two things: a “thought” (natural language reasoning about what to do next) and an “action” (a command to execute in the environment). The environment executes the command and returns an “observation.” The observation is appended to the conversation history, and the loop repeats.
The key innovation is not the loop itself — ReAct has been around since 2022. The innovation is the Agent-Computer Interface (ACI): the set of commands, feedback formats, and guardrails that sit between the model and the operating system.
The ACI addresses four specific failure modes that plague raw shell access:
-
Context flooding. A raw
grepcan return 10,000 matching lines. SWE-agent caps search results at 50 and truncates observations at 100,000 characters. Observations older than 5 turns are collapsed. -
Command complexity. A raw
sedcommand for editing requires the model to generate correct regex, line numbers, and escape sequences — all in a single string. SWE-agent’seditcommand uses a simplestart_line:end_linerange followed by the replacement text, terminated by a delimiter. -
Error propagation. A single bad edit can corrupt a file and cascade into failures across all subsequent steps. SWE-agent runs
flake8after every edit and rejects changes that introduce syntax errors. -
No feedback on empty output. When a command succeeds but produces no output, a human knows to check the exit code. An LLM might interpret silence as failure. SWE-agent returns “Your command ran successfully and did not produce any output” for empty results.
Advanced Pattern 1: Custom Tool Registration
You can add custom tools by implementing the Tool protocol:
# custom_tool.py
from sweagent.tools.tools import Tool, ToolConfig, ToolHandler
class GitBlameTool(Tool):
"""Show who last modified each line of a file."""
name = "git_blame"
signature = "git_blame <file_path>"
docstring = "Show git blame output for a file, limited to 50 lines."
arguments = {
"file_path": {
"type": "string",
"description": "Path to the file to blame",
"required": True,
}
}
def execute(self, file_path: str) -> str:
import subprocess
result = subprocess.run(
["git", "blame", "--line-porcelain", file_path],
capture_output=True, text=True, cwd="/workspace/repo",
timeout=10
)
lines = result.stdout.split("\n")[:50]
return "\n".join(lines)
# Register in your config
config = ToolConfig(
tools=[GitBlameTool()],
blocklist=["rm -rf /", "sudo"],
)
handler = ToolHandler(config)
Advanced Pattern 2: Custom History Processor
History processors transform the message history before it is sent to the LM. You can use them to inject context, filter noise, or re-rank observations:
# custom_processor.py
from sweagent.agent.history_processors import HistoryProcessor
class TestResultSummarizer(HistoryProcessor):
"""Summarize test results into a pass/fail count instead of raw output."""
def __call__(self, history: list[dict]) -> list[dict]:
processed = []
for message in history:
if message["role"] == "user" and "pytest" in message.get("content", ""):
content = message["content"]
passed = content.count("PASSED")
failed = content.count("FAILED")
errors = content.count("ERROR")
summary = (
f"Test results: {passed} passed, {failed} failed, "
f"{errors} errors (full output truncated)"
)
processed.append({"role": "user", "content": summary})
else:
processed.append(message)
return processed
# Register in agent config
agent_config.history_processors = [
TestResultSummarizer(),
ObservationCollapser(max_observations=5),
]
Advanced Pattern 3: Multi-Agent Review Loop
The RetryAgent runs multiple attempts with different configurations and picks the best result:
# multi_agent_review.py
from sweagent.agent.agents import RetryAgent, DefaultAgent
from sweagent.agent.reviewer import ScoreRetryLoop
# Define two agent configurations
primary_agent = DefaultAgent(
model=ModelConfig(name="gpt-4-turbo"),
tools=tool_config,
templates=templates,
)
fallback_agent = DefaultAgent(
model=ModelConfig(name="claude-3-opus"),
tools=tool_config,
templates=templates,
)
# Run with retry loop
retry_agent = RetryAgent(
agents=[primary_agent, fallback_agent],
retry_loop=ScoreRetryLoop(
max_attempts=3,
score_threshold=0.8,
),
)
result = retry_agent.run(env, problem_statement)
Production Considerations
Monitoring. Every trajectory is saved as a JSON file. Parse these to track success rate, average cost, average steps, and common failure modes across all runs:
# Aggregate trajectory statistics
swe-agent trajectory stats --traj_dir ./results --output stats.json
Error handling. The agent handles five error types internally: format errors (malformed action strings), blocked actions (commands on the blocklist), bash syntax errors (invalid shell syntax), timeouts (commands that exceed the time limit), and cost limits (cumulative API cost exceeds the budget). Each error type has a dedicated prompt template that tells the agent what went wrong and how to fix it.
Rate limiting. When running batch mode with 4+ workers, API rate limits are the most common failure mode. Set per_instance_cost_limit to $4 and total_cost_limit to $40 to cap spending. Use num_workers to control concurrency. For large batches, add exponential backoff in the model wrapper:
# rate_limited_model.py
import time
from sweagent.agent.models import ModelConfig
class RateLimitedModel:
"""Wrapper that adds exponential backoff on 429 responses."""
def __init__(self, config: ModelConfig, max_retries: int = 5):
self.config = config
self.max_retries = max_retries
def query(self, history: list[dict]) -> str:
for attempt in range(self.max_retries):
try:
return self._query(history)
except RateLimitError as e:
wait = min(2 ** attempt * 10, 300) # 10s, 20s, 40s, 80s, 160s
time.sleep(wait)
raise MaxRetryError(f"Rate limited after {self.max_retries} retries")
The Results
The numbers tell a clear story. SWE-agent was the first system to demonstrate that LLMs could fix real GitHub issues at a rate that made the approach worth pursuing.
| Metric | Before SWE-agent | SWE-agent (GPT-4 Turbo) | Improvement |
|---|---|---|---|
| SWE-bench Full resolve rate | 3.8% | 12.47% | 3.3x |
| SWE-bench Lite resolve rate | 4.3% | 18.00% | 4.2x |
| HumanEvalFix Python | 47.0% | 87.7% | 1.9x |
| HumanEvalFix JavaScript | 48.2% | 89.7% | 1.9x |
| HumanEvalFix Java | 50.0% | 87.9% | 1.8x |
| Average cost per resolved issue | N/A | $1.21 | N/A |
| Average steps per resolved issue | N/A | 12 | N/A |
What this means for you: SWE-agent proved that automated bug fixing is viable, not just theoretical. The 12.47% resolve rate on SWE-bench Full means that for every 100 real GitHub issues, SWE-agent can fix 12 to 13 of them autonomously. For the remaining 87 to 88, it generates a trajectory that shows exactly where it got stuck — which is itself valuable debugging information.
The cost is $1.21 per successful fix. Compare that to the average developer cost of $50-$100 per hour. Even at 12.47% resolution, SWE-agent pays for itself if it saves a developer from spending 15 minutes triaging each of the 12 issues it can fix.
The trajectory data is equally valuable. Every failed run tells you something about your codebase: confusing function names, missing documentation, inconsistent error handling. Reviewing SWE-agent trajectories is a form of automated code review that surfaces the same pain points a new developer would encounter.
What to Watch Out For
-
Docker is not optional. SWE-agent requires Docker for sandboxed execution. If your CI runners do not support Docker, you cannot run SWE-agent in CI. The container startup adds 5-15 seconds per run, which adds up in batch mode.
-
API costs scale linearly with batch size. Running 100 SWE-bench instances costs $150-$250 in API fees. Running all 2,294 instances costs $3,500-$5,700. Set cost limits and monitor spending. The
per_instance_cost_limitdefault of $4 is reasonable for most issues. -
The linting guardrail only works for Python. If your repository uses JavaScript, TypeScript, Go, or Rust, the
flake8check is a no-op. The agent can introduce syntax errors and not realize it. Consider adding a custom lint command for your language. -
The agent cannot install system packages. If a bug fix requires a new system dependency (a shared library, a database driver, a compiler), the agent will fail. Pre-install common dependencies in your Docker image.
-
Trajectories are long. A 20-step trajectory can contain 50,000+ tokens of history. Parsing these for debugging requires tooling. The
swe-agent trajectoryCLI commands help, but you will want to build custom analysis scripts for production use. -
The agent has no concept of “done.” The agent stops when it calls the
submitcommand or hits the cost limit. It does not know when a fix is complete. It may submit a partial fix that passes the tests but does not fully address the issue. -
Model behavior is non-deterministic. Even with temperature=0, the same model on the same issue can produce different trajectories. Run each issue 3-5 times and take the majority result for benchmarking.
Lesson 1: The ACI is the moat, not the model. SWE-agent’s 3.3x improvement over prior work came from redesigning the interface, not from a better model. When building your own agent systems, invest in interface design first. The model is a commodity; the interface is the differentiator.
Lesson 2: Agents succeed quickly and fail slowly. 93% of resolved instances submit before exhausting the $4 budget, compared to 69% of all instances. If an agent has not found the fix in 12 steps, it probably will not find it in 20. Increasing the budget does not meaningfully improve results.
Lesson 3: The successor is simpler. The SWE-agent team has since released mini-SWE-agent, which achieves 65%+ on SWE-bench verified in approximately 100 lines of Python. The core insight is that you do not need a complex tool system — you need a well-designed prompt and a bash shell. If you are building a new agent system today, start with mini-SWE-agent, not the original.
Advice for Getting Started
Start with mini-SWE-agent, not the original SWE-agent. The original project has been superseded, and the maintainers now recommend mini-SWE-agent for new users. Install it with pip install mini-swe-agent and run mini to start the CLI. The core agent class is 100 lines of Python — you can read the entire implementation in 15 minutes.
If you need the full SWE-agent for batch evaluation or custom tooling, clone the repository and run the examples in the examples/ directory. Start with a single issue from your own repository before scaling to batch mode. Review the trajectory carefully — it will show you exactly where the agent succeeded or failed, and that information is as valuable as the patch itself.
Next in the Open-Source AI Tools Mastery series: Microsoft Agent Framework
Written by Nivant Labs Team
Engineer at Nivant Labs