Day 04 / 共 20 天 · 第 1 周 核心概念

运行一个 OpenHands

前三天(Day 02/03)都在讲"事件/动作"这些概念,今天把它们落地——让 OpenHands 真的跑起来。讲三种启动方式,并读 config.template.toml 的关键配置:你会发现前几天学的"动作全家福、安全评估",其实都由这里的配置旋钮开关;把配置认全了,明天(Day 05)的完整旅程就能对号入座。

📍 第 1 周 核心概念 · 你在这里
Day1 全景 Day2 Action/Obs Day3 事件流 Day4 运行 Day5 完整旅程
L01

三种运行方式

OpenHands(Agent Canvas 形态)有三种跑法,从易到难:

方式命令适合谁
npm 直接跑(无沙箱)npm i -g @openhands/agent-canvas && agent-canvas快速体验(AI 有完整文件权限,有风险警告)
Docker(推荐)docker run ... ghcr.io/openhands/agent-canvas普通用户,带沙箱隔离
从源码make build && make run开发者、想读/改源码的你
先搞清楚"两个进程" 回忆 Day 01:OpenHands V1 是双进程——① app_server(本仓,编排/网关,第2周主角);② agent-server(跑在沙箱容器里,真正执行 Agent 循环)。你启动时,app_server 先起来,等你下任务时它再去拉起一个 sandbox 容器(里面装着 agent-server)。理解这个"一个管家 + 按需开工作间"的模型,后面读代码就不会晕。
L02

Docker 方式(推荐,README 原文)

# README.md:88-106
export PROJECTS_PATH="$HOME/projects"     # 你想让 AI 操作的项目目录
docker run -it --rm -p 8000:8000 \
  -v "$HOME/.openhands:/home/openhands/.openhands" \   # 持久化配置
  -v "${PROJECTS_PATH}:/projects" \                     # 挂载你的项目进去
  ghcr.io/openhands/agent-canvas:1.0.0-rc.11
# 然后浏览器打开 http://localhost:8000
读法:-p 8000:8000 把界面端口映射出来;两个 -v 挂载让"配置"和"你的项目"在容器内外共享。打开 8000 端口就是 Web 界面,在 Settings 里配大模型和 API Key(V1 在 UI 里配,不用手写配置文件),然后就能下任务了。
关于大模型 API Key:OpenHands 自己不含大模型,你得"自带模型"(bring your own model)——填你的 OpenAI/Anthropic/等 API Key。它用 litellmpyproject.toml:55)统一对接上百种模型,所以几乎什么模型都能接(Day 14 讲 LLM 抽象)。
L03

从源码 make run(开发者路线)

# Development.md / AGENTS.md
make build          # 一键构建前后端
make run            # 同时跑前端(3001)和后端(3000)
# 或分开:
make start-backend  # 后端,端口 3000(等价 uvicorn openhands.app_server.app:app)
make start-frontend # 前端,端口 3001

# 用 OpenHands 开发 OpenHands 自己(本地 runtime、不装 docker):
export INSTALL_DOCKER=0
export RUNTIME=local
make build && make run
读法:后端真正的入口是 openhands.app_server.app:app(一个 FastAPI 应用)。openhands/server/ 目录现在只是"向后兼容的转发壳"(server/app.py 只 re-export app_server.app),真正实现全在 app_server——第 2 周我们就读它。
RUNTIME 环境变量很关键:它决定沙箱用什么实现——docker(默认,Docker 容器)、local/process(本地进程,开发用)、remote(远程)。这一个环境变量就切换了整个沙箱后端——第 2 周 Day 09 会看到 config.py 里正是靠它选择不同的 SandboxServiceInjector。
L04

配置文件总览 config.template.toml

🤔 对话:配置文件到底管什么? 👶 小白:一个 Agent 不就是"连上大模型然后干活"吗,为什么还要一大堆配置?
👨‍🏫 老师:因为"能连大模型"只是第一步。它能不能跑命令、能不能上网、在什么环境里跑、最多花多少钱、危险动作要不要先问你——这些都得有人拍板。config.toml 就是那张"拍板清单"。
👶 小白:那我不写配置行不行?
👨‍🏫 老师:行,模板里每项都有默认值。config.toml 只是把你想改的项覆盖掉默认值而已。
💡 本质:配置合并 = 公司报销制度config 就像生活中的公司报销制度:先有一套"全公司默认规则"(模板默认值),部门可以覆盖几条([agent] 等分段),个人还能特批(你在 config.toml 里改的项)。最终生效的是"层层叠加后的结果"。所以读配置就是读"这家公司到底给这个 Agent 批了哪些权限、设了哪些上限"。

headless/CLI 模式用 config.toml(从 config.template.toml 拷贝改)。它是 TOML 格式,分成若干 [段]

配置段管什么
[core]运行时选择、默认 Agent、最大步数/预算、并发数
[agent]Agent 能用哪些工具(浏览/编辑/命令/思考…开关)
[sandbox]沙箱镜像、超时、挂载卷、GPU
[security]confirmation mode、安全分析器
[condenser]历史压缩策略(Day 03 提过)
[mcp]/[kubernetes]MCP 外部工具接入 / K8s 部署
配置文件为什么值得单独学? 因为读配置文件是理解一个系统能力边界最快的方式。看 [agent] 段就知道 Agent 能发哪些动作;看 [sandbox] 就知道它在什么环境跑;看 [security] 就知道有哪些安全闸。配置项 = 系统旋钮,把旋钮认全了,系统的骨架也就清楚了。
L05

[core]:核心配置

