从照片到翻新方案:用 Pascal 3D 编辑器的 MCP Vision 工具链完成实景驱动装修规划
2026/9/12 12:33:22 网站建设 项目流程

从照片到翻新方案:用 Pascal 3D 编辑器的 MCP Vision 工具链完成实景驱动装修规划

【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor

导读

本文讲解 Pascal 3D 编辑器(开源仓库GitHub_Trending/editor93/editor)MCP 服务器中一个完整的 Agent 实战工作流:如何将「renovation_from_photos提示词」与analyze_floorplan_imageanalyze_room_photo两个视觉分析工具组合起来,以真实照片为事实依据,生成一套落地的装修翻新方案。读完本文,你将掌握从「用户丢进 4 张照片 + 一句需求」到「场景被墙、分区、开窗、家具全量建模并通过校验」的完整工具调用链路、底层 MCP Sampling 机制、Zod 输出校验原理,以及apply_patch原子化批量修改与undo/redo时间旅行的工程实现。

该示例文档位于仓库 packages/mcp/examples/renovate-from-photos.md,与同目录的 packages/mcp/examples/photo-to-scene.md(单张平面图直接建场景)互为姊妹篇。


背景:这条工作流解决什么问题

renovation_from_photos的核心目标是:让 AI Agent 基于"当前状态照片 + 参考风格照片"提出最小改动量的翻新方案,而不是凭空生成。它属于 Pascal MCP 服务器提供的三个提示词之一(完整清单见 packages/mcp/README.md):

提示词参数用途
from_brief{ brief, constraints? }从一段文字需求(如"80 m² 两居室")开始增量建场景
iterate_on_feedback{ feedback }以最小 diff 满足用户反馈
renovation_from_photos{ currentPhotos, referencePhotos, goals }串联视觉工具与场景变更工具,产出"照片驱动"的翻新计划

关键设计在于:视觉工具只返回数据、绝不修改场景,任何结构变更都必须由 Agent 通过apply_patch显式提出。这让整条链路可以审计、可回滚。

前置条件:宿主必须支持 MCP Sampling

