SWE-agent

[TOC]

1. Code Analysis

1.1 Whole Picture

Orchestrating、Agent Logic、Tool Parsing、Env Execution、LLM Call ——each layer has its own class,assembled by YAML。

File Structure:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
sweagent/
├── __main__.py # Entrance: python -m sweagent
├── run/
│ ├── run.py # CLI Command ("sweagent run/r/batch/...")
│ ├── run_single.py # RunSingle: Start Env → Run Agent → Save Results
│ └── run_batch.py # RunBatch (SWE-Bench)
├── agent/
│ ├── agents.py # DefaultAgent (ReAct), RetryAgent (Actor-Critic)
│ ├── models.py # LLM abstract layer (litellm, encapsulation, retry, cost tracking
)
│ ├── history_processors.py # Context Compression (cache_control, truncation)
│ ├── reviewer.py # Critic: Reviewer, ScoreRetryLoop, ChooserRetryLoop
│ └── problem_statement.py # problem statement (GitHub issue)
├── tools/
│ ├── tools.py # ToolConfig, ToolHandler, blocklist, bundles
│ ├── parsing.py # LLM output : ThoughtActionParser, FunctionCallingParser
│ └── commands.py # Commands Definition (bash, str_replace_editor, submit)
├── environment/
│ ├── swe_env.py # SWEEnv: Docker Lifecycle + Command Communication
│ └── repo.py # Code Repo Mananage(clone, checkout)
└── utils/
├── log.py # Log System (.info/.debug/.trace)
└── config.py # YAML Conf

1.2 Client Layer

1.3 RunSingle

1.3.1 RunSingle Main

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
class RunSingle:
def run(self):
self._chooks.on_start() # Hook: befroe start

self.logger.info("Starting environment")
self.env.start() # 1. strat Docker + tool installation

self.logger.info("Running agent")
output_dir = self.output_dir / self.problem_statement.id
output_dir.mkdir(parents=True, exist_ok=True)
(output_dir / "config.yaml").write_text( # Persistent Configuration
yaml.dump(self.agent.replay_config.model_dump_json())
)

result = self.agent.run( # 2. run Agent
problem_statement=self.problem_statement,
env=self.env,
output_dir=output_dir,
)

self._chooks.on_instance_completed(result=result) # Hook: complete
save_predictions(self.output_dir, # 3. Save patch + .pred
self.problem_statement.id, result)
self.env.close() # 4. Destroy Docker

1.3.2 Hook

1.3.3 Log System

1
2
3
4
5
6
# run_single.py:141-147
for level in ["trace", "debug", "info"]:
add_file_handler(
output_dir / f"{instance_id}.{{level}}.log",
level=level,
)
  • .info.log: Key decisions made by the Agent(STEP, THOUGHT, ACTION, submit)
  • .debug.log: Docker commands, file uploads, and details of tool installation
  • .trace.log: Every command executed within the container and its corresponding output

1.4 Agent Layer

SWE-bench designs a two-layer agent system.

1
2
3
RetryAgent (Outer Layer — multiple attempts)

└── DefaultAgent (Inner Layer — Single ReAct Execution)

1.4.1 Default Agent

  1. Setup: Assemble 5 parts of prompt,

    • system_template
    • Inject demonstrations
    • Render instance_template
    • Install tools
    • Get the initial state。
    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    14
    15
    16
    17
    18
    19
    20
    # agents.py:561-606

    def setup(...) -> None:
    # apply template configuration to multimodal problem statements
    if hasattr(problem_statement, "type") and problem_statement.type == "swe_bench_multimodal":
    from sweagent.agent.problem_statement import SWEBenchMultimodalProblemStatement

    if isinstance(problem_statement, SWEBenchMultimodalProblemStatement):
    # apply the global disable_image_processing setting if it's not explicitly set
    if not problem_statement.disable_image_processing and self.templates.disable_image_processing:
    problem_statement.disable_image_processing = True

    # 1. install tools (upload bundle to docker, execute install.sh)
    self.tools.install(self._env)
    # 2. add system message to history
    self.add_system_message_to_history()
    # 3. add demonstrations to history (optional few-shot)
    self.add_demonstrations_to_history()
    # 4. add instance to history (including {{problem_statement}})
    self.add_instance_template_to_history(state=self.tools.get_state(self._env))
  2. step(): Single-step execution

    1
    2
    3
    4
    5
    6
    7
    8
    # agents.py:1235-1263
    def step(self):
    n_step = len(self.trajectory) + 1
    self.logger.info("=" * 25 + f" STEP {n_step} " + "=" * 25)
    step_output = self.forward_with_handling(self.messages) # Think + Act
    self.add_step_to_history(step_output) # add output to history,
    self.add_step_to_trajectory(step_output) # add to trajectory
    return step_output
  3. forward(): Think-Act

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    # agents.py:1006-1060
    def forward(self, history: list[dict[str, str]]) -> StepOutput:
    step = StepOutput()
    step.query = copy.deepcopy(history)

    output = self.model.query(history) # Think: Call LLM (line 1042)
    step.output = output["message"]
    step.thought, step.action = \
    self.tools.parse_actions(output) # Parse: extract thought + action from output of LLM
    return self.handle_action(step) # Act: execution action

  4. forward_with_handling

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    13
    def forward_with_handling(self, history: list[dict[str, str]]) -> StepOutput:

    def handle_error_with_autosubmission(exit_status: str, message: str) -> StepOutput:
    ...

    def handle_error_with_retry(exception: Exception, template: str, n_requeries: int) -> list[dict[str, str]]:
    ...

    n_format_fails = 0
    while n_format_fails < self.max_requeries:
    try:
    return self.forward(history)
    ...
  5. run(): main loop

    1
    2
    3
    4
    5
    6
    7
    8
    # agents.py:1265-1294
    def run(self, env, problem_statement, output_dir):
    self.setup(env, problem_statement, output_dir)
    step_output = StepOutput()
    while not step_output.done:
    step_output = self.step()
    self.save_trajectory() # save trajectory
    return AgentRunResult(...)

