跳到主要内容

OpenHands (Agent Canvas) — 架构与原理

30 秒导读: OpenHands 这个仓库(对外叫 Agent Canvas)是一个自托管的编码 agent 控制中心。它自己不实现 agent 的「想—调工具—观察」循环——那部分已经搬到另一个仓库 software-agent-sdk,作为 pip 包 openhands-sdk / openhands-agent-server 被依赖。本仓库做的是编排:开沙箱、克隆代码、装技能与 git hook、把你的一句话拼成一个会话请求,然后 HTTP 转发给沙箱里那台真正干活的 agent-server,再把事件流回前端。

1. 这是什么(零基础也能懂)

一句话定义: OpenHands 是一个控制中心(control center)——你在一个网页界面里发起「让 agent 帮我改这个 repo」,它负责把这件事安全地、可重复地在某个沙箱里跑起来,并能定时/被 Slack、GitHub 触发(自动化)。

解决谁的什么问题。 假设你想让 AI agent 帮你改一个真实代码仓库——它得能跑命令、装依赖、改文件、提 PR。直接在你的笔记本上放手让它跑很危险(它有你整个文件系统的权限)。OpenHands 把每个会话关进一个沙箱(通常是 Docker 容器),并提供一套统一界面去管理多个这样的 agent、跨多个「后端」(本地 / 远程 VM / 云)切换。

它能做什么:

  • 在沙箱里起一个编码会话,自动克隆你选的 git 仓库、跑 .openhands/setup.sh、装 pre-commit hook。
  • 跨后端运行:同一个前端,既能连本地容器,也能连远程 / 云上的 agent。
  • 接任何 agent:自带 OpenHands agent,也能用 Claude Code、Codex、Gemini 等任何讲 ACP(Agent-Client Protocol) 的 agent。
  • 自动化:把会话挂到定时任务或 webhook 上(GitHub issue 自动拆任务、生成报告发 Slack 等)。

用起来什么样(取自 README.md 的 Quickstart):

npm install -g @openhands/agent-canvas
agent-canvas # 起整套本地栈:前端 + agent-server + 自动化后端

一句话直觉 / 类比。 把它想成编码 agent 的「机场塔台」:塔台自己不开飞机(不跑 agent 循环),它负责调度跑道(沙箱)、放行航班(会话)、对接地勤(git / 技能 / 密钥)。真正开飞机的飞行员住在沙箱里,叫 agent-server(来自 openhands-sdk)。

本节不碰代码细节。 记住一个反直觉的事实就够了:这个仓库里没有 agent 的主循环。 见下一节。

2. 一个关键事实:agent 循环不在这个仓库里

这是读这份源码最容易踩的坑,先说清楚。

README.md 顶部有一条迁移说明:agent 与 agent-server 的源码已搬到 OpenHands/software-agent-sdk,Agent Canvas 的源码搬到 OpenHands/agent-canvas。在本仓库里,它们以固定版本的 pip 依赖出现:

# pyproject.toml(节选)
"openhands-agent-server==1.29.0",
"openhands-sdk==1.29.0",
"openhands-tools==1.29.0",

所以仓库里凡是 from openhands.sdk import ...from openhands.agent_server import ... 的,都是外部包,不在 openhands/ 目录下(openhands/sdk 这个目录在本 clone 里根本不存在)。

本仓库真正的代码是 openhands/ 下的这几块:

目录是什么
openhands/app_server/核心:FastAPI 应用服务器(控制中心本体)
openhands/server/旧的 server 入口 / 中间件(app.pylisten.py)
openhands/db/数据库相关
openhands/analytics/分析
skills/公共技能库(Markdown 微 agent 提示)
frontend/前端(Agent Canvas UI)
enterprise/企业版扩展

结论:这份文档讲的是「控制中心」这一层(openhands/app_server)——会话编排、沙箱管理、事件转发、可插拔扩展。agent 怎么思考、怎么用工具,属于 software-agent-sdk,不在本仓库范围。

3. 阅读地图(建议顺序)

按「由浅入深」读:

  1. 01-top-level.md — 顶层全景。 先建立三层心智模型(控制中心 ↔ 沙箱 ↔ agent-server),看一条会话主线怎么端到端流动。先读这章。
  2. 02-conversation-lifecycle.md — 会话生命周期。 主线的细节:从「等沙箱起来」到「POST 给 agent-server」一步步是怎么走的(这是整个仓库工程含量最高的一段)。
  3. 03-sandbox.md — 沙箱。 安全边界:Docker 容器怎么造、端口怎么暴露、session_api_key 怎么当门票、空闲容器怎么回收。
  4. 04-extensibility.md — 可插拔。 三个让它「换后端不改代码」的机制:Injector 依赖注入、EventCallbackProcessor 事件回调(自动化的底座)、多源技能加载。

4. 代码地图(导航索引)

主题文件关键符号
FastAPI 应用装配openhands/app_server/app.pyappcombine_lifespans
全部 v1 路由挂载openhands/app_server/v1_router.pyrouter(prefix='/api/v1')
会话编排主线openhands/app_server/app_conversation/live_status_app_conversation_service.pyLiveStatusAppConversationService._start_app_conversation
会话基类(git / 技能 / 安全)openhands/app_server/app_conversation/app_conversation_service_base.pyAppConversationServiceBase
沙箱抽象openhands/app_server/sandbox/sandbox_service.pySandboxServicewait_for_sandbox_running
Docker 沙箱实现openhands/app_server/sandbox/docker_sandbox_service.pyDockerSandboxService
依赖注入openhands/app_server/services/injector.pyInjector
事件回调(自动化)openhands/app_server/event_callback/event_callback_models.pyEventCallbackProcessor
多源技能加载openhands/app_server/app_conversation/skill_loader.pyload_skills_from_agent_serverbuild_org_configs
SDK 是外部依赖(版本锁)pyproject.tomlopenhands-sdk==1.29.0