Files
Genarrative/docs/【编辑器】画布Agent对话面板-2026-07-03.md
T
kdletters 7072e52a25 收口美术 Agent 整体超时预算
为消息规划链路增加 18 分钟总 deadline。
修正 LLM 重试响应体错误的 attempt 计数。
统一配置、定价失败中文文案并补齐测试文档。
2026-07-21 16:44:46 +08:00

21 KiB
Raw Blame History

画布Agent对话面板

日期:2026-07-16

定位与边界

  • 画布Agent对话 是图片画布工程(/editor/canvas)右侧的对话式编辑器工具:用户通过自然语言调度画布已有的图片类生成与编辑能力,并可附加画布资源或素材库图片作为参考。
  • 它是画布域工具,不承接玩法创作、不产出玩法作品或模板,与 CONTEXT.md 中「表单/图片输入创作工作台」的 Avoid 边界不冲突。
  • 独立于拼图专用的 /api/runtime/creative-agent/sessions(该会话为 api-server 内存态、拼图领域专用,不复用)。

能力范围

对话 Agent 可通过 function-calling 触发以下八类工具,全部复用既有计费收口接口:

工具 后端接口
生成图片 POST /api/editor/images/generations
修改图片(基于附件/画布素材) POST /api/editor/images/edits
生成角色形象 既有角色形象生成入口对应接口
生成图标素材 POST /api/editor/icon-spritesheets/generations
生成 UI 设计图 既有 UI 设计图生成入口对应接口
生成视频 POST /api/editor/videos/generations
生成游戏音效 POST /api/editor/audios/sound-effects/generations
生成背景音乐 POST /api/editor/audios/background-music/generations
  • 意图解析与工具编排在后端 api-server,前端只渲染状态,不承接业务规则。
  • 下面的工具选择口径属于 Agent 规划 prompt / function-calling 约束,不是侧边栏 UI 说明文案;侧边栏面板不展示这些规则解释。
  • 用户要求“规范图 / 视觉规范图 / 风格规范图 / 素材规范展板”时,规划默认选择 generate_image,并在 prompt 中明确要求生成规范展板,包含统一视角、线条粗细、色卡、材质、阴影、圆角、状态层级、尺寸标注等可落地的视觉规范元素。
  • 用户要求“角色规范图”且语义是角色的规范展板、风格展板或设定板时,仍走 generate_image,不要误分流到 generate_character;只有实际生成角色立绘、角色主形象或角色视觉资产时才走 generate_character。用户要求多个图标素材、图集或 spritesheet 时才走 generate_icon_spritesheet
  • 所有生成必须走 execute_billable_asset_operation_with_cost 与模型定价配置,禁止绕过定价收口。
  • 图层操作及其他未注册的画板功能第一期不进入对话工具面,仍走现有面板。

当前分支落地状态

  • 已落地:会话元数据、OSS 消息文档、会话 CRUD、带 clientMessageId 幂等键的普通 JSON 消息请求、后端 LLM 工具规划、右侧对话面板、会话历史、新建 / 软删会话、附件从画布资源 / 账号素材库选择,以及八类图片 / 音视频工具对既有生成入口的复用。
  • 已落地:工具确认 / 取消、external generation task 轮询与会话懒回填。LLM 未配置、请求失败或规划结果解析失败时,后端把 role=system、正文以 ERROR 开头的消息写入 OSS,并通过 deltaMessages 返回,errorMessage 保持为空;前端隐藏 wire 前缀并以红色错误气泡展示。工具执行失败继续保存 status=failed、模型和错误信息,不能只返回瞬时错误。
  • 未落地:附件弹窗末尾上传格。external_generation_job 继续作为后台任务队列真相,对话消息只保存确认、回填状态和轻量媒体结果引用。

