0. 简介

ASPIRE(Agentic Skill Programming through Iterative Robot Exploration)是 NVIDIA GEAR 联合 UMich、UIUC、UC Berkeley、CMU 提出的机器人持续学习系统,面向的是一个长期没被解决好的场景:用 code-as-policy 范式写机器人控制程序时,一次 rollout 失败往往只给出"任务没成"这一条粗粒度信号,智能体既不知道是感知错了、抓取不稳、还是运动规划失败,更谈不上把这次修复经验留给下一个任务。ASPIRE 没有沿用固定的"感知—规划—执行"流水线,而是通过三件东西把调试闭环补齐:一个暴露逐原语多模态执行轨迹的机器人执行引擎、一个把验证过的修复沉淀成可复用条目的技能库、以及一套在程序空间里做进化式搜索的探索机制。从实验看,最值得关注的是它的自我增强曲线——在 LIBERO-Pro 扰动任务上相对最强基线最高提升 77 个百分点,在 Robosuite 双臂交接任务上从 20% 拉到 92%,并且技能库越大,迁移到没见过的长程任务时零样本成功率越高。下面结合论文与其依赖的 CaP-X 框架,重点拆解它的执行引擎、技能库和进化搜索这三条主线是怎么咬合成一个开放式学习循环的。ASPIRE 官方项目页,NVIDIA GEAR

1. 为什么又要给机器人编程造一个新系统

1.1 机器人程序的失败是"多因耦合"的失败

机器人编程之所以出名地难,核心问题在于失败很少是单点的。一段控制程序要同时协调多模态感知、运动规划、抓取生成、接触动力学,还要在长程任务里维持子目标之间的顺序。一次失败的 rollout 只能告诉你"任务没完成",却无法告诉你根因在哪一环。感知框错了物体?抓取姿态不稳?规划器算不出可行路径?还是下游的恢复逻辑崩了?没有细粒度的诊断轨迹,智能体就像蒙着眼睛修车——它甚至不知道该去看哪个证据、该在哪里定位失败、该尝试哪种修复策略。这正是过去 code-as-policy 系统一直卡住的地方。

1.2 现有路线的两个缺口

进一步看,已有工作可以分成两类,各有短板。第一类是**端到端 VLA(Vision-Language-Action)**策略,例如 OpenVLA、 π 0 \pi_0 π0 π 0.5 \pi_{0.5} π0.5,它们把感知直接映射到动作,但行为被压进神经网络权重里,出错时无法"打开来看、逐行改"。第二类是早期的 code-as-policy 系统,例如 Code-as-Policies、ProgPrompt、VoxPoser、RoboCodex,以及本文的直接基线 CaP-Agent0,它们把机器人行为写成可执行程序,理论上可读可改,但依赖的执行环境只给"任务级"反馈,且做完一个任务就把修复经验丢掉了。这里的关键是:一个解到第一百个任务的智能体,本质上和解第一个任务时一样"没经验"。ASPIRE 的核心判断是——要让机器人编程智能体像人类工程师一样,把调试经验复利式地攒起来
在这里插入图片描述

2. 整体框架:coordinator–actor 加一个开放式学习循环

2.1 输入输出接口与三大组件

ASPIRE 采用 coordinator–actor 架构。中央 coordinator 管理共享技能库,并为每个任务派发一个 actor 编程智能体,从而实现跨任务的并行学习;每个 actor 负责在机器人执行引擎里写程序、执行、诊断、修复。这里要厘清的是 actor 之间不交换完整对话历史或原始 rollout 轨迹——可迁移的经验被蒸馏成技能库条目,这样每个 actor 的上下文窗口都能聚焦在当前任务规格、当前程序、以及当前失败对应的结构化执行轨迹上。整个系统由三个组件构成:暴露逐原语轨迹的机器人执行引擎(§3)、沉淀验证修复的技能库(§4)、在程序空间里探索的进化搜索(§5)。它们共同构成一个开放式学习循环——ASPIRE 见的任务越多,技能库越大,向新任务、更长程行为、乃至真机场景的迁移就越强

2.2 一个关键的不对称:actor 写、coordinator 审

这套架构在工程上有一个非常关键的不对称设计。actor 是并行的、允许犯错的、上下文短的一次性工人;coordinator 是串行的、负责审计的、维护长期记忆的管家。actor 报告结构化 findings(失败模式、验证过的修复、可能可迁移的模式),coordinator 审计这些 findings、核对是否符合允许的 API 策略,只把通过调试验证、且大概率能迁移出本任务的修复晋升进共享库。这意味着技能库的写入是串行化的,避免了并行 actor 互相冲突地改库。换句话说,探索靠并行放量,质量靠串行把关,两条线各司其职。


