SWE-agent

一句话定位

SWE-agent 是 Princeton NLP 团队提出的、以「Agent-Computer Interface (ACI)」为核心设计理念的自动化软件工程 harness(论文 arXiv:2405.15793),核心贡献是定制化的文件查看/编辑/搜索工具而非通用 shell access;仓库当前(分析时点)README 明确声明开发重心已转移到姊妹项目 SWE-agent/mini-swe-agent,本文分析的是 SWE-agent 1.0(本体仓库)架构,非 mini-swe-agent。

核心架构总览(目录结构关键路径 + 引用的 commit)

分析基于 commit 0363b9ef787a667d96120e132e8d3d59a692adcd(2026-07-06 docs fix,depth-1 clone)。仓库 MIT 协议,文档站 https://swe-agent.com/latest/

关键目录:

  • sweagent/agent/agents.pyDefaultAgent/RetryAgent 主循环,1294 行)、models.pyLiteLLMModel/HumanModel/ReplayModel,903 行)、history_processors.py(上下文压缩,399 行)、reviewer.py(best-of-N 评审/重试,664 行)、action_sampler.py(单步集成采样,317 行)、hooks/abstract.py(生命周期 hook 接口)
  • sweagent/tools/tools.py(工具安装/黑名单/状态获取,430 行)、parsing.py(多种 parser,621 行)、commands.pyCommand/Argument schema,223 行)、bundle.py(插件/bundle 机制)
  • sweagent/environment/swe_env.py(276 行,SWE-ReX 的薄封装)、repo.py(仓库重置逻辑)
  • sweagent/run/run_single.py(单实例入口)、run_batch.py(442 行,多实例并行)
  • sweagent/inspector/server.py — 本地轨迹查看 HTTP server
  • tools/ — ~14 个一手 bundle(registryedit_anthropicwindowed*searchsubmitforfeitreview_on_submit_m 等)
  • config/default.yaml — 系统/实例 prompt 模板、parser 配置、工具 bundle 装配的规范样例

姊妹仓库:SWE-agent/mini-swe-agent(当前开发重心)、SWE-agent/SWE-ReX(实际沙箱执行后端,本仓库只是其薄封装)、SWE-bench/SWE-benchSWE-bench/SWE-smithSWE-bench/sb-cli

Agent Loop(主循环 / 何时继续何时停)

主循环入口 DefaultAgent.run()agents.py:1265-1294):while not step_output.done: step_output = self.step()

  • step()agents.py:1235-1263)调用 forward_with_handling(),写轨迹、更新 info(submission/exit_status/model_stats)。
  • forward_with_handling()agents.py:1062-1218)是带重试的包装层,max_requeries 默认 3(DefaultAgentConfig.max_requeries, agents.py:158)。可恢复错误(格式错误、动作被拦截、bash 语法错误)会把错误信息追加进历史后重新询问模型(不永久污染历史,只记入轨迹);不可恢复错误(超预算、超上下文窗口、环境死亡、运行时错误、重试耗尽)触发 attempt_autosubmission_after_error(),尝试抽取 git diff 并强制 step.done = True
  • forward()agents.py:1006-1060):self.model.query(history)self.tools.parse_actions(output) 解析 thought/action → handle_action(step)
  • handle_action()agents.py:936-1004):检查 should_block_action(黑名单)、处理字面 "exit" 动作、通过 self._env.communicate(...) 在持久 shell 会话中执行真实 bash 命令、处理 CommandTimeoutError(连续超时达到 max_consecutive_execution_timeouts 默认 3 次后杀死 agent,tools.py:150)、最后 handle_submission() 检测提交信号。
  • 停止条件submit 工具运行(写 /root/model.patch,通过魔法字符串 <<SWE_AGENT_SUBMISSION>> 识别,来自 tools/submit/bin/submit,已核实脚本内容:git add -A && git diff --cached > /root/model.patch && echo "<<SWE_AGENT_SUBMISSION>>");字面 exit 动作;exit_forfeit 工具(tools/forfeit,魔法字符串 ###SWE-AGENT-EXIT-FORFEIT###, agents.py:221);超预算(CostLimitExceededError/TotalCostLimitExceededError);超上下文窗口;连续超时过多;总执行时间超限(total_execution_timeout 默认 1800s,tools.py:145);不可恢复的运行时/API 错误。
  • RetryAgentagents.py:257-441)是包裹 DefaultAgent 的元循环:可在总 cost_limit 下重启整个 agent(_next_attemptenv.hard_reset() + 全新 DefaultAgent)多次,再用评审机制挑选最佳尝试(见「自进化能力」)。