会话与持久化

  • 对话归属单个图片画布工程;每个工程有自己的会话列表,可新开会话。
  • SpacetimeDB 新表 editor_agent_conversation 只存会话元数据:会话 ID、projectId、ownerUserId、标题、软删标记、聊天记录 OSS 对象引用、创建/更新时间。
  • 完整消息内容存 OSSeditor-agent/{conversationId}.json会话粒度整体读写(追加消息=重写对象),不按消息拆对象。
  • 不把对话塞进画布工程快照 payload,不在 api-server 内存中保存会话真相。
  • 会话标题:新会话默认「新对话」,首条含文本的用户消息发出后自动截取前 N 字作为标题;列表摘要、详情和消息回包均携带同一必填标题,前端只展示该标题,不以会话 ID 或本地推导兜底。标题写入失败会使该消息请求失败,不能静默继续。
  • 会话删除:列表项 hover 出删除按钮 + 确认;软删(表打 deleted 标记,OSS 对象保留)。
  • 每次用户主动发送生成一个最长 128 字符的 clientMessageIdeditorAgentClient 对网络错误和通用瞬时状态码显式启用 1 次 POST transport 重试,重试复用同一个已序列化 body、clientMessageIdx-request-id。该字段独立于数字 message.id 并随用户消息写入 OSS。旧消息缺失时按 None 兼容;早期 SSE 文档若把客户端键存成用户消息字符串 id,读取时将其迁入 clientMessageId,同时重建数字定位符。后端在会话锁内检查重复键:内容一致时返回已持久化的同一回合结果,尚无结果时复用原用户消息继续规划;文本或附件身份不同则返回 409,不得再次追加用户消息或调用 LLM。

生成结果落画板(对现有占位规则的例外)

  • 对话入口触发的生成不创建"即将生成"画布占位(区别于其余生成面板);生成中状态由工具消息和外部任务状态承载。
  • 生成完成后:结果图按统一 placement 避让模型(视口中心就近、避开现有图层、32px 间距)落画板为新图层,同时登记到默认项目素材库;前端轮询到任务终态并重新读取会话后,以回填的轻量媒体引用显示纯缩略图并刷新工程快照与素材库,缩略图本身不显示名称也不承担图层跳转。
  • 消息内生成结果缩略图必须携带并优先使用 objectKey / assetObjectId,前端通过 ResolvedAssetImage / /api/assets/read-url 换签后渲染,不能把裸 /generated-* 私有路径直接交给 <img>
  • 既有编辑器 worker 通过 canvasCompletion 写回工程快照;刷新后由 external generation task 状态和会话懒回填恢复结果。
  • 该例外已同步登记在《生成类面板Lovart统一改造方案-2026-06-17》「画布占位落点」节。

