跳到主要内容

Code Agent 与 Behavior Best-of-N

本章讲 S3 区别于前代的两个杀手锏:嵌套 Code Agent(把数据活交给代码)和 Behavior Best-of-N(测试期跑多次再投票)。最后给一段横向对比。

4.1 为什么需要 Code Agent:有些活点鼠标干太蠢

"把 B 列 500 行求和""把日期列统一改成 ISO 格式"——这类任务用 GUI 一格格点既慢又易错,但写几行代码就稳。S3 的回答:给 Worker 一个 call_code_agent 动作,把这类活整包甩给一个嵌套的代码执行子 agent

Worker 的提示词把分工讲得很死(procedural_memory.py:20-58):

用 GUI Agent用 Code Agent
点击、输入、导航、视觉元素、图表、透视表、打印/导出设置所有表格计算、数据处理(过滤/排序/替换)、批量操作、格式批改

注意一条硬规矩:图表、透视表等视觉产物永远用 GUI,绝不用 Code Agent(procedural_memory.py:58)。

4.2 Code Agent 怎么跑:有预算的代码 REPL

CodeAgent.execute(code_agent.py:113)是一个带步数预算(默认 20)的小循环:

收到任务 + 当前截图

└─ while 步数 < budget:
├─ LLM 产出 <thoughts>...</thoughts><answer>```python ...```</answer>
├─ 若 answer == DONE/FAIL → 结束
├─ extract_code_block:抠出 python 或 bash 代码
├─ execute_code:交给 env_controller 真跑
│ run_python_script / run_bash_script
├─ 把执行结果(stdout/stderr/状态)拼回对话
└─ 步数 +1

└─ _generate_summary:让另一个 LLM 把整段执行总结成 <150 词的报告

结束后返回一个结构化结果(code_agent.py:266-273):completion_reason(DONE/FAIL/BUDGET_EXHAUSTED)、steps_executedsummary、完整 execution_history。这个报告会在下一步被拼进 Worker 的消息里(worker.py:209-302),让大脑知道代码侧干了什么。

两个关键约束(都在提示词里):

  • 每步代码不跨步持久化——必须写"自包含"的片段(procedural_memory.py:170)。这强制 agent 把"找文件→读内容→改→验证"拆成独立小步,而不是一坨大脚本。
  • GUI 必须验收:Worker 被反复警告"绝不能只信 Code Agent 的输出,必须用 GUI 打开文件亲眼核对再 agent.done()",而且"改完文件可能要完全关掉再重开应用才看得到变化"(procedural_memory.py:51-54)。

职责切分的妙处: Code Agent 只碰"文件内容和脚本",GUI agent 负责"找到并验证"。代码擅长批量精确变更,视觉擅长确认"用户真的看到对的结果"——两者验收闭环。

执行环境由 env_controller 抽象。CLI 里若开 --enable_local_env,就用 LocalEnv本机直接 subprocess 跑代码(local_env.py)——源码里明确标了"WARNING: Executes arbitrary code locally"。没有可用 controller 时,call_code_agent 动作会被整个隐藏(worker.py:69-73)。

4.3 Behavior Best-of-N:S3 的招牌

S3 论文标题的"首超人类 72.6%"里,从 66%(单跑)到 72.6% 的那一截,主要来自 Behavior Best-of-N(BBoN,行为最优 N 选)

核心思路: 同一个任务,跑 N 次得到 N 条不同的操作轨迹;然后让一个 VLM 评委比较这 N 条,选出完成得最好的那条。难点在于:轨迹有几十步、几十张图,直接全塞给评委又贵又乱。BBoN 的解法是先把每步"翻译成一句话事实字幕",再让评委基于字幕 + 首尾截图来判

这是个离线、两阶段流程(不在实时 cli_app 里,而在 osworld_setup/s3/bbon/ 的评测脚本里编排):

N 条已跑完的轨迹(每条 = 一串截图 + traj.jsonl 动作)

阶段一:BehaviorNarrator —— 给每一步配"事实字幕"
│ 对每对 (前截图, 动作, 后截图):
│ ├─ 在前图上标注动作位置(点=红圈、移动=蓝圈、拖拽=绿线)
│ ├─ 在后图上裁一块放大区(看清细节)
│ └─ VLM 输出"这一步实际发生了什么"的精确描述
│ → 每条轨迹得到一串 fact captions

阶段二:ComparativeJudge —— N 选 1
│ 把 N 条轨迹的 (首截图, 末截图) + 各自字幕喂给评委 VLM
│ 评委按一份严格 rubric 逐条核对,输出胜出轨迹的编号

→ 选中的那条轨迹就是 best-of-N 的答案

阶段一:BehaviorNarrator(行为字幕)

BehaviorNarrator.judge(behavior_narrator.py:170)的精髓是把动作画到截图上再让 VLM 描述,而不是干巴巴给坐标。mark_action(behavior_narrator.py:37)按动作类型画标记:

动作视觉标记
点击红圈 + "Click"
移动蓝圈 + "MoveTo"
拖拽蓝圈起点 + 绿线 + 绿圈终点 "DragTo"

