设计:程序与决策点
一页篇幅的心智模型,供阅读代码或运行 trace 的协作者参考。
大多数基于 LLM 的机器人 agent 让模型留在循环中:观察,输出一个动作(或一次 skill
调用),再观察。这里的模型是程序员,而不是控制器:它编写程序,而一个程序就是任务中
一个步骤的策略。程序先观察(locate、相机),在自由空间中移动,与物体最多发生一次接触
(抓取、推、松开、转移或按压),检查并打印结果,然后结束。程序内部在模型之下运行的部分
(有界 skill、每个 tick 运行的 guard 和控制器、handler、安全层(shield)、通过
run_policy 调用的学习型策略)从不等待模型。
模型在决策点(论文 §3.2)上被再次询问:每完成一个这样的步骤之后,当有情况超出程序的处理 范围时,或者当接下来该做什么取决于机器人尚未看到的东西时(一次演示、一个被遮挡的物体、 学习型策略留下的场景)。每一次,模型都会得到程序的输出、重新测量的场景(每个已定位的物体 都带标签,并在相机图像中描出轮廓,附上每只持物手臂的抓握状态)、已用的环境步数和最新的 相机图像,然后从机器人当前所处的状态出发编写下一个程序。
这正是我们最好的逐步式 harness Z2_v01 的工作方式(每轮一次工具调用,查看每次调用的结果)。 agent 1.2.0 版把它的工具、每轮的场景、起始步骤和工作方法带进了程序;1.0.0 曾要求模型把 它能预见到的任务部分尽可能多地写进一个程序,而在开发对比中,这些程序在多个物体上循环而不 观察(classify_objects:四个程序在 30 分钟的 cell 上限处被终止,没有做过任何检查)。
分工:程序与学习型策略
Section titled “分工:程序与学习型策略”学习型视觉运动策略(默认 agent 中为 Simate)是在完整的 RoboDojo 任务上训练的,每条演示
都从起始姿态开始,并带有该任务本身的指令。它在完全相同的设定下表现最好,而且它擅长程序的
工具不擅长的事:熟练的接触,每秒多次,并带有对过去帧的记忆。它做不到的是阅读:从多个物体
中挑出由名称、写在物体上的数字或标签、或作为目标展示的图片所标识的那一个。因此模型的第一个
动作是一次选择 start(strategy, kind),依据是策略的描述(list_policies())和最初的图像:
| 策略 | 何时(prompt 中的规则) | 发生什么 |
|---|---|---|
| A | 策略学过的任务,且属于其描述中说它通常能完成的类别 | 策略从未被触动的起始姿态一次性运行整个任务;模型负责监督 |
| C | 策略学过的任务,属于其他任何类别 | 策略先运行,然后由程序完成它剩下的部分 |
| B | 在静止的物体中,按标识(名称、数字或标签、图片)从多个物体中挑出一个 | 只用程序自己的工具 |
其他一切都交给策略,包括指令中提到颜色、顺序或类别的情况,以及按外观相互匹配物体的情况:
这些条件都是它学过的内容的一部分。运动中的物体无论目标由什么标识都交给策略(工具对传送带
来说太慢)。选择 B 之后,如果模型自己的工具在策略学过的任务上很早就失败,模型仍可以把任务
交出去(从起始姿态调用 start("A" or "C", kind))。
交出的任务不间断地运行,直到 episode 结束或策略停止(手臂动过之后静止 5 s)。之后模型有
4 次调用(post_policy_calls)来处理明显剩下的部分,此后策略独自运行到 episode 结束。
监视策略并提前接管没有带来任何收益:1.3.0 把交出的任务切成 10 s 的片段运行,片段之间观察
一次,在这些任务上平均得分 31.9,而单独的 Simate 为 42.4;一次运行之后模型自己的工具只
增加了成本,没有增加得分。每条规则的证据见更新日志。
结果是模型调用很少:在 RoboDojo 的评测包 0 上(agent 1.4.1,42 个任务,每个任务三个 layout),125 个 episode 中有 113 个被交给策略,每个 episode 2.3 次调用、$0.003;程序 保留的 12 个 episode 用了 16 次调用和 $0.08。在相同 layout 上,agent 得分 26.2 (RoboDojo 平均分),单独的 Simate 为 22.2。
谁来处理问题,按顺序
Section titled “谁来处理问题,按顺序”- 程序本身,使用解析式 skill(感知、按物体 id 调用的工具、
move_eef等)、自己的 检查和重试、反应层(每个控制 tick 运行的 guard)以及它用on(event, handler)注册的 handler。 - 学习型 skill,在程序调用它们时:用
start交出的任务、通过run_policy调用的 VLA、通过predict_future调用的世界-动作模型。运行时不会自行回退到它们。 - 模型,只处理程序无法处理的部分。然后它从机器人当前所处的状态继续,编写下一个程序。
模型只在以下原因下被调用:start、未处理的事件(escalation)、error、未调用
finish() 就结束的程序(program_end),以及需要后续处理的回复(verify、format)。
例外是接口阶梯中的直接控制级别(R0/R1,src/harness_policy/runtime/direct.py):在那里
模型有意在每一步都被调用(action);它们是本设计用来对比的接口。
在代码中的位置
Section titled “在代码中的位置”| 部分 | 代码 |
|---|---|
| 高层:模型何时被调用,以及原因 | src/harness_policy/runtime/driver.py(run_program_episode) |
| 运行模型的程序;变量在调用之间保留 | src/harness_policy/runtime/kernel.py |
程序调用的 API(locate、pick、start、run_policy、on、predict_future、run_controller、finish 等) |
skills/<name>/,由 src/harness_policy/runtime/session.py 组合 |
| strategy 步骤与学习型策略 | skills/vla/(start、run_policy、list_policies),services/<name>.toml(client.description) |
| 反应层(每个 tick 运行的 guard 和控制器) | src/harness_policy/runtime/reactive.py,skills/reactive/ |
| 安全层,模型无法触及 | src/harness_policy/runtime/shield.py(在仿真器 worker 中运行) |
| 模型被告知的内容 | prompts/robodojo/:API(core.md、learned.md),使用工具的工作方式(method.md:规划,一次接触后观察),分工(rules_learned.md) |
更多细节:episode 运行时。
带着这些认识阅读一次运行
Section titled “带着这些认识阅读一次运行”summary.json:strategy(所做的选择及其匹配的类别)、model_calls和decisions(按原因统计的调用)。交出的任务通常需要两次调用,start和一次program_end,并随 episode 结束;程序保留的任务大约每次接触一次调用。events_handled统计程序自己处理的事件;events_escalated统计交给模型的事件。events.jsonl:每个model_request都带有它的reason;每个cell是一次程序运行, 带有它产生的 skill 事件以及之后的场景报告。kernel.log:每次工具调用(locate、pick等)一行 JSON,附带其结果。
一个 episode 中出现大量 error 或 escalation 调用本身就是一个信号:程序没有消化自己的
失败,通常需要修复的是这一点(一个工具、一个检查、一个 handler),而不是 prompt。
作为参照:被取代的逐步循环(grok-4.7,开发 layout)每个 episode 用了 34 次调用和 823k 输入 token;agent 1.4.1 在上述 125 个评测 episode 上平均用了 3.6 次调用和 57k 输入 token。