首页
/ OpenHands Agent Canvas 架构详解:系统边界、运行模式、运行时服务与打包分发机制

OpenHands Agent Canvas 架构详解:系统边界、运行模式、运行时服务与打包分发机制

2026-09-04 09:12:12作者:翟江哲Frasier

本文基于 OpenHands 仓库中的 docs/architecture.md 展开,系统讲解 Agent Canvas 前端的技术定位与架构设计:它负责渲染与状态管理,但不执行 Agent 动作;主后端为 OpenHands Agent Server,可选搭配 ingress 代理、Automation Server 与 Cloud API。读完本文,你将理解 Agent Canvas 的模块划分、六种运行模式的落地实现(端口、进程与路由)、/server_info.runtime_services 的运行期服务发现机制,以及 npm 包的导出结构与 CI 质量门禁,从而能独立部署、二次开发或以库形式嵌入宿主应用。

一、系统边界:Agent Canvas 的职责与不做什么

Agent Canvas 是一个 React + TypeScript 前端,用于在本地、远程和托管环境中运行并监控 OpenHands Agent。它由 OpenHands 前端适配而来,直接与 OpenHands Agent Server 及相关自动化服务通信(docs/architecture.md)。

从架构文档看,它的职责边界是明确的:

  • 渲染 Agent 会话、终端、浏览器、文件、设置与自动化(automation)界面;
  • 管理会话、后端选择、设置、profile 与本地元数据等前端状态;
  • 将 UI 操作翻译成 OpenHands Agent Server 的 API 调用;
  • 同时以独立应用和库入口(library entrypoints)两种形态打包,供宿主应用嵌入。

它的非职责同样重要,这决定了部署时的信任模型:

  • 不直接执行 Agent 动作(执行发生在 Agent Server 端);
  • 不提供沙箱或工作区隔离层;
  • 不在配置的后端之外托管 LLM 提供商凭据;
  • 没有 automation backend 时不运行定时或事件触发的自动化。

这个"前端只做翻译与呈现"的边界,在源码中对应一套后端注册与切换机制:src/api/backend-registry/types.ts 定义了 Backend 结构,其中 kind 区分 localcloudauthMode 支持 api-keycookie 两种鉴权方式,并带有 connectionRevision 字段——凭据变化时使其失效以刷新按后端隔离的缓存数据。这正是文档中"可连接一个或多个 Agent Server 实例并从 UI 切换"的实现基础。

二、运行时服务:Agent Server、Ingress 与运行期服务发现

2.1 服务拓扑

主后端是 OpenHands Agent Server(来自 OpenHands software-agent-sdk 仓库的 openhands-agent-server 包)。可选的运行时服务包括:

  • Ingress 服务:将前端、Agent Server 与自动化流量收敛到同一个本地 origin 之后;
  • Automation Server:负责定时或事件触发的 Agent 运行;
  • OpenHands Cloud API:托管沙箱与组织级工作流。

各服务的端口与版本在 config/defaults.json 中集中管理,该文件被 npm 启动脚本、Docker entrypoint 与 CI 工作流共同读取,是整个栈的"单一事实来源":

