规则合规的视觉空间规划:多模态大模型落地工程方法

发布时间:2026/9/29 14:54:43
规则合规的视觉空间规划:多模态大模型落地工程方法 之前在做一个物体摆放的视觉应用时反复卡在一个“不起眼但又很致命”的问题上模型生成的摆放位置经常超出桌面边界或者和已有物体严重重叠。试了很多提示词版本结果时好时坏。后来把问题拆开才发现真正的瓶颈并不是模型认不出场景而是它输出的空间规划结果缺少一层“规则校验”兜底。网上关于多模态大模型做识别和问答的资料很多但专门讲“规则合规的视觉空间规划”怎么落地实现的几乎找不到一套系统方案。这篇文章就把这套工程方法完整拆解出来包含核心概念、难点分析、可运行的参考代码和真实项目中常见的问题排查思路。适合正在做多模态应用、机器人视觉、智能家居或者 LLM Agent 的开发者阅读零基础也能顺着步骤理解整体设计。1. 背景与核心概念1.1 什么是多模态大语言模型多模态大语言模型Multimodal Large Language ModelMLLM是在传统文本大语言模型基础上增加了图像、音频、视频等输入模态能力的模型。常见的形态是“文本 图像”输入输出自然语言或结构化文本。例如输入一张办公室桌面照片模型可以说出“桌面上有一台显示器、一个水杯、一个键盘”也能回答“键盘在显示器的左边还是右边”这类空间关系问题。这种能力来自视觉编码器和语言模型的结合视觉编码器把图像转换成特征向量语言模型再基于这些特征做推理和回答。从技术架构看MLLM 并不是一个全新的模型类型更像是“视觉理解模块 语言推理模块”的组合。当前主流做法包括视觉编码器如 CLIP、SigLIP 等提取图像特征视觉-语言连接模块Projector / Adapter把图像特征映射到语言模型的语义空间语言模型Decoder-Only 架构完成推理、生成和输出。这类模型在图像问答、内容描述、OCR 识别、视觉推理等任务上表现很强。但把它用于“给图像中的物体规划一个新位置”时问题就开始出现了。1.2 什么是视觉空间规划视觉空间规划指的是模型根据一张视觉输入比如桌面照片、房间图、仓库俯视图输出一个或一组空间布局决策。典型任务是“桌面上已经放了水杯和笔记本请把键盘放在哪里”这个任务与普通的视觉问答不同。问答只需要模型描述位置关系而空间规划要求模型生成一个具体的、可执行的坐标或区域。这个输出往往是二维坐标(x, y)目标检测框(x1, y1, x2, y2)区域名称如“桌子左上角”或者一段可执行代码如“移动到坐标 400, 300 处摆放”。真正的难点在于模型不能只“看着像合理”就可以它还需要满足任务里给定的约束条件。例如“不能超出桌面”“不能与已有物体重叠”“易燃物不能靠近热源”。当这些约束变成硬性规则时模型自由生成文本的方式就很难保证每次都对。1.3 规则合规为什么是刚需标题中的 Rule-Compliant规则合规强调一个关键点模型的输出必须可以被验证、被约束、被修正而不是“大致正确”。在实际工程项目里规则合规不是锦上添花而是安全底线。一个机器人把杯子放到桌子边缘外会直接导致物品掉落一个仓储调度系统把重物规划到货架承重不足的区域可能引发安全事故一个室内导航系统把路径规划穿过墙则根本无法执行。更现实的问题是多模态大语言模型是概率生成模型同样的输入多次生成结果可能不同。如果没有规则校验层模型的输出质量就很难控制。因此工程上不能只依赖模型“自己理解规则”必须把规则变成代码层面的检查逻辑对模型输出做强制校验。这也是本文方案的核心提示词让模型尽量输出合理结果校验器保证输出真正合规重生成机制在违规时自动修正。2. 视觉空间规划的主要难点2.1 空间关系的理解偏差虽然 MLLM 能回答“杯子在笔记本左边”这种简单问题但它对精确空间坐标的感知并不稳定。原因在于视觉编码器输出的是语义特征而不是像素级的空间几何信息。比如模型可能知道“杯子在桌子左上区域”但当要求它输出具体坐标(156, 89)时它需要把语义位置映射到图像坐标系。这一步映射并不精确常见表现是坐标偏移几十像素生成的坐标落在物体中心点之外把“左边”理解为“左上”忽略图像中桌面以外的区域。这种误差在单纯问答场景可以接受但在需要实际执行的空间规划中会直接导致失败。因此工程方案不能假设模型坐标绝对准确而必须叠加校验和修正步骤。2.2 规则与生成之间的矛盾语言模型的目标函数是“生成概率最高的文本”而不是“生成满足规则约束的文本”。它内部并没有一个可微的规则检查器所以规则只能通过提示词“软性”传入模型。这带来一个矛盾规则越精确模型越容易在长文本生成过程中“忘记”或“选择性忽略”部分规则。例如你告诉它“物体中心必须距桌边至少 20 像素任意两物体间距不小于 60 像素”模型在生成 JSON 时可能记住了第一条却违反了第二条。换句话说提示词约束是必要的但远不够充分。想让输出规则合规必须把“规则判断”从模型内部移到模型外部用代码实现确定性的判断。2.3 现有方案的短板当前常见处理方式可以分成三档第一档只在提示词里写规则。优点是无成本缺点是模型不稳定规则越多越容易出错。第二档提示词加输出格式约束。例如要求模型必须输出 JSON可以解决解析问题但不能解决空间规则问题。第三档生成后做校验但校验失败只报错不让模型重新生成。这种方式在离线分析中可用在真实交互流程中效率低。理想的方案是把三者结合用提示词引导模型靠近正确结果用结构化输出保证可解析性用确定性校验器拦截一切违规输出在校验失败后把错误信息反馈给模型继续修正。下面几个小节正是按这个思路展开的。3. 核心技术思路如何让规划输出真正“守规矩”3.1 规则建模先机器可检查再语言可描述很多项目会把规则写成自然语言“请把东西放在桌面上不要挡住其他物品。”这种描述对模型友好但对程序无法执行。工程上需要把规则拆解成可计算的条件。建议把每一条规则抽象成四个要素规则名称规则描述用于提示词判断函数用于代码校验违反时的反馈文本用于重新生成。以“桌面边界”为例可以这样建模规则名称inside_boundary描述物体中心必须位于桌面区域内且距离桌边至少safety_margin像素。判断safety_margin center_x board_width - safety_margin且safety_margin center_y board_height - safety_margin反馈文本物体 new_1 中心点超出边界或距桌边过近同理“物体间距”规则可以建模为任意两个物体中心点的欧氏距离大于等于min_distance。在实际项目中规则往往不止两三条。常见的空间规划规则还包括规则类型示例边界约束物体不能超出可放置区域间距约束物体之间保持最小安全距离重叠约束物体边界框重叠面积比例不能超过阈值类别冲突热源与易燃物不能相邻优先级约束重物优先放置于承重区可达性约束机器人末端必须能触及目标点规则建模是后续所有工作的基础。规则定义得越清晰提示词越好写校验器也越好实现。3.2 提示词工程把规则写进上下文规则建模之后需要把规则翻译成模型能理解的提示词。这里有几个关键经验。第一规则要放在 System Prompt 中而不是放在 User Prompt 中。System Prompt 的作用是定义模型角色和长期约束模型在生成时会优先参考这部分内容。第二规则之外需要给出明确的输出格式。最好把 JSON Schema 直接写在提示词里并给出一个空模板。这样能显著提升模型输出的可解析性。第三提示词中要加入“违反规则时如何输出”的说明。例如“如果规则无法满足将placement_valid设为 false并说明原因。”这一步非常重要否则模型在无法满足约束时会强行生成一个不合规的坐标而不是主动承认失败。第四企业级项目中应该对提示词做版本管理。因为提示词的小改动也可能影响模型输出建议把提示词模板保存为单独文件并记录版本和变更原因。3.3 结构化输出约束结构化输出是让模型输出可被程序安全处理的必要条件。空间规划的目标输出通常是一个 JSON 对象例如{ reasoning: 键盘放在显示器前方空位距离水杯约 120 像素满足间距要求, placements: [ { object_id: new_1, category: keyboard, center: [400, 300] } ] }要让模型稳定输出这种结构提示词中必须给出完整示例。如果模型偶尔输出 Markdown 代码块或额外解释文本程序端还需要做容错解析。有些模型服务商提供了 JSON Mode 或 Function Calling 能力可以进一步约束输出。但要注意不同平台的实现差异很大依赖这些能力之前应先确认目标平台的接口文档。通用方案是采用“提示词声明 代码解析兜底”的组合方式。3.4 校验-重生成闭环这是整套方案的核心。流程可以简单概括为模型生成候选规划结果解析模型输出校验器逐条检查规则如果全部通过返回结果如果有违规把违规信息拼进提示词让模型重新生成超过最大重试次数仍然违规则返回失败并上报人工处理。为什么要做重生成而不是直接拒绝因为模型是概率生成一次采样失败不代表模型理解不了规则。你把“错误原因”明确告诉它之后第二次生成的成功率会明显提升。这一点在项目实践中非常有用。重生成时常见的做法有两种。一种是保留原始 user_prompt在末尾追加“你上一轮的输出违反了以下规则……请重新输出”。另一种是启用多轮对话把前一回合的输出作为历史消息传入。前者实现简单后者上下文更完整。对于空间规划任务通常前者足够。3.5 约束解码与后处理除了“生成后校验”学术界和工业界也在探索更强的约束方式即约束解码。约束解码不是在模型生成完成后再检查而是在模型解码每个 token 时实时过滤那些会导致规则违反的候选 token。这种方式的优点是准确率高缺点是实现复杂度高而且很多规则无法在 token 级别判断。比如“两个物体中心距离大于 60 像素”这种全局约束只有生成完整 JSON 之后才能计算。因此约束解码更适合简单格式约束而本文的“校验-重生成”闭环更适合复杂空间规则。后处理是另一个值得提的思路。假如模型输出的坐标只是轻微越界可以直接做投影修正把坐标“拉回”合法区域。例如把center_x钳位到[safety_margin, board_width - safety_margin]区间内。这种方法可以在不调用模型的情况下快速修复边界类规则但对间距、重叠这类涉及多个物体的规则无效。更好的做法是把它作为校验失败后的“轻量修正层”修正完再进入规则校验。4. 完整实战桌面物品摆放规划4.1 任务定义为了让方案可运行、可验证我们设计一个具体的演示任务输入一张桌面照片或桌面的检测结果桌面宽 800 像素、高 600 像素桌面上已有若干物体。输出为待放置的新物体比如键盘给出一个位置坐标要求同时满足四条规则桌面边界规则物体中心点距桌边至少 20 像素最小间距规则任意两个物体中心点距离不小于 60 像素重叠规则任意两个物体边界框的重叠面积比例不超过 0.2JSON 输出规则模型只能输出合法 JSON不能包含额外解释。我们将使用一个兼容 OpenAI SDK 的多模态模型客户端来调用模型用一个独立的校验器对象完成规则检查再用一个反馈规划器闭环控制重生成。4.2 环境准备本文代码以 Python 3.9 及以上版本为例核心依赖如下pip install openai pillow如果模型调用走的是自有或云厂商的 HTTP 服务需要确认该服务兼容 OpenAI 的 Chat Completions 接口如果不兼容只需要替换llm_client.py中的请求实现其余代码结构可以保持不变。本文示例中的MODEL_NAME qwen-vl-plus只是一个占位名称你需要替换成实际使用的多模态模型名称并通过API_BASE指向对应的服务地址。版本与接口参数请以你使用的模型服务文档为准本文重点演示工程思路。4.3 项目结构spatial_planner/ ├── config.py # 全局配置 ├── rules.py # 规则定义 ├── scene_parser.py # 场景信息提取 ├── prompt_builder.py # 提示词构建 ├── llm_client.py # 模型客户端 ├── validator.py # 规则校验器 ├── planner.py # 反馈规划器 ├── visualize.py # 结果可视化 └── main.py # 主流程入口每个文件职责单一方便后续替换模型服务和增加规则。4.4 规则定义# 文件路径spatial_planner/rules.py from typing import List, Dict def build_rule_text() - str: return 1. inside_boundary物体中心点必须位于桌面区域内且与桌面边缘的距离 20 像素。 2. min_distance任意两个物体中心点之间的欧氏距离 60 像素。 3. max_overlap任意两个物体边界框的重叠面积比例不得超过 0.2。 4. valid_json输出必须是合法 JSON且只能包含规定字段。 这里用函数把规则文本集中管理后续修改规则时只需要改一个文件。4.5 场景信息提取真实项目中我们需要从照片里获取“已有物体的位置”。这可以通过目标检测模型如 YOLO、DETR、GroundingDINO或云厂商的视觉 API 完成。为了把核心逻辑讲清楚这里的parse_scene使用模拟数据返回结构保持与实际检测结果一致。# 文件路径spatial_planner/scene_parser.py from typing import List, Dict def parse_scene(image_path: str) - List[Dict]: 从图像中提取已有物体的位置和类别。 实际项目可替换为检测模型或在线视觉 API。 返回格式 [ { id: obj_1, category: mug, center: [150, 250], bbox: [110, 210, 190, 290] } ] # 演示数据真实项目中这里应调用检测模型 demo_objects [ { id: obj_1, category: mug, center: [150, 250], bbox: [110, 210, 190, 290], }, { id: obj_2, category: notebook, center: [500, 400], bbox: [440, 340, 560, 460], }, ] return demo_objectsbbox采用[x1, y1, x2, y2]格式即左上角和右下角坐标。center用于计算物体之间的距离bbox用于计算重叠面积。如果你的检测模型输出的是其他格式需要在解析阶段完成格式统一。4.6 提示词构建# 文件路径spatial_planner/prompt_builder.py from typing import List, Dict def build_system_prompt(rule_text: str) - str: return f你是一个视觉空间规划助手。用户会提供桌面场景和待放置物体。 你必须遵循以下规则生成放置方案 {rule_text} 输出要求 1. 只输出 JSON不要输出任何解释或 Markdown 标记。 2. JSON 格式 {{ reasoning: 简述规划依据, placements: [ {{object_id: new_1, category: keyboard, center: [400, 300]}} ] }} 3. center 是图像像素坐标误差控制在 20 像素以内。 4. 如果找不到任何满足规则的放置点输出 {{ reasoning: 无法找到合规位置, placements: [] }} def build_user_prompt( existing_objects: List[Dict], new_objects: List[Dict], board_width: int, board_height: int, ) - str: existing_text \n.join( f{obj[id]}: {obj[category]}, center{obj[center]}, bbox{obj[bbox]} for obj in existing_objects ) new_text , .join(f{obj[id]} ({obj[category]}) for obj in new_objects) return f桌面尺寸{board_width} x {board_height} 已有物体 {existing_text} 请为以下新物体规划位置 {new_text} 请直接输出 JSON。提示词中显式给出手边可计算的桌面尺寸和已有物体信息避免模型“凭空猜测”。把“无法找到合规位置”作为一种合法输出能在危机场景下防止模型硬编一个错误坐标。4.7 模型客户端# 文件路径spatial_planner/llm_client.py import base64 from typing import Optional class BaseMLLMClient: 所有模型客户端的抽象基类便于替换不同服务商。 def chat(self, system_prompt: str, user_prompt: str, image_path: Optional[str] None) - str: raise NotImplementedError class OpenAICompatibleClient(BaseMLLMClient): 兼容 OpenAI Chat Completions 协议的多模态模型客户端。 def __init__(self, api_key: str, model_name: str, base_url: Optional[str] None): try: from openai import OpenAI except ImportError as exc: raise RuntimeError(请先安装 openaipip install openai) from exc self._client OpenAI(api_keyapi_key, base_urlbase_url) self._model_name model_name def chat(self, system_prompt: str, user_prompt: str, image_path: Optional[str] None) - str: content [{type: text, text: user_prompt}] if image_path: if image_path.startswith(http://) or image_path.startswith(https://): image_url image_path else: with open(image_path, rb) as f: encoded base64.b64encode(f.read()).decode(utf-8) image_url fdata:image/png;base64,{encoded} content.append({type: image_url, image_url: {url: image_url}}) resp self._client.chat.completions.create( modelself._model_name, messages[ {role: system, content: system_prompt}, {role: user, content: content}, ], temperature0.2, ) return resp.choices[0].message.content在项目中建议始终使用BaseMLLMClient定义调用接口之后无论换哪个服务商只需要新增一个子类即可。例如本地部署的多模态模型可以使用 vLLM 或 Ollama 的 OpenAI 兼容接口直接复用这个类。4.8 规则校验器# 文件路径spatial_planner/validator.py import math from typing import List, Dict class RuleValidator: def __init__( self, board_width: int, board_height: int, min_distance: int, safety_margin: int, max_overlap_ratio: float 0.2, ): self.board_width board_width self.board_height board_height self.min_distance min_distance self.safety_margin safety_margin self.max_overlap_ratio max_overlap_ratio def _is_inside_board(self, obj: Dict) - bool: cx, cy obj[center] return ( self.safety_margin cx self.board_width - self.safety_margin and self.safety_margin cy self.board_height - self.safety_margin ) def _distance(self, a: Dict, b: Dict) - float: ax, ay a[center] bx, by b[center] return math.hypot(ax - bx, ay - by) def _overlap_ratio(self, a: Dict, b: Dict) - float: ax1, ay1, ax2, ay2 a[bbox] bx1, by1, bx2, by2 b[bbox] ox1, oy1 max(ax1, bx1), max(ay1, by1) ox2, oy2 min(ax2, bx2), min(ay2, by2) if ox2 ox1 or oy2 oy1: return 0.0 inter_area (ox2 - ox1) * (oy2 - oy1) a_area (ax2 - ax1) * (ay2 - ay1) if a_area 0: return 1.0 return inter_area / a_area def validate(self, existing_objects: List[Dict], placements: List[Dict]) - List[str]: 校验新增物体是否满足规则。 返回违规信息列表空列表表示全部通过。 violations [] all_objects existing_objects placements # 规则 1边界约束 for p in placements: if not self._is_inside_board(p): violations.append(f物体 {p[id]} 中心点超出边界或距桌边过近) # 规则 2、3间距与重叠约束 for i in range(len(all_objects)): for j in range(i 1, len(all_objects)): a, b all_objects[i], all_objects[j] if self._distance(a, b) self.min_distance: violations.append( f物体 {a[id]} 与 {b[id]} 距离小于 {self.min_distance} 像素 ) if self._overlap_ratio(a, b) self.max_overlap_ratio: violations.append( f物体 {a[id]} 与 {b[id]} 重叠面积超过 {self.max_overlap_ratio:.0%} ) return violations这里需要注意上面的实现会把“已有物体之间”的距离也纳入检查。如果检测器给出的原始数据本身就存在物体过近全量比较会把历史问题也报出来。实际项目中建议只校验“新增物体与所有物体”之间的关系避免历史数据干扰新规划。4.9 决策闭环反馈规划器# 文件路径spatial_planner/planner.py import json import re from typing import List, Dict, Optional def extract_json(text: str) - str: 从模型输出中提取 JSON 片段兼容 Markdown 代码块。 text text.strip() if text.startswith(): text re.sub(r^(?:json)?|$, , text, flagsre.MULTILINE).strip() start text.find({) end text.rfind(}) if start -1 or end -1 or end start: raise ValueError(模型输出中未找到合法 JSON) return text[start:end 1] class FeedbackPlanner: 生成 - 解析 - 校验 - 反馈重试 的闭环控制器。 max_retries 控制最大尝试次数防止无限循环。 def __init__(self, llm: BaseMLLMClient, validator: RuleValidator, max_retries: int 3): self.llm llm self.validator validator self.max_retries max_retries def run( self, system_prompt: str, user_prompt: str, image_path: Optional[str], existing_objects: List[Dict], ) - Dict: for attempt in range(1, self.max_retries 1): raw_output self.llm.chat(system_prompt, user_prompt, image_path) try: data json.loads(extract_json(raw_output)) except Exception as exc: user_prompt f\n\n注意上一轮输出不是合法 JSON{exc}请只输出 JSON。 continue placements data.get(placements, []) violations self.validator.validate(existing_objects, placements) if not violations: return data user_prompt \n\n上一轮方案违反以下规则请重新规划并只输出 JSON\n user_prompt \n.join(violations) raise RuntimeError( f经过 {self.max_retries} 次尝试仍未得到合规方案最后输出{raw_output} )extract_json的作用是容错。模型偶尔会把 JSON 放在 json 代码块里或者夹杂少量解释文字这个函数可以把核心 JSON 片段安全提取出来。重试次数建议设置为 3 到 5 次太少则成功率低太多则成本和延迟过高。4.10 结果可视化# 文件路径spatial_planner/visualize.py from typing import List, Dict from PIL import Image, ImageDraw def draw_plan( image_path: str, output_path: str, existing_objects: List[Dict], placements: List[Dict], ) - None: img Image.open(image_path).convert(RGB) draw ImageDraw.Draw(img) for obj in existing_objects: x1, y1, x2, y2 obj[bbox] draw.rectangle([x1, y1, x2, y2], outlineblue, width3) draw.text((x1, max(0, y1 - 14)), obj[category], fillblue) for p in placements: cx, cy p[center] radius 25 draw.rectangle( [cx - radius, cy - radius, cx radius, cy radius], outlinered, width3, ) draw.text((cx, cy), p.get(category, ), fillred) img.save(output_path)可视化是排查空间规划问题最直接的手段。边界问题、间距问题、重叠问题在图上几乎一眼就能看出来。实际开发中可以把这张图保存到日志目录方便回看模型的历史输出。4.11 主流程# 文件路径spatial_planner/main.py from config import API_BASE, API_KEY, BOARD_HEIGHT, BOARD_WIDTH, MIN_DISTANCE, MODEL_NAME, SAFETY_MARGIN from llm_client import OpenAICompatibleClient from validator import RuleValidator from planner import FeedbackPlanner from prompt_builder import build_system_prompt, build_user_prompt from rules import build_rule_text from scene_parser import parse_scene from visualize import draw_plan def main(): image_path data/table.png output_path output/plan_result.png existing_objects parse_scene(image_path) new_objects [ {id: new_1, category: keyboard}, ] llm OpenAICompatibleClient(api_keyAPI_KEY, model_nameMODEL_NAME, base_urlAPI_BASE) validator RuleValidator( board_widthBOARD_WIDTH, board_heightBOARD_HEIGHT, min_distanceMIN_DISTANCE, safety_marginSAFETY_MARGIN, ) planner FeedbackPlanner(llm, validator, max_retries3) system_prompt build_system_prompt(build_rule_text()) user_prompt build_user_prompt( existing_objectsexisting_objects, new_objectsnew_objects, board_widthBOARD_WIDTH, board_heightBOARD_HEIGHT, ) result planner.run( system_promptsystem_prompt, user_promptuser_prompt, image_pathimage_path, existing_objectsexisting_objects, ) print(规划结果, result) placements result.get(placements, []) draw_plan(image_path, output_path, existing_objects, placements) print(可视化结果已保存到, output_path) if __name__ __main__: main()把配置集中在 config.py 里方便不同项目复用。API_KEY建议从环境变量读取不要提交到代码仓库。如果某个阶段调用失败可以打开output/plan_result.png直接查看模型给出的坐标是否合理。4.12 运行与预期结果在完成模型服务配置后执行cd spatial_planner python main.py首次运行时模型输出可能会触发一次或多次反馈重试。这是正常现象。如果一切顺利程序会打印类似结果{ reasoning: 键盘放在桌面右侧空白区域与水杯距离约 180 像素与笔记本距离约 120 像素满足所有规则, placements: [ { object_id: new_1, category: keyboard, center: [620, 230] } ] }如果模型在max_retries次内一直无法生成合规位置程序会抛出RuntimeError并保留最后一条模型输出方便排查原因是提示词不足还是场景本身无解。5. 常见问题与排查思路问题现象常见原因解决思路模型返回的不是 JSON提示词约束不足或模型指令遵循能力较弱在提示词中增加 JSON 示例利用extract_json容错检查是否启用了 JSON Mode坐标超出图片范围模型对绝对坐标感知不准确校验器兜底重试反馈增加后处理钳位逻辑距离规则始终不满足模型不知道已有物体的精确坐标在 user_prompt 中列出所有已有物体的 center 和 bbox重试多次仍然失败当前场景确实没有合规位置或规则过严放宽阈值允许模型返回空 placements人工介入审核检测到的已有物体有偏差目标检测模型精度不足更换更强检测模型加入人工校准步骤对 bbox 做平滑后处理API 调用报鉴权或限流错误密钥错误、配额不足、服务不可用检查环境变量查看服务状态增加指数退避重试排查时建议严格按照顺序来先确认图片和检测结果没问题再确认提示词是否覆盖全部规则最后再看校验器逻辑是否与规则描述一致。很多时候问题不是模型“不会”而是上游输入数据和提示词本身有歧义。一个容易被忽略的坑是坐标系不一致。检测模型输出的坐标可能是 0~1 的归一化坐标而规划结果要求像素坐标两者混用会导致空间规划完全错乱。所以工程上必须统一坐标系统并在进入校验器之前完成转换。6. 最佳实践与工程建议6.1 规则设计要独立于模型模型是可以换的规则体系最好在设计之初就独立出来。把规则文本、校验逻辑、反馈文本分开维护能够让你在切换模型服务时只改llm_client.py其余代码不受影响。每一条规则都应该有对应的单元测试。比如inside_boundary可以准备三类测试用例完全合法、边界外、贴近边界。这样后续修改阈值时能快速发现回归问题。规则是系统工程的一部分不应该只是代码里的一段 if 判断。6.2 提示词要版本化和可观测空间规划任务里提示词是影响成功率的最大变量。建议把系统提示词按照版本号管理并在输出日志中记录每次请求使用的提示词版本和模型输出原始内容。当线上效果变差时可以快速回滚到上一个稳定版本。一个更稳妥的做法是把提示词模板和规则配置一起放到配置中心方便灰度调整避免每次修改都要重新发布代码。尤其在机器人、仓储等生产环境中这种可观测性非常关键。6.3 校验器是安全底线无论模型多强大校验器都不能取消。它应该是独立的、确定性的、无副作用的。也就是说校验器不应该依赖网络请求也不应该调用模型。它只用数学和逻辑判断输出是否合规。校验失败后不要直接返回失败优先尝试反馈重生成。重生成仍然失败时要给用户一个明确的兜底动作例如返回默认安全位置、转人工审核、或者彻底取消本次规划。安全相关的动作优先级要提前约定好。6.4 生产环境的性能与成本控制多模态大模型调用成本高、延迟高工程方案必须考虑效率和成本。常见手段有先做后处理修正再进入反馈重试减少无意义的模型调用给规则校验设置短路逻辑某条规则已经判定失败就不必继续检查后续规则对高频场景做结果缓存相同布局和历史结果可以复用在夜间或低峰期批量验证新提示词版本而不是在生产时段临时改。如果反馈重试超过两次成功率会逐步下降。高并发场景下建议设置更低的max_retries优先保证响应时间。6.5 数据安全与最小权限在调用云端多模态模型时要注意图像数据可能包含敏感信息比如监控画面、工单截图、用户个人物品等。上线前需要确认平台的数据协议并进行必要的脱敏处理。对模型服务的密钥统一使用密钥管理系统或环境变量注入避免以明文形式出现在代码配置中。给模型服务的权限也要遵循最小权限原则。如果整个系统只需要图片输入和文本输出就不要让模型服务具备写文件、访问数据库等额外权限。这不是一个加分项而是安全底线。7. 总结与下一步方向到这里规则合规的视觉空间规划已经拆成了一条完整的工程链路先定义机器可校验的规则再用提示词把规则传给多模态大模型强制模型输出结构化 JSON最后用独立校验器拦截违规结果并通过反馈重生成让模型自我修正。这套方案在桌面物品摆放、仓库分拣、机器人抓取规划、智能座舱交互等场景中都可以复用。如果你要开始实践建议从一个非常受限的小任务入手例如只规划一个物体、两条规则、固定桌面大小。先把闭环跑通再逐步增加物体数量和规则类型。你会发现当模型连续三轮输出合规结果时说明你的规则建模和提示词已经达到了一个相对稳定的状态。下一步值得探索的方向有三个一是把目标检测模型接入scene_parser让整个系统从一张真实图片开始工作二是引入约束解码或强化学习进一步降低反馈重试次数三是把单条规划扩展为“多物体顺序规划”考虑后放置物体是否挤压先放置物体。视觉空间规划的下一个难点不是“模型能不能理解空间”而是“模型生成的每个决策是否都能被规则系统证明为安全”。希望这篇文章能帮你把这条合规链路真正落到自己的项目中。