@xingyaow_: 人们一直在问为什么 OpenHands V1 走的是与 Claude Managed Agents 相反的方向。我终于找到了时…

X AI KOLs Following 新闻

摘要

Xingyao Wang 的博客文章解释了为什么 OpenHands V1 选择了与 Claude Managed Agents 不同的架构,认为可靠性来自于实现细节而非拓扑结构。

人们一直在问为什么 OpenHands V1 走的是与 Claude Managed Agents 相反的方向。我终于有时间写了这篇文章。生产中的痛点确实存在,但改变托管代理架构并不能免费解决这些问题:https://xwang.dev/blog/2026/openhands-sdk-claude-managed-agents/…
查看原文
查看缓存全文

缓存时间: 2026/06/18 20:11

人们一直在问,为什么 OpenHands V1 会走与 Claude Managed Agents 相反的方向。我终于抽时间写清楚了。生产中的痛点确实存在,但改变托管 Agent 架构并不能免费解决这些问题:https://xwang.dev/blog/2026/openhands-sdk-claude-managed-agents/…


托管 Agent 的边界应该划在哪里

来源:https://xwang.dev/blog/2026/openhands-sdk-claude-managed-agents/ Anthropic 的 Managed Agents 博客 从一个具体的生产问题出发:在他们早期的设计中,session、harness 和 sandbox 都放在一个容器里。当那个容器故障时,session 就丢失了。当它无响应时,WebSocket 事件流无法判断故障来自 harness、网络还是容器。他们提出的修复方案是将 session、harness 和 sandbox 分离成独立的接口,这样每个组件可以独立地故障或被替换。这篇博文称之为“将大脑与手解耦”。

OpenHands 让这个比较变得有趣,因为它从相反的方向经历了同样的设计空间。V0 已经将 agent 循环和事件流置于 Docker 沙箱之外。而 V1 则将稳定的边界移到了 SDK 中,这样同样的 agent 路径可以在本地、Docker、OpenHands Cloud 或企业自托管部署中运行。

这个反转就是本文要探讨的问题。如果托管 agent 的分隔被描述为通往持久化 session、可重启 harness、可替换 sandbox 和可调试故障的路径,那么为什么 OpenHands 将主要契约移到了别处?

我的论点很简单:拓扑结构可以帮助实现可靠性,但它本身并不能创造可靠性。 大脑/手的分隔可以使持久化、恢复和可调试性更容易实施,但这些属性仍然来自于持久化状态、动作协调、关联遥测以及大量运维工作。不同的拓扑结构可以追求相同的可靠性目标。OpenHands V1 是通过一个可移植的 SDK 契约来实现的,而不是使用同样的托管 harness/sandbox 拓扑。

托管服务的路径

Claude Code 和 Claude Managed Agents 是不同的产品形态。Claude Code 从开发者的工作区出发:agent 读取仓库、编辑文件、运行命令、使用 CLAUDE.md、调用 MCP 工具,并通过面向开发者的权限和检查点工作。

Claude Managed Agents 从一个托管的 session API 开始。当前的 Managed Agents 文档 描述了 agents、environments、sessions、events 和 tools。通俗地说,托管服务会记住 session、调用模型、路由工具动作、存储事件、管理凭证,并将观察结果返回给模型,以便它选择下一步。

Sandbox 是动作执行的地方。在默认的云环境中,Anthropic 管理那个执行环境。使用 自托管 sandbox,文件系统访问、派生的进程和网络出口可以留在客户基础设施内部。工具输入和输出仍然连接到 Anthropic 的托管服务。

Anthropic Managed Agents 架构图,展示了 session、harness、sandbox、工具、资源、MCP 和编排。 Anthropic 的 Managed Agents 图。托管服务拥有 session、harness、编排和凭证边界;执行可以在 Anthropic 管理的 sandbox 或自托管的 sandbox worker 中进行。来源:Anthropic Engineering。 对于由提供商运营的 agent 服务来说,这是一个连贯的形态。Managed Agents 是围绕托管服务所有权设计的:Anthropic 可以决定 session 状态的含义、凭证的存放位置、事件的存储方式、sandbox 的预配方式,以及在运行失败时可以集中收集哪些运维遥测。提供商可以围绕一个托管面集中处理故障处理、sandbox 预配、凭证库、MCP 代理和客户支持。

为什么 OpenHands V0 从 sandbox 开始

OpenHands 始于一个不同的历史时刻。

它作为开源项目 OpenDevin 在 2024 年初启动,当时 Devin 刚刚普及了自主软件工程师框架,而 Anthropic 尚未在 2025 年 2 月将 Claude Code 作为有限研究预览版 推出。

在我的记忆中,OpenHands V0 很快就需要同时服务两类受众。对于工程师来说,它需要是一个面向用户的编码 agent,可以通过 Web UI 或 CLI 运行,并且可以自托管。对于 AI 研究人员来说,它需要是评估和训练编码 agent 的基础设施。