服务 默认端口 版本
Agent Server(openhands-agent-server 18000 1.44.0
Automation Backend(openhands-automation 18001 1.9.0
Ingress 代理(proxy) 8000
容器内 VS Code(openvscode-server) 8001(容器内)

npm run dev 模式下的完整拓扑在 scripts/dev-with-automation.mjs 头部注释中有 ASCII 架构图:所有流量先进入 http://localhost:8000 的 ingress 代理,其中 /api/automation/* 转发到 Automation Backend(18001),/api/*/sockets 转发到 Agent Server(18000),其余路径转发到 Vite 开发服务器(3001)。Agent Server 与 Automation Backend 均通过 uvx 直接从 git 引用启动,无需克隆。

2.2 Ingress:单 origin 反向代理的实现

Ingress 是一个独立的 HTTP 反向代理,实现在 scripts/ingress.mjs,与任何后端实现完全解耦。其关键行为:

  • 路由按最长前缀优先匹配,因此 /api/automation 会优先于 /api(这是拓扑正确的关键);
  • 支持 CLI 参数(--port--route "/path=http://host:port"--default)与环境变量(INGRESS_PORTINGRESS_ROUTESINGRESS_DEFAULT)两种配置方式;
  • 支持 --no-referrer-prefix,对 URL 查询串中携带凭据的上游发送 Referrer-Policy: no-referrer 响应头,避免凭据经 Referrer 泄漏;
  • 通过 --runtime-services-info / INGRESS_RUNTIME_SERVICES_INFO 将运行时服务 JSON 附加到 /server_info 端点。

2.3 运行期服务发现:让 Agent 不再"猜端口"

架构文档中一个容易被忽略但非常实用的机制是:前端把后端提供的运行时服务信息作为 Agent 上下文后缀转发进新会话,使 Agent 能使用正确 URL 而不是自行探测端口。

该机制的单一实现是 scripts/runtime-services-info.mjs,其核心函数 buildRuntimeServicesInfo()

  • Agent 的视角(而非浏览器视角)构造各服务的 URL——即 Agent 在沙箱内应如何 curl/fetch 这些服务。例如 Agent Server 对 Agent 而言始终在回环地址上;
  • 输出结构包含 mode(如 dev:safe)、agent_host_alias(Agent 访问宿主侧服务的主机名)以及 services 对象,后者按需包含 agent_serveringressfrontendautomation 四段,每段带 descriptionurl_from_agent
  • 对 automation 服务,额外暴露 api_prefix(默认 /api/automation)、docs_urlopenapi_url 与鉴权环境变量名(默认 OPENHANDS_AUTOMATION_API_KEY,对应 X-Session-API-Key 请求头);
  • 快速失败设计:若既未提供 agentServerUrl 也未提供 agentServerPort,直接抛错——因为那样会在 Agent 的系统提示词中写入 http://localhost:undefined,比启动失败更糟。

这个模块有两个调用方,体现了它在本地与容器两种形态中的复用:

  1. 开发启动器scripts/dev-*.mjs)以端口集合的方式调用它,结果交给 ingress 或静态服务器挂到 /server_info
  2. Docker 镜像docker/entrypoint.sh)在容器启动时以 CLI 方式运行该脚本——因为容器内的 URL 是运行期配置(端口与 base URL 可在 docker run 时覆盖),无法在构建期写死进镜像,打印的 JSON 再传给静态服务器经 /server_info 暴露。

前端随后读取该后端提供的信息,渲染为 AgentContext 的系统消息后缀(一个 <RUNTIME_SERVICES> 块),Agent 无需探测即可获得可用服务清单。相关行为在 tests/scripts/runtime-services-info.test.tstests/scripts/ingress.test.ts 中有测试覆盖。

三、前端模块结构

架构文档列出的核心源码区域与实际仓库目录一一对应:

