08feae8bef
落地 H5 Session 架构 Patch 1–5(Broker 收口、Router decision、SSE taxonomy、goosed 边界检查), 并新增可选 MEMIND_RUN_STREAM_REPLAY run 事件回放与 H5 假交付 guard;修复 Finish 先于 agent-run gate 导致 UI 永久 loading 的竞态,接入 verify:h5-session-patches 回归脚本。 Co-authored-by: Cursor <cursoragent@cursor.com>
5.0 KiB
5.0 KiB
待修复:H5 页面「假交付」检测与 write_file 自动重试
状态:已实现 guard 模块(2026-07-06),默认关闭
MEMIND_H5_HTML_FINISH_GUARD=0
原暂缓原因:H5 session / broker 分支已在本分支落地,Finish guard 已接入server.mjsonAfterFinish。
症状
- H5 Agent 回复含「页面已生成 / 生成完成 / 已保存至 public/…」及公网 Markdown 链接
- 用户点击链接 404
- 磁盘
MindSpace/<userId>/public/*.html不存在 - Finish 后
syncPublicHtmlAfterFinish无 tool call 可 materialize,文件不会凭空出现
典型案例(103 生产,2026-07-06):
- 目标:
public/kids-posture-business.html - 第一次:Agent 在聊天里输出完整商业包内容 + 链接,但未调用
write_file→ 404 - 用户追问后第二次同会话重试:真正
write_file落盘 → 200 - 后续(同页 10:54):HTML 用
edit_file加了 5 张<img src="assets/*.svg">,Agent 宣称「SVG 已 write_file 落盘」,但public/assets/kids-posture-hero.svg等 5 个文件均不存在 → 页面 200、插图全部 404(与 HTML 假交付同类问题,范围扩展到非.html资源)
根因(非意图识别)
| 层级 | 行为 | 第一次失败时 |
|---|---|---|
| 意图路由 | chat-intent-router / buildAutoChatSkillPrefix 命中「生成页面」 |
✅ |
| 技能提示 | static-page-publish 要求「必须先 write_file」 |
✅ 已注入(软约束) |
| 模型执行 | 应调用 write_file |
❌ 未调用 |
| Finish materialize | 仅从 messages 中 tool call 写盘 | ❌ 无 tool call |
| 回复校验 | 拦截「已生成」但磁盘无文件 | ❌ H5 无此闸门 |
微信通道已有 shouldRetryHtmlGenerationReply(wechat-mp.mjs),检测到链接/宣称与磁盘不一致时会 session 重试。H5 SSE 路径(server.mjs → tkmindProxy.proxySessionEvents → onAfterFinish)没有等价逻辑。
拟议修复(合并其它分支后统一做)
目标行为
当 Assistant 回复同时满足:
- 含「页面已生成 / 生成完成 / 已发布 / …」类成功宣称,或含
MindSpace/.../public/*.html公网链接 - 磁盘上对应
public/*.html不存在(或仅为 stub)
则:
- 自动重试:向同 session 发送 follow-up(参考
wechat-mp.mjs的executeSessionReply),明确要求load_skill→static-page-publish→write_file写入缺失路径 - 继续检验:重试后再次
materializeMissingPublicHtmlWrites+ 磁盘校验;仍失败则打[MindSpace]warn 日志(可选:前端提示「页面落盘失败,请重试」) - 资源完整性:解析已落盘 HTML 中的
src/href(含assets/*.svg|png|jpg|webp),缺失则一并列入 retry 清单(materializePublicHtmlWritesFromSessionEvent目前只处理.htmltool call,不能假设插图已落盘) - 上限:建议最多 1~2 次重试,避免无限 loop
建议落点(实现时再对齐当时主线)
| 模块 | 改动 |
|---|---|
新建 mindspace-h5-html-finish-guard.mjs(或并入 mindspace-public-finish-sync.mjs) |
检测假交付、提取缺失 public/*.html、构建 retry prompt |
server.mjs onAfterFinish |
Finish 后 sync 仍失败 → 调用 guard 重试 → 再 sync + register artifacts |
| 复用 | wechat-mp.mjs 中 shouldRetryHtmlGenerationReply、createPublicHtmlLinkExists、resolveHtmlPublishArtifacts 等(考虑抽到共享模块,避免 H5 直接 import 整个 wechat-mp) |
| 单测 | mindspace-h5-html-finish-guard.test.mjs;接入 npm run verify:mindspace-publish-guards |
| 文档 | 合并后更新 mindspace-publish-and-chat-finish.md |
与进行中分支的协调点
合并前必须先读并对齐:
- docs/h5-session-architecture-20260706.md
session-broker.mjs、agent-run-gateway.mjs、tkmind-proxy.mjs的 Finish / SSE 收口
不要在 broker 分支未合并前单独改上述文件的 Finish 路径,否则合并冲突与行为分叉。
实现 checklist(待开工时)
- 其它分支(H5 session / broker)已合并
main且 CI 绿 - 从
wechat-mp.mjs抽取或复用 HTML 发布校验纯函数 - H5
onAfterFinish接入 guard + 有限次 retry - 单测覆盖:假链接 404、retry 后落盘、正常 Finish 不误触发
npm run verify:mindspace-publish-guards全绿- 103 手工:生成页 → 故意 mock 无 write_file → 确认自动 repair 或明确失败提示
参考
- 回归守卫:mindspace-publish-and-chat-finish.md
- 微信 retry:
wechat-mp.mjs—shouldRetryHtmlGenerationReply、executeSessionReply - 落盘:
mindspace-public-finish-sync.mjs—materializeMissingPublicHtmlWrites、syncPublicHtmlAfterFinish - 技能前缀:
chat-skills.mjs—generate-pageprompt