右侧布局

  • 对话框与既有任务侧栏(ImageCanvasTaskSidebarView互斥展开:展开一个自动收起另一个;各自收起后保留入口按钮。
  • 对话框与左侧素材 / 图层侧栏不互斥,允许同时展开,便于在对话中选取和核对画布素材;左侧栏切换不改变 Agent 面板开关状态。
  • 桌面端对话框固定宽约 360–400px;移动端抽屉式全宽覆盖;收起态为胶囊/圆形入口按钮。
  • 会话管理入口在对话框头部:当前会话标题 + 历史会话下拉(按更新时间倒序)+ 新建对话按钮,全部包在对话框内。
  • 快速切换会话或会话轮询刷新产生并发详情请求时,前端只允许最后发起的请求更新当前会话、消息、错误和加载态;旧响应不得覆盖用户最新选择。
  • 普通 JSON 消息请求的回包必须绑定发送时的会话:用户在等待期间切换到其他会话后,只更新原会话的列表摘要,不得把原会话的 deltaMessages 、错误或画布刷新副作用应用到当前面板。
  • 收起对话框只是隐藏面板,不卸载当前会话 hook;普通 JSON 消息请求的等待态和外部生成任务状态必须在收起 / 重新打开之间保持一致。

附件

  • 输入区 + 按钮打开图片选择弹窗(仅图片,无音视频):
    • 「画布」页签(默认):展示当前工程图层引用的图片资源;
    • 「素材库」页签:账号级素材库(复用 ImageCanvasAssetLibrary 数据源);
    • 多选 + 底部「取消 / 应用」。
  • 网格末尾上传格为后续补齐项;在上传格未落地前,对话附件只从已有画布资源和账号素材库选择。后续若从对话入口上传图片,必须复用素材库 / 画布资源登记链路,不新增对话私有图片类型。
  • 附件选择弹窗使用 PlatformToolModalShell 承接 portal 主题变量和不透明 panel 背景;不能直接把未注入 platform-themeUnifiedModal portal 到 document.body,否则 --platform-modal-fill 失效后面板会变透明。
  • 应用后附件以胶囊 chip 挂在输入框上方;发出的消息内附件渲染为纯文本胶囊 chip(名称 + 小图标),默认无缩略图,鼠标悬浮才浮出缩略图预览
  • 附件领域形状:统一为画布资源 / 素材库对象引用(resourceId / assetId + 可选 objectKey),不存在只属于对话的第三种图;单条消息上限 9 张(前后端共同校验)。前端可携带展示用 imageSrc / thumbnailSrc,后端必须按当前工程和当前账号重新归一、校验归属与 objectKey

工具调用确认展示契约

  • Agent 规划出生成或编辑工具后,先把 status=not_completed 且没有 externalJobId 的工具消息持久化为待确认记录;确认卡必须在真正调用生成 provider 前展示本次提示词、规格参数、目标图和参考图缩略图,用户确认后才执行,取消后保留同一条 status=cancelled 记录。内部 system 文本和图片哈希 ID 不直接展示给用户。
  • 工具消息只保存 statusnot_completed / completed / failed / cancelled)和可选 externalJobIdexternal_generation_job 仍是队列、执行、lease 与计费结算真相;OSS status 仅表示该条对话消息是否已经回填完成结果或失败,不复制 queued / running。
  • 确认接口必须先把工具参数转换为既有编辑器 worker payload,再使用 editor-agent:{conversationId}:{messageId}:{toolName} 稳定 dedupe key 入队;同一确认的请求重试只能得到同一个 external job。入队成功后把返回的 job id 写回同一条 OSS 工具消息,不新增 Agent 工具执行关联表。
  • 前端根据 externalJobId 查询通用 external-generation job 状态;worker 继续通过 canvasCompletion 把生成结果写回工程与素材库。浏览器断线、刷新或 api-server 重启不得导致确认接口重新扣费或重新提交 provider。
  • GET /conversation 会在同一个 conversation lock 内扫描 status=not_completed 且已有 externalJobId 的工具消息:只对这些消息按 job id 定向读取主任务;任务完成后复用对应工具的 format_execute_message 替换 system text、回填轻量图片 / 视频 / 音频引用并写为 completed,任务失败则回填 error 并写为 failed。任务结果读取或 completed payload 解析 / formatter 回填失败时,必须在同一次 GET 内完成首次尝试及最多 3 次重试,三次重试各间隔 100ms 并重新读取任务结果;仍失败才把该工具消息写为 failed 并保存最后错误。该重试不依赖前端再次刷新。排队和执行中都保持 not_completed,整轮扫描结果一次性写回 OSS。
  • EditorAgentToolCall.args 保留为工具返回的原始 JSON,是确认接口重新反序列化并执行工具的唯一参数真相。图片参数继续只保存由真实 data key 计算出的 opaque SHA-256 imageId;不得为了前端预览把 args 中的图片 ID 改写成 objectKey、URL 或展示对象,也不得由前端重组或回传一份新的执行参数。
  • EditorAgentToolCall.displayArgs 是必填、只读的用户确认展示投影,与 args 分离:
    • stringArgs 保存提示词、比例、清晰度、模型、时长等可展示参数的稳定名称、用户可见标题和值;
    • imageArgs 按“目标图片 / 参考图片”等参数分组,每个 refs 项包含与原始参数对应的 imageId,以及后端从已校验会话上下文解析出的 objectKeyimageSrc、可选 thumbnailSrc / label / width / height
    • extras.priceMudPoints 保存创建待确认消息时按后端运行时模型定价快照计算的预计泥点消耗;前端统一展示为“预计消耗 N泥点”,不自行计算价格。
  • displayArgs 只能由 api-server 按已注册 tool 白名单,基于已经通过 ToolArgs 校验的 args 和当前 OSS 会话文档中的附件 / 历史生成结果构建;不能信任 LLM 自报的展示地址、标题或素材元数据。展示投影不参与确认执行,确认接口仍只读取同一条持久化 tool call 的 args,避免“看到的素材”和“实际执行的素材”分叉。
  • extras.priceMudPoints 同样只属于展示投影,不作为扣费输入;确认后仍由既有生成 BFF 按后端运行时定价执行预扣费,因此该字段表达用户确认时看到的价格快照,而不是前端可提交或覆盖的计费真相。
  • EditorAgentToolCall.summary 只是 args 的重复字符串且没有稳定语义,当前契约删除该字段,不再作为展示或执行输入。
  • 前端待确认卡只消费必填 displayArgs,不解析各 tool 私有的 snake_case / camelCase schema,也不把 sha256:* ID 当标题或图片地址。图片统一通过 ResolvedAssetImage 使用 objectKey 换签后显示,签名 URL 不进入消息文档。模块尚未上线,不保留缺少 displayArgs 时读取 raw args 的旧消息降级路径。