目录 职责
src/api/ Agent Server、cloud、设置、git、skills、自动化与后端注册行为的服务适配器
src/components/ 路由与特性 UI:会话、聊天、浏览器、文件、设置、后端、自动化、onboarding
src/hooks/ 可复用的 React Query、状态与特性 hooks
src/stores/ 会话与 UI 状态的 Zustand 存储
src/i18n/ 翻译资源与生成的 bundle(由 make-i18n 脚本从 src/i18n/translation.json 生成,并有 scripts/check-translation-completeness.cjs 校验完整性)
src/mocks/ MSW 处理器,用于 mock 模式开发与测试(worker 目录配置在 package.jsonmsw.workerDirectory 中,指向 public/
bin/scripts/ CLI 与开发栈启动器

从源码结构看,src/api/ 下按服务名分子目录组织(如 backend-registry/automation-service/conversation-service/cloud/mcp-service/ 等),每个服务适配器配套独立的 __tests__/api/ 测试;src/hooks/ 再细分为 query/mutation/ 两类 React Query hook。peerDependencies 声明了 React 19、react-router 7 等宿主必须提供的依赖,说明库形态下这些依赖由宿主应用统一管理。

四、运行模式:从 npm run dev 到库构建

架构文档给出的六种模式表完整如下,右列结合 package.json 的 scripts 与 scripts/ 下的启动器给出落地实现:

模式 用途
npm run dev 直接在宿主机上启动完整本地栈:通过 uvx 启动 agent-server 与 automation backend,加 Vite 开发服务器与 ingress 代理。Agent 可访问宿主文件系统;仅建议在可信环境中使用
npm run dev:minimal 只启动 agent-server 加 Vite 开发服务器,不带 automation backend
npm run dev:static dev 相同,但用生产构建的前端替代 Vite 开发服务器
npm run dev:mock 前端跑在 MSW mock 之上,用于 UI 开发与测试
npm run build 构建独立应用(即 build:app
npm run build:lib 构建用于嵌入 Agent Canvas 组件的库入口

package.json 的对应关系:

  • devscripts/dev-with-automation.mjs(自动读取可选 .env);
  • dev:minimalscripts/dev-safe.mjs,它是"安全"的最小栈:只跑 agent-server 与前端,并复用其中导出的 buildAgentServerCommandassertPortsFreebuildRuntimeServicesInfo 等工具函数(dev-with-automation.mjs 直接 import 这些函数);
  • dev:staticscripts/dev-static.mjs,serve 生产构建;
  • dev:mockcross-env VITE_MOCK_API=true react-router dev,并先执行 make-i18n
  • build:libBUILD_LIB=true 环境变量触发 vite 的库构建分支,再用 tsconfig.lib.json 产出类型声明。

dev 模式还有几个值得注意的运维细节(均来自 scripts/dev-with-automation.mjs 的头部注释):

  • 环境变量 OH_AUTOMATION_GIT_REF 可切换 automation 的 git 引用(默认 main);OH_AGENT_SERVER_LOCAL_PATH 指向本地 software-agent-sdk checkout 时优先级最高,会从本地源码以 editable 方式安装 SDK 包,源码修改即时生效;
  • 会话 API key 会自动种入 agent-server 的 secrets(键名 OPENHANDS_AUTOMATION_API_KEY),agent-server 与 automation backend 使用同一个 key、同一个 X-Session-API-Key 请求头鉴权;AUTOMATION_KV_SECRET 未显式设置时从会话 key 派生,KV 存储在本地开发开箱即用;
  • 版本 pin(如 config/defaults.jsonconstraints.agentClientProtocol = "agent-client-protocol<0.11")在注释中记录了 pin 的原因——上游 ACP 0.11 重排了 prompt() 参数导致 SDK 校验失败,属于有明确依据的临时上界约束。

dev:mock 模式依赖 src/mocks/ 下按领域拆分的 MSW 处理器(会话、设置、secret、MCP、工作区等),mock worker 的启动判定见 src/mocks/should-start-mock-worker.ts

五、打包与分发:@openhands/agent-canvas npm 包

npm 包名为 @openhands/agent-canvas(当前仓库版本 1.16.0,要求 Node >= 22.12.0)。package.jsonfiles 字段声明打包内容为 distbinbuildconfigscriptstools,导出结构包括:

  • agent-canvas 二进制bin/agent-canvas.mjs):用于启动本地栈。postinstall 脚本还会在全局安装后打印启动提示,告知默认入口为 http://localhost:8000
  • 独立应用构建build/,由 react-router build 产出,可用 npm run start 经 sirv 静态服务);
  • 库入口exports 字段),覆盖架构文档所列全部模块:
子路径 产物
. 主入口 dist/index.js(含 .d.ts 与 CJS 版本)
./browser 浏览器组件模块
./conversation 会话组件模块
./files 文件组件模块
./settings 设置组件模块
./sidebar 侧边栏组件模块
./terminal 终端组件模块
./i18n 国际化模块

每个子路径均同时提供 ESM/CJS 双格式与 TypeScript 类型声明,宿主应用可按需只引入所需模块。

带标签(tagged release)的版本通过 Publish to npm GitHub Actions 工作流发布(.github/workflows/npm-publish.yml),使用 npm 可信发布(trusted publishing)并附带 provenance 产物签名,发布链路不依赖长期有效的 npm token。

六、质量门禁:CI 工作流

主 CI 工作流 ci.yml 与架构文档描述的"质量门禁"一一对应,且采用双操作系统测试矩阵(ubuntu-24.04 跑全量检查,windows 跑构建子集,fail-fast: false):

  1. npm ci 安装依赖(Node 固定为 24.15.x 并启用 npm 缓存;注释说明 24.16.0 存在一个 zip 解包回归会导致旧版 Playwright 安装挂起);
  2. npm run lint——其定义为 npm run typecheck && eslint src && prettier --check src/**/*.{ts,tsx},即类型检查、ESLint 与 Prettier 三者合一
  3. npm test——单测与组件测试(Vitest,先执行 make-i18n 生成翻译 bundle);
  4. npm run build——独立应用构建;
  5. npm run build:lib——库构建;
  6. npm pack --dry-run——校验包内容(对应文档中的 "package verification")。

