跳到主要内容

mini-SWE-agent — 架构与原理

30 秒导读: mini-SWE-agent 是 SWE-bench / SWE-agent 原班团队(普林斯顿 + 斯坦福)做的“极简编码 agent”。核心主张:2024 年那套为 agent 量身定制的工具与接口,如今大多不必要了——给模型一个 bash 就够。它的 agent 类只有约 100 行,却能在 SWE-bench Verified 上拿到 >74%。

本项目源码不大但思路密集,所以拆成一个 index(本页)+ 四章。先读本页建立全局认知,再按需下钻。


1. 这是什么(零基础也能懂)

一句话定义: mini-SWE-agent 是一个命令行 AI 编码助手——你给它一个任务(“修好这个 bug”),它反复让大语言模型(LM)写一条 bash 命令、真的执行、把结果喂回去,直到模型宣布完成。

解决谁的什么问题。 假设你在终端里,想让 AI 帮你改一个大项目的代码:定位文件、写复现脚本、改源码、跑测试。市面上的编码 agent 往往很重——一堆自定义工具、复杂的状态管理、难以复现的 shell 会话。mini 的赌注是:把复杂度从“agent 脚手架”挪回“语言模型”本身,脚手架越薄,越好理解、好移植、好做研究(微调 / 强化学习不会过拟合到某个特定脚手架)。

它能做什么:

  • 本地docker/podmansingularitybubblewrapcontree 等环境里执行命令。
  • 任意模型(通过 litellm / openrouter / portkey),支持工具调用接口,也支持纯文本动作格式。
  • 三种运行形态:交互式 CLI(mini)、Python 直接调用、SWE-bench 批量跑分。
  • 自带一个 TUI 轨迹浏览器,回看 agent 每一步。

用起来什么样。 最小的 Python 绑定就三行(摘自 README.md):

agent = DefaultAgent(
LitellmModel(model_name=...),
LocalEnvironment(),
)
agent.run("Write a sudoku game")

一句话直觉/类比。 把它想成一个**“只会用终端的实习生 + 一个把实习生的话原样敲进终端的人”**:实习生(LM)每轮说一句“我要跑这条命令”,执行者(环境)敲下回车、把屏幕输出念回去;实习生看着输出决定下一步。没有别的花活——没有“打开文件工具”“搜索工具”,因为终端里 grep/sed/cat 全都有。


2. 顶层全景(它大概怎么转)

三个可互换的部件

mini 把整个系统切成三个用 Python Protocol(鸭子类型接口)定义的角色,定义在 src/minisweagent/__init__.py:43-80:

部件职责(一句话)默认实现文件
Agent跑主循环:管消息历史、判断何时停DefaultAgentagents/default.py
Model把消息发给 LM、解析出动作、格式化观察LitellmModelmodels/litellm_model.py
Environment真正执行一条 bash 命令、返回输出LocalEnvironmentenvironments/local.py

三者只靠这几个方法签名耦合,所以换沙箱 = 换 Environment,换模型供应商 = 换 Model,Agent 一行不用动。这是整个项目“可移植”的根。

一次任务的数据流

下图是“怎么读”:从上到下是时间顺序,循环回到顶部直到模型发出哨兵串。

task("修好这个 bug")


┌─────────────────────────────────────────────┐
│ Agent.run():拼 system + user 两条初始消息 │
└─────────────────────────────────────────────┘

▼ ←──────────────────────────────────────┐ 循环
┌──────────────────┐ │
│ Model.query() │ 发整段消息给 LM, │
│ │ 解析出 1 条 bash 命令 │
└──────────────────┘ │
│ 命令 │
▼ │
┌──────────────────┐ │
│ Environment │ subprocess 全新子进程执行 │
│ .execute() │ 拿到 stdout + 返回码 │
└──────────────────┘ │
│ 输出 │
▼ │
┌──────────────────┐ │
│ 把观察 append 回 │ 消息列表只增不改 ───────────┘
│ messages 列表 │
└──────────────────┘

│ 当某条命令输出以 COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT 开头

返回 {exit_status, submission}

主线走一遍(高层,不进代码)

  1. run() 渲染两个模板:system 消息(教模型怎么格式化输出 + 怎么宣告完成)和 user 消息(把 {{task}} 填进去)。
  2. 进入 while True:每轮调 step() = query()(问模型、拿动作)→ execute_actions()(执行、收观察)。
  3. 每一步的所有消息都追加self.messages,从不修改旧消息——这就是“完全线性历史”。
  4. 当模型让环境执行 echo COMPLETE_TASK_AND_SUBMIT_FINAL_OUTPUT,环境检测到这个哨兵字符串就抛 Submitted 异常,循环退出。

这套主线的细节在 01-agent-loop.md


3. 四个核心设计(本项目的“精华”预览)

mini 的价值不在代码量,而在几个刻意的取舍。每条都在对应章节展开:

  • 只有 bash 一个工具。 不实现“编辑文件工具”“搜索工具”,让模型用 shell 的全部能力。想让它开 PR?直接在 prompt 里叫它自己搞定,而不是写代码。→ 03
  • 每条命令一个全新 subprocess,没有持久 shell 会话。 动作之间完全独立,因此“换成 docker exec 就能跑在沙箱里”,且天然好并行。代价是 cd/环境变量不跨命令保留(prompt 里专门提醒模型)。→ 02
  • 异常即控制流。 完成、超限、超时、用户打断、格式错误,全用一套继承自 InterruptAgentFlow 的异常表达,run() 的 try/except 把它们翻译成消息或退出。→ 01
  • 模型层吃掉所有供应商差异。 工具调用 vs 纯文本两种动作格式、成本计算、指数退避重试、Anthropic 提示缓存,全封装在 Model 里,Agent 完全不知情。→ 03

4. 阅读地图

建议顺序(由浅入深):

  1. 01-agent-loop.md —— 先把约 100 行的主循环吃透:run/step/query/execute_actions,以及“异常当控制流”的精巧设计。这是项目的心脏。
  2. 02-environment-and-submission.md —— 环境层怎么执行命令,“无 shell 会话”为什么是大事,哨兵字符串如何让 agent 自我了结;docker 沙箱怎么接。
  3. 03-model-layer.md —— 模型层:两条动作解析路线(toolcall / 纯文本)、成本与全局限流、重试、缓存控制。最“工程”的一章。
  4. 04-config-and-runners.md —— YAML 配置怎么组装,三种运行器(mini CLI、交互式 human/confirm/yolo、SWE-bench 批量),给想真正跑起来或扩展的人。

横向对比与边界在第 04 章末尾;每章末尾都有「代码地图」表,可直接 grep 跳进源码。


5. 横向对比(一句话定位)

同 shelf 的 letta 把复杂度押在有状态的分层记忆上(见 docs/letta/),而 mini 押在无状态、线性、极简上——历史只增不改、动作互相独立。两者是“agent 复杂度该放在哪”的两个极端样本。通用原理可对照本库 docs/guide/agent-loop.md(agent 主循环)、docs/guide/execution-environments.md(执行环境与沙箱)。