Day 15 / 共 20 天 · 第 3 周收官

CodeAct 范式精读

OpenHands 的招牌思想。默认 Agent 叫 CodeActAgent——它把"写并执行代码"作为核心动作空间。今天讲清 CodeAct 为什么比"一堆固定工具"更强大,收官 Agent 大脑。

📍 第 3 周 Agent 大脑(SDK)· 你在这里
Day11 SDK概念Day12 工具体系Day13 RuntimeDay14 LLM&提示词Day15 CodeAct
L01

CodeAct 是什么

CodeAct = Code as Action(以代码为动作)。核心主张:与其给 Agent 一堆各式各样的固定工具,不如让它"写代码并执行"——因为代码本身就是最通用、最强大的动作。OpenHands 默认 Agent(default_agent = "CodeActAgent",Day 04)正是这个范式的实现。

先建直觉 想让 Agent 完成"统计这个目录下所有 .py 文件的总行数"。固定工具派:得预先造一个 count_lines 工具、还要一个 list_files 工具、可能还要 filter_by_extension……每种需求造一个工具,造不完。CodeAct 派:直接让 Agent 写一行 find . -name '*.py' | xargs wc -l 并执行——一条命令搞定,不需要任何专门工具。代码/命令是"图灵完备"的——理论上能表达任何操作。给 Agent 一个终端,胜过给它一百个专用按钮。
🤔 痛点:CodeAct 听起来抽象,它在代码里到底"长什么样"?"以代码为动作"是个理念,但落到 OpenHands 里,一个"动作"具体是什么数据?光讲思想容易悬空。
💡 本质:CodeAct = LLM 用"函数/工具调用"表达动作,而非自定义 DSL。看前端类型 frontend/src/types/v1/core/base/action.ts 里的 ExecuteBashAction/FileEditorAction/MCPToolAction——它们就是这些"工具调用动作"的落地形态。而 action-event.ts 里的 ActionEventthought/tool_name/tool_call 三个字段,正是 CodeAct 的"先想(thought)再调工具(tool_call)"。这和你 Day 02 学的 Action、Day 12 学的工具体系是同一件事,只是换了个"以代码为核心动作"的视角看。
CodeAct 就像生活中的"让老员工直接上手干":固定工具派像是"员工每做一件事都得填一张专用申请表(借用表、报销表、请假表……)",表格永远不够用;CodeAct 派则是"给员工一台电脑和权限,让他自己动手操作"——想干啥直接干,不受表格种类限制。
L02

固定工具 vs 代码动作

❌ 纯固定工具派

  • 每种能力造一个专用工具
  • 工具越堆越多,LLM 在几十个工具里容易选晕
  • 组合能力弱:想"先筛选再统计"得有组合工具
  • 遇到没预置工具的需求就卡住

✅ CodeAct(代码为动作)

  • 核心就几个:跑 bash/写代码、编辑文件
  • 任意逻辑用代码表达——图灵完备
  • 组合天然:管道、循环、条件全都行
  • 遇到新需求,现写代码即可
读法:固定工具像"给你一堆专用厨具(切蒜器、开瓶器…)",CodeAct 像"给你一把好菜刀 + 会用刀的手"。专用厨具用起来顺手但有限,好菜刀 + 手能应付任何食材。OpenHands 选了后者为主。
👶💬 对话体:为什么让模型"写代码调用"而不是"从菜单里选一个动作"? 👶 小白:给模型一个下拉菜单,让它选"删文件/读文件/跑命令",不是更省事、更可控吗?
👨‍🏫 老师:菜单能列的动作是有限的,真实任务千变万化,总有菜单里没有的。而"写代码"能表达无限种操作。
👶 小白:可写代码不是更容易出错?
👨‍🏫 老师:恰恰相反——大模型是在海量代码上训练的,"写代码"是它最擅长的事;反而"在几十个菜单项里精确选一个、填对参数"更容易选晕。让它用最拿手的方式行动,命中率更高。
L03

代码作为动作的威力

  • 表达力无上限:任何计算、文件操作、数据处理,一段代码就能表达,不受预置工具限制。
  • 组合天然:管道 |、循环、条件判断——编程语言天生擅长组合,Agent 一步就能表达复杂逻辑。
  • 复用生态:能 pip install 任何库、调用任何命令行工具——整个开源生态都是 Agent 的工具箱。
  • LLM 本来就擅长写代码:大模型在海量代码上训练过,"写代码"是它最强的能力之一。让它用最擅长的方式行动。
最后一点尤其妙 LLM 见过无数 Python/bash 代码,"写代码"这件事它极其在行。而"从 50 个自定义工具里精确选一个、填对参数"反而容易出错。CodeAct 让 Agent 用它最训练有素的方式(写代码)来行动,命中率自然高。这是 CodeAct 论文的核心洞察:动作空间对齐 LLM 的强项。
L04

OpenHands 的实际取舍(不是纯粹主义)

OpenHands 没有极端到"只有一个 bash 工具"。它以 CodeAct 为核心,但也保留了几个精心设计的专用工具(Day 02/12 的动作全家福):

  • bash 执行:CodeAct 的核心——万能。
  • 文件编辑(str_replace):为什么不直接用 bash 的 sed?因为 str_replace 更可靠、能生成 diff、可撤销(Day 02/12)。
  • 浏览器、搜索(glob/grep):高频操作做成专用工具,比每次写代码更省 token、更稳。