两个视觉工具内部使用 MCP 的sampling(createMessage能力把图片交给宿主模型推理:

  • Claude Desktop 当前支持该能力,可直接运行此工作流;
  • 不支持 sampling 的宿主会收到结构化的sampling_unavailable错误;
  • 此时应回退到纯文本的from_brief提示词,改由用户以文字描述现状。

在源码层面,该检查位于 packages/mcp/src/tools/vision/analyze-floorplan-image.ts 与 packages/mcp/src/tools/vision/analyze-room-photo.ts:工具先读取客户端能力server.server.getClientCapabilities(),若caps?.sampling为空则直接抛出McpError(ErrorCode.InvalidRequest, 'sampling_unavailable')。对应的测试用例见 packages/mcp/src/tools/vision/analyze-floorplan-image.test.ts。

用户需求(The Brief)

用户向聊天窗口投入四张照片:

  1. 平面图——PDF 页面导出为 PNG;
  2. 客厅现状照片
  3. 厨房现状照片
  4. 灵感参考图——一本杂志里的极简北欧 Loft 风格。

然后输入:

User:Claude, help me plan a renovation. Here's the current plan and two room photos. I want something like this Scandinavian reference — open-plan, neutral tones, keep the footprint.

这句话事实上已经携带了三个关键信息:保留 footprint(占地轮廓)开放平面中性色调——这些会成为goals参数的内容。

Agent 的执行流程

宿主加载renovation_from_photos提示词,其参数组装如下:

currentPhotos: ["data:image/png;base64,...", "data:image/jpeg;base64,..."] referencePhotos: ["data:image/jpeg;base64,..."] goals: "Open-plan living/kitchen, neutral tones, keep the footprint."

提示词要求 Agent 按顺序执行五步:(1) 分析平面图,(2) 分析每张房间照片,(3) 依据平面图播种场景,(4) 与参考图对比,(5) 提出补丁方案。

提示词的源码实现与参数解析

提示词注册位于 packages/mcp/src/prompts/renovation-from-photos.ts,其argsSchema全部声明为字符串类型(MCP 提示词参数是 stringly-typed),并提供了buildRenovationMessages纯函数来构造完整的消息数组。内部有几层值得注意的健壮性处理:

  • PREAMBLE 规则:要求对每张当前照片调用analyze_floorplan_image和/或analyze_room_photo;对比现状与参考分析,找出与 goals 对齐的具体差异;最终只发出一次apply_patch,且包含收敛到目标所需的最小补丁集。同时硬性规定:不得编造尺寸(必须取自分析工具结果)、不得改动与目标无关的节点只允许工具调用、不许输出散文
  • 图片输入归一化(toImageContent):
    • data:image/...;base64,...前缀会被剥离,mime 类型取自 URI,其余作为图片块(type: 'image');
    • http(s)://URL 回退为文本URL: ...
    • 裸 base64 通过保守检测(长度 ≥ 32、长度是 4 的倍数、仅含 base64 字符)后按image/jpeg当作图片块;
    • 其余情况一律回退为文本,绝不猜测。
  • 照片列表解析(parsePhotoList):支持 JSON 数组字符串与逗号分隔两种形式。
  • 最终消息结构为:intro(含 Goals、图片数量统计)→ "## Current photos" → 各当前照片 → "## Reference photos" → 各参考照片 → "## Task"(要求只用分析工具得出的尺寸与家具生成apply_patch)。

这些行为在 packages/mcp/src/prompts/prompts.test.ts 中有完整测试覆盖:JSON 数组解析、显式 mimeType 的 data URL、逗号分隔回退、空列表仅剩 intro + task 两条消息等。

1. 提取平面图(Extract the floorplan)

Agent 调用analyze_floorplan_image,把平面图 PNG 与比例提示一起交给视觉模型:

// tool: analyze_floorplan_image { "name": "analyze_floorplan_image", "arguments": { "image": "data:image/png;base64,iVBORw0KGgoAAAANS...", "scaleHint": "1 m grid, total footprint ~9.5 m × 7 m" } }

底层原理(见 packages/mcp/src/tools/vision/analyze-floorplan-image.ts):

  1. scaleHint是可选字符串,如"1 cm = 1 m""approximately 80 m²",会被拼进采样指令;
  2. 工具通过server.server.createMessage发起采样请求,temperature: 0maxTokens: 2000,system prompt 强制模型只输出与 schema 严格一致的裸 JSON(禁止 markdown 围栏、禁止解释);
  3. 返回文本先做 JSON 解析,再用OutputSchema(Zod)做safeParse校验;解析失败抛sampling_response_unparseable,校验失败抛sampling_response_invalid(均带原始文本与错误明细);
  4. 校验通过后,payload 同时写入content(JSON 字符串)与structuredContent(结构化对象)两个通道。

工具的输入/输出 schema 如下(源码 analyzeFloorplanImageInput / analyzeFloorplanImageOutput):

  • 输入image(base64 或 http(s) URL,必填)、scaleHint?
  • 输出walls[]start/end[x, z]二元组,thickness?)、rooms[]namepolygonapproximateAreaSqM?)、approximateDimensionswidthMdepthM)、confidence(0~1)。

一次典型返回:

{ "walls": [ { "start": [0, 0], "end": [9.5, 0], "thickness": 0.25 }, { "start": [9.5, 0], "end": [9.5, 7], "thickness": 0.25 }, { "start": [9.5, 7], "end": [0, 7], "thickness": 0.25 }, { "start": [0, 7], "end": [0, 0], "thickness": 0.25 }, { "start": [4.5, 0], "end": [4.5, 7], "thickness": 0.15 }, { "start": [4.5, 3.5], "end": [9.5, 3.5], "thickness": 0.15 } ], "rooms": [ { "label": "Living", "polygon": [[0, 0], [4.5, 0], [4.5, 7], [0, 7]] }, { "label": "Kitchen", "polygon": [[4.5, 0], [9.5, 0], [9.5, 3.5], [4.5, 3.5]] }, { "label": "Bedroom", "polygon": [[4.5, 3.5], [9.5, 3.5], [9.5, 7], [4.5, 7]] } ], "approximateDimensions": { "widthMeters": 9.5, "depthMeters": 7, "areaSqMeters": 66.5 }, "confidence": 0.82 }

