mini-swe-agent

这个项目是 SWE-agent 团队推出的一个极简但功能强大的 AI 软件工程代理(Agent)。它旨在用极简的代码实现核心的智能体循环,同时保持强大的代码修复能力。

一、核心实现架构与技术细节

mini-swe-agent 的实现非常精炼,核心就是一个 while 循环和一套基于异常的信号传递机制

异常信号处理

Submitted

成功提交

LimitsExceeded

达到资源限制

FormatError

模型输出格式错误

开始: 接收问题
(如GitHub Issue)

初始化环境与提示

LLM推理
(思考+行动)

执行工具
(Bash/编辑器)

捕获观察结果
与异常信号

循环终止判断?

输出最终补丁
或结果

1.1 控制流:异常驱动的循环

  • 主循环:用一个 while 不断调用 LLM 获取下一个动作,然后执行该动作并获取观察结果。
  • 终止信号:循环的终止不是通过复杂的条件判断,而是通过抛出和捕获特定的异常来实现。定义了 InterruptAgentFlow 异常层级,包含:
    • submitted 成功提交
    • LimitsExceeded 达到操作次数或时间限制
    • FormatError 模型输出格式无法解析
  • 消息传递:这些异常不仅用于控制流,还能携带消息有效载荷。run() 方法会捕获这些异常,将消息注入对话历史,然后根据异常类型决定是继续循环(如格式错误是可以重试的)还是终止循环(如成功提交就可以正常终止)。这种设计将控制逻辑和业务逻辑清晰分离。

1.2 工具系统:YAML+BASH

  • 工具定义:工具签名、文档字符串和参数说明(通过一个 YAML 配置文件定义)。这使其能渲染为函数调用的JSON Schema 或系统提示词中的工具文档
  • 工具实现:每个工具就是一个独立的 BASH 脚本(bin/ 目录)。这样有三大好处:
    • 通用可移植性:Bash 脚本可以在 Linux 容器中运行
    • 安装简单:只需将工具目录复制到沙箱环境并运行一个 install.sh 脚本
    • 文档自动生成:工具的文档直接源自 YAML 定义,无需手动维护
  • 状态共享:工具之间不共享 Python 对象状态,而是通过环境变量、文件系统和 _state 命令的 JSON 输出进行通信

一个工具包的最小规范示例

根据其设计协议,一个自定义工具包至少需要包含一下文件

tools/my_bundle/
├── config.yaml        # 工具定义
├── bin/
│   └── my_command     # 工具实现(Bash脚本)
└── install.sh         # 依赖安装脚本(可选)

config.yaml

tools:
  my_command:
    signature: "my_command <arg1> [<arg2>]"
    docstring: "这个工具的功能描述,给 LLM 看"
    arguments:
      - { name: arg1, type: string, required: true, description: "第一个参数的说明" }
      - { name: arg2, type: integer, required: false, description: "第二个参数的说明(可选)" }
    state_command: "_my_state"  # 可选,每次动作后运行的命令,用于输出状态 JSON
    env_variables:              # 可选,注入到运行时的环境变量
      MY_VAR: "default_value"

bin/my_command

#!/usr/bin/env_bash
arg1="$1"
arg2="${2:-}"
# 这里实现工具的具体逻辑
echo "执行 my_command,参数1: $arg1, 参数2: $arg2"

1.3 提示词工程:结构化与过程化引导

精心设计的提示词模板

  • 极简系统提示词:“You are a helpful assistant that can interact with a computer to solve tasks.”。所有与具体任务相关的上下文(如问题描述、仓库路径)都放在实例提示词中。这样能让系统提示词被 LLM KV 缓存。
  • 过程化指令:明确引导 LLM 遵循一个类似人类工程师的五步工作流:
    1. 阅读与理解:首先阅读和代码库中与问题描述相关的代码
    2. 复现问题:创建一个脚本来复现错误,并用 python <filename.py> 执行它
    3. 编辑修复:编辑仓库的源代码来解决问题
    4. 验证修复:重新运行复现的脚本,并确认错误已被修复
    5. 考虑边缘情况:思考边缘情况,确保你的修复也能处理他们
  • 无演示(Zero-shot)

二、项目拆解分析

初始化

调用

返回结果/环境信息

支撑模型与环境层

LLM 模型抽象
models/

执行环境抽象
environments/

核心智能体循环

DefaultAgent
agents/default.py

Agent Loop
while True

提示构建与模板渲染

LLM 推理调用

动作解析与执行