务实的平衡:核心用 CodeAct(通用),高频/需要可靠性的操作用专用工具(好用)。"能用代码解决的绝不造工具,但高频关键操作值得打磨专用工具"——这个平衡是 OpenHands 工程成熟度的体现。纯理论派会说"只要 bash 就够",但实践中 str_replace 的 diff、glob 的结构化结果,确实比裸 bash 更适合 Agent。
💡 简化版 → 真实版:一个"动作"要带多少信息 如果让你实现,"执行一个动作"你大概会写:os.system(cmd) 一行了事。但真实的 ActionEventaction-event.ts)远不止命令本身,多出来的每个字段都在解决一个问题:thought(可解释——它为什么这么做)、tool_name/tool_call(结构化——到底调了哪个工具、参数是啥)、security_risk(安全——这动作多危险,Day 17 用)、id(配对——结果回来时靠它找到这条动作,Day 16 用)。朴素做法只管"跑起来",工程级做法还要管"可解释、可审计、可配对、可控风险"。
L05

一次 CodeAct 循环(把全周串起来)

任务:"找出项目里最大的 3 个文件"。CodeActAgent 的实际运转:

# step 1: LLM 决定写命令(CodeAct)→ ExecuteBashAction
$ find . -type f -printf '%s %p\n' | sort -rn | head -3
# → ExecuteBashObservation: "1048576 ./data.bin\n524288 ./model.pkl\n..."

# step 2: LLM 看到结果,判断任务完成 → FinishAction
# "最大的三个文件是 data.bin(1MB)、model.pkl(512KB)、..."
读法:一条命令 + 一次判断,任务就完成了——不需要任何"找大文件"的专用工具。这一步用到了:LLM 智力(Day 14)决定写什么命令 → 工具体系(Day 12)的 bash 工具 → Runtime(Day 13)在沙箱执行 → 产生 Action/Observation(Day 02)→ 进事件流(Day 03)→ app_server 记录(Day 08)→ 前端实时显示。全周知识在这一条命令里全用上了。
📝 举个例子任务"找最大的 3 个文件"。LLM 产出一条 ActionEventthought="我该用 find 排序" + tool_name="execute_bash" + tool_call=find . -type f -printf '%s %p\n' | sort -rn | head -3 → 沙箱执行 → 回来一条 ObservationEvent1048576 ./data.bin ... → LLM 判断完成,产出 FinishAction。整个过程没有任何"找大文件"专用工具,全靠一条 bash。
LLM 大脑 thought+tool_call 沙箱执行 Runtime 跑 bash Observation 结果回喂给 LLM ActionEvent 下一轮循环(看结果再决定) 一次 CodeAct 循环:想 → 调工具 → 看结果
图:CodeAct 循环把 LLM 智力、沙箱执行、事件模型黏合成自主闭环
L06

为什么 OpenHands 在 SWE-bench 领先

SWE-bench 是衡量"AI 解决真实 GitHub issue 能力"的权威榜单。OpenHands 长期是开源方案的领先者,CodeAct 是重要原因:

  • 真实 bug 修复千变万化,固定工具覆盖不全,CodeAct 的通用性正好对症。
  • 能跑测试验证(pytest)→ 看结果 → 自我纠错,闭环完整。
  • 持久终端 + 完整开发环境(Day 12)让它像真人程序员一样操作。
本质:修 bug 这件事,人类程序员就是"看代码、改、跑测试、再改"——CodeAct + 沙箱 + 事件循环,把这个人类工作流忠实地搬给了 AI。动作空间越接近人类程序员的真实操作,Agent 越强。CodeAct 的胜利,是"让 AI 用人类最自然的方式(写代码、敲命令)干活"的胜利。
一句话复述 CodeAct 就像生活中"能干的老师傅":你不用给他 100 个专用工具,给他一间备齐材料的车间(终端 + 沙箱),他自己就能拼出任何解法。⚠️ 小白常误以为"OpenHands 强是因为工具多",其实恰恰相反——它强在工具少而通用,把复杂度交给了 LLM 最擅长的"写代码"。
L07

全周知识串联(一图流)

一个 CodeActAgent 完成任务,用到你第 3 周学的所有东西:

  • Day 11 Agent SDK:step 循环、感知历史、产出 Action
  • Day 12 工具:bash 为核心动作(CodeAct)+ 专用工具(str_replace/glob)
  • Day 13 Runtime:在沙箱里真正执行代码、返回 Observation
  • Day 14 LLM:litellm 接模型、流式、系统提示、成本控制
  • Day 15 CodeAct:以代码为动作,把一切黏合成强大的自主循环
再往前,这一切又建立在第 1 周的 Action/Observation 事件模型、第 2 周的 app_server 编排之上。从"一条 find 命令"能追溯到整个系统每一层——你已经读懂了一个 SOTA 级自主 AI 软件工程师的骨架。
L08

🎓 第 3 周收官 + 动手

第 3 周(Day 11-15)你已读懂 Agent 大脑

  • Day 11 Agent SDK:step 循环、三要素、停止条件
  • Day 12 工具体系:三件套、function calling、MCP
  • Day 13 Runtime:执行引擎、三种实现、vs Sandbox
  • Day 14 LLM:litellm、流式、系统提示、成本
  • Day 15 CodeAct:以代码为动作的招牌范式

下周(Day 16-20)进入 前端 / 安全 / 生态:事件如何渲染成 UI、安全权限机制、微代理定制、企业版、收官。

✋ 动手

# 默认 Agent 是 CodeActAgent
grep -n 'default_agent' config.template.toml
# 回看 bash 动作(CodeAct 核心)与专用工具
sed -n '25,42p' frontend/src/types/v1/core/base/action.ts
# 思考题:如果只保留 bash 一个工具,OpenHands 还能工作吗?为什么仍保留 str_replace?
下周预告 · Day 16:回到前端——事件流如何渲染成界面:type-guards 类型守卫、handleEventForUI 的"观察替换动作"规则、WebSocket 连接管理。看第 1 周的事件模型如何变成你看到的聊天界面。
← Day 14 LLM Day 16 · 前端实时展示 →