记忆与上下文管理(压缩、长期记忆、会话持久化)

历史结构是 role/content 字典的扁平列表(sweagent/types.py: History/HistoryItem);DefaultAgent.messages 属性(agents.py:539-551)在发给模型前应用一条 history_processors 处理链——这是压缩层所在。

history_processors.py 实现的可插拔判别联合类型(discriminated union,history_processors.py:390-399):

  • DefaultHistoryProcessor — 无操作直通。
  • LastNObservations(85-176)— 经典 0.7 版策略:只保留最近 n 条 observation,旧的替换为 "Old environment output: (n lines omitted)";支持 polling(批量改变省略窗口,利于 prompt caching)与按 tag 覆盖(always_keep_output_for_tags/always_remove_output_for_tags)。
  • ClosedWindowHistoryProcessor(215-258)— 针对窗口化文件查看工具:同一文件的历史 view 只保留最后一次展示,其余替换为占位符,避免大文件重复出现在多轮上下文里。
  • CacheControlHistoryProcessor(261-302)— 给最近 N 条 user/tool 消息加 Anthropic prompt-caching 的 cache_control: {"type": "ephemeral"} 标记;这是 config/default.yaml默认处理器(last_n_messages: 2)。
  • RemoveRegex(305-337)— 对除最后 N 条外的历史剥离任意正则模式(默认剥离 <diff>...</diff>)。
  • ImageParsingHistoryProcessor(340-387)— 把嵌入的 base64 markdown 图片转成规范的多模态 image_url content block。
  • TagToolCallObservations(179-212)— 给特定工具调用 observation 打 tag,供上面的”always keep”逻辑使用。

未发现跨会话/长期记忆存储——记忆管理完全是单个 problem instance 内的事。会话持久化 = 每步后写入的 .traj JSON 文件(agents.py:779-787, save_trajectory()),是磁盘持久化状态,但不会自动回灌进活跃 agent 的上下文,只用于事后分析/重放(ReplayModel, models.py:464-526,逐字重放历史 .traj 的动作)。

环境侧的小型持久状态:tools/registry/lib/registry.pyEnvRegistry——一个位于 /root/.swe-agent-env 的 JSON 文件,工具脚本可读写以在同一容器会话的多次工具调用间持久化状态(因为每次工具调用都是独立子进程,环境变量不传播)。

templates.max_observation_length(默认 100,000 字符,agents.py:79)截断单条 observation,附带模板化警告(next_step_truncated_observation_template, agents.py:69-77)告诉模型改用 head/tail/grep 而非整体输出。

上下文窗口溢出是一等异常:ContextWindowExceededError 触发立即强制自动提交(agents.py:1175-1179),而非进一步压缩——即 SWE-agent 的压缩是静态/配置时确定的(history processor 提前选好),不是溢出触发的自适应压缩。

工具体系(定义/调用协议/注册/权限)

工具是 Command 对象(sweagent/tools/commands.py):namedocstringarguments: list[Argument](每个含 type/description/required/enum/argument_format——渲染成 shell 调用的 Jinja 模板),可选 signature 覆盖,可选 end_name(标记多行/heredoc 命令)。

Command.get_function_calling_tool()commands.py:133-165)把 Command 转成 OpenAI 风格的 function-calling JSON schema——当 parse_function.type == function_calling 时这就是发给模型的内容。

