| 层 | 职责 | 不该做的事 |
|---|---|---|
| Agent(决策) | 决定「这轮调不调工具、调哪个、怎么串」 | 不直接 import Skill;不含协议细节 |
| MCP(协议) | 把工具暴露成统一协议;把调用转成执行 | 不做业务编排;不持有凭据(浏览器场景) |
| Skill(实现) | 真正干活(出图/转写/分析),纯能力 | 不假设调用方是谁;不藏全局态 |
换框架不动 MCP/Skill 是分层的验收标准:
本项目把 Agent 从 PageAgent 换到 LangGraph,design-mcp.js 与 image-service.js 零改动。
| 维度 | 显式图(LangGraph) | Re-act 循环 |
|---|---|---|
| 可观测 | 节点轨迹可展示、可断点、可回滚 | 只能看到「思考中…」 |
| Token 控制 | 可在节点里预筛工具、按需检索 后再进 LLM | 全量工具 schema + 线性历史,膨胀 |
| 失败分支 | 「没出图」是显式分支 | 只能事后靠猜兜底 |
| 成本 | 需 importmap + 替身(浏览器) | 轻 |
建议:要 token 可控 + 可观测 → 显式图;只是简单问答 → Re-act 足够。
route(本地预筛,零 LLM/零 token)
→ recall(检索式记忆,按需召回)
→ plan(唯一一次"决定做什么"的 LLM)
→ invoke(执行工具)
→ assert(断言"真出图了吗")
→ reflect(给一句说明,不带工具,避免它再调一次)
→ respond(收尾)
State 只存索引与摘要,大对象(图片 URL、历史正文)放 service / IndexedDB。
config.configurable.__dc(不是闭包,闭包会把首轮 token 固化进编译产物)。node(state, config),第二参是 RunnableConfig 不是你的 ctx(实测坑)。见 agent-mcp-skill.md(契约、命名空间、Worker 约束、凭据纪律)。
无论几个 MCP/Skill,口径必须统一:
usage 真值以真值为准。t0() 包住整个 await LLM 会把 LLM 的 1.4s 算进「框架开销」,
占比虚高到 96%。正确做法:await 期间暂停框架计时,只把「发起调用前后」算框架的。详见 measurement.md。