3. 机器人执行引擎:把粗反馈换成逐原语轨迹

3.1 从固定反馈通道到开放式调试环境

具身编程智能体要调试程序,必须有执行证据。核心问题在于过去的方法用的是固定的、人工设计的反馈接口——要么是人工整理的场景级摘要,要么是一组预定义的观测。这带来一个两难:证据太少会藏住真正出错的原语,证据太多(比如塞一堆原始视觉帧)又会把智能体从"导致失败的因果链"上带偏。ASPIRE 把这个固定通道改造成了一个开放式调试环境。机器人执行引擎为感知、规划、控制的每一次原语调用记录多模态轨迹,把轨迹暴露给编程智能体,并执行智能体写的修复做闭环验证。这里的关键是引擎不给智能体喂完整视频,而是只保留每次原语调用前后各一帧加对应的 overlay 和返回值,让智能体能聚焦在被失败牵连的那几次调用附近。

下面这段代码用 CaP-X 风格的原语 API 展示了一段典型的机器人控制程序长什么样——它组合了感知、规划、抓取三类原语,每一次调用都会被执行引擎记录进轨迹。这里要厘清的是,code-as-policy 范式的价值恰恰在于这种显式性:行为不是藏在权重里的黑箱,而是一段可读、可改、可打断点的程序,执行引擎因此能在每个原语调用点埋探针。换句话说,程序结构本身就是调试接口。

# robot_program.py — CaP-X 风格的 code-as-policy 程序(导航并抓取物体)
def navigate_and_pick(target_name: str):
    # 感知原语:返回物体位姿 + RGB overlay,全部进 trace
    obj = perceive_object(target_name)              # -> Detection(pose, mask, overlay)
    if obj is None:
        return Status.PERCEPTION_ERROR

    # 规划原语:为导航生成可行目标位姿,返回 PLANNING_ERROR 时记录候选点
    goal = compute_navigation_goal(obj.pose, standoff=0.5)
    status = navigate_to_pose(goal)                 # -> Status + 规划日志
    if status != Status.OK:
        return status                               # 失败信号进 trace 供归因

    # 抓取原语:生成候选抓取并执行,overlay 标出抓取点
    grasp = generate_grasp(obj)                     # -> GraspCandidate(pose, score)
    return execute_grasp(grasp)

这段代码做了什么,值得逐层拆开。它把一个"导航去抓收音机"的行为写成三次原语调用的串联,每一次调用都返回带状态码和视觉证据的结构化对象。它为什么这样设计?因为程序被显式表达成可执行原语序列,执行引擎才能在每个 perceive/navigate/generate_grasp 调用点埋下探针,记录 invoked API、输入输出、返回状态、以及 RGB keyframe、overlay、抓取候选、物体位姿、运动规划结果这些多模态证据。工程细节上,navigate_to_pose 返回 PLANNING_ERROR 时不是简单抛异常,而是把生成的候选导航目标一并写进日志——这恰恰是下一节调试能定位到"目标点落在桌子碰撞缓冲区里"的前提。

难点提示(逐原语轨迹为什么重要):把它想象成汽车的行车记录仪加 OBD 诊断口。没有它,修车师傅只知道"车没到目的地";有了它,师傅能回放每一段视频、读出每个传感器的报错码,直接定位到"是转向助力泵在某个转角报故障"。ASPIRE 的执行引擎就是给机器人程序装上了这套"逐帧回放 + 报错码",把不可见的失败变成可归因的证据链。

3.2 一个真实调试回合:收音机为什么抓不到

Fig 2 放大了 BEHAVIOR-1K 里"导航并抓取收音机"的一个调试回合。第一视角关键帧显示,机器人找到了收音机,却反复靠近失败,直到换了个方向才成功。原语轨迹把失败精确定位了:感知成功并返回了收音机位姿,但连续的 navigate_to_pose 调用都返回 PLANNING_ERROR。智能体通过检查导航返回值和相关日志发现,生成的导航目标离桌子边界太近——大约在桌沿 20 厘米以内,触发了碰撞避免,导致规划器失败。这意味着失败根因不是检测或抓取收音机,而是目标位姿在桌子碰撞约束下不可行。这正是逐原语轨迹的价值:它让智能体能选择性地检查显著的原语日志,逐步定位失败,再通过重执行验证修复。
在这里插入图片描述

3.3 修复直接从诊断长出来

修复直接顺着诊断长出来,而不是瞎试。这里的关键是智能体没有去改感知 prompt、也没有去换抓取原语,而是写了一个多角度接近例程(Multi-Angle Approach):在收音机周围采样若干候选导航目标,选一个能清开碰撞缓冲区的接近方向,重新感知再抓取。下面这段修复代码就是这个例程的核心。