工具注册ToolConfig.bundles: list[Bundle]tools.py:83)+ 内置的 BASH_COMMAND(除非 enable_bash_tool=False 始终可用)。Bundlesweagent/tools/bundle.py,已读取源码确认)是一个目录:config.yamlBundleConfigtools: dict[str, dict] + 可选 state_command)+ bin/ 可执行脚本 + 可选 install.sh。跨 bundle 重名工具在配置构建期直接抛 ValueErrortools.py:178-187)。

工具安装入沙箱:ToolHandler.install()_install_commands()tools.py:292-312)把每个 bundle 目录上传到容器(/root/tools/<bundle_name>/)、chmod +x、运行 bundle 的 install.sh(如有)、把 bin/ 加入 $PATH 前部,然后用 which 验证每个声明的命令确实可解析(_check_available_commands)——这发生在容器启动阶段,非每次调用。

调用协议取决于配置的 parser(见 Prompt 设计一节):解析出的 thought+action 通过 Command.invoke_format 变成字面 bash 命令字符串,在持久 shell 会话中直接执行(SWEEnv.communicate)——即没有独立的结构化”工具调用”RPC 送到沙箱;function-calling 只用在模型 API 边界,之后还是被翻译回普通 shell 命令。多行命令(设置了 end_name)通过 _guard_multiline_inputtools.py:382-409)转成 heredoc。

权限ToolFilterConfigtools.py:29-72)是静态黑名单,不是动态审批门blocklist(前缀匹配,如 vim/emacs/nano/gdb/less/tail -f/make)、blocklist_standalone(精确匹配,如裸 python/bash/su)、block_unless_regex(如 radare2/r2 仅在带 -c ... 调用时放行)。被拦截动作抛 _BlockedActionError → 走 requery 循环并附 blocklist_error_template(默认:“Operation ‘{{action}}’ is not supported by this environment.“)。该代码库没有面向工具调用的交互式人工审批门——唯一的人在环路径是独立的 HumanModel/human 模型类型,由(非模型)直接输入动作(models.py:344-443)。

submit 工具与 exit_forfeit 工具按名字特殊处理(ToolConfig.submit_command 默认 "submit",通过 check_for_submission_cmd 中的魔法字符串 <<SWE_AGENT_SUBMISSION>> 检测,tools.py:372-376)。

Prompt 设计(系统提示结构、动态组装)

全部 prompt 都是 Jinja2 模板(jinja2.Template),逐步渲染,非静态字符串。TemplateConfigagents.py:60-146)定义:system_templateinstance_template(首个 user turn,即问题陈述)、next_step_template"Observation: {{observation}}")、next_step_truncated_observation_templatenext_step_no_output_templateshell_check_error_templatecommand_cancelled_timeout_template,可选 strategy_template、可选 demonstration_template

动态组装发生在 _get_format_dict()agents.py:658-673):合并 tools.config.command_docs(自动生成的工具文档字符串)、tools.config.env_variables、问题陈述文本、仓库名,以及问题陈述对象的任意额外字段——全部作为 Jinja 变量注入每个模板。

具体地,config/default.yaml 的系统提示很简短(“You are a helpful assistant that can interact with a computer to solve tasks.“)——多数任务特定指导在 instance_template 里,是一个嵌在 YAML 配置(非 Python)中的 5 步流程(定位相关代码 → 复现 bug 脚本 → 改源码 → 重跑复现 → 考虑边界情况)。

function-calling 模式下模型实际看到的是工具 schema(而非自然语言文档);非 function-calling 的 parser(thought_actionxml_thought_action 等)需要在系统/实例模板里显式包含 {{command_docs}},模型才能纯靠文本发现可用工具(tools/parsing.py 模块 docstring, 29-33 行)。

Demonstration(few-shot 轨迹)可作为单条格式化消息注入(demonstration_template)或逐步重放进历史(put_demos_in_history=True)——agents.py:617-656

错误恢复提示也是模板:format_error_template 默认取当前 parser 自身的 error_message,后者本身按 Jinja {% if error_code == ... %} 分支处理不同的 function-calling 失败模式(缺工具调用、多个工具调用、意外参数——见 parsing.py:236-252 XMLFunctionCallingParser.error_message 和 374-392 FunctionCallingParser.error_message)。