注意labelwidthMeters等字段在示例中为文档演示写法,源码 schema 中对应字段为namewidthM/depthM;坐标一律以米为单位,原点取平面图中心或左下角并保持一致(见 SYSTEM_PROMPT)。

图片来源安全:当imagehttp(s)://URL 时,工具不会直接fetch,而是走 packages/mcp/src/lib/safe-fetch.ts 的SSRF 防护抓取——阻止回环地址、链路本地(含云元数据 169.254.169.254)、私网段、非 http(s) 协议;限制最大 20 MB、10 秒超时、最多 3 次重定向且每次跳转都重新做允许名单校验;还支持PASCAL_ALLOWED_ASSET_ORIGINS环境变量追加来源白名单。抓取后按content-type嗅探 mime 类型再内联为 base64。

2. 分析房间照片(Analyze the room photos)

对客厅、厨房照片分别调用analyze_room_photo

// tool: analyze_room_photo { "name": "analyze_room_photo", "arguments": { "image": "data:image/jpeg;base64,/9j/4AAQ..." } }

返回示例:

{ "approximateDimensions": { "widthMeters": 4.4, "depthMeters": 5.8, "heightMeters": 2.5 }, "identifiedFixtures": [ { "kind": "sofa", "approximatePosition": [2.2, 3.5] }, { "kind": "coffee-table", "approximatePosition": [2.2, 2.4] }, { "kind": "tv-unit", "approximatePosition": [0.3, 2.0] } ], "identifiedWindows": [ { "wallHint": "south", "approximateWidth": 1.4, "approximateHeight": 1.5 } ] }

源码中的真实 schema 见 analyzeRoomPhotoOutput:approximateDimensionswidthMlengthMheightM?)、identifiedFixtures[]typeapproximatePosition?)、identifiedWindows[]wallLabel?approximateWidthM?approximateHeightM?)。system prompt 明确要求:估不准的度量宁可省略可选字段,也不要瞎猜;fixture 的type是短语,如"sofa""kitchen island""door"。厨房照片按同样的方式分析。

两个视觉工具都标记为READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONSreadOnlyHint: trueidempotentHint: trueopenWorldHint: true,见 packages/mcp/src/tools/annotations.ts),即"只读、幂等、面向开放世界",从注解层面保证它们不会改动场景。

3. 播种场景(Seed the scene)

Agent 先读取get_scene,确认默认的空Site → Building → Level骨架存在,然后按平面图结果批量创建墙体:

// tool: apply_patch { "name": "apply_patch", "arguments": { "patches": [ { "op": "create", "parentId": "level-1", "node": { "type": "wall", "start": [0, 0], "end": [9.5, 0], "thickness": 0.25, "height": 2.5 } }, /* ...remaining perimeter + partition walls from the vision result... */ ] } }

接着调用三次set_zone,把平面图识别出的 Living / Kitchen / Bedroom 多边形播种为分区。

坐标约定提醒:Pascal 是右手坐标系,X、Z 构成地平面,Y 朝上,长度单位为米。wall.start/wall.endzone.polygon等二维点都是[x, z]形式,第二个分量是世界的 Z(深度)而不是"上"(详见 packages/mcp/README.md 的 Coordinate conventions 一节)。因此平面图分析工具返回的[x, z]元组可以直接映射到墙体和分区,无需翻轴。

4. 开洞(Cut the identified openings)

对视觉工具报告出的每一扇窗,Agent 在对应外墙上调cut_opening

{ "name": "cut_opening", "arguments": { "wallId": "wall-south", "type": "window", "position": 0.5, "width": 1.4, "height": 1.5 } }

cut_openingposition0..1 的沿墙归一化比例,内部会换算并以"墙局部米"存储(见 packages/mcp/README.md 的工具表)。视觉分析给出的"南墙 1.4 m 宽 × 1.5 m 高"窗户由此转成精确的开洞参数。

5. 提出翻新方案(Propose the renovation)

