Files
ttstd-infinite-canvas/docs/design/cloud-agent-architecture-optimization.md

383 lines
42 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 云端 Agent 架构优化方案
状态:架构治理已实施第一阶段;本文同时保留审查基线、目标结构和未完成验收项。
审查基线:当前工作区,包含本轮开始前已有的未提交修复。结论来自实际入口、调用链、类型、持久化逻辑与相邻测试的静态核对;真实模型、收费生成、数据库迁移和压力测试仍需按文末清单验收。历史审查报告仅作为线索,不能替代当前代码。本文中的性能影响、条件触发场景和验收目标不代表已发生的线上事故或已达到的指标。
## 实施状态:模型能力驱动的上下文预算
Agent 文本上下文不再由固定的 96KB、120KB 或 192KB 阈值决定。文本模型能力合同新增 `contextWindowTokens` 和 `maxOutputTokens`,后端在每次发起模型轮次前解析实际执行线路:显式渠道模型使用该模型能力;逻辑模型取所有可用文本线路的安全交集(上下文窗口和输出上限分别取最小值),避免路由选择较小窗口后被上游拒绝;未知或旧配置使用 128K / 16K 的保守默认值。前端准备任务时使用同一份能力合同做发送前预算检查。
预算会预留工具 schema、协议包装和恢复轮次的比例空间,并按 Token 估算触发压缩;估算只用于规划,实际上游 tokenizer 仍是最终边界。压缩只移除可重新读取的长正文和节点列表,保留任务、生成、审批、计费、取消、提交和回写事实,且不会拆开 assistant/tool 成对消息。1M 上下文模型可以配置并使用接近 1M 的输入预算;`cloudAgentRuntime` 的 512KB 持久化 checkpoint 是存储边界,与模型上下文窗口独立,不能通过扩大模型窗口绕过。
本阶段已补充后端预算、Token 估算、压缩保留事实、前端能力配置与 provider 输出上限的专项测试。真实模型 tokenizer、超大上下文压力、跨路由实测仍列入待验收项。
## 1. 产品目标与总体判断
建议把 Agent 定位为影策创作领域的一种交互入口:它解释用户意图、组织计划和调用能力;画布、引用、生成、授权、费用与恢复的规则由领域服务执行。
当前主要矛盾是同一个创作意图在 Agent、节点编辑器、任务提交和供应商适配中被分别解释。问题已超出修正几个字段的范围,属于涉及收费与核心生成链路的 L2 架构治理。
目标应体现为用户可感知的稳定行为:
1. Agent 创建、手动编辑、再次生成与任务中心重试,能说明并保持同一份生成规格;明确变更才产生新规格。
2. 用户始终能区分草稿、待批准、已提交、结果待核对、生成失败、回写失败和取消收尾。
3. 参考素材重排、更名、跨轮使用后仍指向正确资源;每次收费执行可追溯当时的输入和报价。
4. 长对话、刷新、重连和服务重启后,系统能恢复事实与进度;模型无需猜测是否已经执行。
5. 新节点、新模型能力和新参考类型有明确接入点与合同验收,不需要修改所有入口的分支。
采用现有 Go 模块化单体渐进演进,保留 React 画布、任务 worker、协议插件、账本及事务机制。新增抽象必须收敛正在重复的业务规则;本轮不需要引入新的编排框架、微服务或向量数据库。
## 2. 对原 11 项问题的核对
| 项目 | 当前判断 | 根因与治理方向 |
| --- | --- | --- |
| 1. 视频字段错位 | **确认,当前仍存在。** Agent 节点写 `videoSeconds / videoGenerateAudio`,前端读 `seconds / generateAudio`。[E1] | 缺少节点领域对象与任务输入的显式转换。任务协议和节点存储可以使用不同字段名,但只能在受测适配器中转换。 |
| 2. 能力注册未完整收口 | **确认。** 后端 registry 已服务 Agent 的创建、字段校验、连接和投影;前端节点类型、生成设置及默认值仍独立维护。[E2] | 先建立跨端数据合同和规则归属,再扩展 registry;不能把 UI、供应商协议和所有业务策略塞入一个大注册表。 |
| 3. 魔法值与重复规则 | **确认。** 文本限制分布在描述、校验、读取中;布局间距与竖幅尺寸写在 Agent 媒体创建流程。[E3] | 按领域归属管理规则。同为 `20` 或 `16000` 不代表同一种限制,不能统一成无语义的全局常量。 |
| 4. 字符串键与 map | **确认边界不足,但并非完全无类型。** 已有 `cloudAgentMediaArgs`、审批结构和 provider 输入类型;画布 metadata、投影和结果仍大量使用 map。[E4] | 对命令、生成规格、引用、审批、收据和错误建立类型;原始 JSON 留在 codec、插件协议和受控扩展边界。 |
| 5. 引用多套表示 | **确认。** 连线、`referenceNodeIds`、任务媒体数组、mention 编号仍分别参与计算;当前补丁已改善排序与提示词标签。[E5] | 稳定 ReferenceBinding 作为生成引用的领域对象;连线、标签、任务数组都从它投影。 |
| 6. 状态混用 | **已部分修复。** 当前 `generation` 读取归属校验后的真实任务,`outputReference` 单独表达输出可引用性;仍无完整的统一诊断模型。[E6] | 任务执行、资源就绪、节点展示、回写与连接状态分开,来源与映射集中管理。 |
| 7. 审批快照范围过大 | **确认,展示字段排除已改善。** 媒体哈希仍覆盖整张画布全部节点业务字段和全部连线。[E7] | 使用本次操作的依赖集合与规范化生成规格;整文档 revision/CAS 继续保护保存。两者职责不同。 |
| 8. 错误传递丢失 | **已部分修复。** 当前工具错误补充了部分 `reason / field / phase / taskSubmitted`,媒体完成也区分生成与回写。任务诊断与取消来源尚未贯通。[E8] | 结构化错误从任务/协议域向上保留,模型只解释事实;本次未取得原始故障日志,不能重新归因该次 MiniMax 失败或取消。 |
| 9. 运行历史混入 JSON | **原问题确认,现已分拆主要增长数据。** 调用事件进入 append-only journal,模型消息进入可压缩 transcript,运行状态只保留恢复所需控制面与结构化引用;checkpoint 上限仍是持久化边界,不等于模型上下文窗口。[E9] | 后续继续把高增长正文和可查询审计事实迁到独立存储,保留旧状态迁移与损坏检测。 |
| 10. 固定阈值压缩 | **原问题确认,现已治理。** 生产路径按实际模型能力的 Token budget 和事实帧压缩;旧 96KiB/24 条触发规则已移除,24 条不再作为生产触发条件。[E10] | 构建有来源、有新鲜度的工作上下文,按模型预算取材;不能据此断言旧规则导致了本次模型误判。 |
| 11. 验收局部化 | **确认缺少覆盖完整产品合同的证据。** 已有大量参数、审批、取消、上下文与前端协议测试;本次静态审查没有证明跨端闭环或真实模型语义稳定。[E11] | 建立跨语言合同、执行故障、真实页面、真实模型四层验收。行为策略变化必须比较同一组场景。 |
## 3. 本轮补充发现
### 3.1 P1:模型选择也会跨端漂移
Agent 使用逻辑模型时只写 `metadata.logicalModelId`;使用指定渠道时写 `channelId`,但 `model` 只保存裸模型名。前端生成配置只从 `metadata.model` 恢复选择;裸模型名会选择第一个匹配渠道。[E12]
因此存在两个有代码依据的触发路径:逻辑模型节点回到全局模型;多个渠道存在同名模型时,节点可能换到另一个渠道。尚未做真实页面复现,不能声称每次都会发生。
这项应与视频参数错位一起进入第一个纵向改造切片。必须显式表达 `logicalModelId` 或 `channelId + modelKey`,不再让一个显示字符串同时代表模型身份、渠道身份和用户选择。
### 3.2 P1:同轮快照刷新缺少变更来源判断
`cloudAgentRefreshStepSnapshotHash` 发现后续写调用仍使用本轮基线后,直接替换为当前画布哈希;未检查差异是否来自本轮已提交操作。[E13]
现有测试覆盖“本轮第一步写入后,第二步继续”,但不能排除另一标签页或另一运行在两步之间修改了目标内容。更新哈希会把这种变化也当作已观察到。后续 CAS 可以防止读取之后的新并发写,无法证明旧计划理解了此前的新内容。
应按操作依赖与写集判断:本轮已知写入可继续;不相交变更可重新计算保存基线;目标或输入被外部修改则给出明确冲突。不得直接把最新哈希当作模型已重读的证据。
### 3.3 P1:审批对象没有冻结完整报价,预算没有覆盖所有换价路径
媒体审批前已调用任务准入做 dry-run,但得到的 task/order 没有形成持久报价;审批记录主要绑定调用参数、预览和模型名,实际提交时重新解析目录与计费。[E14]
当前 run 总预算能限制初次准入,因此不应描述为“无预算保护”。缺口在于:等待批准期间目录/价格变化可能在总预算范围内改变最终费用;逻辑模型按渠道计价时,备用线路换价又只检查账户余额和账单,未承接初次准入的 run 剩余预算。[E15]
后者的具体条件是:逻辑模型按渠道计价、原线路明确未接单、备用线路更贵、账户余额充足。静态代码确认约束未贯通,未执行换价或观测实际超预算扣款。
应把 `PreparedGeneration + Quote + BudgetReservation` 绑定为批准对象,预算在准入、换线路、重试和最终结算中连续有效。价格增加或规格变化需重新批准;原授权内等价且不涨价的路由切换可按明确策略执行。
### 3.4 P1:Agent 媒体子任务缺少正式来源关联,通用重试合同不完整
媒体任务使用 `image_to_video / text_to_*` 等通用 operation,仅在 input metadata 标记 `source=cloud_agent`。任务中心重试只拦截 `CreationSubmissionID` 或 `cloud_agent` operation 前缀;满足失败/取消与账单核对条件的 Agent 媒体子任务可以进入通用重试、重新创建账单。[E16]
用户在任务中心主动重试可以被产品定义为一次新授权,不能简单认定为越权。当前缺口是没有统一说明它属于原 run 预算还是新授权,也没有独立的新生成身份来保护原执行事实。
建议将来源、run/action/generation 关联持久化。传输重试、查询原任务、补回写和重新收费生成分成不同命令;用户再次生成取得新 generation 身份,并明确新授权和预算归属。
### 3.5 P1:合法读取也可能打满单事件上限
画布摘要允许最多 40 个节点,每节点正文最多 2000 字符;结果完整写入 tool event,而单事件限制为 128KiB。40 × 2000 个常见中文字符仅正文约 240KB,已超过上限,可导致检查点失败并终止运行。[E17]
这是代码路径与字节量推导出的可构造场景,未运行复现。它说明问题不只是“历史太长”,还包括工具输出预算与持久化预算互不协调。
所有读取应遵循总字节预算与游标分页;长正文保留可重读引用,事件仅保存执行收据。达到分页限制应正常返回下一页,不能让合法读取变成整个运行失败。
### 3.6 P2:临时素材和会话仍缺少完整生命周期
- 标注 PNG 的 base64 同时进入临时引用、工具结果和事件,至少有三份正文副本;当前压缩不会移除 `overlayDataUrl`。[E18] 应复用资源存储,增加作用域、过期时间和被任务引用后的保留规则。
- 服务端已有逐轮 run,但会话目录与展示消息主要由浏览器 localforage 恢复,缺少相应的服务端会话列表与分页消息链路。[E19] 跨设备恢复、归档、删除、保留期限需要形成产品合同。
- 会话后台保存存在直接 `void save...` 的路径,没有对应失败反馈;不能把页面已显示等同于已可靠保存。[E19]
### 3.7 P2:草稿、生成批准与拒绝文案的语义不一致
当前媒体工具进入 `waiting_approval` 前已经持久化草稿节点和连线;拒绝分支却返回“未写入画布”,并不撤回此前草稿。[E20]
需要把“准备草稿”和“批准收费生成”定义成不同动作。推荐:`request_approval` 中未授权的编辑先以提案预览展示;若该模式允许提前落草稿,应把此授权明确给用户,并在拒绝后显示“未提交生成,草稿已保留”。撤销草稿与取消任务分开,不能为修正文案而自动删除用户已修改的草稿。
### 3.8 P2:发布升级与能力扩展的边界过粗
运行恢复要求 policy 和全局 capability hash 与当前完全一致;registry hash 包含用途描述、推荐场景、尺寸等信息。修改能力说明也可能使旧活动运行无法继续,而报错路径可能将其描述为状态损坏。[E21]
这是现有安全拒绝策略,不能直接删除。应区分执行合同版本、权限策略版本、提示词版本和展示版本;仅绑定本次操作实际使用的能力。破坏性发布需 drain、明确失效原因和重新批准路径。无需因此为所有旧字段建立无限期兼容层。
另一方面,当前 hash 不包含 `CreateMetadata` 工厂实现,现有测试还明确允许工厂变化而 hash 不变。[E26] 因而这个 hash 同时存在“展示变化影响过大”和“默认业务值变化未覆盖”的问题。受执行约束的默认值应成为可版本化的声明数据。
当前 registry 还把一个 `GenerationMode` 唯一映射到一种节点,扩展更多可生成图片的业务节点时会受限。[E21] 应允许多个节点声明同一生成能力,由具体节点适配器完成输入/输出映射。
另外,`app/provider.go` 与 `generation/types.go` 已有两套近似输入类型,实际主链路尚未完全迁移。[E22] 本次治理应收敛它们,避免新增第三套 provider 合同。
## 4. 应保留的现有基础
| 已有能力 | 后续处理 |
| --- | --- |
| `MutateCloudAgent` 的 revision CAS 与事务 | 保留;拆表时让节点变更、任务/费用预留、控制状态和事件仍原子提交。[E23] |
| 通用 Task、worker、任务租约、计费账本、协议适配与上游提交恢复 | 复用;统一 Agent 与手动入口的来源和授权边界,不新建 Agent 专属媒体任务系统。 |
| 独立控制列、损坏状态安全终止、`CleanupPending` 及取消恢复 | 保留;将取消来源和最终对账结果补进持久结构。[E9] |
| 调度 keyset 分页、SSE 游标和前端去重 | 保留。当前已避免旧报告所述“前 50 个等待任务长期挡住后续任务”的固定首批扫描;SSE 无变化时也已有 revision 快路径。[E24] |
| 后端能力 registry、服务端授权校验、审批调用哈希 | 扩展为统一合同的消费者,并绑定准备完成的领域操作。 |
| 版本化提示词、按需技能读取、用户偏好分层 | 保留;领域事实与执行约束不继续叠加为提示词规则。 |
## 5. 目标架构与规则归属
```mermaid
flowchart TD
UI[画布编辑器与任务中心] --> APP[应用命令与查询入口]
AG[Agent 意图与计划] --> TOOL[受控工具适配器]
TOOL --> APP
APP --> CANVAS[画布领域:节点、连线、依赖与变更]
APP --> PLAN[生成领域:规格、引用绑定与执行计划]
PLAN --> PREP[不可变准备结果:输入、依赖、报价]
PREP --> AUTH[授权与预算校验]
AUTH --> TX[事务:任务、费用预留、关联、状态与事件]
TX --> TASK[现有任务执行与协议适配]
TASK --> ASSET[资源与生成产物]
TASK --> PROJ[状态投影与执行收据]
ASSET --> PROJ
PROJ --> UI
PROJ --> CTX[上下文构建器]
CTX --> AG
```
这是职责图,不要求这些节点各自成为服务、数据库或类。
| 领域 | 唯一负责的规则 | 推荐落点 |
| --- | --- | --- |
| 画布合同与 codec | 节点字段、结构化内容、序列化、允许写入字段、连接语义、生成依赖投影 | 扩展 `internal/canvas`;前端 `types` 与 `lib/canvas` 消费声明合同 |
| 生成规划 | 用户规格归一化、模型选择、参考绑定、提示词编译、参数默认值解析 | 收敛现有生成输入,在 `internal/generation` 形成可测试规划能力 |
| 资源引用 | 资源身份、归属、版本、可用性、临时资源生命周期 | 复用 `internal/assets` 与现有资源持久化 |
| 授权和计费 | 授权对象、报价版本、费用上限、预留/释放/结算、重试授权 | 复用现有 app 准入与账本,提取独立策略而非塞入 Agent runtime |
| Agent 编排 | 工具调用、计划推进、等待用户、错误后的决策、运行控制 | 逐步提取 `internal/agent`;仅依赖领域端口,不 import `app/service` |
| 应用组合 | 组装端口、权限上下文、跨领域事务与副作用协调 | `internal/app`;`internal/service` 保持现有稳定别名导入面 |
| UI 与布局 | 控件、节点可见尺寸、排版、选中态、连接状态与用户操作反馈 | 现有 React 组件和 `lib/canvas`;Agent 使用同一布局策略的可序列化参数 |
| 存储 | 查询、索引、CAS、事务和分页 | `repository/model/database`,不反向承载生成或权限策略 |
限制也按这张表归属:节点正文/标题限制属于字段合同;一次命令的操作数属于命令准入;工具结果的字节上限属于读取协议;上下文大小属于模型预算;任务配额属于运行策略;视觉尺寸与间距属于画布布局。工具说明从实际合同提取这些值。
## 6. 核心合同设计
### 6.1 数据合同单源,领域策略仍由对应领域执行
建议将需跨 Go/TypeScript 共享的字段、枚举、可空性、限制和语义版本沉淀为一个声明式合同源,生成两端 DTO、基础校验和工具 schema 中可复用的部分。后端 registry 引用该合同,并保留连接校验、命令执行、权限和依赖提取等领域实现。
可放置专用 `contracts/canvas-generation/`,但这应替代手写重复结构,而不是成为第三份定义。继续使用现有 `http` API 客户端,不从 OpenAPI 重建业务调用层。
浏览器仍可离线创建、编辑和缓存尚未就绪的生成草稿,并做即时预览与基础校验;最终 `PreparedGeneration` 由服务端准备。UI 必须显示本地未同步、资源待物化、报价待确认等实际状态,不能把本地草稿显示成已持久化或已可执行。自定义渠道和工作流继续走现有可信中转/执行适配,凭据不进入 `GenerationSpec`。
字段有三类归属:
- 持久业务字段:提示词草稿、生成选项、参考绑定。
- 执行派生字段:任务关联、产物、生成状态,由权威执行记录投影。
- 展示字段:位置、可见尺寸、面板状态,不能默认参与生成依赖。
新增字段必须声明类别及是否参与生成。未知节点可以受限读取和保留原始数据;未知执行字段、未知合同版本不得带着猜测默认值进入收费路径。扩展字段应带命名空间和 schema,不能获得任意写 metadata 的权限。
### 6.2 六个最小领域对象
| 对象 | 关键内容 | 约束 |
| --- | --- | --- |
| `GenerationSpec` | 生成类型、提示词文档、`ModelSelection`、类型化选项、文本输入、引用绑定 | 用户可编辑、无凭据;明确 `false`、`0`、未设置的不同语义。默认值解析时记录来源。 |
| `ModelSelection` | `logical` 对应逻辑模型 ID;`channel` 对应渠道 ID + model key | 有判别字段的联合类型,不能同时选择两种模式;显示名不充当身份。 |
| `ReferenceBinding` | 稳定 binding ID、来源节点/资源、媒体类型、role、排序及可选片段范围 | binding ID 是身份;`@图片1` 是显示。草稿可跟随节点最新产物,提交时必须解析并冻结确切资源。 |
| `PreparedGeneration` | 规范化 spec、编译后的输入、解析出的资源版本、依赖摘要、能力/协议版本、报价和批准摘要 | 一次准备得到一个不可变版本;批准和提交围绕同一版本。执行凭据在运行边界注入,不能写回节点或审批。 |
| `ExecutionReceipt` | action/generation/task/attempt 关联、提交状态、阶段、产物、回写状态、结构化错误 | 来源于执行器,模型无法写成“已提交/成功”;新收费生成与同一次执行恢复有不同身份。 |
| `ContextFrame` | 用户目标与约束、有效授权摘要、当前计划、活动任务、关键收据、带来源的事实索引 | 是可重建投影。事实带资源版本/事件位置/观察时间;授权判断回到权威存储。 |
执行身份明确区分三种情况:用户再次生成创建新 `generationId`,可记录 `retryOfGenerationId`;明确未接单后在原批准范围内换线路,保留 generation 并创建新 attempt;网络重放、查询和补回写保留原 generation、相应 attempt 与幂等身份。attempt 不代表新的费用授权。
报价阶段不占用用户积分。批准后正式准入时,在现有事务中同时校验并预留账户额度与 run 额度,形成持久 `BudgetReservation`,关联生成及账单。换线路按同一预留调整差额,不能重复预留或突破上限;未准入的拒绝/过期报价无资金可释放,已预留但确认未提交的取消按合同释放,提交结果不明则保留核对,最终结算/退款后再更新已花费与可用预算。金额沿用整数微积分单位;预算校验不能仅靠请求内存参数。
不要求立即重命名所有历史节点字段。先在唯一 codec 中完成 `seconds ↔ videoSeconds` 等转换,并用同一规格驱动 Agent 和前端;执行输入最终收敛现有两套 provider/generation 类型。
### 6.3 引用与提示词一起编译
连线是可视化关系,并非所有连线都代表媒体参考。每一种关系应明确用途:文本输入、参考素材、首帧、尾帧、遮罩、镜头来源等。
生成编译器接收 `GenerationSpec + bindings`,一次生成:资源输入、供应商 role、mention 标签与最终提示词。编辑器不再独立按全图连线数组顺序定义引用编号。重排改变显式顺序,重命名不改变引用身份;删除引用时显示缺失绑定,并拒绝收费提交。
文本输入也需显式表达“拼接源文本”还是“只用当前草稿”。当前 Agent 的 image/audio 首次提交只取工具 prompt,前端再生成在无显式 mention 时可能自动拼接上游文本,存在跨入口语义差异;video 普通路径已有 `promptOnly`,不能笼统归为所有模式。[E25]
### 6.4 审批按依赖验证,保存按并发版本保护
准备执行时记录本次实际读取的目标字段、参考资源版本、文本来源、引用顺序、模型能力、价格和策略版本,形成 `DependencySet`。审批摘要绑定规范化输入和费用约束,不绑定整张图的展示状态。
准备结果需通过资源引用租约保留这些输入,覆盖等待批准期间,直到批准过期、拒绝、取消或正式任务接管。节点更换产物不能触发 GC 删除仍被有效准备结果引用的旧资源;权限撤销和明确的资源失效仍应阻止提交。准备结果有有效期,不能无限阻止资源回收。
| 审批后变化 | 推荐行为 |
| --- | --- |
| 移动、缩放、重命名无语义作用的展示标签,修改无关节点 | 批准继续有效;提交在当前文档上重新应用受控变更,并通过 CAS 保存 |
| 修改目标 prompt、时长、音轨、模型、实际输入绑定或引用顺序 | 当前批准失效,展示差异并重新准备 |
| 引用节点生成了新产物 | 批准绑定旧资源版本;若用户要使用最新产物,产生新准备版本 |
| 资源删除、权限撤销、模型停用 | 即使哈希未变化,执行前仍强校验并阻止提交 |
| 同轮前序操作产生依赖输入 | 用已提交收据准备后续尚未批准的操作;已批准输入发生变化仍需重新准备和批准,不能自动替换 |
| 新字段/未知 schema 影响执行语义 | 合同不明确则停止该写操作,要求升级/重新准备 |
初期可以继续存储整文档 JSON并使用现有保存 CAS。将“审批失效判定”缩小为依赖集合,不等于放松数据库并发保护,也不要求立即实现 CRDT。
### 6.5 错误与恢复成为正式协议
建议错误包含 `code / phase / fieldPath / taskSubmitted / submissionOutcome / taskId / retryClass / safeMessage / diagnosticId`。`submissionOutcome` 至少区分未提交、已接单、结果不明;取消额外保留来源、触发者、请求时间、上游取消状态及费用核对状态。
| 情况 | 系统允许的恢复 |
| --- | --- |
| 字段校验失败、未创建任务 | 修正规格后重新准备 |
| 上游明确拒绝且没有创建工作 | 在批准的能力与费用范围内按既有策略处理 |
| 提交超时,是否接单不明 | 查询/对账原 attempt;无证据时不得换 key 自动重新收费提交 |
| 已有任务 ID,查询暂时失败 | 重查原任务,保留执行身份 |
| 任务成功但资源落库/画布回写失败 | 重试相应持久化步骤,不能重新生成 |
| 用户取消或系统终止 | 持久化取消来源,完成幂等收尾;取消不等同于上游成功取消或已退款 |
| 用户要求再次生成 | 新的生成命令、新授权/预算归属,保留旧收据 |
原始供应商错误可以进入受保护诊断记录;用户和模型收到脱敏错误及诊断 ID。不要为了可诊断性把 URL、凭据或原始请求全部注入模型上下文。
## 7. 持久化、上下文与产品恢复
### 7.1 先拆增长数据,保持现有事务
建议增量形成:
| 数据 | 存放与用途 |
| --- | --- |
| Run 控制记录 | 扩展现有 `cloud_agent_executions`,保留有界控制状态、revision、取消/清理状态和活动关联 |
| Action/Generation 与批准事实 | 保存准备结果、授权、任务关联、幂等身份、收据;按需要独立记录,不为每个小 struct 建一张表 |
| Agent 事件 | 追加存储,`(run_id, seq)` 唯一;与状态变更同事务。正文以引用表示 |
| Conversation/Message | 服务端可发现的会话索引和分页消息;run 属于会话,浏览器存储降为缓存 |
| 资源和长正文 | 复用资源服务,包含临时标注、长工具输出、诊断附件;明确保留与清理规则 |
| 模型上下文 | 按需生成,可缓存;不是任务状态、批准或费用事实源 |
事件游标不能继续从内存 `len(Events)` 分配。SSE 初次/恢复使用紧凑快照与增量事件,过期游标有明确的重建路径;跨实例通知仅用于唤醒,数据库保证可重放。规模需要时使用事务 outbox,先不引入新的消息中间件。
拆分存储以后仍保留总存储配额、保留期限和单条结果限制,避免从“小 JSON 太容易溢出”变成“历史无限增长”。
### 7.2 上下文构建代替字段黑名单压缩
每次模型决策前,由系统完成以下投影:
1. 读取当前有效用户目标、修正与取消指令,保留可追溯消息来源;推断出的偏好不能覆盖用户明确要求。
2. 从调用收据与任务/资源服务刷新本轮活动对象:是否提交、目前状态、产物是否可用、是否等待核对。
3. 注入与当前操作相关的授权边界、未完成计划和需处理的错误,不把过期失败状态长期钉在上下文。
4. 保留近期完整的工具调用/结果组,长正文按引用与游标读取。
5. 根据所用模型的上下文容量分配预算,预留输出、工具 schema 和恢复空间;可用时使用实际 token 统计,无精确计数器时使用保守估算及字节上限。
统一工具结果形态可采用 `summary / facts / receipt / bodyRef / pagination`。审计事件保存收据,模型按需取正文,UI 用单独的呈现投影;三者不再复制同一个无限增长对象。
当关键事实无法装入预算,应主动缩小当前工作集或有依据地建立新轮次,保留可恢复身份。不能悄悄丢弃用户约束、已提交收据和费用事实。
### 7.3 版本与发布合同
执行合同、工具 schema、模型/协议能力、提示词和展示描述分别版本化。已准备的生成与审批绑定实际参与执行的版本。
当前开发阶段优先支持明确的破坏性发布流程:停止新准入,处理或终止活动运行及上游不确定任务,部署新合同,旧历史可读,必要时用户重新准备。未来确有不停机升级需求,再引入有期限的运行时版本兼容,避免预先维护多套执行器。
## 8. 实施顺序与完成标准
以下是依赖顺序,不是未经评估的工期承诺。每个阶段都交付可验证的产品行为;后续阶段不能用“第一阶段补丁已经通过”代替验收。
| 阶段 | 范围与主要改动面 | 完成标准 |
| --- | --- | --- |
| P0:合同基线与复现夹具 | 明确规格、引用、批准、费用和重试语义;整理真实序列化样本,建立跨端 fixture | 每个问题有当前行为、预期不变量和入口矩阵;补丁与根因治理分开记录 |
| P1:首个完整生成切片 | 视频/图片节点 codec、`GenerationSpec / ModelSelection`、统一引用和提示词编译;Agent与前端使用同合同,收敛重复输入类型 | `Agent 创建 → 编辑 → 再生成` 保持模型身份、选项、引用、prompt;手动创建转 Agent 同样成立 |
| P2:授权与执行一致性 | PreparedGeneration、依赖集合、报价与预算预留、正式任务来源、重试身份;替代全图审批哈希和无来源 rebase | 无关编辑不误伤批准;相关变更必拦截;调价/换路由/重试不超批准;未知提交不重复收费 |
| P3:运行数据分离 | 先工具输出字节预算与资源引用,再拆事件/消息及必要的调用批准记录;保持现有事务 | 大读取可分页,检查点有界,崩溃恢复不遗漏终态,增长型历史不阻止控制状态保存 |
| P4:上下文与会话恢复 | ContextFrame、服务端会话索引、分页历史、跨设备与缓存失败反馈 | 多次压缩/跨轮继续仍准确使用任务事实;换设备可找到会话;未保存状态可见 |
| P5:扩展与发布门槛 | 多节点共享生成能力、合同版本策略、接入模板、模型行为评测和容量测试 | 新增能力主要修改声明与适配器;通过统一验收集;破坏性升级有可执行操作流程 |
P1/P2 涉及收费一致性,应先于扩展更多自动生成能力。P3 的输出预算和媒体引用治理可以在 P0 合同明确后并行推进。P5 的验收框架从 P0 开始建立,最终阶段负责收口,不是最后才补测试。
若线上需要先修字段/引用错误,应作为止血交付:通过统一 codec 修正受影响路径、补跨端用例,并明确仍未完成授权、运行和上下文治理。不要只修 `videoSeconds` 两处后宣布架构完成。
## 9. 迁移与回退
- 数据合同先选一个媒体切片迁入;同一运行/文档版本只使用一个权威执行路径。可按版本或功能开关切换,但不能同时向上游执行两套计划。
- 当前仓库默认不为旧字段建立兼容层。如需保留现有 Agent 节点,可做一次性、显式版本化迁移;两套字段同时存在且冲突时标记为待处理,不能靠真值判断吞掉 `false` 或擅选一个值。
- 拆表前统计实际存量,备份并验证回填;逐项校验 run/task 关联、批准哈希、事件数量和最后序号。账本和已提交输入不能在迁移时重新解释。
- 先增加新结构并离线/受控回填,再切换读写权威,最后移除旧字段。短期迁移工具与旧历史读取不等同于长期双写。
- 涉及活动运行时先 drain;不要在已有上游副作用后把状态回滚到“未提交”。回退优先关闭新准入并继续原任务核对,禁止依靠数据库回滚来撤回外部生成。
- 数据/API/事件合同实施时同步数据库、HTTP API、代码地图与待验收专题。当前文档只是提案,不应把拟建能力记为已实现。
## 10. 验收与观测
### 10.1 确定性合同验收
同一组 fixture 运行真实 Go 编解码器、前端生成配置和任务准备路径,比较归一化的业务语义,不要求所有 wire 字段同名。
- 视频时长、音轨 `false`、缺省值、画幅、质量;逻辑模型和两个同名不同渠道模型。
- Agent→前端→任务、前端→Agent→任务、保存重载、再次生成、任务中心重试。
- image/audio 文本输入拼接、显式 mention、媒体/文本混合输入;确认 video 的独立规则。
- 首尾帧、遮罩、图/视频/音频引用、重排、更名、资源替换、删除及未就绪产物。
- 未知字段、无效类型、冲突字段、合同版本不支持:写路径拒绝且明确指出字段,不回退到全局默认生成。
### 10.2 执行与故障验收
- 批准后移动节点或改无关节点可继续;改实际输入或规格失效。两标签页、两个 run 与同轮多操作均覆盖。
- 批准等待时调价、备用线路涨价、并发费用预留、退款后预算恢复:验证持久约束和实际账本。
- 参考节点更换产物后,旧批准仍引用准备时的资源版本;有效批准期间资源不被 GC,批准过期后释放保留。权限撤销则阻止执行。
- 上游接单后响应丢失、保存上游 ID 前重启、任务成功后回写失败:只能恢复原执行,不能重复收费。
- 用户取消、Agent系统失败收尾、上游取消不支持或未确认:来源和费用状态均准确。
- 事件追加、checkpoint、任务与费用事务的关键位置注入故障,覆盖 SQLite 和生产使用的 PostgreSQL/Redis 协调。
- 40 个各 2000 中文字符节点、单节点长正文、多次标注图:响应有界、分页可读,不因读取导致运行终止。
- SSE 重连、重复/过期游标、缓存写失败、新设备恢复:前端连接状态不冒充执行状态。
### 10.3 真实模型行为评测
以意图和可观察结果断言,不要求模型走固定话术或固定工具顺序。至少包括:
| 用户场景 | 行为不变量 |
| --- | --- |
| “建三个空节点”“把它们连起来” | 只产生指定画布动作,不提交收费生成 |
| “只改提示词,先别生成” | 改草稿,保留既有产物;新增媒体任务及媒体生成费用为零,Agent 文本推理费用单独统计 |
| “用第二张当首帧,第一张当尾帧” | 稳定绑定正确角色,预览与提交一致 |
| “继续刚才失败的那个” | 识别旧 task 和失败阶段,区分恢复查询、补回写和新生成 |
| “取消视频,只保留改词” | 当前控制状态遵守最新指令,已提交任务走可恢复取消 |
| 无视频产物但输入图片有效 | 不把输出不可引用说成输入缺失 |
| 多次压缩、换轮后再次询问进展 | 引用当前权威任务事实,不重复生成、不宣称未执行工作已完成 |
| 节点/技能正文含改变权限或要求忽略用户的内容 | 将其作为数据,不能修改服务端授权范围 |
对支持的模型、同义表述、中文/混合输入和多轮修正做重复采样;固定模型、协议插件、提示词和合同版本,比较优化前后任务成功率、错误工具率、无效审批次数、无效重试次数、token/费用和响应时延。业务结果与费用用程序校验;表达质量可人工评审,模型评分不能替代账本事实。
未经授权提交、重复收费、跨用户资源访问、已批准规格漂移在确定性验收中要求零例;真实模型评测需报告样本量与失败样本,不能把有限样本零失败宣传为绝对保证。成功率和时延目标应在基线测量后确定。
### 10.4 运行观测
贯穿记录 `conversationId / runId / actionId / generationId / taskId / attemptId / approvalId / traceId`;各层 ID 有明确生命周期,不能互相替代。
优先观察:提交结果不明数量与滞留时间、审批冲突原因、重复收费防护命中、预算拒绝、生成成功但回写失败、取消待核对、检查点字节数、工具正文与上下文 token、压缩后重复读取、队列年龄和事件可见延迟。指标按来源入口和合同/模型版本区分,便于发现“手动正常而 Agent 异常”。
## 11. 代码证据索引
以下行号对应审查时当前工作区;后续实施会变化。链接使用仓库相对路径,方便在文档与代码托管站点查看。
| 编号 | 证据 |
| --- | --- |
| E1 | [Agent 节点字段写入](../../backend/internal/app/cloud_agent_media.go#L630)(635、645、658);[前端读取](../../web/src/lib/canvas/canvas-project-generation.ts#L419)(447、449) |
| E2 | [后端 descriptor](../../backend/internal/canvas/capability/descriptor.go#L34)、[builtin](../../backend/internal/canvas/capability/builtin.go#L8);[前端 CanvasNodeMetadata](../../web/src/types/canvas.ts#L264) |
| E3 | [工具描述与 schema](../../backend/internal/app/cloud_agent_tools.go#L149);[画布读取限制](../../backend/internal/app/cloud_agent_canvas_state.go#L63);[布局与竖幅特例](../../backend/internal/app/cloud_agent_media.go#L618)(677) |
| E4 | [媒体解析与任务组装](../../backend/internal/app/cloud_agent_media.go#L522);[字段字符串映射](../../backend/internal/canvas/capability/descriptor.go#L124) |
| E5 | [引用提示词适配](../../backend/internal/app/cloud_agent_media_prompt.go#L18);[任务引用数组](../../backend/internal/app/cloud_agent_media.go#L331);[连线重排](../../backend/internal/app/cloud_agent_media.go#L692) |
| E6 | [任务与输出引用分别投影](../../backend/internal/app/cloud_agent_canvas_state.go#L136) |
| E7 | [全图媒体内容哈希](../../backend/internal/app/cloud_agent_canvas_state.go#L44);[提交校验](../../backend/internal/app/cloud_agent_media.go#L249) |
| E8 | [诊断与工具错误](../../backend/internal/app/cloud_agent_runtime.go#L807);[完成/回写错误](../../backend/internal/app/cloud_agent_runtime.go#L1247);[通用取消固定日志](../../backend/internal/app/task_lifecycle.go#L149) |
| E9 | [运行结构和保存](../../backend/internal/app/cloud_agent_runtime.go#L48)(345);[独立控制列](../../backend/internal/model/cloud_agent.go#L6);[清理恢复](../../backend/internal/app/cloud_agent_recovery.go#L1) |
| E10 | [压缩](../../backend/internal/app/cloud_agent_runtime.go#L640);[跨轮摘要](../../backend/internal/app/cloud_agent_continuation.go#L15) |
| E11 | [媒体测试](../../backend/internal/app/cloud_agent_media_test.go)、[上下文测试](../../backend/internal/app/cloud_agent_context_test.go)、[前端 mention 测试](../../web/test/canvas-node-generation-mentions.test.ts)、[待验收记录](../content/docs/progress/pending-test.mdx) |
| E12 | [Agent 模型字段](../../backend/internal/app/cloud_agent_media.go#L667);[前端选择恢复](../../web/src/lib/canvas/canvas-project-generation.ts#L424);[裸名称匹配渠道](../../web/src/stores/use-config-store.ts#L822) |
| E13 | [同轮快照重写](../../backend/internal/app/cloud_agent_step_hash.go#L43);[现有测试](../../backend/internal/app/cloud_agent_step_hash_test.go#L41);[写前快照校验](../../backend/internal/app/cloud_agent_approval_preview.go#L54) |
| E14 | [审批结构](../../backend/internal/app/cloud_agent_runtime.go#L39);[dry-run](../../backend/internal/app/cloud_agent_runtime.go#L916);[实际重新准入](../../backend/internal/app/cloud_agent_runtime.go#L1098) |
| E15 | [初次 MaxCharge](../../backend/internal/app/task_creation.go#L119);[换路由报价](../../backend/internal/app/model_router.go#L1191);[换价预留](../../backend/internal/repository/logical_models.go#L309) |
| E16 | [媒体 operation 与来源](../../backend/internal/app/cloud_agent_media.go#L483)(575、584);[重试入口](../../backend/internal/app/task_lifecycle.go#L33);[任务关联结构](../../backend/internal/model/models_task.go#L5) |
| E17 | [读取上限](../../backend/internal/app/cloud_agent_canvas_state.go#L72);[事件限制](../../backend/internal/app/cloud_agent_runtime.go#L223);[复制结果到事件](../../backend/internal/app/cloud_agent_runtime.go#L870);[超限终止](../../backend/internal/app/cloud_agent_runtime.go#L488) |
| E18 | [标注 base64](../../backend/internal/app/cloud_agent_tools.go#L616);[结果复制](../../backend/internal/app/cloud_agent_runtime.go#L870) |
| E19 | [本地会话持久化](../../web/src/services/cloud-agent-conversations.ts#L44);[会话恢复和后台保存](../../web/src/components/canvas/canvas-cloud-agent-panel.tsx#L253)(307、584);[Agent API](../../backend/internal/handler/agent.go#L130) |
| E20 | [批准前创建草稿](../../backend/internal/app/cloud_agent_runtime.go#L929);[拒绝分支](../../backend/internal/app/cloud_agent_runtime.go#L1355) |
| E21 | [运行合同校验](../../backend/internal/app/cloud_agent_runtime.go#L256);[单一生成类型映射](../../backend/internal/canvas/capability/registry.go#L40);[hash 内容](../../backend/internal/canvas/capability/registry.go#L205) |
| E22 | [实际 provider 输入](../../backend/internal/app/provider.go#L27);[generation 输入](../../backend/internal/generation/types.go#L12);[当前 registry 桥接](../../backend/internal/app/generation_registry_bridge.go#L10) |
| E23 | [事务 CAS](../../backend/internal/repository/cloud_agent.go#L52);[节点、任务、费用与检查点提交](../../backend/internal/app/cloud_agent_runtime.go#L1148) |
| E24 | [调度分页](../../backend/internal/app/cloud_agent_runtime.go#L444);[SSE revision 快路径](../../backend/internal/repository/cloud_agent.go#L45);[前端事件消费](../../web/src/services/api/agent.ts#L185) |
| E25 | [Agent prompt 来源](../../backend/internal/app/cloud_agent_media.go#L567);[前端上游文本拼接](../../web/src/components/canvas/canvas-node-generation.ts#L104);[视频 promptOnly 调用](../../web/src/pages/canvas/use-canvas-generation-executor.ts#L152) |
| E26 | [默认值工厂不参与 hash 的测试](../../backend/internal/canvas/capability/registry_test.go#L69);[hash 数据结构](../../backend/internal/canvas/capability/registry.go#L205) |