OpenHands Agent Canvas 在 Windows 上的 Docker 部署实战:PowerShell 版快速上手指南
本文基于仓库中的 Windows 快速上手文档 README.windows.md 展开,面向 Windows 开发者讲清楚如何用 Docker Desktop 一键拉起 OpenHands Agent Canvas(AI 编码智能体控制台)的完整沙箱环境。读完本篇,你将掌握 PowerShell 下拉取并运行 agent-canvas 镜像的全套命令、两个持久化卷(~/.openhands 与 PROJECTS_PATH)的映射原理,以及容器内部三个服务如何通过统一入口端口 8000 暴露为 /canvas 页面的底层机制。
这份文档在整个安装体系中处于什么位置
OpenHands 仓库的 README.md 给出了三种运行 Agent Canvas 的选项:
- Option 1(无沙箱):
npm install -g @openhands/agent-canvas后直接运行agent-canvas,agent-server 直接跑在宿主机上,agent 将获得对宿主文件系统的完整访问权限; - Option 2(Docker 沙箱):使用
ghcr.io/openhands/agent-canvas镜像,将 agent 关进容器,仅通过显式挂载的卷访问宿主文件; - Option 3(从源码运行):克隆仓库后
npm install && npm run dev,适合开发者。
README.md 的 Option 2 一节只给出了 macOS/Linux 的 shell 语法,并明确将 Windows 用户指向本文档:
Windows (PowerShell / Windows Terminal): See README.windows.md for the equivalent commands.
因此 README.windows.md 的定位非常聚焦:它不是新的功能说明,而是 Option 2(Docker 沙箱模式)在 Windows 上的等价命令写法,解决的是 PowerShell 与 Bash 在环境变量语法、路径拼接、续行符上的差异。
运行前提
文档列出的两个前置条件,在动手前务必确认:
- Docker Desktop for Windows:本文档的命令依赖 Docker 的卷挂载与端口转发能力,Docker Desktop 需在后台运行(Linux 子系统或 WSL2 后端均可,命令写法不受影响)。
- 宿主项目目录:一个用于
PROJECTS_PATH的宿主目录,里面存放你希望 agent 可以访问的项目文件夹。该目录必须在启动容器之前创建——这是文档中强调的时序要求,因为容器通过-v将其绑定挂载到容器内固定的/projects路径。
PowerShell 完整命令与逐行解析
以下是文档给出的完整操作流程,可直接复制运行:
docker pull ghcr.io/openhands/agent-canvas:1.16.0 # x-release-please-version
$env:PROJECTS_PATH = Join-Path $HOME "projects" # directory containing your project folders
New-Item -ItemType Directory -Force -Path $env:PROJECTS_PATH, (Join-Path $env:USERPROFILE ".openhands") | Out-Null
docker run -it --rm `
-p 8000:8000 `
-v "$($env:USERPROFILE)\.openhands:/home/openhands/.openhands" `
-v "$($env:PROJECTS_PATH):/projects" `
ghcr.io/openhands/agent-canvas:1.16.0 # x-release-please-version
逐行拆解:
| 步骤 | 命令 | 作用 |
|---|---|---|
| 拉取镜像 | docker pull ghcr.io/openhands/agent-canvas:1.16.0 |
下载 all-in-one 镜像。行尾的 # x-release-please-version 是 release-please 工具的占位标记,发版时自动更新镜像标签,当前仓库版本与 package.json 中的 "version": "1.16.0" 一致 |
| 设置项目目录 | $env:PROJECTS_PATH = Join-Path $HOME "projects" |
PowerShell 等价于 Bash 的 export PROJECTS_PATH="$HOME/projects"。默认指向 C:\Users\<you>\projects |
| 预创建目录 | New-Item -ItemType Directory -Force -Path ... |
一次性创建 PROJECTS_PATH 和 ~/.openhands 两个目录,对应下面两个 -v 挂载的宿主侧路径。-Force 使命令在目录已存在时也不会报错 |
| 端口映射 | -p 8000:8000 |
容器内统一入口端口 8000 转发到宿主 8000 |
| 状态卷 | -v "$($env:USERPROFILE)\.openhands:/home/openhands/.openhands" |
持久化卷一:设置、密钥、会话记录、automation 数据库都落在容器内 /home/openhands/.openhands |
| 项目卷 | -v "$($env:PROJECTS_PATH):/projects" |
持久化卷二:宿主的 projects 目录映射为容器内 /projects,agent 只能访问这里的项目 |
| 运行参数 | -it --rm |
交互终端运行,容器退出后自动清理容器(注意:--rm 只清理容器本身,不影响两个绑定挂载卷里的数据) |
两个 Windows 特有的写法值得留意:
- PowerShell 中环境变量赋值用
$env:NAME = ...而非export NAME=...; - 多行命令的续行符是反引号
`(Bash 中是反斜杠\);变量插值则必须写成"$($env:USERPROFILE)\.openhands"这种子表达式形式,直接写$env:USERPROFILE\.openhands会因反斜杠不是 PowerShell 的转义符而导致路径拼接出错。
容器里到底跑了什么:单镜像三服务架构
理解这套命令,最好知道容器内部的结构。docker/Dockerfile 的头部注释写明,这是一个 "all-in-one" 镜像,把三个服务打进同一镜像:
- Agent Server——基于
ghcr.io/openhands/agent-server(上游 SDK 镜像),默认监听容器内 18000 端口,提供运行多个 agent 的 REST API; - Automation Server——通过
uv pip install openhands-automation安装,默认监听 18001 端口,负责按调度或事件触发 agent 任务; - Frontend——Agent Canvas 的静态构建产物,由 Node.js 静态服务器托管,并兼作 ingress 代理,统一暴露在 8000 端口。
这些默认值来自 config/defaults.json(镜像构建时生成 defaults.env 供入口脚本读取):
"ports": {
"agentServer": 18000,
"automation": 18001,
"proxy": 8000,
"vscode": 8001
}
docs/SELF_HOSTING.md 中的部署拓扑图同样印证了这一结构:ingress 代理按前缀路由——/api/automation/* 转发到 automation 后端(18001),/api/*、/sockets 转发到 agent server(18000),其余路径(包括 /canvas)由静态前端接管。docker/entrypoint.sh 中以 static-server.mjs 的 --route 参数注册了这份路由表(如 entrypoint.sh 的 L390-L409),这也是为什么你在浏览器里访问的是 http://localhost:8000/canvas 而不是裸端口。
几个从入口脚本可以看出、对 Windows 用户同样成立的细节:
- 前端挂载路径是
/canvas:镜像构建参数VITE_BASE_PATH默认为/canvas(见 docker/Dockerfile 的 L43-L46),所以 Docker 方式访问 UI 是http://localhost:8000/canvas,而 npm/源码方式(agent-canvas命令)访问的是http://localhost:8000——两个入口地址不同,是 README.md 末尾专门提示过的点; - 密钥自动持久化:docker/entrypoint.sh 会在首次启动时自动生成
OH_SECRET_KEY(设置/密钥加密用)和会话 API Key,并持久化到~/.openhands/agent-canvas/下的secret-key.txt与api-key.txt(见 entrypoint.sh 的 L179-L219)。由于这两个文件位于你挂载的~/.openhands卷内,容器重启后密钥保持不变——这正是第二个卷存在的意义; - automation 数据库默认 SQLite:未提供
AUTOMATION_DB_URL时,入口脚本默认使用~/.openhands/automation/automations.db,文件同样落在持久化卷里,容器可随意重建。
两个持久化卷各自装了什么
Dockerfile 用 VOLUME 指令声明了这两个卷(见 docker/Dockerfile 的 L159-L164),入口脚本中对应的环境变量也指明了落盘结构(见 entrypoint.sh 的 L167-L173):
| 容器内路径 | 内容 | Windows 宿主机默认位置 |
|---|---|---|
/home/openhands/.openhands |
加密密钥、会话 API Key、会话记录(agent-canvas/conversations/)、bash 事件(agent-canvas/bash_events/)、automation SQLite 数据库与运行工作区 |
C:\Users\<you>\.openhands |
/projects |
你授权 agent 读写的实际项目代码 | C:\Users\<you>\projects |
/projects 是前端识别的项目根路径:src/components/features/home/workspace-dropdown/folder-browser-modal.tsx 中硬编码了 const PROJECTS_PATH = "/projects",首页的工作区下拉会把 /projects 作为默认候选目录。也就是说,把仓库克隆或解压到宿主的 projects 目录下,agent 即可通过该路径访问——这与文档末句 "The agent will be able to access any project under PROJECTS_PATH" 完全一致。
验证与后续操作
- 运行
docker run后,容器日志会依次打印Starting agent-server on port 18000...、Starting automation server on port 18001...、Starting frontend + proxy on port 8000...,最后输出All services started. Unified entry point: http://0.0.0.0:8000/(入口脚本以 60 秒超时等待两个后端就绪后才拉起代理)。 - 浏览器打开
http://localhost:8000/canvas即可进入 Agent Canvas UI,之后可以在界面中配置 LLM、添加项目、从 UI 内添加其他后端(本地/远程 Agent Server)。 - 停止服务直接
Ctrl+C或docker stop;--rm保证容器清理,而两个卷里的设置、密钥与会话记录都保留在宿主目录中,下次重新docker run无缝续接。
适用前提与注意事项
- 端口冲突:确认宿主 8000 端口未被占用,否则需要改用
-p 8080:8000之类的外部端口映射(容器内部端口保持 8000 不变)。 - 卷是访问边界:Docker 沙箱模式下 agent 的文件系统访问被限定在
/projects与容器内部环境,这比 README.md 中 Option 1/Option 3 那种"agent 直接获得宿主机完整文件系统权限"的模式安全得多;但相应地,PROJECTS_PATH之外的宿主文件 agent 一律不可见。 - CI 覆盖现状:docs/TESTING_MATRIX.md 明确记录了 Windows 平台"尚未被 CI 覆盖",因此本文档描述的命令是官方给出的等价写法,但如果你在 Windows 上遇到与 macOS/Linux 行为不一致的问题,建议同时对照 README.md 的 macOS/Linux 命令排查(两者语义完全等价,差异只在 shell 语法)。
- 版本:以上命令基于当前仓库的 1.16.0 版本(package.json 与 config/defaults.json 中的
agentCanvas: "1.16.0"),升级镜像标签时注意x-release-please-version标记会随发版自动滚动。
综上,Windows 开发者只需要 Docker Desktop 和一段 PowerShell 命令,即可获得与 macOS/Linux 完全一致的 Agent Canvas Docker 沙箱体验:一个 8000 端口、/canvas 路径的统一入口,背后是 agent server、automation 后端与前端代理三合一的容器化部署,且设置与会话数据通过两个绑定挂载卷在容器生命周期之外持久保存。
atomcodeClaude Code 的开源替代方案。连接任意大模型,编辑代码,运行命令,自动验证 — 全自动执行。用 Rust 构建,极致性能。 | An open-source alternative to Claude Code. Connect any LLM, edit code, run commands, and verify changes — autonomously. Built in Rust for speed. Get StartedRust0623
Hy4-previewHy4 preview 是由腾讯混元团队研发的新一代混合专家(MoE)旗舰模型。模型总参数量 770B,每个 token 激活 49B,主干共包含78层,第一层采用标准 FFN,其余 77 层均为 MoE 结构,每层包含 256 个路由专家与 1 个共享专家,每个 token 激活 top-8 路由专家及共享专家。主干之外原生内置 1 层 MTP(总参数量 10B,激活 0.7B)以支持投机解码。Python00
GLM-5.3GLM-5.3 与 GLM-5.2 使用相同的基座模型——所有提升均来自后训练。与 GLM-5.2 相比,它在复杂编程和长程任务上的表现显著提升。Jinja00
GLM-5.3-FlashGLM-5.3-Flash (320B-A18B),是GLM-5系列的首个原生多模态模型。320B总参数,能力超过GLM-5.2Jinja00
Spark-X2.5-4BSpark-X2.5-4B 旨在让强大的 AI 更实用、更高效、更易获得。在广泛日常任务中表现强劲,涵盖对话、写作、翻译、推理、编码、工具调用以及智能体工作流,并在同等规模的开源模型中取得领先成绩。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00
Spark-X2.5-1.7BSpark-X2.5-1.7B 旨在让强大的 AI 更加实用、高效且易于获取。这些模型在广泛的日常任务中表现出色,涵盖对话、写作、翻译、推理、编程、工具调用和智能体工作流,并在同等规模的开源模型中取得领先结果。Spark-X2.5 将面向效率的架构与最高 1M tokens 的原生上下文窗口相结合,并支持 200 多种语言。Python00