[core]
runtime = "docker"                # 沙箱后端:docker/local/remote
default_agent = "CodeActAgent"    # 默认用哪个 Agent(Day 15 讲 CodeAct)
max_iterations = 500              # 一个任务最多循环多少步(防失控)
max_budget_per_task = 0           # 每个任务最多花多少钱(0=无限)
enable_browser = true             # 是否允许浏览器动作
max_concurrent_conversations = 3  # 最多同时几个会话
读法:max_iterationsmax_budget_per_task 是两条"安全带"——防止 Agent 陷入死循环无限烧钱。循环步数超过 500 就强制停;花费超过预算也停。default_agent="CodeActAgent" 指定了默认的 Agent 类型,它是 OpenHands 的招牌(Day 15 精读)。
为什么要限制步数和预算? Agent 自主循环最怕"停不下来"——它可能反复尝试同一个错误方案,每一步都在调大模型(花钱)。max_iterations/max_budget 是硬性熔断。这呼应了我们在 eino 教程里反复讲的"成本经济学"——能自主行动的 AI,必须配硬性的花费上限。
💥 没有它会出什么事故:假设不设上限,某次 Agent 卡在一个改不对的 bug 上反复"改代码→跑测试→失败→再改",每一轮调一次大模型(比如每轮 ¥0.5、每轮几秒)。它可能这样空转成百上千轮——一觉醒来任务没做完,账单先烧了几百块。max_iterations=500 和预算上限就是那道熔断闸。顺带体会 500 这个量级:一个正常任务通常几十步就收工,500 是"明显不对劲了"的红线。沿用报销制度的比喻,max_budget_per_task 就像"单次报销封顶额"——超了系统直接拒付,不问缘由。
L06

[agent]:工具开关决定 Agent 的能力

[agent]
enable_cmd = true       # 能跑 bash 命令(ExecuteBashAction)
enable_editor = true    # 能编辑文件(FileEditorAction)
enable_browsing = true  # 能浏览网页(Browser*Action)
enable_jupyter = true   # 能跑 Jupyter
enable_think = true     # 能记录思考(ThinkAction)
enable_finish = true    # 能结束任务(FinishAction)
📝 举个例子enable_browsing = false 写进配置 → 会话开始的 SystemPromptEvent 里就不声明浏览器工具 → 大模型压根不知道自己能上网 → 它永远不会产出 BrowserAction。一个开关,等于给 Agent"少发一只手"。
[agent] enable_cmd=true SystemPrompt tools 声明 bash 大模型决策 ExecuteBash 沙箱执行 → Observation
图注:一个配置旋钮,贯穿到动作真正执行(Day02+03+04 串起来)
读法:每个开关直接对应 Day 02 学的一类动作。关掉 enable_browsing,Agent 就发不出浏览器动作、系统提示里也不会声明浏览器工具——它就"失去了上网这只手"。这些开关和 Day 03 的 SystemPromptEvent.tools 是一回事:开关决定了会话开头告诉大模型"你有哪些工具"。
串起来看(Day 02+03+04) 配置 enable_cmd=true → 会话开始时 SystemPromptEvent 里就包含 bash 工具的定义 → 大模型知道自己能跑命令 → 于是它可能产出 ExecuteBashAction → 沙箱执行 → 返回 ExecuteBashObservation。一条链,从配置旋钮一直贯穿到动作执行。前三天的概念在这里全串起来了。还能用 [agent.CustomAgent] + classpath 注册你自己写的 Agent。
L07

[sandbox] 与 [security]

[sandbox]
base_container_image = "nikolaik/python-nodejs:..."  # 沙箱基础镜像
timeout = 120                     # 命令超时
volumes = "/host/path:/container/path"  # 把宿主目录挂进沙箱

[security]
confirmation_mode = false         # 危险动作执行前是否要人工确认
security_analyzer = "llm"          # 用什么评估动作风险(默认用 LLM)
读法:[sandbox] 定义"AI 的工作间长什么样"——用哪个 Docker 镜像(决定里面有 python/node 等什么工具)、命令超时、挂载哪些目录。[security] 则是安全闸:confirmation_mode=true 时,高危动作(还记得 Day 03 的 security_risk 吗?)执行前会暂停等你点头。
security_analyzer="llm":用大模型来判断"这个动作危不危险"(对应 Day 03 ActionEvent 的 security_risk)。配合 confirmation_mode,就实现了"AI 自评风险 + 高危人工确认"的完整安全链(Day 17 深入)。沙箱隔离(物理隔离)+ confirmation(人工闸)= OpenHands 的双重安全带。
L08

今日小结 + 动手

🧠 今天你应该能回答

  • 三种运行方式?"双进程"(app_server + agent-server)是怎么回事?
  • RUNTIME 环境变量控制什么?
  • 后端真正的入口是哪个(app_server.app),server 目录现在是什么?
  • [agent] 的开关如何决定 Agent 的能力?(串起 Day 02/03)
  • 两条"安全带"配置?双重安全机制?

✋ 动手:读配置模板

# 1. 通读配置结构
grep -nE '^\[' config.template.toml    # 列出所有配置段

# 2. 核心段
sed -n '/\[core\]/,/\[/p' config.template.toml | head -40
sed -n '/\[agent\]/,/\[/p' config.template.toml | head -30

# 3. 确认 server 只是转发壳
cat openhands/server/app.py
明天预告 · Day 05(第1周收官):把前四天串成一条完整故事线——一次任务的完整旅程:你下任务 → app_server 建会话/沙箱 → agent-server 跑 Action/Observation 循环 → 事件 webhook 回推 + 前端 WebSocket 实时展示 → 任务完成。看数据怎么在双进程间流动。
← Day 03 事件流 Day 05 · 完整旅程 →