跳到主要内容

第 4 章 · 配置、装配与三种运行器

本章讲清楚:mini 的行为为什么“几乎全在 YAML 里”;三个部件怎么从字符串名装配起来;以及三种把它跑起来的方式——交互式 mini CLI、Python 绑定、SWE-bench 批量跑分。最后给边界、横向对比。

4.1 Prompt 即配置

mini 一个很强的取舍:agent 的“行为”主要不写在代码里,写在 YAML 模板里config/default.yamlconfig/mini.yamlconfig/benchmarks/swebench.yaml 各是一套“人格 + 规则”。

default.yaml:1-19system_template,它教模型三件事:① 每次回复恰好一个 bash 代码块;② 命令前要写 THOUGHT 推理;③ 用 <format_example> 给出确切格式。整个“agent 怎么行动”的协议就是这段提示词。换个 YAML,就是换个 agent——不用动 Python。

这也解释了为什么 第 1 章的模板用 StrictUndefined:既然 prompt 是配置,模板里写错变量名就该立刻报错,而不是悄悄渲染成空。

注意一个细节: default.yaml 用的是纯文本动作格式(mswea_bash_command 围栏),而 mini.yaml 用的是工具调用格式(prompt 里说“至少一个 bash 工具调用”)、swebench.yaml 同样走工具调用。这对应 第 3 章的两条解析路线——配置和模型类要配套。

4.2 配置怎么合并:UNSET 哨兵 + 递归 merge

命令行参数、多个 YAML、键值对覆盖,全靠 recursive_merge(utils/serialize.py:6-29)按顺序深合并,后者覆盖前者。一个小巧思:用一个 UNSET = object() 哨兵表示“这个命令行选项用户没给”,merge 时跳过 UNSET(serialize.py:20-21),这样“没传 --model”不会用 None 把 YAML 里的值冲掉。mini.py:73-92 把 CLI 选项打包成一层 dict,凡未提供的都填 UNSET,再和配置文件 merge。

4.3 工厂:按字符串名 import 装配

三个 get_* 工厂(get_agent/get_model/get_environment)做同一件事:拿一个短名或全路径字符串,动态 import 出类,实例化。例如环境工厂(environments/__init__.py):

# 示意,非源码:get_environment_class 的核心
_ENVIRONMENT_MAPPING = {"docker": "minisweagent.environments.docker.DockerEnvironment", ...}
full_path = _ENVIRONMENT_MAPPING.get(spec, spec) # 短名→全路径,或直接当全路径
module_name, class_name = full_path.rsplit(".", 1)
return getattr(import_module(module_name), class_name)

好处: 你可以在配置里写 environment_class: docker(短名),也可以写 your_pkg.MyEnv(自己的类全路径)——不改 mini 源码就能插自定义环境/模型/agent。模型工厂还多一步:根据模型名自动选类、自动给 Anthropic 开缓存(见 第 3 章)。

4.4 运行器一:mini CLI + 交互式三模式

run/mini.py 是默认可执行文件 mini。它装配出的默认 agent 不是 DefaultAgent 而是 InteractiveAgent(mini.py:101,default_type="interactive")——把人放进循环。

交互式 agent(agents/interactive.py)在 DefaultAgent 上加了三种模式(InteractiveAgentConfig.mode,interactive.py:24-31):

模式含义
human命令由敲,立即执行(你亲自驾驶)
confirm(默认)模型给命令,非白名单的要你按回车确认
yolo模型给命令,不确认直接执行

它主要靠覆盖三个方法实现,完美利用了 第 1 章的可扩展点:

  • 覆盖 add_messages(interactive.py:43-56):每条消息漂亮地打印到终端。
  • 覆盖 query(interactive.py:58-94):human 模式下从你这儿读命令;还接住 LimitsExceeded 让你当场提高限额继续(但 TimeExceeded 不行——墙钟没法靠提限额绕过,interactive.py:75-79 专门注释了这点)。
  • 覆盖 execute_actions(interactive.py:124-139):执行前问确认;用 try/finally 保证即使被拒绝也把已有输出存进历史

白名单(whitelist_actions,正则列表)让你把“lscat 这类只读命令”免确认,只对危险命令拦一道(interactive.py:162-163)。Ctrl-C 会被接住、让你打字给模型留言而不是直接崩(interactive.py:109-122)。这套就是在本地直接跑(无沙箱)时的人工安全闸。

4.5 运行器二:Python 绑定(最小例子)

run/hello_world.py 是“怎么用 Python 调”的最小样板:DefaultAgent(LitellmModel(...), LocalEnvironment(), **yaml里的agent段),然后 agent.run(task)。就这。库的设计目标之一就是这种“三行起步”的可嵌入性。

4.6 运行器三:SWE-bench 批量跑分

run/benchmarks/swebench.py 是“在 SWE-bench 上刷分”的批量器,把前三章的部件全用上了:

  1. 加载数据集:DATASET_MAPPING(swebench.py:53-62)把 verified/lite 等短名映射到 HuggingFace 数据集;支持 filter/slice/shuffle。
  2. 每个实例一个 docker 环境:get_sb_environment(swebench.py:79-94)按实例 id 推出对应的 SWE-bench 官方镜像名(get_swebench_docker_image_name,swebench.py:68-76,注意它把 docker 不允许的双下划线换成魔法 token _1776_)。
  3. 线程池并行:ThreadPoolExecutor(max_workers=workers)(swebench.py:256-263)并行跑多个实例;每个实例落一份轨迹 + 把补丁写进共享的 preds.json(用一把 threading.Lock 保护写入,swebench.py:65,97-108)。
  4. 提交物 = 哨兵后的 git diff:回看 第 2 章,模型 echo COMPLETE...; git diff 产出的补丁就被环境捞成 submission,这里写进 preds.jsonmodel_patch 字段(swebench.py:97-108)。
  5. 断点续跑:已在 preds.json 里的实例默认跳过(swebench.py:229-232),--redo-existing 才重做。

