·15 min read

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, and search_dir let 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 open command displays a file 100 lines at a time with line numbers, total line count, and scroll indicators. The agent can scroll_down, scroll_up, or goto a specific line. This prevents the model from dumping entire files into context.

  • Edit with Linter: The edit command replaces a range of lines with new content. After every edit, the system runs flake8 on 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:

  1. Clone the repository at the specified commit
  2. Install dependencies
  3. Start the ReAct loop
  4. Search for relevant files
  5. Read and edit code
  6. Run tests to verify
  7. 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 flake8 guardrail 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 edit command 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:

  1. Context flooding. A raw grep can 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.

  2. Command complexity. A raw sed command for editing requires the model to generate correct regex, line numbers, and escape sequences — all in a single string. SWE-agent’s edit command uses a simple start_line:end_line range followed by the replacement text, terminated by a delimiter.

  3. Error propagation. A single bad edit can corrupt a file and cascade into failures across all subsequent steps. SWE-agent runs flake8 after every edit and rejects changes that introduce syntax errors.

  4. 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

  1. 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.

  2. 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_limit default of $4 is reasonable for most issues.

  3. The linting guardrail only works for Python. If your repository uses JavaScript, TypeScript, Go, or Rust, the flake8 check is a no-op. The agent can introduce syntax errors and not realize it. Consider adding a custom lint command for your language.

  4. 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.

  5. Trajectories are long. A 20-step trajectory can contain 50,000+ tokens of history. Parsing these for debugging requires tooling. The swe-agent trajectory CLI commands help, but you will want to build custom analysis scripts for production use.

  6. The agent has no concept of “done.” The agent stops when it calls the submit command 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.

  7. 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

NL

Written by Nivant Labs Team

Engineer at Nivant Labs

Share this post