OpenHands

以 Agent 状态机、事件流与中断恢复为核心的 AI 编程 Agent 生态;2026 年重构为多仓——OpenHands/OpenHands 仅前端(agent-canvas React SPA),Python 运行时在 OpenHands/software-agent-sdk,由 OpenHands/openhands-server 托管。本篇记录三仓分工、运行时真实位置与一条 agent run 生命周期。

#type / resource #status / growing #tech / dev / backend #tech / dev / frontend #resource / python

[!info] related notes

  • 所属 MOC:源码阅读 MOC
  • 同栈并列:Langflow(React+Python 工作流 UI)· Dify 源码阅读
  • BodySense 对照:run / runtime_event / interaction / tool_call / checkpoint / ask_user / resume / thread_projection
  • 架构说明:前端仓库 docs/architecture.md

OpenHands

这是什么

Agent 状态机、事件流、中断恢复 为核心的 AI 编程 Agent 生态。用户想用它补「Agent Runtime 专项」:一个 Agent run 的生命周期、Action/Observation 数据模型、事件如何驱动状态、用户中断后如何恢复、后端事件如何映射到 React UI、runtime 与 agent policy 怎样隔离。

[!warning] 重要:2026 年已重构为多仓,原仓库不再是单体 经典架构(用户引用,对应历史上的 openhands/ Python 包):

Agent
AgentController
State
Action
Observation
EventStream        ← 组件通信的中心
Runtime

这些 Python 核心已经不在 OpenHands/OpenHandsmain 分支里。本次实抓确认:OpenHands/OpenHandsmain 现在是纯前端 agent-canvas(React 19 SPA),全仓只有 src/ 下的 .tsx/.ts没有任何 openhands/ Python 包、没有 pyproject.tomlAll-Hands-AI/OpenHands 是镜像仓,同样已前端化。

Python 运行时真实位置(已查实):

  • OpenHands/software-agent-sdk —— Agent 运行时 SDK(openhands-sdk v1.41.0,Py≥3.12,commit 281843c78094,2026-08-10)。含 openhands-sdk(核心)、openhands-agent-server(FastAPI 托管)、openhands-toolsopenhands-workspace
  • OpenHands/openhands-server —— 进程宿主(openhands_server v0.1.0,Py≥3.12,commit 6ff130c8317b,2025-09-29),对外提供 API 与事件流(SSE)。

所以「读 OpenHands 的 Agent runtime」实际要读 三个仓:前端 UI(OpenHands/OpenHands)+ SDK(software-agent-sdk)+ 宿主(openhands-server)。这本身就是一个值得学的「前端 / 运行时协调 / Python 模型与 Agent」三栈分工范例。

在你三层参考架构里的位置

用户定位:Agent Runtime 备选教材——专学「Agent 状态机 + 事件流 + 中断恢复 UI」。业务是代码执行 agent(sandbox/terminal/browser/repository),复杂度高于 BodySense,当专项教材而非整体模板。其「三仓分离」恰好对应 BodySense 应坚持的分工。

版本快照

分支commit语言/版本角色
OpenHands/OpenHandsmain21f0967c8f7b(2026-08-11)React 19.2.8 + react-router 7.18.2@openhands/agent-canvas v1.12.0前端 agent-canvas SPA + Electron 桌面端
OpenHands/software-agent-sdkmain281843c78094(2026-08-10)Python >=3.12openhands-sdk v1.41.0)Python Agent 运行时 SDK(核心)
OpenHands/openhands-servermain6ff130c8317b(2025-09-29)Python >=3.12openhands-server v0.1.0)进程宿主:API + 事件流(SSE)

三仓分工与连接

OpenHands/OpenHands  (React SPA, agent-canvas)
   └─ ACP client  src/api/agent-server-adapter.ts
        │  HTTP/SSE(Agent Client Protocol)

OpenHands/openhands-server  (Python, openhands_server)
   └─ api.py → event_router.py / event_service.py  (SSE 事件流)
        │  in-process 调用

OpenHands/software-agent-sdk
   ├─ openhands-agent-server  (FastAPI 托管 Agent)
   └─ openhands-sdk/openhands/sdk/   ← Python 运行时核心
        agent/  conversation/  event/  context/  tool/  llm/

经典架构 → SDK 真实模块映射