受参考图分析结果的引导(明亮中性色、开放平面、极简陈设),Agent 提出单一逻辑补丁

  • 拆除 Living 与 Kitchen 之间的隔墙;
  • 把厨房中岛向西移动;
  • 删除笨重的电视柜 item,保留沙发与茶几;
  • 将合并后的分区重命名为"Open-Plan Living / Kitchen"

全部操作放进一次apply_patch

{ "name": "apply_patch", "arguments": { "patches": [ { "op": "delete", "id": "wall-partition-living-kitchen", "cascade": false }, { "op": "update", "id": "zone-living", "data": { "label": "Open-Plan Living / Kitchen", "polygon": [[0, 0], [9.5, 0], [9.5, 3.5], [0, 3.5]] } }, { "op": "delete", "id": "zone-kitchen", "cascade": false } /* + item moves / deletes for the TV unit etc. */ ] } }

apply_patch的原子性保证(见 packages/mcp/src/tools/apply-patch.ts):

  • 输入为patches[],每个 patch 支持create/update/delete三种 op,均通过PatchSchema校验;
  • 所有补丁先整体验证,再逐个应用——任何一个非法都会让整批失败,不会出现半改状态;
  • 整批操作被 Zundo 时间中间件捕获为一个可撤销的时间步
  • 返回值包含appliedOpsdeletedIdscreatedIds,并通过publishLiveSceneSnapshot触发实时同步(若编辑器与 MCP 共享PASCAL_DATA_DIR,浏览器标签页可通过/api/scenes/:id/events的 SSE 流实时看到 Agent 的每次改动)。

正因如此,用户随时可以用undo回退到翻新前,用redo回到翻新方案——整条链路天然支持"前后对比"。

6. 收尾校验(Sanity-check)

Agent 最后做两层一致性检查:

// tool: validate_scene { "name": "validate_scene", "arguments": {} } // → { "valid": true, "errors": [] } // tool: check_collisions { "name": "check_collisions", "arguments": { "levelId": "level-1" } } // → { "collisions": [] }
  • validate_scene:用 Zod 逐个校验节点并检查父子完整性({ valid, errors: { nodeId, path, message }[] });
  • check_collisions:查找重叠 item 与越界摆放({ collisions: { aId, bId, kind }[] })。

随后 Agent 汇报变更摘要 + 近似可用面积(来自pascal://scene/current/summary资源),用户即可在@pascal-app/viewer中打开场景查看翻新后的 3D 布局。

关键经验(Takeaways)

  • 视觉工具只返回数据analyze_floorplan_imageanalyze_room_photo被标记为只读/幂等(READ_ONLY_OPEN_WORLD_TOOL_ANNOTATIONS),从不直接改动场景;每一次结构变更都由 Agent 通过apply_patch显式声明,整个流程可审计、可追溯。
  • 照片提供了纯文字简报无法提供的信息——近似尺寸、家具类型、窗户位置等先验数据。当用户同时给出参考图与具体文字目标时,把本工作流与from_brief风格的提示词结合使用,效果最佳。
  • 每个翻新步骤都是单一时间步:一次apply_patch= 一个可撤销单位,配合undo/redo,用户可以在任意时刻对比"改造前 / 改造后"。

延伸阅读

  • 提示词实现与注册:packages/mcp/src/prompts/renovation-from-photos.ts
  • 视觉工具实现与测试:packages/mcp/src/tools/vision/analyze-floorplan-image.ts、packages/mcp/src/tools/vision/analyze-room-photo.ts、packages/mcp/src/tools/vision/analyze-floorplan-image.test.ts
  • 原子化补丁工具:packages/mcp/src/tools/apply-patch.ts
  • SSRF 防护抓取:packages/mcp/src/lib/safe-fetch.ts
  • 提示词测试:packages/mcp/src/prompts/prompts.test.ts
  • 姊妹示例(单图建场景):packages/mcp/examples/photo-to-scene.md
  • MCP 服务器总览与全部工具/资源/提示词清单:packages/mcp/README.md

【免费下载链接】editorOpen-source 3D architectural editor with a local CLI, MCP tools, and practical workflows for humans and AI agents.项目地址: https://gitcode.com/GitHub_Trending/editor93/editor

创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询