diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 657b100cb..e030bfbb1 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -19,7 +19,7 @@ ## 2026-07-22 画布 Agent 普通规划通过 HTTP abort 中断 - 背景:画布 Agent 规划可在 `agent.prompt(...)` 及 LLM 重试中长时间等待,用户输入有误时需要立即释放会话锁并发送新 prompt。2026-07-17 的旧决策以“fetch 中断不能保证后端停止”为前提移除了停止入口,但当前 Axum 0.8.9 / Hyper 1.8.1 和 Pingora 代理链可通过真实断连回归验证 handler Future 被 drop。 -- 决策:普通消息等待期间将“发送”切换为“停止”。前端为每轮请求创建 `AbortController`,主动停止时 abort `/messages` POST;Hyper 在断连 / HTTP/2 `RST_STREAM` 后 drop `editor_agent_message`,连带 drop `agent.prompt(...)` 与 LLM reqwest Future,RAII 释放 conversation lock。`AbortError` 不进入 POST transport retry、不展示失败、不删除已持久化的用户消息;停止后的静默会话对账直接 `await` 在 abort 分支内,`isAborting` 覆盖从 abort 开始到 GET 结束的整个阶段,期间 `isWaiting` 继续阻止下一次发送,GET 结束后允许新 prompt。不新增 abort API、运行态 registry、消息状态或 SpacetimeDB 字段。 +- 决策:普通消息等待期间将“发送”切换为“停止”。前端为每轮请求创建 `AbortController`,主动停止时 abort `/messages` POST;Hyper 在断连 / HTTP/2 `RST_STREAM` 后 drop `editor_agent_message`,连带 drop `agent.prompt(...)` 与 LLM reqwest Future,RAII 释放 conversation lock。`AbortError` 不进入 POST transport retry、不展示失败、不删除已持久化的用户消息;停止后的静默会话 GET 作为尽力而为的 UI 对账直接 `await` 在 abort 分支内,成功时应用权威详情,失败时静默保留当前消息。`isAborting` 覆盖从 abort 开始到 GET 成功或失败结束的整个阶段,期间 `isWaiting` 继续阻止下一次发送,GET 结束后允许新 prompt。锁释放由 handler drop 与 RAII 保证,不把 GET 成功设为恢复前置条件;后续 POST 重新读取 OSS 权威会话并按 `clientMessageId` 幂等处理,因此对账失败不会破坏后端一致性。不新增 abort API、运行态 registry、消息状态或 SpacetimeDB 字段。 - 边界:这会 drop 整个当前 HTTP handler,不是已确认 external generation job 的取消;上游模型已接收请求后是否立即停止计算由 provider 决定。用户消息保留为普通历史,会继续进入后续 LLM 上下文。 - 影响范围:画布 Agent 对话 hook、发送区交互、API client abort 识别、Axum / Pingora 断连回归和专题 / 架构文档。 - 验证方式:前端定向测试锁定 signal、按钮、不重试和中断后新 prompt;api-server 真实 TCP 回归锁定 HTTP 请求 drop 后 handler 析构与 mutex 释放;Pingora smoke 锁定下游中断传播到 API 上游。追加类型检查、编码和 diff 门禁。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index d938aee56..5c2cfdd24 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -77,7 +77,7 @@ npm run check:server-rs-ddd - `module-editor-agent` 只承载纯领域校验:标题派生、附件上限、消息输入规则和会话软删访问规则;不直接依赖 Axum、SpacetimeDB、OSS、LLM 或 Tokio。 - `spacetime-module` 的 `editor_agent_conversation` 只保存元数据;创建、列表、读取、更新时间和软删通过 `create_editor_agent_conversation_and_return`、`list_editor_agent_conversations_and_return`、`get_editor_agent_conversation_and_return`、`touch_editor_agent_conversation_and_return`、`delete_editor_agent_conversation_and_return` procedure 完成,`api-server` 只能经 `spacetime-client` facade 访问。 - 完整消息文档存 OSS `editor-agent/{conversationId}.json`,由 `api-server` 负责 2 MiB 上限、会话内串行锁、读改写、消息与工具结果持久化和 `touch` 元数据更新时间;该 JSON 不进入 `editor_canvas.layers_json`,也不作为画布布局真相。LLM 未配置、连接已经断开、请求明确失败、达到最终安全上限或规划不可解析时,必须写入 `role=system`、正文以 `ERROR ` 开头的消息,并通过 `deltaMessages` 返回,`errorMessage` 保持为空;前端隐藏前缀并显示红色错误气泡,面向用户的错误正文使用中文语义,不暴露 `completion error` 等 framework 内部前缀或原始配置/定价错误;原始诊断只写后端结构化日志。后端仍把该 system 消息注入后续 LLM memory,使 Agent 能读取失败上下文。普通 JSON POST 尚未结束不形成持久化消息;工具失败同样必须形成可回读记录,不能只返回瞬时错误。 -- 主动 HTTP abort 不是规划失败,不追加 `ERROR` 消息。用户消息在进入 LLM 前已写入 OSS,中断后继续作为普通历史保留并进入后续 LLM 上下文;前端重新读取会话,GET 成功返回后再恢复发送,以对齐权威历史并确认旧锁已释放。 +- 主动 HTTP abort 不是规划失败,不追加 `ERROR` 消息。用户消息在进入 LLM 前已写入 OSS,中断后继续作为普通历史保留并进入后续 LLM 上下文。前端在恢复发送前尽力重新读取一次会话:GET 成功时应用 OSS 权威历史,失败时保留当前 UI 消息并在该 GET 结束后恢复发送,不把对账失败当作发送失败。旧 handler 的 drop 与 conversation lock 释放由 Hyper 断连传播和 RAII 保证,不以该 GET 成功作为确认条件;后续 POST 会重新读取 OSS 权威会话并按 `clientMessageId` 幂等处理,因此 GET 失败不会破坏后端一致性。 - 画布 Agent 的 `gpt-5.4-mini` Chat Completions 规划使用 1024 `max_tokens`。前端在 POST pending 120 秒后显示不入库的耐心等待提示;provider request future 明确返回 connect/timeout/HTTP/transport 错误时立即进入正式失败,尚未返回则继续等待。专用 provider 单 attempt hard timeout 为 8 分钟;请求发起阶段的 timeout、连接失败、`408`、`429` 与 `5xx` 读取 `GENARRATIVE_LLM_MAX_RETRIES`,但画布 Agent 最多重试 1 次,显式配置 0 仍可关闭,专用重试退避最多 60 秒。消息规划生命周期从 handler 入口开始计入 18 分钟总 deadline,进入 `agent.prompt(...)` 时只使用剩余预算;该 deadline 覆盖会话锁/上下文准备与最多 3 轮规划,并为错误持久化/HTTP 返回预留约 2 分钟,不允许多轮规划绕过前端 20 分钟 timeout。已收到成功响应头后的响应体读取或解析失败直接按明确失败收口,并使用该成功响应所属的真实 attempt 记录错误。重试只包围 LLM 规划请求并发生在任何待确认工具执行之前,因此不会重复提交生成任务或扣费。 - 对话附件只允许引用当前工程 `editor_project_resource` 或当前账号 `editor_asset` 的图片;前端可提交展示用 `imageSrc` / `thumbnailSrc`,后端必须按 `resourceId` / `assetId` 重新归一、校验 owner / project 和 `objectKey`,再给 LLM 或生成工具使用。 - 画布 Agent 工具复用既有编辑器图片生成 / 修改 / 图标 spritesheet BFF,并继续使用后端模型定价和 `execute_billable_asset_operation_with_cost`;前端不提交 `priceMudPoints`。 diff --git a/docs/【编辑器】画布Agent对话面板-2026-07-03.md b/docs/【编辑器】画布Agent对话面板-2026-07-03.md index 8177e4a8b..b3d14b683 100644 --- a/docs/【编辑器】画布Agent对话面板-2026-07-03.md +++ b/docs/【编辑器】画布Agent对话面板-2026-07-03.md @@ -62,7 +62,7 @@ - 桌面端对话框固定宽约 360–400px;移动端抽屉式全宽覆盖;收起态为胶囊/圆形入口按钮。 - 会话管理入口在对话框头部:当前会话标题 + 历史会话下拉(按更新时间倒序)+ 新建对话按钮,全部包在对话框内。 - 快速切换会话或会话轮询刷新产生并发详情请求时,前端只允许最后发起的请求更新当前会话、消息、错误和加载态;旧响应不得覆盖用户最新选择。 -- 普通 JSON 消息请求的回包必须绑定发送时的会话:用户在等待期间切换到其他会话后,只更新原会话的列表摘要,不得把原会话的 `deltaMessages` 、错误或画布刷新副作用应用到当前面板。整轮发送从首次创建会话前就必须注册为可停止;若用户在新会话创建完成前停止,允许创建请求正常收口,但创建完成后不得继续发送规划请求。用户主动停止后不显示发送失败、不重试 POST、不删除已写入 OSS 的用户消息;停止后的静默会话重读直接执行在 abort 分支内,`isAborting` 一直保持到 GET 结束,因此面板继续显示“停止中”,并由未结束的 `isWaiting` 阻止下一次发送。该阶段不显示“刷新中”或额外 loading;GET 结束后一次性退出“停止中”并恢复发送。 +- 普通 JSON 消息请求的回包必须绑定发送时的会话:用户在等待期间切换到其他会话后,只更新原会话的列表摘要,不得把原会话的 `deltaMessages` 、错误或画布刷新副作用应用到当前面板。整轮发送从首次创建会话前就必须注册为可停止;若用户在新会话创建完成前停止,允许创建请求正常收口,但创建完成后不得继续发送规划请求。用户主动停止后不显示发送失败、不重试 POST、不删除已写入 OSS 的用户消息。停止后的静默会话 GET 是尽力而为的 UI 对账:成功时应用权威详情,失败时静默保留当前消息,不把对账失败误报为发送失败,也不阻塞后续发送。`isAborting` 覆盖从 abort 开始到该 GET 成功或失败结束的整个阶段,期间 `isWaiting` 继续阻止下一次发送;GET 结束后一次性退出“停止中”并恢复发送。该阶段不显示“刷新中”或额外 loading。后续 POST 由后端重新读取 OSS 权威会话,并通过 `clientMessageId` 保证回合幂等,不依赖前端当前消息列表,因此对账 GET 失败只会让 UI 暂时未刷新,不会破坏后端会话一致性。 - 收起对话框只是隐藏面板,不卸载当前会话 hook;普通 JSON 消息请求的等待态和外部生成任务状态必须在收起 / 重新打开之间保持一致。 ## 附件