经典概念SDK 真实位置(software-agent-sdk/openhands-sdk/openhands/sdk/
Agentagent/agent.py(+ agent/acp_agent.py
AgentControllerconversation/goal/controller.py + conversation/goal/runner.py(运行循环)
Stateconversation/state.py
Actionevent/llm_convertible/action.py(+ event/user_action.pyevent/acp_tool_call.py
Observationevent/llm_convertible/observation.py
EventStream(中心)event/(base.py / conversation_state.py / types.py / token.py)+ conversation/event_store.py(持久化)
Runtimecontext/(agent_context.py / memory.py / view/)提供 sandbox/文件系统/workspace 视图;+ 宿主仓 sandbox/

[!note] 没有顶层 runtime/ 包 旧架构里的 Runtime 在 SDK 里不单独成包,而是拆成了「执行上下文 context/」+「宿主仓的 sandbox/(docker 沙箱)」。读的时候把这两者合起来理解成 runtime 即可。

仓库鸟瞰:Python 运行时核心(software-agent-sdk

software-agent-sdk/
├── openhands-sdk/openhands/sdk/        # 核心运行时
│   ├── agent/                          # agent.py / acp_agent.py / abstract.py
│   ├── conversation/
│   │   ├── conversation.py  state.py    # State
│   │   ├── goal/                        # controller.py(控制器)+ runner.py(循环)+ judge.py
│   │   ├── event_store.py               # 事件流持久化
│   │   ├── cancellation.py              # 中断取消
│   │   └── impl/  local_conversation.py / remote_conversation.py
│   ├── event/
│   │   ├── base.py  conversation_state.py  token.py  types.py
│   │   ├── llm_convertible/             # action.py(Action) / observation.py(Observation)/ message.py
│   │   ├── user_action.py  acp_tool_call.py
│   │   └── resume_transcript.py         # 中断恢复:重放事件流
│   ├── context/                         # agent_context.py / memory.py / view/(Runtime 视图)
│   ├── tool/  llm/  critic/  mcp/  skills/  workspace/
│   └── pyproject.toml
├── openhands-agent-server/openhands/agent_server/   # FastAPI 托管
│   ├── api.py  event_router.py  event_service.py  sockets.py  pub_sub.py
│   ├── conversation_router.py  conversation_service.py
│   └── pyproject.toml
└── openhands-tools/  openhands-workspace/  examples/  tests/

宿主仓 OpenHands/openhands-serveropenhands_server/

openhands_server/
├── api.py            # FastAPI 装配
├── event/            # event_router.py / event_service.py(SSE 事件流)
├── sandbox/          # docker 沙箱(Runtime 的执行环境)
├── sandboxed_conversation/   # 托管对话的生命周期
├── user/  services/  utils/  dependency.py  database.py

前端仓 OpenHands/OpenHandssrc/(agent-canvas):api/(ACP client)、stores/types/(action/observation/agent-state)、components/routes/

顶层边界职责

仓 / 目录回答什么问题备注
OpenHands/OpenHands/src/事件如何渲染、对话状态怎么存React SPA + Electron
openhands-server/openhands_server/API 与事件流怎么对前端暴露SSE 是关键通道
software-agent-sdk/.../sdk/agent/一次 run 怎么驱动 LLMAgent 抽象
.../sdk/conversation/goal/run 循环与控制器controller + runner
.../sdk/event/ + conversation/event_store.pyAction/Observation 与事件流通信中心
.../sdk/context/ + 宿主 sandbox/Runtime 视图与执行环境合起来 = 旧 Runtime

分层与依赖方向

flowchart TB
    UI[OpenHands/OpenHands src/ React] -->|ACP/SSE| SRV[openhands-server api.py]
    SRV --> EVT[event_service SSE]
    SRV --> AS[software-agent-sdk agent_server]
    AS --> AGENT[sdk/agent/agent.py Agent.run]
    AGENT --> CTRL[conversation/goal/controller+runner]
    CTRL --> STATE[conversation/state.py]
    CTRL --> ACT[event/llm_convertible/action.py Action]
    ACT --> LLM[(LLM)]
    LLM --> OBS[event/llm_convertible/observation.py Observation]
    OBS --> STREAM[event/ + event_store.py EventStream]
    STREAM -->|SSE| UI

关键入口与调用链

角色路径说明
前端 ACP 适配OpenHands/OpenHands/src/api/agent-server-adapter.ts前后端协议适配
前端事件 storesrc/stores/event-message-store.ts事件流如何进 React
宿主 APIopenhands-server/openhands_server/api.pyFastAPI 装配
宿主事件流openhands-server/openhands_server/event/event_service.pySSE 推事件
Agent 抽象software-agent-sdk/.../sdk/agent/agent.pyAgent.run
运行控制器.../sdk/conversation/goal/controller.py + runner.pyrun 循环
状态.../sdk/conversation/state.pyState
Action / Observation.../sdk/event/llm_convertible/{action,observation}.py数据模型
事件流持久化.../sdk/conversation/event_store.pyEventStream 落盘
中断 / 恢复.../sdk/conversation/cancellation.py / event/resume_transcript.py

阅读路线(聚焦 Agent Runtime)

  1. 前端 src/types/action-type / observation-type / agent-state(数据模型,对应经典架构)—— 但注意这是前端镜像,真实定义在下一条。
  2. software-agent-sdk/.../sdk/event/llm_convertible/{action,observation}.py真实 Action/Observation 来源)
  3. sdk/agent/agent.pyAgent.run 入口)+ sdk/conversation/goal/{controller,runner}.py(运行循环)
  4. sdk/conversation/state.py + event_store.py(State 与 EventStream)
  5. sdk/conversation/cancellation.py + event/resume_transcript.py(中断与恢复)
  6. openhands-server/openhands_server/event/event_service.py(事件如何经 SSE 到前端)
  7. 回到前端 src/stores/event-message-store.ts + src/api/agent-server-adapter.ts(事件如何映射到 React)

深挖一条线:一次 agent run 的生命周期(跨三仓)

选「用户发一条消息 → Agent 跑一轮 → 流式回结果 → 中途可中断/恢复」作为主线。这条线穿过了全部三个仓,正好印证三栈分工。

[前端 OpenHands/OpenHands]
src/components/ 用户提交消息
  → src/api/agent-server-adapter.ts         ACP client 发 HTTP/SSE 请求

        ▼  (Agent Client Protocol over HTTP/SSE)
[宿主 OpenHands/openhands-server]
openhands_server/api.py                       FastAPI 接收
  → event/event_router.py → event_service.py  建立 SSE 事件流通道
        │  in-process 调用

[SDK OpenHands/software-agent-sdk]
openhands-agent-server/.../conversation_service.py   托管对话
  → sdk/agent/agent.py  Agent.run(ctx, input)
        → sdk/conversation/goal/controller.py  进入运行控制器
              → goal/runner.py                  运行循环:取 State
                    → conversation/state.py      读取/更新 State
                    → event/llm_convertible/action.py   构造 Action(调 LLM)
                          → LLM 返回
                    → event/llm_convertible/observation.py  构造 Observation
                    → event/ + conversation/event_store.py  追加进 EventStream(中心)
                          → 每个事件经 SSE 推回前端

        ▼  (SSE 回传)
[前端]
event_service.py → SSE → src/stores/event-message-store.ts → components 增量渲染

中断(用户取消)sdk/conversation/cancellation.py 置取消标志 → goal/runner.py 循环在下一轮检查到 → 跳出 → 写一条取消 Observation 进 EventStream。 恢复(resume)sdk/event/resume_transcript.py 重放 EventStream 中的历史事件 → 重建 conversation/state.py 的 State → 从断点继续,无需重跑全部。

[!tip] 跟这条线时的断点建议

  1. sdk/conversation/goal/runner.py 循环体 —— 看一轮 run 如何被切成 Action→LLM→Observation。
  2. event/llm_convertible/action.py 构造处 —— Action 与 LLM 请求如何对应。
  3. event/event_store.py 追加处 —— EventStream 作为「通信中心」为什么是单一真相源。
  4. openhands-server/.../event_service.py SSE 推送处 —— 后端事件如何变成前端能消费的事件。
  5. 前端 src/stores/event-message-store.ts —— 同一个事件如何驱动 React 重渲染。

自检(能不看代码回答就过关)

  • 一个 Agent run 为什么是「事件序列」而非「一次返回」?(EventStream 是中心)
  • Action 与 Observation 谁产生谁消费?中间隔了什么(LLM)?
  • 中断为什么能「无损」恢复?(EventStream 可重放 → 重建 State)
  • 前端 action-type.tsx 与 SDK action.py 是否同源 schema?(决定前后端契约如何对齐——待验证)

值得偷师 / 不建议照抄

做法评价我的判断
Action / Observation 显式数据模型BodySense 的 runtime_event / interaction 可直接对照
EventStream 作为通信中心(单一真相源)事件驱动 UI 的清晰范式,且天然支持重放/恢复
中断恢复 = 重放事件流重建 State对照 BodySense 的 checkpoint / ask_user / resume
三仓分离:前端 / 运行时协调 / Python 运行时与 BodySense 应坚持的分工同构
SSE 做后端→前端事件流与 BodySense 当前 SSE 方案可对齐
代码执行 agent 的 sandbox/terminal/browser业务远超 BodySense,不当模板
仓库规模与 Electron 双端不抄

我的疑问与待验证

  • Python Agent 运行时在哪? 已查实:在 OpenHands/software-agent-sdk(核心)+ OpenHands/openhands-server(宿主),不在 OpenHands/OpenHandsmain(那里已纯前端化)。
  • 前端 src/types/action-type.tsx 的 Action 模型与 SDK event/llm_convertible/action.py 是否同源生成(共享 schema)?这决定了前后端契约如何对齐,也决定「改一处要不要改两处」。值得读 openhands-agent-server 的 openapi/类型生成确认。
  • software-agent-sdkopenhands-server 两个仓的版本(v1.41.0 vs v0.1.0)演进节奏不一致,宿主如何锁定 SDK 版本?对照 BodySense 的 Go/Python 协同发版。

沉淀出的笔记

相关链接 / 官方入口

入口地址
前端仓https://github.com/OpenHands/OpenHands
Python 运行时 SDK 仓https://github.com/OpenHands/software-agent-sdk
宿主服务仓https://github.com/OpenHands/openhands-server
架构说明(前端仓)https://github.com/OpenHands/OpenHands/blob/main/docs/architecture.md
创建于 2026/8/11 更新于 2026/8/11