LLM 与计费

  • 编排复用 creative_agent_gpt5_client 的 LLM 接入配置(同 provider/env,独立用途标识),画布 Agent 规划请求固定使用 VectorEngine gpt-5.4-mini Chat Completionsfunction-calling 注册八类工具。
  • 每个用户回合必须由 LLM 返回结构化计划;LLM 未配置、连接已经断开、请求明确失败、达到最终安全上限或返回格式不可解析时,后端写入正文为 ERROR <错误内容> 的 system 消息,不使用本地关键词或“收到:...”回显兜底。面向用户的规划错误使用中文语义,不暴露 completion error 等 framework 内部前缀或原始配置/定价诊断;原始错误只记录在后端日志。该错误消息与其它 system 消息一样进入后续 LLM memory,使 Agent 能看到上一轮失败上下文。普通 JSON POST 尚未结束只表示 provider request future 仍在等待,不能伪装成已持久化失败。
  • 规划 prompt 必须自动带入上一条已完成生成结果的 latestGeneratedImage 引用,内容只包含上一轮 generation 的 toolName / resourceId / objectKey / assetObjectId 等轻量元数据,不把私有签名 URL 或大图内容塞进 prompt。
  • 工具参数中的图片 ID 是由真实 object key 或图片地址计算的稳定 SHA-256 标识;真实 data key 仅存于 api-server 的工具上下文映射,所有图片工具在执行时查表恢复,不能把 object key 或图片地址作为 LLM 可见的工具 ID。
  • 用户使用「这张」「刚才那个」「上一张」「把衣服换成……」等方式指代或编辑上一张结果图时,LLM 默认选择 edit_image 并引用 latestGeneratedImage 作为源图;除非用户明确要求全新生成,否则不能因为本轮没有重新上传附件而降级为 generate_image
  • 规划 prompt 必须显式区分“规范展板”和“实际素材产出”:规范图、视觉规范图、风格规范图、素材规范展板、角色规范图等规范展板请求走 generate_image,并补齐统一视角、线条粗细、色卡、材质、阴影、圆角、状态层级、尺寸标注等要求;实际角色立绘才走 generate_character,多个图标素材 / 图集才走 generate_icon_spritesheet
  • 画布 Agent 规划请求使用 Chat Completions 和 1024 max_tokens。发送后 120 秒是前端软提示阈值,不是 provider 失败 deadline:若普通 JSON POST 仍 pending,消息流临时显示“仍在处理中,请耐心等待”并继续等待,提示不写入 OSS 消息历史;连接或请求明确失败则立即按正式错误收口。provider 单 attempt 保留 8 分钟 hard timeout;请求发起阶段的 timeout、连接失败、4084295xx 读取 GENARRATIVE_LLM_MAX_RETRIES,但画布 Agent 最多重试 1 次,专用重试退避最多 60 秒。消息规划生命周期从 handler 入口开始计入 18 分钟总 deadline,进入 agent.prompt(...) 时使用扣除会话锁和上下文准备后的剩余预算;该 deadline 覆盖非法 JSON/工具校验失败触发的后续规划轮,并为错误持久化和 HTTP 返回保留约 2 分钟,不再让前端 20 分钟 transport timeout 先触发。已收到成功响应头后的响应体读取或解析失败直接按明确失败收口,错误计数/日志使用该响应所属的真实 attempt。规划重试发生在任何生成工具执行之前,不会重复提交生成任务或扣费;生成图片/编辑图片仍走对应生成工具和模型计费。
  • function-calling runner 必须把“等待用户确认”作为显式工具语义:当本批所有工具都校验成功并进入待确认状态时,立即以成功结果结束当前规划回合并持久化助手文本与待确认卡,不得继续依赖 LLM 自行停止;未知工具、参数错误、普通连续工具和不可解析响应仍受 max_turns 保护。
  • 对话回合免费(聊天、分析回复不扣泥点),仅 Agent 实际触发生成工具时按对应模型定价扣泥点。
  • 工具调用前后端校验泥点余额;不足时该次生成失败并在对话中以明确错误气泡告知,对话本身可继续。

