为什么AI分不清"车是朝我开的"?SpatialClaw:把Python当成行动接口
为什么AI分不清"车是朝我开的"?SpatialClaw:把Python当成行动接口
> 核心直觉:人类看空间是"边看边想"——瞄一眼暖气,发现分割歪了,换个算法重新算;看看距离不对劲,再换个坐标系验证。但现有VLM工具要么"一次性写完所有代码再执行"(错了没法改),要么"只能调用固定的API"(想不出新招)。SpatialClaw的 trick 是——把Python解释器变成一个"可反复交互的沙盘",让VLM像人一样:写一步、看一步、改一步。
---
一、一个让人崩溃的日常问题
看这张图:
> "房间里有个白色暖气片,旁边是门。暖气片和门之间有多远?"
人类瞄一眼:暖气在墙左,门在墙右,中间大概隔了两三步,估计 1.5米左右。
最先进的视觉语言模型(VLM)呢?
它可能回答:
- "6.5米"(完全离谱)
- "1.638米"(好像挺精确,但其实是错的)
- "不确定"(诚实但没用)
- "这辆车是在朝镜头开,还是远离镜头?"
- "这个人刚才向左转了吗?"
- "这四个摄像头拍的是同一个场景吗?"
---
二、现有方案:两个死胡同
业界不是没有尝试。过去的研究走的两条路,各有硬伤:
路线A:单次代码执行(pySpatial, VADAR)
思路:给VLM一套Python工具库(深度估计、分割、点云计算),让VLM一次性写完整程序,然后执行。
# 典型单次代码风格
depth = tools.get_depth(image)
mask = tools.sam(image, "radiator")
mask_valid = tools.check_valid(mask) # 如果mask无效怎么办?已经晚了
dist = tools.compute_distance(mask, door_mask)
print(dist) # 输出1.638(实际上mask可能完全没对准暖气)
致命问题:代码执行前必须"承诺"完整策略。如果中间某一步发现分割错了、深度估计失效了、或者需要换一种距离计算方式——已经没机会了。程序一次性跑完,错了就是错了。
路线B:结构化工具调用(SpaceTools)
思路:把感知能力封装成固定的API(get_depth(), segment(), compute_distance()),让VLM按JSON格式一步一步调用。
{
"tool": "segment",
"object": "radiator",
"image_id": 0
}
致命问题:API是固定的。如果VLM需要一种API设计者没预料到的操作(比如"用KDTree找最近点而不是用质心"),它就无能为力。工具组合被锁死在预定义的参数模式里。
两种方案的共同缺陷:
- 没有中间验证——无法停下来看看"分割结果对不对"
- 没有即时修正——发现问题后不能调整策略
- 没有自由组合——不能把工具输出和Python科学库混用(numpy、scipy、KDTree)
三、SpatialClaw 的核心创新:代码不是程序,是"编排空间"
SpatialClaw 的颠覆性洞察很简单,但被之前的框架完全忽略了:
> 代码不应该是一次性程序,也不应该是API分发接口。代码应该是一个"编排空间"——一个agent可以在其中编排、组合、诊断它所拥有的感知工具的环境。
翻译成人话:不要一次性写完所有代码。每次只写一小段,执行,看结果,再写下一小段。
这听起来像常识,但技术实现上需要解决几个关键问题:
1. 状态持久化:变量不会消失
传统每次调用工具都是独立的——segment() 返回一个mask,然后compute_distance() 再接收这个mask。两个调用之间没有共享状态。
SpatialClaw 用了一个持久化的Python内核:
# 步骤1:分割暖气
seg_heater = tools.SAM3(img, "white radiator heater")
# seg_heater 变量留在内核里,不会消失
# 步骤2:可视化验证
show(seg_heater) # 模型"看到"自己分割的结果,发现好像多了一块
# 步骤3:修正分割策略
seg_heater = tools.SAM3(img, "white radiator heater", point_prompt=[[x,y]])
# 基于上一步的观察,调整提示
# 步骤4:计算距离(发现质心不准,换KDTree)
tree = scipy.spatial.KDTree(points_heater)
dists, _ = tree.query(points_door, k=1)
print(f"Min distance: {min(dists)}") # 0.9439 ✓
2. 中间反馈:模型能"看见"自己的中间结果
每次执行后,反馈不是简单的"成功/失败",而是完整的上下文:
- 标准输出:
print()的值 - 变量摘要:新变量的类型、长度、大小
- 可视化图像:
show()注册的图像直接嵌入模型上下文 - 错误追踪:精简的traceback,模型可以根据错误修正
3. 自由组合:感知工具输出就是普通Python变量
tools.SAM3() 返回的掩码是一个numpy数组。你可以:
- 用numpy做布尔运算
- 用scipy做空间查询(KDTree、ConvexHull)
- 用matplotlib可视化
- 用普通Python做控制流(if/for/while)
4. 即时修正:策略可以动态调整
# 步骤3发现用get_centroid(质心)找最近点不对
# 步骤4直接换成 scipy.spatial.KDTree
# 如果KDTree也不对,步骤5可以再用别的方法
这种试错-验证-修正的循环,是单次代码和固定API完全做不到的。
---
四、架构设计:五个阶段的循环
SpatialClaw 的系统架构(图3)是一个清晰的五阶段循环:
阶段I:规划(Planning)——独立LLM,不看图
关键设计:规划器是一个独立的LLM会话,而且不接收输入图像(纯文本+元数据+工具文档)。
为什么?效率。如果每次都要看图才能规划,上下文太长、太慢。让纯文本LLM做"战略层"的分析:
- 这个问题需要哪些工具?
- 证据链是什么?
- 可能需要几步?
阶段II:代码生成(Code Generation)——VLM写代码
VLM接收:问题 + 计划 + 执行轨迹 + 历史可视化图像
输出格式被强制为markdown结构:
**Purpose**: [当前步骤目的]
**Reasoning**: [为什么这样做]
**Next Goal**: [下一步要做什么]
**Code**: [Python代码单元格]
强制结构的好处:模型不会胡写,每一步都有明确的意图和推理。
阶段III:代码执行(Code Execution)——安全沙箱
- AST静态检查:拒绝
exec、eval、文件I/O、网络访问 - 持久内核执行:变量跨步骤保持
- 正则补充:捕获
.save()、.to_csv()等隐蔽写入
阶段IV:反馈组装(Feedback Assembly)——让模型"看见"
| 反馈类型 | 内容 | 用途 |
|---|---|---|
| stdout | print()输出 | 数值结果、验证信息 |
| 变量摘要 | 新变量的类型/长度/大小 | 调试、检查数据形状 |
| 可视化图像 | show()注册的图像 | 直接看分割结果、深度图 |
| 错误追踪 | 精简traceback | 定位错误、修正代码 |
阶段V:答案提交(Answer Submission)——终止条件
- 触发:
ReturnAnswer()调用 - 验证:检查答案格式是否符合问题类型
- 失败:继续循环或强制终止(CoT回退→正则提取)
五、六大公共入口点:模型的"工具箱"
SpatialClaw 定义了六个核心入口点,构成模型的"工作空间":
| 入口点 | 功能 | 关键特性 |
|---|---|---|
InputImages | 图像/视频序列输入 | 最多32帧,长边≤768px |
Metadata | 帧率、时长、帧索引 | 支持时序推理 |
tools | 感知与几何原语 | 核心工具命名空间 |
show(...) | 注册图像用于视觉反馈 | 直接嵌入代理上下文 |
vlm | 独立VLM会话 | vlm.locate()视觉定位,vlm.ask_with_thinking()推理 |
ReturnAnswer(...) | 提交答案 | 终止循环 |
vlm 子会话:它允许代理在迭代过程中随时调用另一个VLM做辅助判断(比如"定位这个物体的像素坐标"或"这张图里有没有什么异常")。这相当于模型自带了一个"参谋"。---
六、核心感知工具:Depth Anything 3 + SAM3
SpatialClaw 的感知层建立在两个强大的视觉模型之上:
tools.Reconstruct:3D场景重建
- 底层:Depth Anything 3 (DA3Nested-Giant-Large)
- 输出:每帧深度图、相机内参/外参、稠密点云
- 关键:真实世界度量尺度,可以直接回答"几米"这种问题
tools.SAM3:分割一切
- 底层:Segment Anything Model 3
- 输入:文本、点、框提示
- 输出:分割掩码
工具命名空间总览
tools.Reconstruct → 3D重建与深度估计
tools.SAM3 → 分割与掩码操作
tools.Geometry → 几何计算(质心、距离、角度)
tools.Mask → 掩码操作(并、交、过滤)
tools.Time → 时序分析
tools.Graph → 场景图构建
tools.Draw → 可视化辅助
---
七、最反直觉的实验发现
结果1:SpatialClaw 平均59.9%,比最好的空间代理高11.2个百分点
| 模型 | 方法 | 20基准平均 |
|---|---|---|
| Gemma4-31B | 无工具 | 53.4% |
| Gemma4-31B | SpatialClaw | 59.9% |
| 提升 | +6.5pp |
多视角和视频推理的提升最大——这正是单次代码和固定API最弱的地方,因为需要跨帧/跨视角的链式推理。
结果2:三种行动接口的正面PK
| 基准 | 无工具 | 单次代码 | 结构化工具 | SpatialClaw |
|---|---|---|---|---|
| 20基准平均 | 53.4% | 55.2% | 56.7% | 59.9% |
| ERQA | 58.3% | 58.3% | 59.0% | 61.3% |
| MindCube | 57.5% | 57.5% | 62.4% | 72.8% |
| MMSI | 37.9% | 42.3% | 43.0% | 51.3% |
| DSI-Bench | 45.3% | 57.9% | 58.4% | 62.9% |
结果3(最反直觉):移除预定义工具函数,性能几乎不变
这是整篇论文最让我震撼的消融实验:
| 变体 | 配置 | 15基准平均 |
|---|---|---|
| SpatialClaw (完整) | 感知工具 + 工具函数 + 科学库 | 56.9% |
| (I) 无工具函数 | 仅SAM3/DA3 + numpy/scipy | 56.4%(-0.5) |
| (II) 无感知工具 | 仅代码接口 + 科学库 | 51.4%(-5.5) |
| 无工具基线 | 仅VLM思考 | 48.7%(-8.2) |
get_centroid、compute_distance这些helper),性能只掉0.5个百分点。这意味着什么?持久内核+Python科学库(numpy、scipy)本身就足够强大,可以补偿大部分预定义工具的功能。 只要给了模型"写Python的自由"和"看中间结果的能力",它自己就能用科学库组合出需要的计算。
换句话说:SpatialClaw的核心价值不是"提供了多少工具",而是"提供了怎样的交互方式"。
结果4:归因分析——52.2%的提升来自"代码组合"
| 归因类别 | 占比 | 说明 |
|---|---|---|
| 代码组合 | 52.2% | 多工具调用链式组合 |
| 控制流 | 19.5% | if/for条件分支与迭代 |
| 接口中性 | 28.3% | 其他因素 |
---
八、工程细节:NVIDIA的硬核实现
双角色服务架构
| 角色 | 功能 | 技术 |
|---|---|---|
| 语言模型服务 | 所有LLM调用(代理、规划器、视觉会话) | vLLM,OpenAI兼容接口,前缀缓存 |
| 感知模型服务 | Depth Anything 3 + SAM3 | 轻量HTTP服务 |
安全沙箱:AST遍历 + 正则补充
# 被拒绝的操作示例
exec("...") # 动态代码执行
open("file.txt") # 文件I/O
requests.get(...) # 网络访问
img.save(...) # 图像写入(正则捕获)
df.to_csv(...) # 数据导出(正则捕获)
如果代码被沙箱拒绝,拒绝原因会返回给代理,支持同步骤修正——不是终止,而是"这行不让写,换个写法"。
帧类型契约:防止静默语义错误
所有帧索引相关的输出都被封装为带类型容器,组合时自动验证索引匹配。防止"把帧A的mask应用到帧B的点云"这种灾难性错误。
错误处理层级:从优雅降级到强制终止
响应格式错误 → 替换占位符,提醒重读格式
↓
沙箱拒绝 → 返回原因,同步骤重试
↓
执行异常 → 精简traceback(仅保留最终异常+源码行)
↓
单元格超时 → 清除命名空间,重新注入环境
↓
感知工具失败 → 自动重试(指数退避,切换端点)
↓
硬预算耗尽 → 强制终止:CoT回退 → 正则提取
---
九、设计哲学:通用原则,而非任务特定指令
SpatialClaw 的系统提示设计非常值得借鉴。它没有任务特定的指令或few-shot示例,而是编码了通用的空间推理原则:
鲁棒计算五原则: 1. median > mean(中位数比均值更鲁棒) 2. 多帧验证(单帧可能出错,多帧交叉确认) 3. 检查度量单位(别把像素当米) 4. 检查数量级(结果合理吗?) 5. 打印验证(用print输出中间值,别盲信)
交叉验证四证据源:
- 视觉感知(分割、深度图)
- 几何计算(距离、角度)
- 可视化(show()看到的)
- 逻辑推理(常识检查)
这些不是针对某个特定任务的提示工程,而是通用的空间推理元认知——教模型"怎么检查自己是否错了"。
---
十、更大的图景:从"工具调用"到"代码交互"
SpatialClaw 的意义远超空间推理本身。它揭示了一个更深层的设计范式转变:
| 范式 | 代表 | 交互方式 | 灵活性 | 可验证性 |
|---|---|---|---|---|
| 无工具 | 原生VLM | 直接生成答案 | 无 | 无 |
| 单次代码 | pySpatial, VADAR | 一次性完整程序 | 高(Python语法) | 无 |
| 结构化工具 | SpaceTools | JSON/XML命令序列 | 低(预定义API) | 有限 |
| 代码交互 | SpatialClaw | 迭代式Python单元格 | 极高(任意组合) | 完全 |
这跟软件开发的真实体验一样——你很少一次性写对100行代码。你写5行,运行,看输出,调试,再写5行。SpatialClaw 让VLM也能这样工作。
这种范式可以扩展到很多领域:
- 数据科学:迭代式数据清洗、可视化、建模
- 机器人控制:观察传感器反馈,调整运动策略
- 科学实验:设计实验、观察结果、调整假设
---
结语:为什么"写Python"比"调API"更适合Agent
SpatialClaw 的标题说得很清楚:"Rethinking Action Interface for Agentic Spatial Reasoning"——不是"新工具",而是"新接口"。
传统工具增强的方法把VLM当成API调用者——给定一组预定义功能,让模型选择调用哪个。
SpatialClaw 把VLM当成程序员——给它一个Python环境、一些基础工具、一个可以反复执行和观察的沙箱,让它自己写代码、调试、修正。
这两者的区别,不是技术细节,是哲学:
- API调用者:功能边界由设计者定义,模型在边界内选择
- 程序员:功能边界由语言定义(Python),模型在语言内创造
NVIDIA + KAIST 的这篇论文,用59.9%的准确率和11.2个百分点的提升,给了一个清晰的信号:在Agent时代,"代码作为行动接口"可能比"API作为行动接口"更自然、更强大。
---
参考来源:
- Cho, S., et al. (2026). "SpatialClaw: Rethinking Action Interface for Agentic Spatial Reasoning." arXiv:2606.13673.
- 项目页面:https://nvlabs.github.io/SpatialClaw
- NVIDIA Research
🌟 智谱 GLM-5 已上线
我正在智谱大模型开放平台 BigModel.cn 上打造 AI 应用,智谱新一代旗舰模型 GLM-5 已上线,在推理、代码、智能体综合能力达到开源模型 SOTA 水平。
🎁 领取 2000万 Tokens