Router / 编排(任务分解、多 agent、子 agent)

未发现”planner 把任务拆成子任务、路由给专家 agent”的通用架构。实际存在的是:

  • RetryAgentagents.py:257-441):串行多次尝试编排——跑完一个完整 DefaultAgent,评审打分,可选再开一次全新尝试(带环境 hard_reset()),直到 cost_limit/max_attempts 耗尽,再选出胜者。这是时间维度上的尝试级并行,不是单实例上的并发多 agent 协作。
  • ActionSampleraction_sampler.py):单步(非单集)级别的集成机制。
    • AskColleagues(49-93 行):对当前步骤从同一模型采样 n_samples 个补全,拼成”你的同事们有以下想法”提示,让模型挑选/综合出最终动作——本质是套着”同事”多智能体外皮的轻量自一致性机制,仍是单模型实例串行调用,不是独立自治 agent。
    • BinaryTrajectoryComparison(96-314 行):采样 min_n_samples..max_n_samples 个候选动作,过滤解析失败与重复项,再做成对锦标赛比较(“两位初级开发者建议…“)由 LM 评审选胜者。同样是单模型,无独立 agent 进程。
  • Chooser/Preselectorreviewer.py:242-372):作用于(来自 RetryAgent 的)整条尝试,而非单次尝试内的子 agent——LM 评审在若干独立跑完的尝试中挑最优轨迹,候选多时可先跑一遍更便宜的预筛选(PreselectorConfig)。
  • run_batch.pyThreadPoolExecutor 并行(main_multi_worker, 268-289 行)是跨独立问题实例的编排(例如并行跑很多 SWE-bench 任务),不是单任务内的子 agent 分解。

结论:SWE-agent 实现的是 best-of-N / 重试选优编排模式(对同一 agent 架构做时间维度和采样维度的 ensembling),而非层级化的 planner→子agent 任务分解。sweagent/agent/sweagent/run/ 中未发现”spawn 子 agent 处理 X”的原语。

Skill / 插件体系

Bundle 机制(sweagent/tools/bundle.py)就是 skill/插件体系:每个 bundle 是自包含目录(config.yaml + bin/ 可执行文件 + 可选 lib/ + 可选 install.sh),在 agent 配置中通过 tools.bundles: [{path: tools/registry}, {path: tools/edit_anthropic}, ...] 声明(参见 config/default.yaml:41-44)。

Bundle 可声明 state_commandBundleConfig.state_command, bundle.py:14)——每次动作后运行的 shell 命令,抽取结构化状态(如当前打开文件、工作目录)到 /root/state.json,由 ToolHandler.get_state()tools.py:337-348)读回并合并进下一条 observation 的模板变量。

Bundle 可通过 hidden_tools 对模型隐藏特定工具,同时仍供内部使用(bundle.py:19,37-40,52-57)——用于不打算被 LM 直接调用的辅助脚本。

仓库自带约 14 个一手 bundle,位于 tools/registry(状态持久化库,始终加载)、edit_anthropic(Anthropic 风格文件编辑器)、windowed/windowed_edit_linting/windowed_edit_replace/windowed_edit_rewrite(窗口化文件查看/编辑器家族——ACI 论文的定制文件查看器,本次未深入读实现)、searchsearch_dir/find_file/search_file——ACI 论文的定制搜索工具)、submitforfeitreview_on_submit_mdiff_statefilemapweb_browserimage_toolsmultilingual_setup。每个都可按配置独立插拔——是真正的插件架构,非硬编码工具列表。

未发现动态/运行时插件发现机制(如扫描 marketplace 或从 URL 加载)——bundle 在 agent 构建时通过本地文件系统路径在 YAML 配置中静态引用。

自进化能力(自我改进 / 学习型记忆 / eval 驱动纠错)

未发现在线学习/权重更新/持久化跨集学习记忆——这明确超出该代码库的范围(SWE-agent 是推理时 harness,不是训练循环)。