Lovart 参照做/不做清单(验收标准)

做(第一期):

  1. 助手文本随普通 JSON 消息响应一次性返回;
  2. 消息请求等待态,以及工具任务的待确认、生成中、完成 / 失败状态;
  3. 工具/模型标注行(生成时显示模型名 + 图标);
  4. 消息内生成结果缩略图(纯预览,不显示名称,不点击聚焦图层);
  5. 生成中的进行中动画;
  6. 错误气泡(失败/余额不足,带原因);
  7. 普通消息请求等待期间禁用发送按钮,不提供客户端停止操作;前端持续等待后端响应,超过 120 秒但 POST 仍 pending 时在思考气泡中显示“仍在处理中,请耐心等待”,最终成功或失败后自动移除,避免后端已持久化消息但前端中断请求后产生会话状态错位。

不做(明确排除,防止后人补齐):

  • 点赞/点踩反馈按钮;
  • 消息复制、分享/导出对话;
  • Agent 模式切换下拉(固定单一 Agent);
  • 语音输入、@引用、多 Agent 协作;
  • Lovart 的积分/加速档位显示(泥点扣费只在生成动作上体现)。

顺手需求

  • 左侧侧边栏默认隐藏:useImageCanvasEditorChrome.tsactiveSidebarPanel 初始值 'assets'null
  • 小地图默认隐藏:isMinimapOpen 初始值 truefalse
  • 纯默认值修改,不加 localStorage 偏好记忆。

后端分层落位

  • module-editor-agent(新 crate):领域规则——会话/消息校验、状态机、附件上限、软删规则、工具清单领域定义;纯逻辑无 IO。
  • spacetime-module:新表 editor_agent_conversation + procedure;同步 migration.rs、表目录、生成绑定,运行 npm run check:spacetime-schema
  • spacetime-clientfacade 读写方法。
  • api-server
    • GET/POST /api/editor/projects/{projectId}/agent-conversations(列表/新建);
    • GET/DELETE /api/editor/agent-conversations/{conversationId}(详情/软删);
    • POST /api/editor/agent-conversations/{conversationId}/messagesJSON);
    • Agent 编排(function-calling 循环、工具内部调既有生成执行链路)放 api-server 编排层,独立文件,不复用 creative_agent.rs 内存会话。
  • shared-contracts + packages/sharededitorAgent 会话、消息、工具确认展示与轻量媒体结果 DTO;消息响应返回 conversationdeltaMessages 和可选 errorMessage

实施顺序

  1. 契约与领域规则(shared-contracts / packages/shared + module-editor-agent);
  2. 存储层(spacetime-module 表 + procedure + migration + spacetime-client + schema check);
  3. api-server 会话 CRUD + 消息 OSS 读写 + JSON 回显桩(不接 LLM,先保证会话链路端到端真实落库);
  4. 前端最小纵切(对话框、会话管理、消息流、附件弹窗、侧边栏/小地图默认值)——可与 3 并行:3 只碰 server-rs/4 只碰 src/ 且先以契约 mock 客户端联调,汇合点在 4 末接真实 API;
  5. LLM 编排 + 八类工具接入 + 生成落画板 + 请求等待 / 错误态(替换回显桩这一个点);
  6. 验证与文档:定向测试、类型检查、npm run check:encodinggit diff --checknpm run check:spacetime-schema、api-server smoke /healthz;同步 CONTEXT.md 与相关文档。

第一阶段验收补充

  • 打开画布 Agent 后任务侧栏应关闭,再次打开任务侧栏时 Agent 面板应关闭;素材 / 图层面板与 Agent 可同时展开,互不改写开关状态。
  • 发送消息时先本地追加用户消息,再应用 JSON 响应中的 deltaMessages;请求等待期间发送按钮保持禁用,前端不主动中断当前回合。
  • Agent 消息内生成结果缩略图只用于预览,不显示名称,也不点击跳转图层;轮询到任务终态并完成会话懒回填后统一刷新工程快照和素材库。
  • 对话内容可被用户选中复制;用户从输入框或对话内容点击回画布图层 / 生成器时,焦点应回到画布对象,Backspace / Delete 等画布快捷键继续生效。