跳转到内容

正在加载搜索…

设计:程序与决策点

一页篇幅的心智模型,供阅读代码或运行 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 上限处被终止,没有做过任何检查)。

学习型视觉运动策略(默认 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。

  1. 程序本身,使用解析式 skill(感知、按物体 id 调用的工具、move_eef 等)、自己的 检查和重试、反应层(每个控制 tick 运行的 guard)以及它用 on(event, handler) 注册的 handler。
  2. 学习型 skill,在程序调用它们时:用 start 交出的任务、通过 run_policy 调用的 VLA、通过 predict_future 调用的世界-动作模型。运行时不会自行回退到它们。
  3. 模型,只处理程序无法处理的部分。然后它从机器人当前所处的状态继续,编写下一个程序。

模型只在以下原因下被调用:start、未处理的事件(escalation)、error、未调用 finish() 就结束的程序(program_end),以及需要后续处理的回复(verify、format)。

例外是接口阶梯中的直接控制级别(R0/R1,src/harness_policy/runtime/direct.py):在那里 模型有意在每一步都被调用(action);它们是本设计用来对比的接口。

部分 代码
高层:模型何时被调用,以及原因 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 运行时。

  • 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。