# repair_multi_angle_approach.py — 由失败轨迹诊断长出的修复
def multi_angle_approach(obj_pose, n_angles=8, radius=0.6, clearance=0.25):
    for theta in sample_angles(n_angles):           # 绕物体环形采样接近方向
        cand = pose_around(obj_pose, radius, theta)
        # 关键:先检查候选点是否落在障碍碰撞缓冲区内,落在里面直接跳过
        if min_dist_to_obstacles(cand) < clearance:
            continue
        if navigate_to_pose(cand) == Status.OK:
            return re_perceive_and_grasp(obj_pose)  # 换到可达侧重新感知+抓取
    return Status.PLANNING_ERROR

这段代码为什么关键?因为它把"目标点落进碰撞缓冲区"这个诊断结论直接编码成了防御逻辑——min_dist_to_obstacles(cand) < clearance 这一行显式过滤掉了会触发碰撞避免的候选点,正好对应上一节发现的"20 厘米桌沿"问题。工程细节上,clearance 取 0.25 米比诊断出的 0.20 米更保守,留了安全裕度。执行引擎随后暴露证据、验证这段打过补丁的程序,让智能体分析日志、形成假设、做出有针对性的修复决策。验证通过后,这段修复被收编成一条可复用的 Multi-Angle Approach 技能——注意,进库的是"导航恢复模式"这个模式,不是完整的收音机抓取程序。


4. 技能库:把验证过的修复变成可迁移知识

4.1 存的是修复模式,不是整段任务程序

程序失败会跨任务反复出现,但可复用的知识很少是一整段任务程序。ASPIRE 的技能库存的是异构的修复知识:定位启发式、感知 prompt、抓取约束、导航恢复策略、运动原语、场景理解例程、调试工作流。这里要厘清的是——这套分类不是预先规定的。技能是从验证过的修复里归纳出来的:编程智能体从执行轨迹诊断失败、给程序打补丁、在调试配置上验证修复,coordinator 只把可复用的模式收编进共享库。对前面的收音机任务,进库的技能是一条导航恢复模式,而不是完整的收音机抓取程序——当规划器在障碍边界附近反复报错、因为采样目标位姿落进碰撞缓冲区时,就在物体周围采样其它接近方向,再重试感知和抓取
在这里插入图片描述

4.2 每条技能长什么样

每条技能被存成紧凑的 in-context 指引,包含失败签名(failure signature)何时适用(when-to-apply)条件修复策略,以及在有用时附上一段代表性代码草图。这种表示让未来的 actor 直接复用验证过的修复,而不必在测试时重新推理一遍;它支撑向更难的模拟任务做零样本迁移;也提供了让选定的"模拟中发现的技能"跨本体泛化、迁移到真机的机制。下面这段展示了一条技能条目的结构化 schema。

# skill_entry.py — 技能库条目的结构化表示
skill = {
    "name": "multi_angle_approach",
    "category": "navigation_recovery",
    "failure_signature": "repeated PLANNING_ERROR near obstacle boundary",
    "when_to_apply": "sampled nav goal falls inside collision buffer of a nearby surface",
    "repair_strategy": "sample alternative approach directions around object, "
                       "filter by clearance, re-perceive from reachable side",
    "code_sketch": "multi_angle_approach(obj_pose, n_angles=8, clearance=0.25)",
    "origin_task": "behavior1k/navigate_and_pick_up_radio",
    "validation_success_rate": 0.88,
}

这个 schema 为什么这样设计?核心在于 failure_signaturewhen_to_apply 这两个字段——它们决定了未来 actor 遇到新失败时能不能检索到这条技能。failure_signature 是失败的可匹配指纹(“障碍边界附近反复 PLANNING_ERROR”),when_to_apply 是情境化的检索守卫。工程细节上,validation_success_rate 记录了这条修复在调试配置上的验证成功率,coordinator 据此判断是否值得晋升进库。

直觉理解(技能库为什么像索引卡):把老工程师脑子里"遇到 X 症状就试 Y 招"的经验,写成一张张可以被检索的索引卡。卡片正面是症状(failure_signature),卡片的适用条件是"什么时候该翻出这张卡"(when_to_apply),背面才是具体招式。新手(新 actor)遇到问题时不用从头推理,先按症状翻卡片,命中了就照着招式改——这就是 in-context 检索比"每次都重新想一遍"高效的地方。

4.3 coordinator 的准入审计

…详情请参照古月居

Logo

DAMO开发者矩阵,由阿里巴巴达摩院和中国互联网协会联合发起,致力于探讨最前沿的技术趋势与应用成果,搭建高质量的交流与分享平台,推动技术创新与产业应用链接,围绕“人工智能与新型计算”构建开放共享的开发者生态。

更多推荐