存在的是单个问题实例内的 eval 驱动重试/自我纠错,机制在 reviewer.py

  • Reviewer.review()(375-449 行):LM-as-judge 对已完成轨迹采样 n_sample(默认 5,ReviewerConfig.n_sample)次独立打分,取平均(可选按方差惩罚 reduce_by_std),并在尝试未干净 submit 时施加 failure_score_penalty
  • ScoreRetryLoop(559-658 行):每次尝试后调用评审;若得分 < accept_score 且预算/次数尚有余量,触发下一次完整尝试(RetryAgent._next_attemptagents.py:321-326,做环境硬重置 + 全新 agent)。最终 get_best() 选出评审分最高的轨迹(同分按 API 调用数最少/最便宜打破平局)。
  • ChooserRetryLoop(499-556 行):更简单的变体——不做每次尝试打分,收集 N 次尝试后让 LM Chooser 在最后一次性挑出最优(候选多时可选先跑 Preselector 缩小范围)。

这是”通过重采样 + LM 评审做自我纠错”,即推理时 eval 驱动纠错,不是跨不同问题实例持久化的学习/适应机制。该代码库中未发现微调、梯度更新或跨运行持久化的技能/策略记忆。

轨迹可转换为未来运行的 few-shot demonstrationdocs/config/demonstrations.md,本次仅引用未精读;另有 sweagent/run/run_traj_to_demo.py 存在但本次未打开)——这是”从过去运行中学习”最接近的形态,但属于人工/离线策展步骤(人挑一条好轨迹转成 demo),非自动。

可观测性(日志 / trace 格式)

通过 Python logging 做结构化日志,每个子系统有 emoji 前缀的具名 logger(get_logger("swea-agent", emoji="🤠")"swea-tools""swea-env""swea-lm""swea-run""reviewer""chooser_loop" 等),定义在 sweagent/utils/log.py(本次未精读,但使用遍布全代码库)。

每实例多级别日志文件:<instance_id>.trace.log.debug.log.info.logrun_batch.py:411-420, _add_instance_log_file_handlers),外加共享的 run_batch.log

轨迹格式.traj 文件为 JSON,每步后写入(DefaultAgent.save_trajectoryagents.py:779-787)。顶层字段:trajectoryTrajectoryStep 列表:actionobservationresponse(LM 原始输出)、thoughtexecution_timestate(环境状态字典)、query(发给 LM 的完整消息列表,自 SWE-agent 1.1.0 起替代旧的 message 字段,见 docs/usage/trajectories.md:27-30)、extra_info)、history(整个episode的完整原始消息列表)、infoAgentInfo:submission、exit_status、model_stats、编辑文件上下文片段)、replay_configenvironment 名。

sweagent/inspector/server.py — 本地 HTTP server(http.server/socketserver),读取 .traj 文件供人类浏览的静态 Web UI(sweagent/inspector/static.py),通过 sweagent inspector CLI 子命令启动(sweagent/run/inspector_cli.py,本次未打开)。

成本/用量跟踪本身也是一种可观测性:InstanceStatsmodels.py:292-308instance_costtokens_senttokens_receivedapi_calls)按 agent 记录,外加进程级 GlobalStats/GLOBAL_STATS 单例(线程锁保护,models.py:273-289)跟踪一次 run-batch 调用中所有并发实例的 total_costlast_query_timestamp

run_batch.pyRunBatchProgressManager(来自 sweagent/run/_progress.py,未打开)配合 rich.live.Live 渲染跨并行 worker 的终端实时进度表,并写出 run_batch_exit_statuses.yaml 汇总每实例退出状态。

安全与权限(审批门、密钥管理)

不存在面向自治工具执行的交互式人工审批门——模型提出的每个动作(一旦解析且未被黑名单拦截)都立即在沙箱中执行。唯一的”人在环”模式是 HumanModel/human_thought 模型类型,由人直接替代 LM输入动作(models.py:344-461)——这是调试/手动驾驶模式,不是叠加在 LM agent 上的审批层。

