在 Hacker News 上,“How do you correct spatial reasoning of LLMs?” 这个讨论所反映的问题,很多做 LLM 应用的团队都遇到过。文本摘要、意图识别、检索问答做得不错,一旦进入导航指令、室内布局、CAD 辅助、自动排版等场景,模型就可能把“左边”和“右边”搞混,在坐标换算上给出错误结果,甚至对简单二维网格里的移动路线都算不明白。
空间推理能力弱,不是某一类模型独有的缺点。它更接近大语言模型的基本短板:模型通过文本 token 预测进行生成,内部没有连续的坐标轴、没有视觉渲染器,也没有可靠的几何计算引擎。它依赖训练数据中的语言模式来推断空间关系,这种推断在简单情形下可以蒙对,在稍微复杂或视角多变的情形下就会崩塌。
这篇文章的目标不是推荐某种“万能模型”,而是给出一条可复现的工程纠正路径。你会看到如何用少量测试题判断模型到底错在哪里,如何用提示词、JSON 结构和确定性工具把空间推理从“让模型硬猜”变成“让模型调用代码计算并验证”,以及在实际项目中接入校验、重试和权限控制时应该注意哪些坑。阅读前最好已经了解 LLM API 的基本调用方式和 function calling 的概念。
1. 先理解 LLM 空间推理问题的本质,才能决定该纠正什么
1.1 空间推理出错时,一般会出现哪些现象
LLM 空间推理出问题,通常表现为几类现象:
第一类是方向关系颠倒。比如“苹果在梨的右边,梨在苹果的哪一边”,模型可能直接回答“右边”。这个问题在二维平面上属于基础方向判断,但模型如果没建立坐标系,就只能靠语言模式猜测。
第二类是坐标计算错误。比如“物体起点在坐标 (2,3),向右走 4 步再向下走 2 步,最终坐标是多少”,正确答案是 (6,1)。模型可能把“向下”误认为 y 坐标增加,输出 (6,5),也可能把 x 和 y 搞混。
第三类是二维或三维布局混淆。当问题同时包含多个物体、多层嵌套关系、前后关系时,模型经常丢失约束。例如“书架一共有三层,第一层有 5 本书,第二层比第一层多 2 本,第三层是第二层的 2 倍”,这种问题已经接近数值推理,但空间版本会变成“物体 A 在物体 B 的左前方,物体 C 在物体 A 的右侧,C 相对于 B 在哪里”,此时模型需要同时维护多个坐标关系。
第四类是信息不足时强行回答。现实空间问题经常缺少距离、尺寸或视角信息,模型却倾向于给出一个确定答案,而不是识别出“无法唯一确定”。这种问题的危害比算错坐标更隐蔽,因为在生产环境里,一个自信但错误的布局结果会直接导致后续决策错误。
1.2 LLM 为什么会在这类任务上失效
LLM 本质上是自回归语言模型,每生成一个 token 都在做条件概率采样。它在生成“左”和“右”时,不是在读取一张空间地图,而是在训练数据形成的文本分布中选择更可能的词。训练语料里出现“A 在 B 左边,C 在 A 右边”这种句式时,模型会利用共现模式推断,但共现模式不等于几何运算。
空间推理高度依赖中间状态。人类解决这类问题会先在脑内建立坐标系,把每个物体的位置记录成坐标,再根据约束更新坐标。LLM 没有显式工作记忆,上下文里即使已经写了“A 的坐标是 (-1,0)”,后续生成也可能忽略这个状态。这是模型在路径规划和多物体排序中频繁出错的主要原因。
另一个容易被忽略的因素是观察者视角。中文里“左边”可能指观察者的左边,也可能指图中人物的左边。模型没有图像或实时环境,只能依赖上下文猜测。如果不固定视角,任何纠正策略都没有稳定基础。
模型还会在 token 化过程中丢失数值结构。坐标“12”可能被拆成两个 token,整数加减运算对 LLM 来说是概率任务而不是计算任务。即使参数量很大,模型也只是在模仿“看起来像计算”的文本,并不具备可验证的算术引擎。这也是为什么单纯把模型变大,空间推理能力不一定同步提升。
1.3 模型部署精度和空间推理能力不是一回事
关于 LLM 的精度问题(fp16、fp32、bf16),开发者在部署模型时经常遇到。这些参数影响张量的存储精度、显存占用和推理速度,但不改变模型的空间关系理解能力。
一个在 fp16 下左右判断混乱的模型,换成 bf16 或 fp32 加载后,大概率还是左右混乱。因为问题不出在权重数值精度,而在于任务没有被分解成可验证的步骤。fp16 与 bf16 在指数位和尾数位上的差异,可能导致极端数值场景下的微小偏差,例如很长坐标累加时结果不稳定,但这和“模型是否理解左右相对关系”是两个维度。
实际项目里要注意一个现象:同一模型在不同推理引擎或不同精度下,输出可能有细微波动,尤其在 temperature 大于 0 时更明显。如果判断空间推理是否被修复,必须固定精度、固定采样参数,使用同一套测试集做批量回归,否则很容易误判为“换精度后变好了”。
注意:不要看到模型在某个精度下答对了几道题,就认为是精度修复了空间推理。先跑一轮至少 20 条测试题,再下结论。
2. 先做诊断:用一组可量化的测试题找出模型的空间推理短板
2.1 建议建立一个小型空间推理评测集
纠正空间推理,第一步不是改代码,而是建立评测集。没有可重复的评估,后续所有调整都无法判断是变好还是变坏。
建议准备 5 个难度层,每层准备 4 到 6 道题,初期 20 到 30 条足够发现问题。测试题要覆盖方向、坐标、路径、多物体关系、信息不足五种情况。
第一层,基础方向判断:“桌上有苹果和梨,苹果在梨的右边,梨在苹果的哪一边”这类题可以快速看模型是否掌握左右语义。
第二层,多物体排序:“A 在 B 的左边,C 在 B 的右边,三个物体从左到右的顺序是什么”。
第三层,坐标换算:“物体起点在坐标 (2,3),向右走 4 步再向下走 2 步,最终坐标是多少”。
第四层,路径约束:“在一个 5x5 网格中,从 (0,0) 移动到 (4,4),每次只能向右或向上移动,给出一种路径”。
第五层,信息不足题:“物体 P 在 Q 的左前方,Q 的坐标是 (3,3),P 的坐标是否唯一?说明理由”。这类题用来判断模型是识别不确定性,还是强行给答案。
评分不必只判对错,可以给错误分类,例如方向反了、坐标计算错、输出格式错、信息不足判断失败。每个错误类型对应不同的修复策略。
2.2 用最小脚本批量跑模型
拿到测试集之后,需要一个小脚本来批量调用 LLM。下面是通用接口调用示意,不同服务商需要替换 endpoint、model 和鉴权方式。建议把 API key 存在环境变量里,不要硬编码进代码。
import os import requests def call_llm(prompt, model="", endpoint_url="", api_key=""): # endpoint_url 是服务商提供的 API 地址,model 是模型名称 # api_key 建议从环境变量读取 resp = requests.post( f"{endpoint_url}/chat/completions", headers={"Authorization": f"Bearer {api_key}"}, json={ "model": model, "messages": [ { "role": "system", "content": "你是一个空间推理助手,回答要简洁,并且严格按用户要求输出。", }, {"role": "user", "content": prompt}, ], "temperature": 0, }, timeout=30, ) resp.raise_for_status() return resp.json()["choices"][0]["message"]["content"]调用时设置temperature为 0,是为了减少随机采样带来的输出波动。空间推理是确定性问题,随机性越强越难评估真实能力。如果模型支持 JSON mode 或其他结构化输出参数,可以在这一阶段先不开启,因为你要看的是模型在自由输出下的原始水平。
检查点很简单:脚本能拿到回答,并且每条输出都记录到结果表里,定位到具体题目。先不急着优化,把第一次结果整理成错误分类清单。
2.3 结果记录与错误分类
推荐用表格记录每一轮评测结果,而不是只记录模型回答。留一条手工复核列,因为模型可能答对了最终结果,但中间推理完全错误。空间推理项目里,中间过程往往比最终答案更重要。
| 题目编号 | 难度层 | 模型输出 | 是否通过 | 错误类型 | 人工备注 |
|---|---|---|---|---|---|
| 01 | 方向判断 | 右边 | 否 | 左右反转 | 没有定义视角 |
| 02 | 坐标换算 | (6,5) | 否 | 坐标计算错误 | 向下移动方向理解错 |
| 03 | 信息不足 | P 在 (2,2) | 否 | 强行给出唯一答案 | 缺少距离信息 |
如果错误集中在“左右反转”,修复重点是固定观察者视角;如果集中在“坐标计算错误”,修复重点是接入外部工具;如果集中在“强行给出唯一答案”,修复重点是让模型把问题转换为坐标系并识别约束不足。错误分类决定了后面每一步的重心。
3. 不改模型也能改善空间推理:提示结构与输出约束
3.1 强制把空间问题转成坐标和步骤
空间推理最常见的错误是模型跳过中间计算直接猜答案。一个有效的提示词策略是要求模型先定义坐标系,再把每个空间描述转换成坐标约束。
下面是一个提示词示例:
你是一个空间推理助手。对于