Agent 日报

拆解 OpenHands:AI 编码 Agent 怎么执行代码

Cover image for 拆解 OpenHands:AI 编码 Agent 怎么执行代码

拆解开源 AI 编码 Agent OpenHands 如何规划、改文件、在沙箱里跑代码。事件流循环、动作-观察循环,以及为什么隔离才是全部关键。

TL;DR — OpenHands(原 OpenDevin)让 AI 编码 Agent 跑在一个循环上:模型发出一个 Action(跑命令、改文件、浏览网页),沙箱化的 runtime 执行它,结果作为 Observation 返回。这个动作-观察循环,记录到事件流里,就是整个架构。聪明的部分不是模型,而是 runtime 隔离——它让模型能安全地跑自己刚写的任意代码。

OpenHands 到底是什么

OpenHands 是一个面向 AI 软件开发 Agent 的开源平台。你给它一个任务(“修好 auth.py 里失败的测试”),它做的事跟开发者一样:读文件、写代码、跑测试、读报错、再试。它靠在 SWE-bench Verified 上领跑开源阵营拿到了 60K+ GitHub star

大多数报道停在”它帮你写代码”。这不有趣。有趣的是底下那个循环,因为 2026 年每个严肃的编码 Agent 都收敛到这个循环。理解它,你就知道怎么造一个,而不只是怎么用。

核心循环:动作与观察

剥掉 UI,OpenHands 就是一个循环:

flowchart LR
    A[Agent: LLM] -->|发出 Action| B[Runtime]
    B -->|在沙箱执行| C[结果]
    C -->|包装成 Observation| D[事件流]
    D -->|追加到历史| A

每一轮:

  1. Agent(一个 LLM)看事件流历史,发出一个 Action
  2. Runtime 在隔离沙箱里执行该动作。
  3. 结果变成 Observation
  4. 观察被追加到事件流,循环重复,直到 Agent 发出 finish 动作。

动作是一个封闭词表。主要的几个:

动作做什么返回的观察
CmdRunAction跑 shell 命令stdout、stderr、退出码
FileEditAction改文件(行范围或整体)成功或 diff
FileReadAction读文件内容文件文本
IPythonRunCellAction在 Jupyter kernel 跑 Pythoncell 输出
BrowseURLAction抓取/交互网页页面内容
AgentFinishAction宣告任务完成终止循环

这是关键设计决策:Agent 没有任意能力。它有一组固定动作,每个都带一个返回的带类型观察。这个约束让 Agent 可调试。出问题时,你重放事件流,精确看到哪个动作产生了哪个观察。

事件流就是记忆

没有单独的”记忆模块”。事件流——每个动作和观察的有序日志——就是 Agent 的工作记忆。每一轮 Agent 的上下文都从这条流重建。

这很优雅,也有真实代价。长任务产生长事件流,而流每一轮都被重放进 context window。一个 40 步的调试 session 能轻松冲破 100K token。OpenHands 用 condensation 处理:旧事件被总结,让上下文有界。这跟到处驱动 Agent 记忆架构的 context window 压力是同一个,只是用在编码循环上。

Runtime:真正的工程在这里

这是大多数人忽略的洞察。LLM 挑动作是简单的部分——任何前沿模型都能做。难的、有价值的部分是 runtime:动作执行的沙箱环境。

想想你实际在干什么。一个语言模型写代码,然后在一台机器上跑它。如果那台机器是你的笔记本或生产服务器,一个坏动作——rm -rf、fork bomb、被污染依赖触发的外传脚本——你就完了。模型没有后果的概念;它靠模式匹配走到动作。

OpenHands 把每个动作跑在 Docker 容器(或远程 runtime)里。Agent 拿到一个完整的 Linux 环境:shell、文件系统、Python kernel、网络访问。但这是个一次性环境。最坏情况,Agent 砸了一个你删掉重建的容器。