安全由「工具体系」一节所述的静态 ToolFilterConfig 黑名单实现(拦截已知危险/交互式命令:编辑器、gdbnohupmake、裸 shell/python 调用等),并对 radare2 等工具设正则门控例外。

密钥管理GenericAPIModelConfig, models.py:66-201,及 docs/installation/keys.md):

  • 密钥可通过环境变量、.env 文件(load_environment_variables() 加载)或 --agent.model.api_key CLI flag 解析。
  • api_key: SecretStr | None —— 用 pydantic SecretStr 避免原始值意外出现在 repr/日志中。
  • 支持用 ::: 分隔符拼接多个密钥并按线程轮转(choose_api_key_by_thread, models.py:119-190),使并行 run-batch worker 获得缓存友好的一致密钥或随机分散的密钥。
  • propagate_env_variables: list[str]tools.py:86-92)明确警示:“the value of the environment variables can be read in debug log files, so be careful with your API keys”——代码库自身标记了一条真实的密钥泄露风险路径(把宿主密钥透传进沙箱化工具环境时)。
  • 仓库根目录 .env.example 只文档化了 GITHUB_TOKEN(用于克隆仓库/开 PR);模型 provider 密钥(OPENAI_API_KEYANTHROPIC_API_KEY 等)在 docs/installation/keys.md 中说明,但未在 .env.example 里硬编码/模板化。

内容策略处理:litellm 抛出的 ContentPolicyViolationError 被当作值得重采样的软失败捕获(agents.py:1130-1134),非硬停止——agent 只是重新查询。

沙箱与执行隔离

SWE-agent 1.0 的环境层(sweagent/environment/swe_env.py)官方文档明确说是”如今只是我们 SWE-ReX 包的薄封装”(docs/background/architecture.md:8)——真正的沙箱化/进程隔离逻辑在独立的 SWE-agent/SWE-ReX 仓库(https://swe-rex.com),**不在本仓库内**。这是本 dossier 的重要范围边界:命名空间/cgroup/进程隔离的具体细节不在 SWE-agent 仓库本身。

SWE-agent 确实控制的部分:EnvironmentConfig.deployment: DeploymentConfigswe_env.py:24-49),默认 DockerDeploymentConfig(image="python:3.11")——即默认隔离单元是 Docker 容器。部署抽象(swerex.deployment.abstract.AbstractDeployment)也支持远程后端(文档提及 modalawsdocs/background/architecture.md:9 明确把 Docker vs. Modal/AWS 等远程系统列为可互换的 Deployment 后端)。

容器内,SWE-ReX 启动一个持久 bash 会话(CreateBashSessionRequest(startup_source=["/root/.bashrc"], ...), swe_env.py:184-188),所有 agent 动作都通过 communicate()swe_env.py:197-232)在其中执行,后者封装 deployment.runtime.run_in_session(BashAction(...))。这意味着状态(cwd、环境变量、打开的 shell 变量)在一个实例的多轮间自然持久——shell 会话本身就是状态/隔离单元。

尝试间的仓库/代码库隔离:_reset_repository()swe_env.py:149-166)运行 git fetch && git status && git restore . && git reset --hard && git checkout <base_commit> && git clean -fdq 把仓库恢复到干净状态——这是 RetryAgent 多次尝试之间避免互相污染而不用重建整个容器的方式(相对更昂贵的 hard_reset() 会停止+重启整个 deployment)。

执行超时按命令强制(execution_timeout,默认 30s,tools.py:139),并按 episode 累计强制(total_execution_timeout,默认 1800s,tools.py:145),加上连续超时达 max_consecutive_execution_timeouts(默认 3)后的硬性截断(tools.py:150-152,在 agents.py:968-982 强制执行)。

本仓库中未发现”直接在宿主机无隔离运行工具调用”的模式——Docker(或远程沙箱化 deployment)是贯穿全文档的默认与隐含假设。

与模型的协同设计