此外,仓库还有多组补充工作流覆盖文档所说的"可选的实时端到端 QA":mock-llm-e2e.ymlmock-llm-docker-e2e.yml 跑 Playwright 的 mock-LLM E2E(对应 test:e2e:mock-llm / test:e2e:mock-llm:docker 脚本),test:e2e:live 则用于对指定 PR 运行实时 E2E。仓库还配置了 Stryker 变异测试(test:mutation* 脚本与 stryker.config.mjs)。

七、安全姿态:本地模式的信任假设

架构文档的安全结论可以概括为一句话:本地运行模式可能让 Agent 访问用户工作区npm run dev 明确给出警告——该模式下 Agent 具有宿主文件系统访问权限,只能在可信环境使用。

对应到仓库中的缓解手段:

  • docs/SELF_HOSTING.md 与 README 提示笔记本场景应使用 Docker 沙箱模式docker/Dockerfile 构建 ghcr.io/openhands/agent-canvas 镜像,docker/entrypoint.sh 在启动时注入运行期服务信息);
  • 自托管部署应采用常规服务器加固实践:鉴权、HTTPS、防火墙规则与审慎的工作区作用域控制;
  • config/defaults.json 的注释可见一个具体的同源权衡:捆绑编辑器(openvscode-server)通过 /vscode 路径前缀挂在 canvas 同一 origin 上(OH_VSCODE_BASE_PATH),这样单 origin 部署无需额外发布端口,但路径前缀只做路由不做隔离——编辑器与 canvas 共享浏览器 localStorage,文档将其标注为已知取舍;
  • ingress 的 --no-referrer-prefix 能力专门用于防止查询串中的凭据经 Referrer 头外泄。

小结

Agent Canvas 的架构核心是清晰的职责切割:前端只做渲染、状态管理与 API 翻译;执行、隔离与凭据托管全部落在 Agent Server 及其后端生态中。理解它的关键在于三个机制:backend registry 让 UI 可切换多个 local/cloud 后端;ingress + /server_info.runtime_services 让浏览器与 Agent 各自获得正确的服务 URL;六种运行模式则覆盖了从可信宿主全栈开发、最小栈、mock 开发到独立应用与库嵌入的完整场景。进一步阅读建议从 docs/architecture.md 出发,结合 docs/DEVELOPMENT.mddocs/SELF_HOSTING.mddocs/TESTING_MATRIX.md 深入开发、部署与测试细节。

登录后查看全文
热门项目推荐
相关项目推荐

项目优选

收起
kernelkernel
deepin linux kernel
C
33
18
ops-transformerops-transformer
本项目是CANN提供的transformer类大模型算子库,实现网络在NPU上加速计算。
C++
1.12 K
2.72 K
ops-nnops-nn
本项目是CANN提供的神经网络类计算算子库,实现网络在NPU上加速计算。
C++
904
1.82 K
docsdocs
暂无描述
Markdown
889
5.78 K
pytorchpytorch
作为 Ascend for PyTorch 社区的核心组件,TorchNPU 是昇腾专为 PyTorch 打造的深度学习适配插件,使 PyTorch 框架能够直接调用昇腾 NPU,为开发者提供昇腾 AI 处理器的超强算力。
Python
854
1.34 K
kernelkernel
openEuler内核是openEuler操作系统的核心,既是系统性能与稳定性的基石,也是连接处理器、设备与服务的桥梁。
C
527
590
jiuwenswarmjiuwenswarm
JiuwenSwarm 是一款基于openJiuwen开发的智能AI Agent,它能够将大语言模型的强大能力,通过你日常使用的各类通讯应用,直接延伸至你的指尖。
Python
3.52 K
1.01 K
ops-mathops-math
本项目是CANN提供的数学类基础计算算子库,实现网络在NPU上加速计算。
C++
1.33 K
1.45 K
AscendNPU-IRAscendNPU-IR
AscendNPU-IR是基于MLIR(Multi-Level Intermediate Representation)构建的,面向昇腾亲和算子编译时使用的中间表示,提供昇腾完备表达能力,通过编译优化提升昇腾AI处理器计算效率,支持通过生态框架使能昇腾AI处理器与深度调优
C++
540
384
flutter_flutterflutter_flutter
本仓库是 Flutter SDK 与 Flutter Engine 的 OpenHarmony 适配版本,由 CPF-Flutter 团队维护。开发者可使用熟悉的 Flutter 技术栈开发 OpenHarmony 应用,3.35.7 及以后的适配版本可基于本仓库源码构建支持 OpenHarmony 的 Flutter Engine。
Dart
1.17 K
341