观察结果与异常处理

入口与配置层

CLI / Python API
run/mini.py

配置加载与合并
config/

2.1 生命周期

  1. 初始化:系统首先从配置文件和命令行参数中加载和合并配置,随后,根据配置实例化模型、环境和智能体本身
  2. 运行:Agent 的 run() 方法被调用,启动核心的智能体循环。循环内部不断进行"思考-行动-观察"(ReAct) 的迭代,直到完成或达到某个限制
  3. 终止:循环通过异常机制优雅推出,整个执行轨迹(消息、动作、成本等)序列化为一个 JSON 文件,保存下来,便于分析和回放。

2.2 Agent Loop

# 伪代码,源于default.py的run方法
def run(self, task):
    # 初始化消息历史
    self.messages = [system_message, instance_message]
    
    while True:
        try:
            # 1. 调用LLM获取下一步动作
            response = self.model.query(self.messages)
            # 2. 解析动作并执行
            action, observation = self.env.execute(response)
            # 3. 将结果添加到历史
            self.messages.append({"role": "assistant", "content": response})
            self.messages.append({"role": "user", "content": observation})
            
        except InterruptAgentFlow as e:
            # 4. 捕获特定异常来控制流程
            if isinstance(e, Submitted):
                break  # 成功完成,退出循环
            elif isinstance(e, (LimitsExceeded, TimeExceeded)):
                break  # 达到限制,退出循环
            elif isinstance(e, FormatError):
                # 处理格式错误,可能重试
                self.n_consecutive_format_errors += 1
                if self.n_consecutive_format_errors > self.config.max_consecutive_format_errors:
                    break

2.3 Tool

  • 执行逻辑:在环境中通过命令行调用实现。环境负责接收 LLM 的工具调用指令,并将其映射为具体的系统命令执行,然后捕获输出返回给 Agent

2.4 Environment

  • 核心类:local.py 实现了在本地文件系统上执行命令的 LocalEnvironment,这是最常用的环境。此外,singularity.py 等提供了在容器(如 Singularity)中执行的环境,用于隔离和安全性。
  • 职责:环境的职责时执行动作并返回观察结果,它接收来自 Agent 的字符串执行(如 Bash 命令或一个编辑指令),在目标工作目录中执行,并将命令的输出、执行状态等信息格式化后返回给 Agent,作为下一轮循环的输入。

2.5 Prompt

通过模板+变量的方式,分离了结构和内容

  • 模板定义:提示词的骨架模板定义在配置文件(如config/mini.yaml)中。
    • system_template:系统提示,定义 Agent 的角色、能力和行为规范。
    • instance_template:示例提示,包含具体的任务描述。
  • 变量填充与渲染:在运行时,Agent 的 get_template_vars() 方法会收集来自配置、环境、模型以及当前状态的所有变量,合并成一个dict,用Jinja2模板引擎进行渲染,生成最终发送给 LLM 的完整提示词。
  • 动态上下文:模板可以引用动态变量,例如 {{working_dir}} 当前工作目录 {{problem_statement}} 问题描述

2.6 Memory

非常直接,用完整的消息历史列表

  • 存储位置:记忆保存在 DefualtAgent 实例的 self.messages 属性中,这是一个list,按顺序存储完整的对话历史
  • 内容:遵循 OpenAI 的消息格式 [{"role":"system", "content":"系统提示"}]
  • 持久化:运行结束后,这个完整的 messages 列表会被序列化为 JSON。

2.7 LLM 调用

models 模块中

  • 模型抽象:该目录定义了模型接口,调用各种 LLM API (基于 LiteLLM)
  • 调用点:在 Agent Loop 的每一次迭代中,self.model.query(self.messages) 会被调用,query 方法接收完整历史消息,返回模型响应。
  • 配置:模型名称、API 密钥、参数(如温度,Top-p),并在模型初始化时传入

2.8 各个模块之间如何通信

模块间通信遵循“依赖注入”和“显式方法调用”的原则,清晰且低耦合

  1. Agent 初始化时,接收实例化的 ModelEnvironment 对象。
  2. Agent 调用 Model:通过 self.model.query()
  3. Agent 调用 Environment:Agent 将 LLM 的响应传递给 self.env.execute()。Environment 执行命令后,将观察结果字符串返回给 Agent。
  4. 配置作为 “胶水”,YMAL 配置指定了要使用的模型类、环境类及其初始化参数。get_modelget_enviroment 等工厂函数,根据配置动态创建相应实例
Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