
LLM大语言模型在文本理解、代码生成、知识问答等任务上已经表现得非常惊艳但一旦遇到空间推理Spatial Reasoning问题很多模型会暴露明显的短板。比如让它判断“桌子左边三个物体第二个是什么颜色”或者“A在B的北方B在C的西方那么A在C的哪个方向”模型可能给出看似通顺但完全错误的答案。这类问题不仅在 Hacker News、Reddit 等社区被反复讨论在实际业务中也会成为拦路虎机器人导航指令解析、室内地图问答、3D 场景理解、GIS 空间查询、多模态智能体规划等场景都依赖模型具备稳定的空间推理能力。本文围绕“如何修正 LLM 的空间推理能力”这一主题先分析问题根源再给出从评测、提示工程、外部工具到微调的完整解决路径并提供可运行的代码示例和工程建议。1. 背景与核心概念1.1 什么是 LLM 空间推理能力空间推理是指个体能够根据空间关系进行判断、比较、转换和推导的能力。对 LLM 而言空间推理一般体现在输入文本或图像后能够正确理解并回答关于方位、距离、布局、路径、遮挡关系等空间语义的问题。常见的 LLM 空间推理子任务包括子任务示例难点方位判断“门在桌子的哪一侧”需要理解参照物相对位置链“B 在 A 的右边C 在 B 的前边C 相对 A 在哪里”需要多步推导路径规划“从 A 点走到 D 点怎么绕过障碍物”需要顺序规划和避障判断尺寸比较“杯子比盒子高盒子比椅子矮谁最矮”需要传递性推理二维/三维布局“俯视图从左到右依次是什么颜色”需要视角转换这些任务看起来是常识问题但对 Token 序列化建模的 LLM 来说空间信息被压缩成线性文本模型缺乏真正的几何坐标感知因此容易出现“语言通顺但空间错误”的失败模式。1.2 为什么大模型在空间推理上频频翻车先来看一个典型的空间推理问题房间里有 5 个物体从左到右依次是苹果、香蕉、杯子、盘子、瓶子。 请问杯子右边第二个物体是什么答案应该是“瓶子”。但 LLM 有时会答成“盘子”甚至在推理过程中出现物体排序错乱。为什么会这样主要原因有以下几点空间关系被文本线性化后模型容易丢失“左右优先级”信息。当问题中包含多个参照物时模型需要维护一个心理布局但 Transformer 的注意力机制对这种结构化空间信息的显式建模能力有限。训练数据中的空间问答占比低。通用语料更多包含常识知识、语言搭配而精确的相对坐标、方向推导类数据较少模型没有充分学习到空间关系的约束规则。空间推理与语言流畅性解耦。模型可以生成一句语法正确的话但内部的潜在空间表征并不稳定容易出现“说得对但想错”的情况。参照系混淆。中文里的“左边”“前面”等词在不同语境下绝对方位 vs 以观察者为参照含义会变化模型很难自动区分。理解了这些根因我们才能针对性地设计修正方案。1.3 为什么开发者需要关注这个问题如果你只把 LLM 当作文本聊天机器人空间推理弱一点似乎影响不大。但今天 LLM 已经深度嵌入各类智能系统智能座舱和机器人指令解析用户说“把右边的那个红色箱子拿过来”模型需要正确理解空间坐标。地图和导航助手用户问“从 A 到 B 怎么走”模型需要处理方向、顺序和距离。平面图和室内设计辅助用户说“客厅里沙发靠墙电视在沙发对面”模型需要能重建布局。自动驾驶和安防描述系统需要将自然语言与三维空间感知结果对齐。多模态智能体给定截图或场景图要求模型操作界面元素位置关系判断错误会直接导致操作失败。因此修正 LLM 的空间推理能力是“从玩具到生产力工具”过程中绕不开的问题。2. 先评估再修正空间推理能力怎么量化在动手修正之前建议先建立一套可重复的评测方法。没有量化指标你无法判断提示词改进了一点点还是反而变差了。2.1 公开评测基准你可以先用公开基准做横向对比SpatialEval专门评估 LLM 的空间推理能力包含方向、距离、路径、布局等题目。BIG-Bench 中的 spatial reasoning 任务覆盖二维空间关系、几何直觉等。SPARTQA、spatial-llm 等研究项目提供的测试集。多模态场景可以用 ScanNet 描述、MP3D 导航指令等数据集做迁移评测。不同基准难度差异较大建议在自建测试集上同时保留简单和困难题目避免模型在单一基准上过拟合。2.2 自建测试集思路自建测试集的好处是可以贴合你的业务场景。例如你开发的是一个室内机器人问答系统就应该生成室内布局相关的题目。一个简单的构建方法是使用规则自动生成空间布局# 自动生成空间推理测试数据 import random import json objects [苹果, 香蕉, 杯子, 盘子, 瓶子, 书本, 手机, 鼠标, 耳机, 台灯] positions [左边, 右边, 前面, 后面, 最左边, 最右边] def generate_question(num_objects5): chosen random.sample(objects, num_objects) # 生成长度为 num_objects 的水平布局 layout random.sample(chosen, num_objects) target random.choice(layout) target_idx layout.index(target) # 询问左边第二个是什么 offset random.randint(1, max(1, num_objects - target_idx - 1)) answer_idx target_idx offset answer layout[answer_idx] return { layout: layout, question: f在一条水平线上的物体从左到右依次是{, .join(layout)}。请问{target}右边第{offset}个物体是什么, answer: answer } samples [generate_question() for _ in range(10)] print(json.dumps(samples[:2], ensure_asciiFalse, indent2))运行后你会得到类似这样的测试样本{ layout: [苹果, 耳机, 手机, 书本, 杯子], question: 在一条水平线上的物体从左到右依次是苹果, 耳机, 手机, 书本, 杯子。请问耳机右边第2个物体是什么, answer: 书本 }通过批量生成我们可以构造出几十到几百道空间推理题用来评估模型改进前后的准确率。2.3 一个简单的 Python 评测脚本下面是一个评估 LLM 在自建空间推理数据上表现的脚本。这里以 OpenAI 风格的 API 调用为例实际使用时需要替换为你的模型服务地址和密钥。import json import re from openai import OpenAI client OpenAI(api_keyYOUR_API_KEY, base_urlYOUR_BASE_URL) def ask_llm(prompt, modelgpt-4o-mini): response client.chat.completions.create( modelmodel, messages[ {role: system, content: 你是一个空间推理助手请根据给出的布局信息回答问题只输出答案不要额外解释。}, {role: user, content: prompt} ], temperature0 ) return response.choices[0].message.content.strip() def evaluate(samples, modelgpt-4o-mini): correct 0 total len(samples) for item in samples: answer ask_llm(item[question], modelmodel) # 简单判断答案文本中包含正确答案即视为正确 is_correct str(item[answer]) in answer if is_correct: correct 1 else: print(f错误案例: {item[question]}) print(f预期答案: {item[answer]}, 模型回答: {answer}) print(---) print(f准确率: {correct}/{total} {correct / total:.2%}) # 使用第 2.2 节生成的 samples samples [generate_question() for _ in range(50)] evaluate(samples)需要注意的是真实场景中模型回答可能带着解释上面用“答案中包含正确字符串”的方式判断只是一个简化方案。更严谨的做法是让模型输出 JSON 结构再用程序解析。3. 不换模型也能救提示工程层面的修正很多空间推理问题并不需要训练新模型改动输入方式就能显著提升准确率。下面按成本从低到高介绍几种提示工程方案。3.1 把模糊问题变成结构化输入LLM 天然擅长处理结构化文本。如果将自然语言空间描述转换为表格、列表或伪坐标模型理解的难度会下降很多。例如原来的问题房间里有苹果、香蕉、杯子、盘子、瓶子它们从左到右依次排列。 杯子右边第二个是什么可以改成物体水平排列坐标从左到右递增 - 苹果: x0 - 香蕉: x1 - 杯子: x2 - 盘子: x3 - 瓶子: x4 请计算坐标 x2 的物体右边第 2 个物体是什么。这个改写的本质是把空间关系从隐式的语言描述变为显式的坐标映射让 LLM 的算术和逻辑能力发挥出来。3.2 配合思维链Chain-of-Thought追问如果模型直接跳到最后结论容易出错可以让它先列出推理步骤。一个示例 Prompt请逐步推理回答下面的空间关系问题。 问题B 在 A 的右边C 在 B 的前边D 在 C 的左边。 如果以 A 为原点坐标为 (0,0)B 的坐标设为 (1,0)请推导 C 和 D 的坐标然后回答 D 相对于 A 在什么位置 请按以下步骤 1. 列出每个物体的相对坐标。 2. 用相对位置变换计算最终坐标。 3. 根据最终坐标给出方位结论。这种“显式要求输出推理步骤”的方式并不复杂但对空间关系链较多的题目能明显减少跳步错误。3.3 用坐标和场景图代替自然语言更进一步的思路是不要让模型自己去脑补布局而是在 Prompt 中直接提供结构化场景图。场景图Scene Graph - 节点A, B, C, D, E - 空间关系 - A: center, position(0,0) - B: right_of(A), position(5,0) - C: above(B), position(5,3) - D: left_of(C), position(3,3) - E: below(A), position(2,-2) 问题从 A 到 C 的直线路径方向是什么当模型有了每个节点的绝对坐标空间推理就变成了“从坐标计算方向/距离”难度大大降低。这种做法的核心原则是把模型不擅长的“空间想象”转化为它擅长的“符号运算”。3.4 自洽性采样Self-Consistency空间推理问题中模型单次推理可能因为偶然的注意力偏差而犯错。我们可以让模型生成多个推理链再投票得出最终结果。伪代码思路import statistics def ask_llm_with_reasoning(prompt, modelgpt-4o-mini, n5): responses [] for _ in range(n): response client.chat.completions.create( modelmodel, messages[ {role: system, content: 请逐步推理最终输出答案放在【答案】标记后。}, {role: user, content: prompt} ], temperature0.7 ) responses.append(response.choices[0].message.content.strip()) # 提取答案并进行多数投票 answers [] for resp in responses: match re.search(r【答案】[:]?\s*(.), resp) if match: answers.append(match.group(1).strip()) if not answers: return responses[0] # 基于字符串频率投票 return max(set(answers), keyanswers.count)自洽性采样会增加推理耗时但能显著提升复杂空间推理问题的稳定性适合离线分析和准确率要求较高的场景。4. 进阶方案借助外部工具和知识表示提示工程可以解决一部分问题但遇到高质量要求或复杂空间布局时依赖模型内部推理仍然不够稳定。这时可以引入外部工具或知识表示。4.1 场景图注入与检索增强生成RAG如果你的业务系统本身拥有结构化空间数据比如建筑信息模型BIM、室内地图、平面布局图那么可以把空间关系提前建模为场景图并在推理时检索相关子图注入上下文。一个简化版实现from typing import List, Dict class SceneGraphIndex: def __init__(self): self.nodes {} self.edges [] def add_entity(self, entity_id: str, x: float, y: float, attributes: Dict): self.nodes[entity_id] {x: x, y: y, attributes: attributes} def add_relation(self, from_entity: str, relation: str, to_entity: str): self.edges.append({from: from_entity, relation: relation, to: to_entity}) def retrieve_subgraph(self, center_entity: str, radius: float 10.0) - str: center self.nodes.get(center_entity) if not center: return lines [] for eid, node in self.nodes.items(): dist ((node[x] - center[x]) ** 2 (node[y] - center[y]) ** 2) ** 0.5 if dist radius: lines.append(f{eid}: position({node[x]}, {node[y]}), attributes{node[attributes]}) return \n.join(lines)实际使用中你可以在收到用户问题后将问题的中心实体映射到场景图节点然后检索周边实体连同坐标一起注入 Prompt。这种方案不仅让模型“看得见”空间数据还大大减少了模型猜测布局的错误。4.2 借助代码计算空间关系当空间推理退化为坐标变换和几何计算时最可靠的方式是直接用代码完成而不是让模型心算。一个常见的工程模式是LLM 将用户自然语言转换为结构化查询。程序调用空间计算库如 shapely、geopy完成精确计算。LLM 再把计算结果组织成自然语言回答。示例用 shapely 判断两个多边形的关系。from shapely.geometry import Polygon, Point # 定义两个房间或区域 room_a Polygon([(0, 0), (0, 10), (10, 10), (10, 0)]) room_b Polygon([(10, 0), (10, 10), (20, 10), (20, 0)]) # 判断关系 print(A 与 B 是否相邻边界相交, room_a.touches(room_b)) print(A 的质心, room_a.centroid) print(B 的质心, room_b.centroid) # 给定一个点判断在哪个房间 p Point(15, 5) print(点 (15,5) 在房间 A 内, room_a.contains(p)) print(点 (15,5) 在房间 B 内, room_b.contains(p))输出A 与 B 是否相邻边界相交 True A 的质心 POINT (5 5) B 的质心 POINT (15 5) 点 (15,5) 在房间 A 内 False 点 (15,5) 在房间 B 内 True这种方式的准确率接近 100%代价是需要额外开发空间数据接口但值得在核心业务上投入。4.3 多模态模型的视觉输入如果你的任务涉及图片、截图或者三维场景不再局限于纯文本可以引入多模态大模型。输入一张带网格或坐标标注的图像让模型在视觉空间中进行定位判断。对于复杂场景建议在图像上叠加坐标网格并在 Prompt 中提示模型“先描述物体的大致位置再回答关系”。这是一张平面图图像上叠加了坐标网格左下角为原点 (0,0)。请回答 1. 办公桌的中心坐标是多少 2. 办公桌和窗户之间是否有遮挡 3. 从办公桌到门的最短路径应该朝哪个方向走多模态模型的空间能力并不完美但视觉输入能提供更多锚点对二维布局判断通常优于纯文本。5. 根治思路微调与合成数据如果提示工程和外部工具仍然不能满足你的准确率要求最后的手段是针对空间推理任务做微调。空间推理虽然难但它的训练数据可以低成本合成这反而是个优势。5.1 构造空间推理合成数据合成数据是缓解空间推理训练数据匮乏的重要手段。我们可以用规则生成成千上万条带标注的空间推理问答对。一个数据生成示例import random import json DIRECTIONS [北, 南, 东, 西] DX {北: 0, 南: 0, 东: 1, 西: -1} DY {北: 1, 南: -1, 东: 0, 西: 0} def generate_composite_question(num_objects4): objects [f物体{i} for i in range(num_objects)] positions {} # 从原点开始随机放置物体 x, y 0, 0 positions[objects[0]] (x, y) text_lines [f{objects[0]}在原点(0,0)] for i in range(1, num_objects): d random.choice(DIRECTIONS) step random.randint(1, 3) x DX[d] * step y DY[d] * step positions[objects[i]] (x, y) text_lines.append(f{objects[i]}在{objects[i-1]}的{d}方向{step}米处) # 随机选择一个问题 q_obj random.choice(objects) q_type random.choice([方向, 距离, 相对坐标]) if q_type 方向: ref random.choice(objects) while ref q_obj: ref random.choice(objects) qx, qy positions[q_obj] rx, ry positions[ref] dx qx - rx dy qy - ry if dx 0: answer 东 elif dx 0: answer 西 elif dy 0: answer 北 else: answer 南 question f{q_obj}相对于{ref}在什么方向 else: ref objects[0] qx, qy positions[q_obj] answer f({qx}, {qy}) question f{q_obj}的坐标是多少 return { context: 。.join(text_lines) 。, question: question, answer: answer } data [generate_composite_question() for _ in range(100)] with open(spatial_train.jsonl, w, encodingutf-8) as f: for item in data: f.write(json.dumps(item, ensure_asciiFalse) \n) print(已生成 100 条空间推理训练数据)生成的数据格式为 JSONL每行包含 context、question、answer 三个字段。你可以根据业务场景调整术语、方位类型、物体类型。5.2 微调训练参考流程微调的实现细节会依赖你选择的模型和框架。常见流程如下数据准备将 JSONL 数据整理为指令微调格式例如{instruction: ..., input: ..., output: ...}。选择模型在通用底座基础上微调或使用较小的模型先验证数据质量。训练方式如果资源有限优先考虑 LoRA 等参数高效微调方法。验证与测试保留 10%~20% 的数据作为测试集避免训练数据泄露。下面是一个基于transformerspeft的简化微调脚本示例只表示流程思路实际参数需要根据环境和模型调整from datasets import load_dataset from transformers import AutoModelForCausalLM, AutoTokenizer, TrainingArguments from peft import LoraConfig, get_peft_model from trl import SFTTrainer model_name Qwen/Qwen2.5-1.5B-Instruct tokenizer AutoTokenizer.from_pretrained(model_name) model AutoModelForCausalLM.from_pretrained(model_name, trust_remote_codeTrue) lora_config LoraConfig( r8, lora_alpha32, target_modules[q_proj, k_proj, v_proj, o_proj], lora_dropout0.1, biasnone, task_typeCAUSAL_LM ) model get_peft_model(model, lora_config) training_args TrainingArguments( output_dir./spatial-lora, num_train_epochs3, per_device_train_batch_size4, gradient_accumulation_steps4, logging_dir./logs, save_steps500, save_total_limit2, remove_unused_columnsFalse, ) trainer SFTTrainer( modelmodel, argstraining_args, train_datasetload_dataset(json, data_filesspatial_train.jsonl)[train], tokenizertokenizer, max_seq_length1024, dataset_text_fieldprompt, ) trainer.train()注意上面代码只是给出思路SFTTrainer的dataset_text_field需要你的数据中预先构造好完整的 prompt 文本字段并且依赖版本需要匹配。5.3 数据质量与验证合成数据最大的风险是“模型练熟了模板却遇不到真实分布的题”。所以在微调前要对数据做多样性检查方位类型是否覆盖东南西北、上下左右、前后距离步长是否有变化物体名称是否与业务场景一致是否包含多步推理的复合问题是否包含负样例比如“不在……方向”微调后不要只看训练集准确率一定要用一份独立的、人工标注的真实业务测试集来评估。6. 常见问题与排查思路在实际操作中容易遇到下面这些问题。这里整理成表格方便快速定位。问题现象常见原因解决思路提示词加了坐标后准确率仍然不高坐标信息过于稀疏或模型没有理解坐标定义在 Prompt 开头加上坐标系统说明并辅以示例尽量使用整数坐标模型能复述布局但回答位置关系时出错模型没有把布局转换为内部空间表征强制模型先输出“布局表格”或“坐标列表”再回答引入思维链后输出过长但结果不变推理步骤太泛化缺少中间计算约束使用“请给出每个物体的坐标”等硬性中间变量自洽性采样后答案不稳定本身任务难度较高或 prompt 存在二义性检查问题描述中的参照系是否唯一增大采样数量微调后训练集准确率高但测试集低合成数据分布与真实数据不一致加入真实业务数据进行混合训练扩充数据多样性外部工具计算与 LLM 回答不一致工具返回结果没有正确注入模板建立标准化的工具输出模板并在 prompt 中强制引用多模态模型仍然出错图片分辨率低或坐标网格不明显裁剪局部图像、放大目标区域后再提问排查空间推理问题时建议遵循以下顺序先判断问题本身是否含有歧义。例如“左边”是观察者的左边还是物体的左边再确认输入是否结构化。自然语言描述越结构化模型正确率越高。检查是否依赖模型记忆。如果物体布局每次变化模型需要的是“即时推理”而不是背诵固定答案。最后再考虑提示工程、外部工具和微调的叠加使用。7. 最佳实践与工程建议结合实际项目落地经验下面给出几条专门针对 LLM 空间推理改进的工程建议。7.1 优先使用显式坐标而不是隐式方向词在任何空间推理任务中能用坐标表示就不要用模糊的方向词。方向词受参照系影响很大而坐标是绝对信息。例如“A 在 B 的右侧”不如“A 的 x 坐标大于 B 的 x 坐标”稳定。7.2 把空间数据做成服务而不是塞进模型如果你的应用涉及大量真实空间数据建议把空间关系计算独立成服务。LLM 负责意图理解和语言生成空间计算交给程序完成。这样可以兼顾准确率和扩展性。7.3 建立空间推理回归测试集空间推理问题很容易在模型版本升级后发生回归。建议将业务中真实出现的空间问题沉淀为回归测试集并接入 CI/CD 流程。每次更换模型或修改提示词时自动跑一遍测试。一个简单的回归测试文件[ { id: case_001, layout: 苹果, 香蕉, 杯子, 盘子, 瓶子, question: 杯子右边第二个物体是什么, expected: 瓶子 }, { id: case_002, question: B 在 A 的东边C 在 B 的北边C 在 A 的哪个方向, expected: 东北 } ]这样的回归测试并不难维护但对长期演进帮助巨大。7.4 记录失败模式反向优化提示词当模型出错时不要只标记“错了”要记录错误类型是方向反了是参照系理解错了是中间步骤跳步了是坐标计算错了通过失败模式分析你可以针对性设计提示词模板。例如如果发现模型经常把“右边”和“左边”搞反就可以在 Prompt 中显式加入“请以观察者视角判断左右观察者面向布局方向。”7.5 注意数据隐私与成本微调和外部工具接入过程中要关注数据隐私问题。如果空间数据涉及真实建筑平面图、用户位置等敏感信息建议在本地化环境运行模型或者在数据进入模型前做脱敏处理。7.6 设置合理的模型回退机制在实际系统中可以设计分级策略简单空间问题直接让 LLM 回答。复杂空间问题调用坐标计算服务。无法解析的问题回退到人工处理或模糊查询。这种“人机协同 规则兜底”的架构远比单一口径的 LLM 解决方案稳定。8. 总结与下一步学习路线修正 LLM 的空间推理能力并不是一个单一技巧可以搞定的事情。从本文的完整路径来看你应该遵循以下思路先评估用基准测试和自建测试集量化模型的基线能力。再做提示工程把模糊自然语言转化为结构化坐标、场景图并辅以思维链和自洽性采样。引入外部工具空间计算、场景图检索、多模态视觉输入。最后考虑微调通过合成数据业务数据做 LoRA 微调从根源上强化模型的空间关系建模能力。这四步的成本逐级升高效果也逐级更扎实。对大多数业务场景建议从“结构化输入 外部工具兜底”开始不要一上来就投入大量算力做微调。下一步可以继续深入的方向有从二维空间推理延伸到三维空间关系例如俯仰角、遮挡关系、深度顺序。研究基于场景图的 LLM 推理增强把空间知识库与检索增强生成结合起来。在多模态智能体中验证空间推理的收益例如让模型操作平面设计工具或室内导航系统。关注空间推理评测集的最新论文理解学术界如何定义和度量这类能力。空间推理是 LLM 通往物理世界智能体的关键能力之一目前远没有解决但通过组合工程手段可以做到“在可控场景下足够可靠”。希望本文能给你提供一套系统化的修正思路。如果你在项目中遇到有意思的空间推理失败案例欢迎在评论区分享一起讨论排查经验。