From 9bae10c397f2c00c0c3cc6673cd723420c9e49d6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E5=BE=B7=E5=AE=87?= Date: Wed, 22 Jul 2026 17:39:21 +0800 Subject: [PATCH] =?UTF-8?q?=E8=AE=B0=E5=BD=95=E7=94=BB=E5=B8=83=20Agent=20?= =?UTF-8?q?HTTP=20=E5=81=9C=E6=AD=A2=E6=96=B9=E6=A1=88?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 同步普通规划停止、消息保留和会话锁释放边界 记录 isAborting 覆盖静默会话重读的最终交互 --- docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md | 1 + docs/project-memory/shared-memory/decision-log.md | 11 +++++++++++ ...端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md | 2 ++ docs/【编辑器】画布Agent对话面板-2026-07-03.md | 7 ++++--- 4 files changed, 18 insertions(+), 3 deletions(-) diff --git a/docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md b/docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md index c1c881dbe..6137c036f 100644 --- a/docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md +++ b/docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md @@ -32,4 +32,5 @@ - 消息文档最大 2 MiB;该限制用于阻止单个会话无限增长。后续如果需要更长历史,应引入归档、分页对象或摘要压缩,不应把正文回填进 SpacetimeDB 表。 - 会话软删只打表标记,OSS 对象保留,便于恢复与审计。 - 规划或工具生成失败也必须写入消息文档:规划失败保存 `ERROR ` system 消息,工具失败保存失败状态、模型和错误信息,便于用户回看失败原因和后续排障。 +- 用户主动中断普通规划时,浏览器 abort 当前 HTTP 请求,Axum / Hyper drop handler 并释放会话锁。该情况不记为规划失败、不追加 `ERROR`;因用户消息已在 LLM 前写入 OSS,其作为普通历史保留,前端中断后重读会话完成对齐。 - 若未来出现跨会话消息检索需求,需另建投影或索引,不回退为消息入表。 diff --git a/docs/project-memory/shared-memory/decision-log.md b/docs/project-memory/shared-memory/decision-log.md index 01345b3cb..069bd310d 100644 --- a/docs/project-memory/shared-memory/decision-log.md +++ b/docs/project-memory/shared-memory/decision-log.md @@ -16,6 +16,17 @@ --- +## 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 字段。 +- 边界:这会 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 门禁。 +- 关联文档:`docs/【编辑器】画布Agent对话面板-2026-07-03.md`、`docs/adr/【ADR】画布Agent会话消息存OSS-2026-07-03.md`。 + +--- + ## 2026-07-20 角色动作抠图前禁止透明 padding - 背景:图片画布角色动作此前在 BgFilter 前复用最终帧 finalizer,把 FFmpeg 抽帧先转成目标尺寸 RGBA 画布并用透明黑像素补边;透明区域进入 BgFilter、阿里云和本地键色共同读取的 OSS 源帧后,会干扰主体边缘判断并降低抠图质量。 diff --git a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md index c631b90c2..d938aee56 100644 --- a/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md +++ b/docs/【后端架构】server-rs与SpacetimeDB数据契约-2026-05-15.md @@ -73,9 +73,11 @@ npm run check:server-rs-ddd ### 图片画布 Agent 对话 - `/api/editor/projects/{projectId}/agent-conversations` 负责当前工程会话列表和新建;`/api/editor/agent-conversations/{conversationId}` 负责详情读取、终态工具消息懒回填和软删;`POST /api/editor/agent-conversations/{conversationId}/messages` 负责发送消息并返回普通 JSON `EditorAgentMessageResponse`,画布 Agent 不提供 `/messages/stream` SSE 路由。消息请求必须携带最长 128 字符的 `clientMessageId`;前端对该 POST 显式启用 1 次瞬时 transport 重试,并复用同一个序列化 body、`clientMessageId` 和 `x-request-id`。同一会话在锁内按该键幂等,重复键同内容返回已有回合或从已保存用户消息继续,异内容返回 `409`。数字 `EditorAgentMessage.id` 仍只作为工具确认 / 取消的后端消息定位符,不能复用为客户端幂等键。 +- 用户主动中断普通规划时,前端通过 `AbortController` 取消当前 `/messages` POST,该 `AbortError` 不进入 transport retry。Hyper 在断连或 HTTP/2 `RST_STREAM` 后 drop `editor_agent_message` Future,连带 drop `agent.prompt(...)` 和 LLM reqwest Future,RAII 释放 conversation lock。 - `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 成功返回后再恢复发送,以对齐权威历史并确认旧锁已释放。 - 画布 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 63749082d..67c4cdd55 100644 --- a/docs/【编辑器】画布Agent对话面板-2026-07-03.md +++ b/docs/【编辑器】画布Agent对话面板-2026-07-03.md @@ -1,6 +1,6 @@ # 画布Agent对话面板 -日期:`2026-07-16` +日期:`2026-07-22` ## 定位与边界 @@ -34,6 +34,7 @@ - 已落地:会话元数据、OSS 消息文档、会话 CRUD、带 `clientMessageId` 幂等键的普通 JSON 消息请求、后端 LLM 工具规划、右侧对话面板、会话历史、新建 / 软删会话、附件从画布资源 / 账号素材库选择,以及八类图片 / 音视频工具对既有生成入口的复用。 - 已落地:工具确认 / 取消、external generation task 轮询与会话懒回填。LLM 未配置、请求失败或规划结果解析失败时,后端把 `role=system`、正文以 `ERROR ` 开头的消息写入 OSS,并通过 `deltaMessages` 返回,`errorMessage` 保持为空;前端隐藏 wire 前缀并以红色错误气泡展示。工具执行失败继续保存 `status=failed`、模型和错误信息,不能只返回瞬时错误。 +- 已落地:普通消息规划期间可主动“停止”。前端通过 `AbortController` 取消当前 `/messages` HTTP 请求;Axum / Hyper 收到连接关闭或 HTTP/2 `RST_STREAM` 后 drop `editor_agent_message` Future,连带 drop `agent.prompt(...)` 和 LLM reqwest Future,并由 RAII 释放 conversation lock。这不是已确认外部生成任务的取消能力。 - 未落地:附件弹窗末尾上传格。`external_generation_job` 继续作为后台任务队列真相,对话消息只保存确认、回填状态和轻量媒体结果引用。 ## 会话与持久化 @@ -61,7 +62,7 @@ - 桌面端对话框固定宽约 360–400px;移动端抽屉式全宽覆盖;收起态为胶囊/圆形入口按钮。 - 会话管理入口在对话框头部:当前会话标题 + 历史会话下拉(按更新时间倒序)+ 新建对话按钮,全部包在对话框内。 - 快速切换会话或会话轮询刷新产生并发详情请求时,前端只允许最后发起的请求更新当前会话、消息、错误和加载态;旧响应不得覆盖用户最新选择。 -- 普通 JSON 消息请求的回包必须绑定发送时的会话:用户在等待期间切换到其他会话后,只更新原会话的列表摘要,不得把原会话的 `deltaMessages` 、错误或画布刷新副作用应用到当前面板。 +- 普通 JSON 消息请求的回包必须绑定发送时的会话:用户在等待期间切换到其他会话后,只更新原会话的列表摘要,不得把原会话的 `deltaMessages` 、错误或画布刷新副作用应用到当前面板。用户主动停止后不显示发送失败、不重试 POST、不删除已写入 OSS 的用户消息;停止后的静默会话重读直接执行在 abort 分支内,`isAborting` 一直保持到 GET 结束,因此面板继续显示“停止中”,并由未结束的 `isWaiting` 阻止下一次发送。该阶段不显示“刷新中”或额外 loading;GET 结束后一次性退出“停止中”并恢复发送。 - 收起对话框只是隐藏面板,不卸载当前会话 hook;普通 JSON 消息请求的等待态和外部生成任务状态必须在收起 / 重新打开之间保持一致。 ## 附件 @@ -115,7 +116,7 @@ 4. 消息内生成结果缩略图(纯预览,不显示名称,不点击聚焦图层); 5. 生成中的进行中动画; 6. 错误气泡(失败/余额不足,带原因); -7. 普通消息请求等待期间禁用发送按钮,不提供客户端停止操作;前端持续等待后端响应,超过 120 秒但 POST 仍 pending 时在思考气泡中显示“仍在处理中,请耐心等待”,最终成功或失败后自动移除,避免后端已持久化消息但前端中断请求后产生会话状态错位。 +7. 普通消息请求等待期间将“发送”切换为“停止”;停止通过 `AbortController` 取消 HTTP 请求并释放后端会话锁,不触发 POST transport retry,已持久化的用户消息保持为普通历史。未停止且超过 120 秒时,思考气泡显示“仍在处理中,请耐心等待”。 不做(明确排除,防止后人补齐):