这第二类受众解释了为什么架构如此专注于 Docker。当我开始设计早期的 OpenDevin / OpenHands 架构时,我考虑的是 SWE-Bench 风格的评估和 RL 训练环境。这个方向后来具体落在了 SWE-Gym 上,这是一个用于在真实仓库任务上训练软件工程 agent 和验证器的环境。

对于这种场景,sandbox 为评估和训练运行提供了一个可重现的边界:同一个任务可以从受控的文件系统、依赖集和测试环境开始。它还提供了隔离的工作区、可重置的任务状态,以及许多可以快速创建和销毁的并行运行。Docker 是一个自然的起点。

所以 OpenHands V0 有一个合理的设计:将 agent 循环和事件流放在执行运行时之外,然后将工具执行放在 Docker sandbox 内部。Shell 命令、文件编辑、浏览器和 IPython 都跨越了这个运行时边界。

OpenHands ICLR 2025 架构图,展示了用户界面、agent、事件流和 agent 运行时,其中包含 Docker sandbox、IPython、bash shell 和浏览器。 OpenHands ICLR 2025 论文 中的图 2。V0 围绕三个部分:一个 agent 抽象、一个跟踪动作和观察结果的事件流,以及一个在 Docker sandbox 内执行动作的 agent 运行时。 在形态上,V0 类似于托管 agent 的分隔:循环和事件流位于执行 shell、浏览器、文件和工具动作的环境之外。但动机不同。OpenHands 使用那个边界是因为开放的编码 agent 研究需要跨多台机器进行安全且可重现的任务执行。Claude Managed Agents 则是在托管产品中使用类似的分隔,其中长时间运行的 session、凭证、harness 和执行环境需要不同的生命周期。

Sandbox 是评估、训练和故障重现的正确原语。问题是让这个原语成为每个产品用户首先要处理的东西。

为什么默认设置必须改变

一个新开源用户通常首先想要一个简单的东西:将 agent 指向一个仓库,让它编辑文件、运行测试,然后看看是否有帮助。如果在第一次有用的动作之前,Docker 设置就失败了,那么架构已经泄漏到了用户体验中。

托管部署暴露了另一个成本。在我们的 V0 托管部署中,远程动作可能跨越几个额外的边界失败:就绪检查、服务间认证、超时、事件流和生命周期协调。Sandbox 可能崩溃。Kubernetes 可能在容器内的动作执行服务器准备就绪之前就将容器进程标记为运行状态。流可能在动作之后、观察结果协调之前断开。

V0 的教训不是移除 sandbox,而是停止让 sandbox 成为每个用户必须配置的第一件事。

V1 的转变:从本地到云端的一条路径

在 OpenHands SDK 论文 中,我们将 V1 呈现为围绕本地到远程执行可移植性的重新设计。V1 保留了 V0 的教训,即执行需要一个明确的边界,但将稳定契约向上移动了一层:从 sandbox/运行时边界移到了 SDK。

OpenHands V1 将 Conversation 作为稳定的单元。核心职责是:

  1. Conversation 运行一个 agent session。
  2. EventLog 和 ConversationState 记录消息、动作、观察结果、执行状态和运行时元数据。
  3. Workspace 决定文件、shell、浏览器和工具副作用发生的位置。
  4. Agent、Tool 和 LLM 规范描述了运行的可移植配置。

用户代码创建一个 Agent,为其提供工具和 LLM,将其绑定到一个 Workspace,发送一条消息,然后运行这个对话。

OpenHands V1 SDK 幻灯片,展示了用户代码定义可序列化的 Agent、LLM 和 Tool 规范,Conversation 将它们实例化为运行中的 session,LocalWorkspace 作为工具动作边界,ConversationState 是唯一有状态的组件。 该图区分了两个边界:ConversationState / EventLog 持有真相源记录;配置了持久化后,该记录可以在进程重启后存活。Workspace 决定文件、shell、浏览器和工具副作用发生的位置。 如果没有一条从本地到远程再到云端的路径,三个成本会迅速显现。如果本地 CLI、OpenHands Cloud 和企业自托管部署各自发展出自己的 agent 循环,那么项目就必须让多个代码库在语义上保持一致。

第一个问题是行为兼容性。一个工具可能在本地上工作,但在云端因为工作区语义不同而失败。事件格式可能漂移。恢复行为可能分歧。秘密处理可能遵循微妙不同的规则。然后用户会遇到最令人沮丧的 agent 错误类型:“它在一个环境中能用,但我无法在另一个环境中重现它。”

第二个问题是维护。每个新的工具、事件类型、回调、权限规则、模型配置、秘密机制和可观测性钩子都必须在多个路径中实现和测试。短期的纪律可以隐藏这个成本。长期的产品开发通常会暴露它。