这里也是 第 3 章那个全局成本限额真正发挥作用的地方——并行几十个实例时,只有进程级总闸能兜住预算。

4.7 配套:轨迹浏览器

因为历史是线性 JSON,回看很容易。run/utilities/inspector.py 是个 Textual TUI,把消息按“步”分组(_messages_to_steps,inspector.py:25-38:遇到带 actions 的或 assistant 消息就开新一步)来翻页浏览。这直接受益于“线性历史”这一设计——轨迹就是消息,无需额外重建。

4.8 巧妙之处汇总(可借鉴的技术)

  • Prompt 即配置: 行为协议写在 YAML 模板里而非代码,换 agent = 换 YAML(config/*.yaml)。
  • UNSET 哨兵: 优雅区分“用户没给这个选项”和“用户显式设为 None/0”(utils/serialize.py)。
  • 字符串名工厂 + 鸭子类型 Protocol: 三个部件都能被外部类无侵入替换(*/__init__.pyget_*)。
  • 靠覆盖钩子做交互式 agent: InteractiveAgent 只覆盖 add_messages/query/execute_actions,~200 行就加出 human/confirm/yolo + 限额续命 + Ctrl-C 留言(agents/interactive.py)。
  • 哨兵字符串当“完成 + 提交”双信号: 同一条 echo COMPLETE...; git diff 既宣告结束又交出补丁(见 第 2 章)。

4.9 边界与局限(诚实)

  • 本地环境无沙箱: LocalEnvironment 直接在你机器上 shell=True 跑模型给的任意命令;安全完全靠 docker/singularity 等外层环境,或交互式 confirm 模式人工把关。
  • 无内置历史压缩/裁剪: 线性历史只增不改,长任务可能撞上下文窗口上限(由模型层 ContextWindowExceededError 中止,不会自动 summarize)。对比 letta 是反向选择——它把记忆分层、做 compaction(见 docs/letta/)。
  • 每次回复几个动作,取决于配置: default.yaml/mini.yaml 的 prompt 强制单动作,换来解析与执行的极度简单(execute_actions 也是顺序跑、无并发,见 第 1 章 §1.9)。但这不是项目级硬约束:跑分用的 swebench.yaml 反而显式开了并行多 tool call——prompt 明说“可以在一次回复里发多个 bash tool call”(swebench.yaml:55,并给了多调用示例 swebench.yaml:63),且 model_kwargs.parallel_tool_calls: true(swebench.yaml:183)。所以“单动作”只是默认配置的取舍,benchmark 配置为了搜索效率允许并行。
  • 状态不跨命令: cd/env 不持久(见 第 2 章),靠 prompt 提醒,偶尔仍绊住模型。
  • 依赖模型自律: 因为没有“编辑工具”这类硬约束,改文件全靠模型自己用 sed/cat <<EOF(prompt 给了范例),弱模型可能把文件改坏。

4.10 横向对比

  • vs letta(同 shelf): letta 把复杂度押在有状态分层记忆 + compaction;mini 押在无状态线性极简。是“agent 复杂度放哪”的两端。参见 docs/letta/02-agent-loop.mddocs/letta/04-compaction-and-internals.md
  • vs 自家 SWE-agent: mini 是 SWE-agent 的“100x 极简版”——SWE-agent 强调为 agent 定制工具与接口(ACI),mini 赌“现代模型 + 纯 bash 就够”。两者共享 SWE-bench 评测与轨迹浏览理念。
  • 通用原理对照本库 guide: docs/guide/agent-loop.md(agent 主循环模式)、docs/guide/execution-environments.md(执行/沙箱)、docs/guide/instruction-authority.md(prompt 作为行为权威)。

4.11 代码地图

主题文件符号
默认行为协议(纯文本格式)src/minisweagent/config/default.yamlsystem_template instance_template
mini CLI 配置(工具调用)src/minisweagent/config/mini.yamlsystem_template observation_template
SWE-bench 跑分配置src/minisweagent/config/benchmarks/swebench.yamlinstance_template
递归合并 + UNSET 哨兵src/minisweagent/utils/serialize.pyrecursive_merge UNSET
agent 工厂src/minisweagent/agents/__init__.pyget_agent get_agent_class
模型工厂src/minisweagent/models/__init__.pyget_model get_model_class
环境工厂src/minisweagent/environments/__init__.pyget_environment
mini CLI 入口src/minisweagent/run/mini.pymain
交互式三模式 agentsrc/minisweagent/agents/interactive.pyInteractiveAgent InteractiveAgentConfig
确认/拒绝/白名单src/minisweagent/agents/interactive.py_ask_confirmation_or_interrupt _should_ask_confirmation
最小 Python 绑定src/minisweagent/run/hello_world.pymain
SWE-bench 批量主流程src/minisweagent/run/benchmarks/swebench.pymain process_instance
实例 → docker 镜像名src/minisweagent/run/benchmarks/swebench.pyget_swebench_docker_image_name get_sb_environment
写补丁(线程安全)src/minisweagent/run/benchmarks/swebench.pyupdate_preds_file
轨迹分步浏览src/minisweagent/run/utilities/inspector.py_messages_to_steps