对任何执行生成代码的 Agent,这是不可商量的。我们在为什么自主 Agent 需要安全沙箱里做了完整论证,而 OpenHands 是这个原则的参考实现:沙箱不是外挂功能,而是整个循环坐落其上的地基。

# runtime 契约的概念形态
class Runtime:
    def execute(self, action: Action) -> Observation:
        """在隔离沙箱里跑一个动作,返回发生了什么。
        Agent 永远碰不到宿主。每个副作用都被关在这里。"""
        ...

为什么动作-观察格式胜过自由格式

你可能会问:为什么不直接让模型写个脚本整个跑掉?因为动作-观察循环在每一步都给 Agent 反馈。

当 Agent 跑 pytest 看到 ImportError: no module named requests,这个观察会在下一个动作之前返回。Agent 读到它,发出 pip install requests 作为下一个动作。自由格式的”写脚本然后跑”会让整个脚本失败,被迫从头重启。

这种逐步反馈是编码 Agent 在 2025-2026 年大幅变好的原因。这也是为什么人类开发者不会写 200 行然后跑一次——你增量地跑、对错误做反应。这个架构把那个工作流编码进去了。

如果你要造一个,这意味着什么

你不需要 OpenHands 也能用上它的经验。可迁移的架构:

  1. 定义封闭的动作词表。 别给 Agent “做任何事”。给它 run_commandedit_fileread_filefinish。受约束的动作就是可调试的动作。
  2. 让每个动作返回带类型的观察。 观察是 Agent 自我纠错的方式。丰富的错误观察胜过干净的失败。
  3. 永远在隔离里跑动作。 如果你的 Agent 能执行代码,那代码跑在容器或 microVM 里,绝不在宿主上。没有例外。
  4. 把事件日志当记忆。 全部追加。增长时总结。重放它来重建上下文。

FAQ

OpenHands 跟 Devin 是一回事吗?

不是。Devin 是 Cognition 的闭源商业 Agent。OpenHands(原名 OpenDevin)是开源项目,最初是社区想造个类似东西的努力。它们目标相同——自主软件工程——但 OpenHands 是 MIT 协议、可自部署。

OpenHands 能不用 Docker 跑吗?

它可以用本地 runtime,但你真的不该不带沙箱跑。整个安全模型依赖 Agent 在隔离环境里执行代码。直接在宿主上跑动作,等于拿掉了保护你免受坏动作的唯一东西。

什么模型能配 OpenHands?

它通过 LiteLLM 做到模型无关,所以任何 OpenAI 兼容端点都行——Claude、GPT-4o、Gemini、DeepSeek 和开源模型。编码表现因模型差很多;最强的 SWE-bench 分数来自前沿模型。你可以通过 SandBase 这样的单一网关路由,换模型不改代码。

Agent 怎么知道自己做完了?

它发出 AgentFinishAction。Agent 自己根据事件流判断任务完成——比如测试通过之后。这也是一个失败模式:Agent 有时会过早宣告胜利,所以在 finish 前加一个验证步骤(跑测试、检查输出)很重要。

这跟 CrewAI 这种多智能体框架有什么不同?

OpenHands 是单个 Agent 在一个紧凑的执行循环里,为编码优化。CrewAI 这类框架编排多个基于角色的 Agent。不同的问题——多智能体那一面见 AutoGen vs CrewAI。你甚至能把 OpenHands 风格的 Agent 当成一个更大 crew 里的节点。

关键要点

  • OpenHands 就是一个循环:Agent 发出 Action,沙箱 runtime 执行它,结果作为 Observation 返回,事件流记录一切。
  • 事件流就是记忆。没有单独的记忆模块,只有一个 append-only 日志每轮重放进上下文(增长时做 condensation)。
  • 工程价值在 runtime,不在模型。隔离让模型跑自己刚写的代码变得安全。
  • 造你自己的:封闭动作词表、带类型观察、强制沙箱、事件日志当记忆。这就是模板。

猜你喜欢