第三个问题是调试。Agent 系统在正常路径崩溃时最难处理。如果本地、远程和云端部署共享相同的 Conversation、Workspace 和 EventLog 契约,那么故障就可以通过一个抽象来追踪。如果每个部署目标都有自己的循环,调试就会变成一个跨代码库的考古项目。

出于这个原因,OpenHands V1 将 SDK 作为 agent 核心。部署可以改变。Agent 路径不应改变。

这本身并不是一个可靠性保证。它给项目提供了一个地方来实现持久化事件日志、动作/观察语义、工作区重载行为、负载处理和关联遥测。协议和运维工作仍然必须完成。

Agent-server 和控制平面的位置

OpenHands V1 围绕那个 SDK 路径组织成一组小包:openhands.sdk 用于核心抽象,openhands.tools 用于工具实现,openhands.workspace 用于工作区实现,以及 openhands.agent_server 用于在 API 后端运行对话。

OpenHands V1 包图,展示了 OpenHands GUI、CLI 和自定义客户端使用 openhands.sdk、openhands.tools、openhands.workspace 和 openhands.agent_server。 OpenHands V1 包结构。SDK 定义了 agent 路径;工具和工作区扩展它;agent-server 通过 REST 和 WebSocket 暴露相同的路径以进行远程执行。 远程 OpenHands 部署仍然运行 SDK 对话路径。SDK 程序成为它所绑定工作区的客户端。在本地工作区中,Conversation 操作可以就地执行,针对本地文件系统和 shell。对于远程工作区,包括 DockerWorkspace,启动工作区也会启动或连接到一个运行 OpenHands agent-server 的运行时。

从用户代码来看,契约保持不变:创建一个 Conversation,发送一条消息,运行它,并读取事件。在内部,远程工作区将这些交互转换为对 agent-server 的 REST 调用和 WebSocket 流。服务器在该运行时内实例化 SDK 对话,执行文件、shell、浏览器和工具副作用,并将事件、状态和状态更新流式传输回客户端。

OpenHands V1 SDK 幻灯片,展示了相同的 Conversation API 从本地工作区移动到 DockerWorkspace,其中 agent-server 容器暴露 REST API 和 socket,接收序列化的规范,实例化工具执行器,并将事件和状态更新流式传输回来。 注意当执行移动到远程时什么保持不变:客户端仍然使用 Conversation,而远程端运行 agent-server,实例化工具执行器,并将事件流式传输回来。 实现证据也指向同一个方向。OpenHands 应用在 pyproject.toml 中依赖 SDK、tools 和 agent-server 包(参见 这里),而 Cloud chart 设置了 RUNTIME=remote(参见 这里),同时使用 agent-server(参见 这里)运行时镜像。

所引用的 Cloud/远程配置使用的是相同的 SDK 加上 agent-server 路径,而不是展示一个独立的 agent 实现。运维者和基础设施边界不同,但 agent 路径不必分叉。

对于 OpenHands 运行的对话,OpenHands Agent Control Plane 位于该 SDK 定义的路径周围,用于处理生产问题,例如模型路由、MCP 访问、秘密、预算、认证、审计和策略。

拓扑结构不够用的地方

Anthropic Managed Agents 博客 从一个具体的生产故事开始。在他们早期的设计中,session、harness 和 sandbox 共享一个容器。本地文件编辑是直接的系统调用,但一个不健康的容器可能拖垮 session,而 WebSocket 流无法揭示故障来自 harness、网络还是执行环境。

博文的修复方案是将系统拆分为独立的接口:一个持久化的 session 日志、一个可重启的 harness,以及可替换的执行环境或工具。我同意这些目标。

分歧在于这种拓扑结构是否是必需的。 OpenHands V1 通过一个 SDK 加工作区边界追求相同的目标,但它仍然必须实现底层的持久化、恢复和遥测契约。

持久化状态

持久化是一个存储生命周期属性,而不是拓扑结构属性。 在托管 agent 博文描述的原始单容器设计中,博文提到“如果一个容器失败了,session 就丢失了”(原文)。我会将其诊断为生命周期不匹配:session 状态与可能失败的进程或容器生命周期绑定在一起。

一个 Kubernetes 容器可能死亡,而它的状态却存活下来。如果对话日志、bash 历史、仓库检出和工作区文件位于 PersistentVolume 或持久化存储中,那么重启的容器可以重新加载它们。OpenHands 企业自托管就使用这种模式:每个 sandbox 的持久化存储,对话和 bash 事件状态写入 /workspace 下(参见 这里)。

具体的规则很简单:将会话日志和工作区状态写入某个地方——

相似文章

@unicodef1wn: https://x.com/unicodef1wn/status/2070179071548395916

X AI KOLs Timeline

一篇推文解释了Anthropic在Claude Code中的动态工作流如何让Claude为复杂任务构建自定义框架,通过将工作拆分到不同的智能体来防止智能体惰性、自我偏好偏差和目标漂移等失败模式。内容包含供用户参考的实用示例和模式。