Provider 抽象层是 litellmsweagent/agent/models.py 引入 litellm),即 SWE-agent 设计上是显式多模型/多 provider 的,而非为单一特定模型家族协同设计——但代码中有若干值得注意的 provider 特定特殊处理,这是”模型协同设计”最接近的证据:

  • Anthropic 特定:CacheControlHistoryProcessor(prompt caching 标记)是 config/default.yaml 中的默认历史处理器,docstring 明说”Use this when running with anthropic claude”(history_processors.py:261-264)。
  • Claude 3.7/Sonnet-4 的最大输出 token 特殊处理:models.py:608-620 —— 检测模型名中的 claude-3-7-sonnet/claude-sonnet-4,默认把 max_output_tokens 设为 64000,除非设置了 anthropic-beta: output-128k-2025-02-19 额外 header,并给出明确警告日志告诉用户如何解锁 128k 输出。
  • anthropic_beta/thinking_blocks 支持:StepOutput/历史项携带 thinking_blocks 字段(agents.py:1046history_processors.py 不处理它,但 models.py:774-778,861-864 会透传)——即扩展思维/推理轨迹的保留对能产出它的模型(Claude extended thinking/推理模型)端到端打通。
  • o1 模型适配:convert_system_to_user: bool 配置项(models.py:98-101)——“useful for models that do not support system messages like o1”。
  • Function-calling 能力检查:LiteLLMModel.__init__models.py:587-594)调用 litellm.utils.supports_function_calling(model=...),若配置模型不支持 function-calling 而 parse_function 设为该类型,则警告(非硬失败)。
  • 多种 parser 策略专门用于适配指令遵循较弱或不支持 function-calling 的模型(thought_actionxml_thought_actionxml_function_calling 面向输出 XML 风格工具调用的开放权重模型、jsonall_bash_code_blocks)——tools/parsing.py 模块 docstring 明确把 FunctionCallingParser 定为能力较强模型的推荐路径,其余作为兜底。
  • 模型-分词器协同设计:custom_tokenizer 配置项(models.py:146-150)允许覆盖用于 token 计数/上下文窗口检查的分词器——对不在 litellm 默认注册表里的本地/开放权重模型有用。

未发现 SWE-agent 是”为某个特定前沿模型量身打造”的证据(不同于作为某模型自有产品一部分发布的 harness);它是模型无关的,叠加了务实的按 provider 适配。

轨迹利用(session/trajectory 是否反哺训练/评测)

直接证据表明轨迹反馈进评测sweagent/run/hooks/swe_bench_evaluate.py(在 run_batch.py:223-233 中被引用,本次未精读)——一个 RunHook,作为 run-batch 的一部分触发 SWE-bench 评测(continuous_submission_every=30),直接把轨迹产出(preds.json,由 sweagent/run/merge_predictions.py 生成)接入 SWE-bench 评测 harness(sb-cli,README 中引用)。

直接证据表明轨迹反馈进训练相关数据产品sweagent/run/run_traj_to_demo.py(文件名强烈暗示”轨迹→demonstration”转换,文档中引用但本次未打开该文件)与 docs/config/demonstrations.md(本次未精读)——轨迹可以变成 few-shot demonstration,通过 TemplateConfig.demonstrationsagents.py:90-93,617-656)反馈进未来的 agent 运行。

README 链接了 SWE-bench/SWE-smith(生成 SWE-bench 风格训练数据/任务的姊妹项目)并引用了 SWE-agent-LM-32b(一个开放权重模型,README.md:42)——强烈的旁证表明 SWE-agent 的轨迹/环境被用于上游训练专用代码 agent 模型,但实际训练流水线在本仓库之外(在 SWE-smith / SWE-agent-LM 训练代码里),本次未检视。

单次运行内,轨迹也被 reviewer/chooser(见「自进化能力」)消费用于推理时 best-of-N 选择——这是一种”轨迹复用”,但停留在单个问题求解 episode 内,不是跨 episode 学习。

本仓库内未发现.traj 文件自动反馈进持久化记忆或微调循环的机制——所有此类利用(SWE-smith、SWE-agent-LM、demo 策展)依据 README/文档引用来看,都是手动/离线/仓库外发生的,本仓库代码中没有对应实现。