1.4.2 Retry Agent(Actor-Critic)

Actor(agent_encapsulation):

1
2
3
4
5
6
7
8
9
10
11
# agents.py:303-319
def _setup_agent(self):
agent_config = self.config.agent_configs[
self._i_attempt % len(self.config.agent_configs) # 轮换不同配置
]
# 动态调整 cost limit
remaining_budget = self.config.retry_loop.cost_limit - self._total_instance_stats.instance_cost
if remaining_budget < agent_config.model.per_instance_cost_limit:
agent_config.model.per_instance_cost_limit = remaining_budget
self._agent = DefaultAgent.from_config(agent_config)
self._agent.setup(env=..., problem_statement=..., output_dir=...)

Critic (Reviewer):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# reviewer.py:416-449
class Reviewer(AbstractReviewer):
def review(self, instance, submission) -> ReviewerResult:
# 1. Abnormal Exit → Penalty Points
penalty = self._config.failure_score_penalty if exit_status != "submitted" else 0

# 2. n_sample=5, Take 5 samples and calculate the average score
for _ in range(self._config.n_sample):
answer = self._model.query(messages)["message"]
score = self.interpret(answer) # get score from LLM output
accepts.append(score)

accept = sum(accepts) / len(accepts) - penalty
std = np.std(accepts).item()
if self._config.reduce_by_std > 0:
accept -= std * self._config.reduce_by_std # High Variance → Penalty
return ReviewerResult(accept=accept, ...)

1.5 Env Layer

2.

2.1 Plan & Execute

Plan:

config/default.yaml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
# config/default.yaml
agent:
templates:
instance_template: |-
<uploaded_files>
{{working_dir}}
</uploaded_files>
I've uploaded a python code repository in the directory {{working_dir}}.
...
Can you help me implement the necessary changes...
Follow these steps to resolve the issue:
1. As a first step, it might be a good idea to find and read code...
2. Create a script to reproduce the error...
3. Edit the sourcecode of the repo to resolve the issue
4. Rerun your reproduce script...
5. Think about edgecases...

2.2 Actor-Critic

1
2
3
4
5
6
RetryAgent (Orchestrator)

├── DefaultAgent (Actor) — Solve problem, generate patch

└── Reviewer (Critic) — Score the Actor's output

When Actor finished, package all outputs of Actor:

1
2
3
4
5
6
7
8
if step_output.done:
self._rloop.on_submit(
ReviewSubmission(
trajectory=self._agent.trajectory,
info=self._agent.info,
model_stats=self._agent.model.stats, # Call times ...
)
)

Reviewer to get a score:

1
2
3
4
5
6
7
8
9
10
11
12
13
RetryAgent.run()

└─ submission = ReviewSubmission(
trajectory = Actor's info
info = exit_status + patch,
model_stats = API call tims...
)

└─ _rloop.on_submit(submission)

├─ ScoreRetryLoop:

└─ ChooserRetryLoop:

ScoreRetryLoop:

Attempt 1: Actor Finish → Reviewer: Review trajectory + patch → “0.3 points” → fail Attempt 2: Actor Finish → Reviewer: Review new trajectory + new patch → “0.7 points” → not enough Attempt 3: Actor Finish → Reviewer: Review new trajectory + new patch → “0.85 points” → Pass and Stop

ChooserRetryLoop:

Attempt 1 → save

Attempt 1 → save

….

Attempt 10 → save

Chooser Receive 10 patches + edited_files: Submission 0: (patch_0) Submission 1: (patch_1) … “The third is best” → Return index 3

Critic Prompt:

config/benchmarks/250212_sweagent_heavy_sbl.yaml:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
retry_loop:
type: chooser
cost_limit: 6.0
max_attempts: 10
min_budget_for_new_attempt: 1.0
chooser:
system_template: |
You are an expert software engineer reviewing code. Your thinking is very thorough, so it is ok if its very long.
instance_template: |
You will be given a problem statement and a list of patch submissions.

Pick the most reasonable patch.
The patch should solve the problem described in the problem statement in a way that is consistent with the rest of the codebase and the conventions of the codebase.

Note: Disregard all testing code in the patch, as testing was already done in a separate step.
Having a test in the patch does not make it any better.

<IMPORTANT>The last line of your response should be the index of the patch you chose.
You must choose a single index no matter what. If you cannot decide between two or more
submissions, choose the first one of these.
</IMPORTANT>

Problem statement:
{{problem_statement}}

Submissions:
{% for submission in submissions %}
Submission {{loop.index0}}:

{{submission}}

{% endfor %}

<IMPORTANT>The last line of your response should be the index of the patch you chose without any other text.</IMPORTANT>
submission_template: |
Patch:

```python
{{submission}}
    The final edited file with 30 lines of context:

    
1
{{edited_files30}}

```

本文链接:https://wangyier.top/2026/07/15/SWE-agent/

版权声明:本博客所有文章除特别声明外,均采用 CC BY-NC-SA 4.0 许可协议。转载请注明来自 The Great Library