8.2 KiB
name, description, type
| name | description | type |
|---|---|---|
| LLM 识图审图 Agent —— 架构决策(已定稿) | cad_mcp_frame + 自研 Agent 的架构决策与分阶段路线——混合式(流水线骨架+受控只读取证子循环)、基座 B 最小集、标准化路线(先 stdio 桥) | project |
LLM 识图审图 Agent —— 架构决策(2026-10-13 定稿)
决策
- Agent 形态:混合式自研(非纯 ReAct、非纯流水线):确定性流水线骨架
(规划 → 逐图框取证 → 审查 → 汇总)+ 审查阶段给 LLM 一个受控只读取证工具子循环
(白名单 15 个只读工具,
add_*/delete_*/set_*/draw_*一律禁止)。 - 审查依据:清单驱动(
review_checklist.json)+ LLM 自主判断。 - 输出:Markdown + JSON 报告 + 可选 CAD 图上批注回写(统一落
AI_REVIEW层)。 - 基座改造:只做「B 最小集」——换行分帧(
\n为消息边界)、去掉单次recv(2048)限制、isError映射、id支持 string/number + 通知不回包;不做initialize/ping握手。 - 标准化路线:先自研 agent 跑通 → 再做 stdio↔TCP 桥 → 远期才考虑 Streamable HTTP。
(2026-10-14 更新:stdio 桥已实现并验证通过,见
project_stdio_bridge.md。 远期仅剩 Streamable HTTP。)
Why(关键结论,非显而易见):
- 基座是裸 TCP 自定义传输。规范虽允许自定义传输,但标准客户端(Claude Desktop / CodeBuddy / MCP Inspector)只实现 stdio 与 Streamable HTTP——因此裸 TCP 无论怎么补 生命周期都无法被标准客户端直连。"能被标准客户端接入"只有两条路:stdio 桥 (省力、CAD 侧零改动)或 Streamable HTTP(彻底、要在 CAD 内做 HTTP+SSE、最贵)。
- 自研 agent 直接连 TCP,传输是否"标准"不影响,故标准化不是前置条件。
mcp_server.cpp原来单次recv仅 2048 字节,请求超 2KB 会被截断成-32700; 这是服务端限制、客户端绕不过去 → 必须先改基座。- image 内容块
{type:image,data,mimeType}本就是 MCP 标准(source:{type,mediaType}是 Anthropic Messages API 的格式,别混淆),无需改。
How to apply:新增审图工具仍走 cad_mcp_plugins + mcp_config.json;agent 侧工具从
tools/list 动态发现,加工具零代码。日后要做标准客户端接入,从 stdio 桥起步。
新增产物
dwg_review/(Python 包):config.py(环境变量,无硬编码 Key)、mcp_client.py(唯一 TCP 封装:单飞锁 + 换行分帧 + 重试 + 兼容error/isError)、llm_client.py、cad_tools.py(动态工具表 + 只读白名单READ_ONLY_TOOLS)、checklist.py、phases/{planning,evidence,review,report}.py、annotator.py、__main__.py。 取代根目录散落的dwg_review_agent.py/agent_loop.py等重复脚本。verify_mcp_frame.py:基座改动验证脚本(分帧 / 大报文 / string id / 通知 / isError)。
环境事实
- Pillow 已安装(12.3.0)。截图下采样策略(
dwg_review/llm_client.py: downsample_image_b64): 视觉 token 成本由像素尺寸决定而非文件大小 → 只在长边超IMAGE_MAX_EDGE(默认 1280)时才缩放; 既不需要缩放、格式又没变时原样返回不重编码(避免给 CAD 线稿引入无谓压缩损失); 缩放后默认输出无损 PNG(IMAGE_FORMAT=png,适合线稿),需要更小体积可设IMAGE_FORMAT=jpeg。 缺少 Pillow 时自动降级为原图直传。 - 视觉模型
deepseek-flash。 DEEPSEEK_API_KEY设在 WindowsMachine(系统)级。因新设的系统变量不会传播到已启动 的进程,dwg_review/config.py的_read_env()在进程环境读不到时回退读注册表 (HKLM\SYSTEM\CurrentControlSet\Control\Session Manager\Environment→HKCU\Environment)。 否则 CodeBuddy 内的命令永远读不到新设的 Key。- AutoCAD 2020 自动加载 ARX(用户配置,不在仓库内):在
bin/Debug-R230/下建acad.rx(内容一行cad_mcp_frame.arx),并把该目录加入 CAD 的搜索路径与信任路径。 这是 AutoCAD 官方 RX 自动加载机制,比acaddoc.lsp+ LISP 更直接。 - AutoCAD 2020 安装于
C:/Program Files/Autodesk/AutoCAD 2020/;用户配置目录C:/Users/gjm/AppData/Roaming/Autodesk/AutoCAD 2020/R23.1/chs/(acaddoc.lsp已用于 PDF2CAD,勿覆盖)。 - 改
cad_mcp_plugins后重链接必须先关 CAD:插件 DLL 被 CAD 进程持有(LNK1168: cannot open ...dll for writing)。模板cad-mcp-frame.json里的"hot_reload": true只是占位、代码未实现,mcp_Tools::clear()(唯一FreeLibrary处)仅在 ARX 卸载时调用 → 没有热重载。 流程:关 CAD →cmake --build --preset R230 --config Debug→ 重启acad.exe(acad.rx自动加载) → 轮询127.0.0.1:8080监听。编译阶段可先行(语法错误会被提前暴露,只有链接会因占用失败)。
基座健壮性:工具执行的异常安全(2026-10-14)
不变量:cad_mcp_frame 的工具执行运行在 AutoCAD 主线程的窗口消息处理链上
(TcpServerLoop → PostMessage → WndProc → ExecuteTaskInMainThread → mcp_Tools::Call),
任何异常都不得逃逸出 WndProc,且异常路径上不得遗留文档写锁。
Why:插件 Execute 在参数类型不符时会抛 nlohmann::type_error(如
args.value("bulge", 0.5) 收到字符串、parsePoint 对 ["a","b"] 取 get<double>()——
它只查 is_array()/size()>=2,不查元素类型)。而 dwg_review 由 LLM 驱动、
模型经常生成类型错误的参数 → 这是日常路径,不是"恶意客户端"场景。
原实现全程无 try/catch:异常逃逸出 WndProc 属未定义行为(表现为 CAD 崩溃),
且 mcp_Tool_DllProxy::Execute 的 unlockDocument 在异常路径被跳过 → 写锁泄漏。
How to apply:基座已统一兜住(mcp_Tools::Call try/catch → -32603 → result.isError=true;
HandleToolsCall 校验 params/arguments 必须为对象、name 必须为字符串,否则 -32602;
ExecuteTaskInMainThread 兜底 try/catch + !req.is_object() → -32600;
DLL 代理写锁改为 ScopedDocumentWriteLock RAII)。插件作者无需自行 try/catch,
但仍不要在异常路径上留下副作用。新增/改动工具后跑 verify_mcp_fault_injection.py 回归。
同一根因的另一面:阻塞。 工具响应由 AutoCAD 主线程发送
(WndProc → ExecuteTaskInMainThread → SendAll),所以发送路径也必须有时限——
accept 时同时设了 SO_RCVTIMEO/SO_SNDTIMEO(各 5s),否则慢客户端能把 CAD 的 UI 线程卡死。
分帧规则:\n 优先,其次「暂时无更多数据、且累积内容已是完整 JSON」(兼容不发 \n 的旧客户端);
不要再引入基于等待时长的分帧判定(旧的 150ms select 探测会把分片间隔大的大报文误截断)。
回归脚本:verify_mcp_tcp_hardening.py。
验证结论(2026-10-13 实测通过)
verify_mcp_frame.py4/4 PASS(换行分帧 / >2KB 报文不截断 / string id 原样回显 / 通知不回包 / isError)。verify_annotate.py9/9 PASS(批注工具无回归)。dwg_review --list-tools= 26 个工具;--plan-only取证正常;全流程跑出报告。- 受控取证子循环实测生效:LLM 自主调用了白名单内的
get_drawing_info/get_layers/find_zero_length/find_duplicate_entities/get_dimensions做二次确认。 - 批注回写实测通过;注意
add_leader_annotation的points必须 ≥2 个点(首个为箭头)。
验证结论(2026-10-14 补充)
verify_mcp_fault_injection.py22/22 PASS(畸形/类型错误请求:结构性错误回-32602/-32600/-32700;工具内部类型异常被兜为result.isError=true;反复注入 10 次后 CAD 存活、写锁未泄漏)。回归verify_mcp_frame4/4、verify_stdio_bridge24/24、verify_annotate9/9、dwg_review --check均正常。