上一篇讲的是顶层边界:Codex 和 Claude 只是入口,SketchUp Agent Harness 的核心在中间那套 harness。
这一篇继续往下拆:自然语言进入 agent CLI 后,为什么不能直接变成一段 SketchUp Ruby 脚本?为什么中间需要 MCP server、execution trace 和 Ruby bridge?以及为什么 bridge 的结果还必须回到 design_model.json?
这条链路可以简化成:
natural language
-> agent CLI
-> MCP tool call
-> project truth
-> bridge execution trace
-> JSON-RPC request
-> Ruby bridge execution
-> structured response
-> design_model.json feedback
这个结构看起来比“让 AI 操作 SketchUp”复杂,但复杂性不是多余的。它是在把一个不可控的自然语言请求,逐步压缩成可验证、可执行、可失败、可修复的工程边界。
MCP server 不只是转发层
如果 MCP server 只是把用户说的话转给 SketchUp,它就没有承担真正的架构职责。
在 SAH 里,MCP server 应该负责更靠近产品核心的事情:
- 读取当前 project workspace;
- 理解
design_model.json、design_rules.json、component metadata 和 import state; - 暴露 agent 可以调用的 tools;
- 把用户意图变成项目状态变更;
- 校验项目 truth 是否足够完整;
- 从结构化 truth 生成 bridge execution trace;
- 在没有 live SketchUp 时进行 headless planning;
- 在 bridge 可用时把 trace 发送给 Ruby bridge;
- 把执行结果写回
design_model.json。
换句话说,MCP server 是 agent 和产品核心之间的工具层,也是 project truth 和 host application execution 之间的编译层。
这层不应该被某个 agent CLI 绑死。Claude、Codex 或未来其他入口,都应该调用同一个 MCP server,而不是各自实现一套 SketchUp 行为。
为什么要先生成 execution trace
直接执行 live SketchUp 操作的问题是:一旦失败,你很难知道失败发生在哪一层。
是自然语言理解错了?是项目 truth 不完整?是 component metadata 缺失?是坐标不合法?是 SketchUp bridge 没启动?还是 Ruby 侧执行失败?
Bridge trace 的价值,就是把“我要改模型”变成一组显式操作:
- operation id
- operation type
- payload
- rollback behavior
例如一个 component placement,不应该只是“放一个马桶”。它应该在 trace 里变成明确的 operation:放置哪个 component、实例 id 是什么、尺寸是什么、位置是什么、如果真实 .skp asset 不存在是否允许 procedural fallback。
一旦有了 trace,系统就可以在 live SketchUp 之前做更多判断:
- 当前
design_model.json是否能生成完整 trace; - 是否有 space、component 或 lighting 无法转换成 operation;
- 是否应该拒绝 partial execution;
- 是否需要 clean replay;
- 是否存在 stale import overlay 或旧生成对象;
- 是否能在 headless 环境下做 smoke check。
这就是为什么 SAH 把 planning 和 execution 分开。规划失败是一个问题,bridge 执行失败是另一个问题。混在一起,调试成本会很高。
JSON-RPC 是稳定协议边界
MCP server 生成 trace 后,不应该让 agent 随机拼接 Ruby 代码发给 SketchUp。更稳定的边界是协议。
SAH 的 bridge 层使用 JSON-RPC 形状:请求有 method、params、id;成功响应有 result;失败响应有 error,并且 error data 应该能说明失败 operation、rollback 状态和模型 revision。
这种边界有几个好处:
- Python 侧和 Ruby 侧可以独立演进;
- 每个 operation 都有可追踪 id;
- 失败可以归因到具体 operation;
- rollback 行为可以在协议层表达;
- bridge response 可以被记录、测试和回放;
- later agent calls 不需要猜 SketchUp 里发生了什么。
对开发者来说,这意味着 Ruby bridge 不是一段临时脚本,而是一个宿主应用 adapter。它应该像产品接口一样被设计,而不是像一次性 automation snippet。
Ruby bridge 应该窄而硬
Ruby bridge 运行在 SketchUp 侧。它不应该理解所有产品策略,也不应该知道 Claude 和 Codex 的差异。
它更适合承担这些职责:
- 接收结构化 operation;
- 校验 payload 是否能在 SketchUp 中执行;
- 创建、修改或查询 SketchUp entity;
- 在失败时返回结构化错误;
- 在需要时触发 rollback;
- 返回 entity ids、spatial delta、model revision 和 elapsed time 之类的执行元数据。
这个分工的关键是:Ruby bridge 不做产品级推理。产品级推理留在 MCP server 和 project truth 层。Ruby bridge 专注于宿主应用执行。
这样做还有一个现实收益:SketchUp live 环境有很多不可控因素,例如窗口状态、插件是否加载、模型是否打开、host application 是否忙碌。Ruby bridge 应该把这些情况变成结构化 blocker 或 error,而不是让 agent 只看到“没反应”。
执行反馈必须回写到 project truth
很多自动化系统会停在“操作执行成功”。对 agent harness 来说,这还不够。
如果 bridge 创建了墙、门、窗口、组件或灯光,后续 agent 需要知道这些对象在 SketchUp 里对应哪些 entity。否则下一次修改时,它只能重新猜。
所以 SAH 的 project-backed execution 会把 bridge feedback 写回同一个 design_model.json:
- 当前 replay 的 bridge operations;
- 每个成功 operation 返回的 entity ids;
- space wall 的 execution metadata;
- explicit wall 和 hosted opening 的 execution feedback;
- component 和 lighting instance 的 entity id;
- 每个 instance 最近一次执行对应的 operation id。
更重要的是,旧 metadata 不能无限堆积。replay 后,过时的 bridge operations 应该被替换,targeted wall 和 opening 的旧 execution 信息也应该被清掉。否则 agent 会读到 stale state。
这就是“SketchUp scene 是 execution view,design_model.json 是 project truth”的实际工程含义。
Partial execution 默认应该被拒绝
如果一个设计项目里有 spaces、components、lighting,但其中某些对象不能转换成 bridge operation,系统可以有两种选择:
- 尽量执行能执行的部分;
- 默认拒绝,并要求 agent 明确告诉设计师哪些对象被省略。
SAH 选择后者作为默认方向。
这不是保守,而是产品质量边界。对设计软件来说,silent skip 很危险。用户看到的 SketchUp scene 可能看起来成功,但其实漏掉了组件、墙体或灯光。后续视觉审阅、截图、保存和演示都会建立在错误场景上。
如果确实需要 partial execution,也应该显式说明 omitted instances,再由 agent 或用户决定是否继续。换句话说,partial execution 可以存在,但不能静默存在。
Clean replay 解决的是场景污染
另一个真实问题是重复执行。
如果每次 replay 都在 SketchUp 里追加新墙、新门、新组件,很快 scene 就会被旧几何污染。视觉上可能看起来“变复杂了”,但实际上只是 stale objects 没有清掉。
所以 SAH 支持 clean replay 的思路:执行当前 truth 前,先清理受管理的层或对象,再根据当前 design_model.json 重放。
对于 import replay,还需要更谨慎。source overlays、SketchUp template entities、旧导入墙体、占位对象都可能影响视觉判断。系统需要清楚地区分:
- 保留用户手工几何;
- 清理 harness 管理的对象;
- 在需要时做 full-scene clean;
- 执行后检查是否还有不该存在的 scene contamination。
这不是 UI 细节,而是 agent harness 的一致性问题。你不能一边说 design_model.json 是 truth,一边让 SketchUp 里残留的旧对象影响判断。
坐标和单位必须在协议层稳定
自然语言里用户会说“2 米 x 1.8 米”。SketchUp 和几何协议不能依赖这种表达。
SAH 的空间约定是协议内统一使用 millimeters,并采用 Z-up 坐标。用户表达中的米、英尺或英寸,应由 Python / MCP 层转换成内部数值。Ruby bridge 接收的 payload 应该已经是稳定几何数据,而不是自然语言尺寸。
这层约定很基础,但它决定了后续所有 operation 是否可验证:
- face vertices 是否共面;
- box dimensions 是否为正;
- wall alignment 是否明确;
- bounding box 是否能比较;
- placement 是否能复现;
- spatial delta 是否有意义。
如果单位和坐标约定不稳定,后面的 validation、trace、bridge response 和视觉审阅都会变得不可靠。
这个模式对其他 agent harness 的意义
SAH 的例子是 SketchUp,但这个模式适用于更广泛的 host application agent:
LLM intent
-> tool contract
-> structured project truth
-> deterministic execution trace
-> protocol boundary
-> host bridge
-> structured feedback
-> updated project truth
这种架构的核心不是 MCP 这个名字,也不是 Ruby 这个语言,而是边界:
- LLM 不直接拥有宿主应用状态;
- tool layer 不只是转发自然语言;
- execution trace 让 live execution 之前有可检查中间物;
- bridge 专注执行和反馈;
- project truth 吃回反馈,供下一轮 agent 决策使用。
如果你要做一个能长期维护的 agent 产品,这些边界比一次漂亮 demo 更重要。