与同类 harness 的关键差异(1-3 条,可以先留一句概述,后续 synthesis 阶段会做跨 harness 对比)

  1. ACI(Agent-Computer Interface)是其立身之本:不像很多 harness 直接给模型裸 shell 或通用 file-edit 工具,SWE-agent 的核心论文贡献就是定制化的窗口化文件查看器(tools/windowed*)与搜索工具(tools/search),刻意约束/塑造模型与环境的交互面,而非追求最大自由度。
  2. 编排策略是”同架构多次采样选优”而非”层级化子 agent 分解”RetryAgent/ActionSampler/Reviewer/Chooser 一整套机制都是在单一 DefaultAgent 架构上做时间维度(多次尝试)和采样维度(单步多候选)的 ensembling,与规划器→专家子agent 的路由范式明显不同,值得在跨 harness 对比中重点标注。
  3. 执行隔离层完全外包给独立项目(SWE-ReX):本仓库只声明 Deployment 配置(Docker/Modal/AWS),真正的沙箱机制不在本仓库源码内,这与一些”隔离逻辑与 agent 逻辑同仓库耦合”的 harness 形成对比,调研时需注意这个仓库边界。

(后续跨 harness synthesis 阶段再做系统对比)

原始源码定位

  • repo: https://github.com/SWE-agent/SWE-agent
  • commit/version analyzed: 0363b9ef787a667d96120e132e8d3d59a692adcd(depth-1 clone,2026-07-07;HEAD commit message: “docs: fix multimodal disable-image-processing flag hierarchy (#1445)“,日期 2026-07-06)
  • 关键文件列表(相对路径):
    • sweagent/agent/agents.py
    • sweagent/agent/models.py
    • sweagent/agent/history_processors.py
    • sweagent/agent/reviewer.py
    • sweagent/agent/action_sampler.py
    • sweagent/agent/hooks/abstract.py
    • sweagent/exceptions.py
    • sweagent/tools/tools.py
    • sweagent/tools/parsing.py
    • sweagent/tools/commands.py
    • sweagent/tools/bundle.py
    • sweagent/environment/swe_env.py
    • sweagent/environment/repo.py(部分读取)
    • sweagent/run/run_single.py
    • sweagent/run/run_batch.py
    • sweagent/inspector/server.py(部分读取)
    • tools/registry/lib/registry.py
    • tools/forfeit/config.yaml + tools/forfeit/bin/exit_forfeit
    • tools/submit/bin/submit
    • config/default.yaml
    • docs/background/architecture.md
    • docs/background/aci.md
    • docs/installation/keys.md
    • docs/usage/trajectories.md
    • docs/config/environments.md
    • .env.example
    • README.md
    • 未深入读取(下阶段可跟进):sweagent/agent/problem_statement.pysweagent/agent/extra/shell_agent.pysweagent/run/hooks/open_pr.pysweagent/run/hooks/swe_bench_evaluate.py(完整版)、sweagent/run/run_traj_to_demo.pydocs/config/demonstrations.mdtools/windowed/* 完整实现

一手源存档(sources/)

/Users/zhao/projects/self-wiki/ai-research/sources/harness/swe-agent/ 下:

  • NOTES.md — 完整 Stage 1 调研笔记(本文所有引用均核实自此文件及以下源文件)
  • key-files/agents.pykey-files/models.pykey-files/history_processors.pykey-files/reviewer.pykey-files/action_sampler.pykey-files/agent_hooks_abstract.pykey-files/exceptions.py
  • key-files/tools.pykey-files/parsing.pykey-files/commands.pykey-files/bundle.py
  • key-files/swe_env.py
  • key-files/config_default.yaml
  • key-files/tools_registry_lib.pykey-files/tools_forfeit_config.yamlkey-files/tools_forfeit_exit_forfeit.shkey-files/tools_submit_bin.sh
  • key-files/docs_aci.mdkey-files/docs_architecture.mdkey-files/docs_environments.mdkey-files/docs_keys.mdkey-files/docs_trajectories.md