它还会在"后截图"上裁一块放大去噪的区域(get_zoomed_image,behavior_narrator.py:106),并画红框标出放大位置——帮 VLM 看清"那个小勾选框到底勾上没有"这种细节。提示词(BEHAVIOR_NARRATOR_SYSTEM_PROMPT,procedural_memory.py:297)反复强调:别假设动作成功了,要看视觉标记和前后变化的实证

为什么先配字幕? 直接把几十张原图丢给评委,它很难精确说清"第 7 步到底改了啥"。先把每步"压缩成一句经过视觉核对的事实",评委就能基于可靠、紧凑的文字证据来比较,而不是自己脑补图里发生了什么。

阶段二:ComparativeJudge(比较评委)

ComparativeJudge.judge(comparative_judge.py:66)把 N 条轨迹的首/末截图 + 字幕拼进一条消息,用 VLM_EVALUATOR_PROMPT_COMPARATIVE_BASELINE(procedural_memory.py:326)那份很长的 rubric 来判。rubric 里全是"硬约束":必须保持表格原格式/行列顺序、必须关掉自己开的弹窗/标签、必须用软件内置功能而非 hack、Chrome 设置必须关标签页才生效……评委逐条核对后,在 <answer> 里吐一个整数——胜出轨迹的编号(comparative_judge.py:137-148)。

这套评测在 run_judge.py 里被组织成"渐进式":BoN2、BoN3……分别对应"从 2 次/3 次 rollout 里选",用来画出"采样次数越多、准确率越高"的曲线(osworld_setup/s3/bbon/run_judge.py:154-177)。

4.4 把两件事连起来看 S3 的世界观

S2 的赌注:把智能花在"规划"
Manager → DAG 子任务 → 知识库/网络检索 → 逐个执行

S3 的赌注:把智能花在"采样 + 评选 + 工具"
单 Worker 一路走 ──┐
数据活甩给 Code Agent ┤→ 跑 N 次 → BehaviorNarrator 配字幕
GUI 负责验收 ──┘ → ComparativeJudge N 选 1

这是两种"把算力换准确率"的不同哲学。S3 证明:对 computer-use,简单执行器 + 测试期扩展,胜过复杂的前置规划。

4.5 横向对比(同 shelf 兄弟)

维度Agent S3典型对照
定位方式两段式:大脑只描述,独立定位模型(UI-TARS)出坐标不少 computer-use agent 让单一 VLM 直接吐坐标
规划无层级,单 Worker(S3);S2 才有 DAG 规划很多框架坚持显式分层规划
测试期扩展离线 Behavior Best-of-N(配字幕→VLM 评委)朴素 self-consistency / 多数投票
数据任务专门的嵌套 Code Agent + GUI 验收纯 GUI 点操作

4.6 边界与局限(诚实)

  • Behavior Best-of-N 是离线的。 它在 osworld_setup/s3/bbon/ 的评测脚本里跑,依赖磁盘上已存的 step_*.pngtraj.jsonl;实时 cli_app.py 的单次运行自动做 best-of-N。要在线用得自己编排。
  • 本机代码执行有风险。 --enable_local_env 会在你的机器上 exec/subprocess 跑模型生成的任意代码,源码自己标了 WARNING;提示词里甚至硬编码了 OSWorld 评测环境的 sudo 密码 osworld-public-evaluation(procedural_memory.py:110)——那是为基准容器准备的,不是给你的真机。
  • 强依赖一个好的定位模型。 整个准确率的地基是 UI-TARS 这类定位模型的质量;generate_coords 的坐标解析也很朴素(正则抓数字,grounding.py:244),定位模型回复古怪时会脆。
  • CLI 默认只跑 15 步。 run_agent 的循环写死 range(15)(cli_app.py:160),长任务在 CLI 演示模式下可能没走完就停了。
  • set_cell_values 绑死 LibreOffice + Linux。 它走 UNO socket(grounding.py:51),非 Linux 平台会被 skipped_actions 跳过(worker.py:64-65)。

代码地图(导航索引)

主题文件路径符号名
嵌套代码 agentgui_agents/s3/agents/code_agent.pyCodeAgent.execute / _generate_summary
触发 code agent 的动作gui_agents/s3/agents/grounding.pyOSWorldACI.call_code_agent
GUI/Code 分工提示词gui_agents/s3/memory/procedural_memory.pyconstruct_simple_worker_procedural_memory(Agent Usage Guidelines)
Code agent 提示词gui_agents/s3/memory/procedural_memory.pyCODE_AGENT_PROMPT
本机执行环境gui_agents/s3/utils/local_env.pyLocalEnv / LocalController
行为字幕(BBoN 阶段一)gui_agents/s3/bbon/behavior_narrator.pyBehaviorNarrator.judge / mark_action / get_zoomed_image
比较评委(BBoN 阶段二)gui_agents/s3/bbon/comparative_judge.pyComparativeJudge.judge
评委 rubricgui_agents/s3/memory/procedural_memory.pyVLM_EVALUATOR_PROMPT_COMPARATIVE_BASELINE
BBoN 离线编排osworld_setup/s3/bbon/generate_facts.py / run_judge.pygenerate_fact_captions_parallel / run_experiment