文档:纳入客户端本地埋点方案与需求来源

新增客户端本地埋点与后续主站入库契约,明确本期不加密、不上传。
保存原始需求为仓库内历史来源,消除对个人文档目录的引用依赖。
更新文档入口、生命周期索引和团队共享决策。
This commit is contained in:
2026-09-21 04:14:57 +00:00
parent f947807fba
commit 3e657e6a6a
5 changed files with 939 additions and 0 deletions
+2
View File
@@ -25,6 +25,8 @@
## AI 游戏创作与 Agent Runtime
- [客户端本地埋点与主站入库契约](./technical/【技术方案】客户端本地埋点与主站入库契约-2026-09-21.md):当前埋点方案,尚未实施;本期只做事件采集和明文 JSONL 持久化,不做加密或上传,待定稿建议在文内单列。
- [AGC 资源 kind 枚举化契约](./technical/【技术方案】AI游戏创作智能体App实施计划-2026-06-24.md#2026-09-15-gamecreationapp-资源-kind-枚举化当前权威口径)GameCreationApp 资源 kind 的 Rust enum、ts-rs 绑定、Unknown 可观测性和 shell 内重构边界。
- [策划 Agent 生产迁移与工作区浏览](./technical/【技术方案】策划Agent生产迁移与工作区浏览-2026-09-10.md):已完成;当前策划入口统一使用 Design Agent,采用阶段审批与用户工作区文件浏览。旧 V1/V2 会话、命令、专用展示和测试不再作为兼容目标。
@@ -1,5 +1,11 @@
# 决策记录
## 2026-09-21 客户端埋点方案进入团队共享文档
- 当前合同唯一维护入口为[客户端本地埋点与主站入库契约](../../technical/【技术方案】客户端本地埋点与主站入库契约-2026-09-21.md),原始需求作为仓库内历史来源保存;后续里程碑规范与实施计划放在 `docs/project-memory/plans/`
- 已确认本期只做本地事件采集和明文 JSONL 持久化,不做加密与上传;一个项目对应一个目标,事件按业务节点采集,5 分钟封存,7 天或 20 MiB 清理。后续上传周期为 15 分钟。
- 当前尚未实施,参数建议及剩余口径见方案第 11 节;文档入库不表示这些建议已获确认或功能已上线。
## 2026-09-21 本批自查(PR #441):三处修正
- 背景:推 PR 后按「局部到整体」自查这一批(三需求 + 验收修正),查出三条:①拖动到对话的落点在 `pointermove` 上每帧都 `setState` 一个新对象;②替换面板相对 **stage** 写死 `top: 8.5rem`(与刚修的任务开关同一类隐患:工具条换行会压上去),且它和「生成任务」面板抢画布右上角同一个位置;③替换面板不显示「在替换哪张源素材」,而非模态化之后那点线索(画布上的源素材光环)会被一次空白点击清掉。
@@ -0,0 +1,498 @@
# 客户端本地埋点与主站入库契约
Version: 0.10
Status: 当前方案,已确认口径作为实施依据;第 11 节保留待定稿建议,尚未实施
Date: 2026-09-21
需求来源:[Game Agent 埋点设计原始方案](./【需求来源】GameAgent埋点设计原始方案-2026-09-05.md);原始方案与后续已确认决策有差异时,以本文为准。
## 1. 交付目标与范围
交付结果:在 Game Agent 与现役 Design Agent 客户端的真实业务节点产生有明确语义的产品事件,以明文 JSONL 批次持久化在应用级目录;后续主站按统一事件合同入库。
本阶段验收以本地记录为准:字段来源可追溯、事件不会冒充业务成功、正常重放不重复、跨账号不串归属、埋点故障不影响正常操作。没有上传成功、服务端去重或报表已可用的承诺。
优先级:
- 必须项:字段合同、身份与因果关联、原方案 13 类事件逐项对照(采集其中 12 类,暂不采集 session_timeout)、新增策划进度快照事件、本地未闭合会话标记、本地 JSONL 持久化、静默失败、定向验证。本版合计启用 13 类事件。
- 风险项:项目目标身份稳定性、跨账号在途操作、多窗口前台时间、崩溃未闭合区间、revision 与保存事实、重复回调。
- 可选项:无;本阶段不扩充分析维度或细粒度点击事件。
本阶段不实现文件加密、上传定时器、HTTP 接口、数据库表、服务端聚合或后台看板。不读取或批量转换已有对话历史为埋点,不改变现有 Agent 产物与恢复账本的职责。
2026-09-21 存储范围确认:用户决定本阶段先不做加密,直接保存明文事件批次;无公钥配置、密钥生成或轮换前置要求。以后若启用加密,另行定义文件格式与接收兼容合同。
2026-09-21 产品口径确认:一个项目就是一个创作目标。项目内全部创作请求、澄清、审批、重试及跨会话继续均属于同一目标;不再根据消息内容划分新目标。
2026-09-21 异常会话口径确认:下次启动时,确认旧实例已退出且旧会话没有正常结束标记,再将本地会话标为 incomplete(不完整)。不设置周期心跳或检查点定时器、不等待超时阈值、不补记 session_timeout,不补造退出时间或未闭合前台时长。
2026-09-21 执行终态口径确认:等待澄清或审批算本次 run 正常完成,通过 end_reason 区分;用户取消和进程崩溃不计入 agent_run_failed,不进入已知成功/失败终态的分母。
2026-09-21 本地保留策略确认:埋点队列及相关索引最多保留 7 天、总量上限 20 MiB。任一条件达到即按第 7.3 节清理,未上传的数据也适用;不影响项目、Agent 对话或创作成果。本版尚不上传,数据不会无限保留等待上传功能上线。
2026-09-21 本地封存时间确认:从当前批次第一条事件进入开始,5 分钟到期后封存落盘;没有事件不生成空批次。该时间与后续每 15 分钟上传的周期独立,500 条及 1 MiB 的批量上限仍为草案建议。
2026-09-21 策划进度采集确认:不设固定采集周期;项目打开成功、相关策划状态成功持久化时触发,从 `.agent/design-agent/session.json` 提取白名单状态快照,内容无变化不重复记录;项目切换或关闭前尽力补采。5 分钟是封存落盘周期,后续 15 分钟是上传周期,均不是采集周期。不为策划埋点专门增加文件 revision 机制。
用户已确定的后续要求:上传至主站数据库;客户端每 15 分钟上传一次;绑定真实用户与业务标识;成功后删除对应待上传副本;失败无弹窗、不阻塞正常进程;失败是否重试可选。本草案建议后续失败保留至下一个周期重试,不做立即重试;这是建议,不是已实现行为。
本文是团队共享的埋点技术方案唯一维护入口,原始需求副本仅用于追溯。正式编码前,按仓库规范驱动工作流在 `docs/project-memory/plans/` 补齐里程碑规范及当前里程碑实施计划,并完成对应评审;本文的建议项不因迁入仓库而自动视为已确认。本次仅交付文档。
## 2. 已核实的现有记录与复用边界
| 现有记录 | 当前事实 | 本阶段复用方式 |
| --- | --- | --- |
| `.agent/conversations/project.jsonl` | Game Agent 正式对话历史,包含正文 | 复用正式受理、终态的业务入口;不复制正文到埋点 |
| `.agent/runtime/direct-codex/turns/<clientTurnId>.jsonl` | 回合工具审计,条目有上限,写入可失败 | 参考执行事实;不能把其条数作为完整产品事件数量 |
| `.agent/agent.db` | 逐行 JSON 本地索引与审计,非 SQLite | 保持原用途,不作为待上传队列 |
| `.agent/design-agent/session.json` | 策划会话、对话、工具结果、阶段、审批、当前回合与恢复状态 | 状态保存成功后触发,提取白名单进度快照;不上传完整会话文件 |
| `design_artifacts/` | 正式策划成果 | 保持原文件保存行为,本版不为统计新增 revision;文件内容不进入事件 |
| 应用级 `direct-executions/``direct-delivery/` | 执行控制与交付状态 | 关联正式执行生命周期;不额外复制一套业务状态机 |
旧 Planning V1/V2 与 Fast GDD 不作为新埋点接线目标。主站已有 HTTP tracking 也不等于本方案事件已经被接收或保存。
## 3. 总体链路与非阻塞原则
```text
真实业务节点产生不可变事件候选(冻结身份、时间与业务引用)
→ 有界内存队列,非阻塞投递
→ 单个后台写入器校验,在有界内存中组成 JSON 批次
→ 序列化为明文 JSONL,写入事件文件与最小控制元数据
→ 应用级本地待上传目录,批次不可变
→ 后续阶段:15 分钟上传 JSON 事件批次、主站入库、整批确认后删除
```
- 业务线程不等待磁盘写入、文件清理或网络。队列满时丢弃本次埋点并增加本地计数,不拖慢业务。
- 写入失败、权限异常、格式错误、磁盘满:埋点失败静默处理,不向业务调用返回错误、不显示弹窗、不回滚已经成功的操作。
- 字段缺失不能通过伪造值满足校验。整条事件不合规时不写入正式事件文件,在内部记录事件名、原因码及丢弃数量,不记录正文或凭据。
- 捕获时生成 event_id 与 event_time,后台排队延迟不得改变事件发生时间。
- 正常退出仅尽力排空已经在队列中的事件,不新增等待上传或等待写盘的退出门禁。
- 这一取舍允许“业务已成功,但埋点尚未持久化就进程退出”的丢数窗口。不能同时宣称完全不等待与绝对零丢数;本方案优先满足用户要求的不阻塞。
## 4. 公共事件合同
### 4.1 序列化规则
- UTF-8、无 BOM;字段名统一 snake_case。本地使用 JSONL,每行一个完整公共事件对象,按第 7 节组织批次文件。
- 新写入事件固定 `schema_version: 1`;语义或类型发生不兼容变化时升版本,不能悄悄改变旧字段含义。
- 顶层字段全部显式出现;不适用的可空字段为 null,不使用空串、0、`unknown` 或设备 ID 冒充真实业务身份。
- properties 是按 event_name 定义的封闭对象,只允许本方案列出的字段;必填字段不可缺少,可选字段不可得时省略。
- 公共字段不在 properties 中重复。所有 ID 为字符串,不将数据库整数 ID 转为可能丢精度的 JavaScript number。
- 时间为 UTC ISO 8601,毫秒精度;时长和计数为非负整数,限制在 JavaScript 安全整数范围。时间倒退、跨重启无法可靠计算时写 null 或省略,不写负数或猜测值。
### 4.2 字段定义
| 字段 | JSON 类型 | 来源与约束 |
| --- | --- | --- |
| schema_version | integer | 固定 1;本文新增,供本地读取与后续服务端识别版本 |
| event_id | string | 客户端 UUID v4,单条事件唯一;重试、复制批次与恢复重放沿用原值 |
| event_name | string | 仅允许第 6 节本版启用的 13 个英文事件名(原方案 12 类加 design_progress_snapshot);session_timeout 不在本版写入白名单中 |
| event_time | string | 业务事实发生时捕获的 UTC 时间;不能以重启检测时间冒充旧会话的退出时间 |
| user_id | string/null | 由已确认的平台登录会话取得;匿名明确 null,不从邮箱、设备或项目所有者猜测 |
| editor_session_id | string | 埋点专用 UUID;不复用 Runtime sessionId、threadId 或 clientTurnId |
| project_id | string/null | 项目 manifest 的稳定 ID;第 6 节规定项目事件必须非空 |
| creative_task_id | string/null | 项目对应的唯一创作目标 ID;有任务关联时直接等于该事件的 project_id,规则见第 5 节;不另造随机任务身份 |
| agent_run_id | string/null | 一次受理的 Agent 执行 ID;仅 run 终态事件填写 |
| agent_turn_id | string/null | 真实底层技术回合标识,可选;禁止把 run ID 复制进来凑字段 |
| status | string/null | 按本版事件固定映射为 success、failed 或 null;不能任意填写;本地会话 incomplete 不属于本字段 |
| error_code | string/null | 仅 agent_run_failed 非空,来自稳定错误类别映射;其余为 null |
| source | string | 仅 editor、direct、design_agent、asset_canvas、resource_editor、ui_editor、manual、system;表示事实产生模块 |
| client_version | string | 从实际运行的应用版本元数据读取,不使用示例值或空串 |
| properties | object | 对应事件的专属字段,允许空对象 |
source 新增 design_agent 以表达现役策划实现;不使用旧 supervisor 标签代替。尚未接线的来源不能因枚举存在而被认为已覆盖。
### 4.3 用户归属与主站目标
- 用户与平台地址一并在事件发生时冻结。队列的本地路由元数据保存 `destination_origin`(经规范化的平台协议、主机、端口,无路径、查询串与凭据)和 user_id;它不是额外的产品指标。
- 同一批文件只包含同一个 destination_origin 和 user_id 的事件。使用不含用户标识的随机批次文件名;路由元数据不保存 Token、Cookie 或 API Key。文件内容与路由元数据均为明文,其采集边界见第 7.4 节。
- 账号 A 的在途任务结束事件继续归属发起执行时的 A;不得在后台写入时读取当前 B 账号并替换。
- 账号切换、登录与注销:闭合旧身份下的 focus 区间,原子切换埋点身份后按实际窗口状态开启新区间。编辑器会话可保持不变,按用户统计时必须按事件归属拆分,不能将整个会话时长归给最后登录者。
- 匿名事件保持 user_id=null;不因之后登录自动回填。缺乏可解析的平台目标时可以以 destination_origin=null 本地保存,后续禁止默认发给当前任意服务器,待上传阶段明确处理策略。
- 后续上传必须验证凭据主体与批次所属账号、平台一致。无法认证 A 时保留 A 的数据,不使用 B 的凭据代传;匿名接收规则在上传阶段明确。
- 本地 user_id 只是待验证声明,后续服务端不能不核验鉴权就信任客户端填报的用户身份。
## 5. ID、因果关系与执行语义
### 5.1 编辑器会话与多窗口
一个 GUI 应用实例对应一个 editor_session_id,从可用初始化到该实例实际退出。新进程新 ID;WebView 重载不重建会话。Runner 继续在后台运行不等于编辑器仍有前台会话。
同实例多个可交互编辑器窗口的前台区间取并集,不累加重叠时长。以宿主查询的窗口总体状态为准,聚合窗口切换;不同应用实例独立记会话。后台 Runner 自己不产生 editor_session_start。
### 5.2 项目与创作目标一一对应
- 用户已确认:一个项目就是一个目标。同项目内修改需求、改变方向、普通追加、澄清、审批和用户重试都不产生新目标;只有不同项目才对应不同目标。
- 建议采用最简单的字段映射:creative_task_id 直接复用 manifest 的 project_id 值,保留两个字段的契约名称但不维护第二份随机 ID 或映射表。该等值映射是本文实现方案,不表示 clientTurnId、agent_run_id 也可复用项目 ID。
- 项目改名、移动目录、重开、客户端重启、换账号以及从 Design Agent 转到 Game Agent 均不重建目标 ID。另存为新项目时应使用业务实际生成的新 project_id;复制目录但仍保留相同 project_id 的情况按同一项目身份处理,埋点不自行修正项目身份。
- creative_task_submit 的拟定采集口径保持每个目标首次受理创作请求时一次;仅创建或打开项目不算提交。之后的执行用多个 run 表达,不把消息条数算成目标数。此事件频次属于草案口径,需与第 11 节一并评审。
- 首次提交事实随项目已有受理记录或最小标记持久保存,不能只存在 React 状态或会过期的上传索引中。上传后清理批次不清除目标已提交事实,避免重开项目重复首次提交。
- 现有项目的目标 ID 可由真实 project_id 直接确定,但不补发历史事件。无法证明是首次创作请求时,不将首次启用埋点或第一次遇见旧项目冒充首次提交;后续 run、revision 等仍可记录并关联项目目标。
- 不新增“新目标/继续目标”选择器,不使用模型、关键词、消息间隔或最近任务推断目标归属。所有任务相关事件校验 creative_task_id == project_id。
### 5.3 Agent 执行
- 一次用户指令或明确继续动作被 Runtime 受理后生成 agent_run_id,执行至正常返回、最终失败、取消或中断。一次任务可以包含多个 run。
- 同一执行内部的 Provider 重试不创建新 run;相同受理请求重放复用原 run ID。
- 用户主动重试形成新的 run,沿用任务 ID。retry_index 定义为该任务的用户主动重试序号:初始执行为 0,主动重试递增;普通澄清/审批继续不递增。它不表示 Provider 的请求重试次数。
- 正常等待澄清或审批结束本次 run,以 completed + end_reason 区分;用户答复后新建 run。completed 表示本次执行正常收束,不代表整个任务完成或用户满意。
- 用户取消、崩溃、结果不确定不伪造 completed 或 failed。原方案未覆盖这些 run 终态,本版不新增对应终态事件,明确它们不进入成功/失败分母。
- duration_ms 用单调时钟测量本次执行,含内部等待与自动重试,不含等用户答复的间隔;跨进程恢复无法可靠累加时为 null。
### 5.4 下游因果关联
project_revision_created、preview_ready、project_save 已知所属项目,因此 creative_task_id 填同一 project_id,包括人工编辑与保存。这个关联只表示项目目标归属,不表示修改由 Agent 造成;实际来源由 source 与 revision_source/save_source 表达。其 agent_run_id 与 agent_turn_id 按原需求保持 null,不将“当前运行的 Agent”作为推定因果。
执行上下文冻结 project_id、creative_task_id、agent_run_id、用户与平台身份;后续不能从当前选中项目重新取值。run 的 output_change_detected 必须根据本次执行可归因的有效修改判定,不能只比较全局 revision 前后值,因为其他窗口也可能改动项目。
## 6. 事件合同(原方案启用 12 类,新增 1 类策划进度快照)
本节 properties 中“可选”字段在不可得时省略;未标可选的字段必须存在。公共 status、ID 与 source 的赋值同时受第 4、5 节约束。
### 6.1 editor_session_start
- 触发:GUI 完成可用初始化,宿主首次确认会话开始;不是每次组件挂载。
- 公共字段:status=successproject_id 仅在已成功打开项目时填写;任务/run/turn ID 均为 nullsource=editor。
- propertiesentry_source 为 direct_launch / project_association / app_restorefirst_project_id 为已确认首次项目 ID 或 null。之后打开项目不回写本事件。
- 去重事实键:editor_session_id + event_name。
### 6.2 editor_session_end
- 触发:应用实际正常退出,先尝试闭合前台区间;关闭项目窗口但应用仍运行、退出被取消时不记。
- 公共字段:status=successproject_id 为最后成功活动项目或 null;任务/run/turn ID 均 nullsource=editor。
- propertiesend_reason=user_exit / app_restartsession_duration_ms 为可靠时长或 nulllast_project_id 为稳定 ID 或 null。
- 去重事实键:editor_session_id + event_name。正常退出事件也可能因不等待后台写盘而丢失,不能据此声称进程必然崩溃。
### 6.3 session_timeout(本版暂不采集)
- 按用户确认,本版不产生该事件,也不新增其他异常退出产品事件替代它。后续若需要服务端异常会话统计,另行定义接收合同。
- 下次启动在后台检查本地旧会话:仅当缺少正常结束标记,且能够确认所属旧实例已退出时,将旧会话标为 incomplete。
- 旧实例仍在运行则不处理;无法确认进程归属或存活状态时保持原记录,不猜测退出。实例确认需防止 PID 被复用,复用宿主实例锁或 PID 与进程启动身份的组合,不仅检查 PID 数字。
- 发现即可标记,不要求等待 30 分钟,不维护 60 秒检查点定时器,也不添加启动等待门禁。
- incomplete 只表达记录不完整,不认定原因是崩溃。标记保持旧会话及原用户归属,不改成当前登录用户;重复启动处理保持幂等。
- 不补记 editor_session_end、editor_focus_end、session_timeout,不改写已封存的历史事件。未闭合 focus 区间不计入完整前台时长,已正确配对的历史区间仍可计入。
- 用户不再启动时,旧记录保持未闭合;主站后续只可将缺结束或缺配对记录视为不完整,不能从上传缺席推出确定退出时间。当前本地 incomplete 标记不随事件批次上传。
### 6.4 editor_focus_start
- 触发:实例的可交互窗口总体状态从无前台变为有前台;启动时已获得焦点也需要记录。
- 公共字段:status=nullproject_id 为开始时实际活动项目或 nullsource=editor;任务/run/turn ID 均 null。
- propertiesfocus_interval_idUUID);focus_reason=initial_focus / window_focus / restore / account_changeactive_project_id(可为 null)。
- focus_interval_id 是本文新增的事件专属关联 ID,确保存在丢数与乱序时仍能准确配对;不新增顶层业务 ID。
- 项目切换不拆分区间,因此不能用 active_project_id 把整个区间归为某个项目的编辑时长。
- 去重事实键:focus_interval_id + event_name。
### 6.5 editor_focus_end
- 触发:总体前台状态关闭、最小化、可观测的锁屏/休眠或正常退出。锁屏/休眠监听能力必须实际验证,无法观察的情况标为统计限制。
- 公共字段:status=nullsource=editorproject_id 为结束时活动项目;任务/run/turn ID 均 null。
- properties:原 focus_interval_idblur_reason=window_blur / minimized / app_exit / system_suspend / account_changefocus_duration_ms 为单调时钟时长或 nullactive_project_id(可为 null)。
- 重复 blur 不重复结束。缺 start 的 end 或缺 end 的 start 都不组成有效配对,不凭空补齐另一端。
- 去重事实键:focus_interval_id + event_name。
### 6.6 project_create_success
- 触发:新项目初始化完成,manifest 已持久化且稳定 project_id 已确认;覆盖自动创建与用户选目录创建。
- 公共字段:status=successproject_id 必填;source=editor;任务/run/turn ID 均 null。
- propertiescreation_source=home_game / home_design / template / selected_directoryproject_template_id 可选,仅填真实模板稳定 ID。
- 打开已有项目、幂等初始化不记创建;文件夹刚建立但后续失败不记成功。
- 去重事实键:project_id + 本次创建操作 ID + event_name,沿用业务操作身份,不读取路径当项目 ID。
- 只有成功事件,不能计算创建成功率;创建尝试数不在本版范围。
### 6.7 project_open
- 触发:一次显式导航或启动恢复成功加载项目并进入工作区;后台读取与同页面刷新不记。
- 公共字段:status=successproject_id 必填;source=editor;任务/run/turn ID 均 null。
- propertiesopen_source=create / picker / recent / app_restore / project_associationis_first_open 为 boolean 或 null。
- is_first_open 口径为“当前账号在本安装可观察范围内首次成功打开此项目”,不是全平台首次。使用本地有界索引保存该事实;无历史覆盖证据、旧项目首次遇到或索引清理后填 null,不将未查到当 true。新创建并首次进入可确认为 true。
- 去重事实键:本次工作区打开操作 ID + event_name;之后真正重新打开必须有新的操作 ID。
### 6.8 creative_task_submit
- 触发:项目目标的首次创作请求通过基本校验并被业务受理,且首次提交事实可确认;不是创建项目、点击按钮、未提交草稿或请求重放。
- 公共字段:status=successproject_id、creative_task_id 必填;source=direct / design_agentrun/turn ID 均 null。
- properties:空对象。公共字段不重复,不增加 Prompt、长度、附件或任务分类。
- 去重事实键:creative_task_id + event_name;同项目的澄清、审批、追加和重试只创建后续 run。项目级首次提交标记跨应用会话及批次清理保留。
- creative_task_id 必须等于 project_id;同一项目先策划再开发也不再记第二次目标首次提交。
### 6.9 agent_run_completed
- 触发:本次执行达到确定正常终态;现役 Direct 与 Design 在各自正式生命周期接线,不以“返回一个字符串”或 HTTP 200 代替业务成功。
- 公共字段:status=successproject_id、creative_task_id、agent_run_id 必填;agent_turn_id 可空;source=direct / design_agent。
- propertiesagent_type=game_agent / design_agentrun_source=user_submit / user_continue / clarification / approval / user_retryduration_msinteger/null);retry_indexinteger);output_change_detectedboolean/null);revision_id 可选;end_reason=finished / waiting_for_user / waiting_for_approval。
- output_change_detected=true 必须有本次归属的有效变更证据;false 必须完成检测且确认无变化;无可靠证据填 null。revision_id 仅填本次归属的最新已提交 revision。
- 策划阶段快照不能作为文件内容变化证据;Design 执行若没有独立可靠的成果变更检测,output_change_detected=null 并省略 revision_id,不因阶段推进而填 true,也不因阶段没变而填 false。
- 去重事实键:agent_run_id + terminal;同一个 run 最多一个正常终态,不因 GUI 与 Rust 双写而重复。
### 6.10 agent_run_failed
- 触发:业务已经判定本次执行最终失败;内部重试尚在继续时不记。
- 公共字段:status=failed;同 completed 的 ID 要求;error_code 必填。
- properties:同 completed 的公共执行属性;end_reason=failed。失败前可能已产生有效修改,因此 output_change_detected 可以为 true。
- error_code v1 白名单:provider_auth_failed、provider_rate_limited、provider_unavailable、provider_timeout、provider_invalid_response、local_io_failed、runtime_failed、runtime_error_unclassified。
- 错误码由结构化错误种类映射;无法分类才使用 runtime_error_unclassified。不能通过异常正文猜分类,不保存错误正文、文件路径或堆栈。普通工具失败被 Agent 修复后完成不记 run_failed。
- 去重事实键:agent_run_id + terminal。取消与异常中断不冒充 failed;本版 run 完成率仅比较已观察到的 success/failed。
### 6.11 project_revision_created
- 触发:代码、资源、UI 等成果确实发生有效变化,且现有业务修改与正式 revision 都成功提交。存储损坏或不确定提交不记成功。
- 公共字段:status=successproject_id 必填;creative_task_id 与 project_id 相同;run/turn ID 为 nullsource 使用实际修改模块。
- propertiesrevision_id(将项目内真实 revision 转为字符串,不使用 event_id);revision_source=agent / asset_canvas / resource_editor / ui_editor / manual_edit / system_projectionchange_kind=code / asset / ui / design_document / mixedfiles_changed_count 可选。
- 只更新聊天历史、访问时间或运行状态不属于有效变化。system_projection 仅在投影真实业务成果时允许,纯修复缓存/同步元数据不进入有效变化率。
- 当前 Direct 的 outputs_changed 与 manifest_requires_sync 要区分;不能给 revision 递增函数加一个无条件事件后就宣称满足语义。
- Design Agent 本版改用第 6.14 节的进度快照,不为了埋点给策划写文件、补丁或删除补建 revision。不得把阶段名、turn_index、session.updated_at 或审批结果填成 revision_id;仅当已有业务实际产生正式 revision 时,才可按本事件原条件记录。
- 去重事实键:project_id + revision_id + event_name。一个原子 revision 只记一次;多来源合并提交按真实提交范围填写,禁止任选最后一个来源。
- 外部编辑器的修改仅在宿主已有变化确认与 revision 提交时覆盖;本版不新建全盘监听,不宣称能看到所有磁盘改动。
### 6.12 preview_ready
- 触发:宿主对本次预览实例的实际入口执行访问检查成功,且确认检查期间项目版本仍匹配;Web 以真实入口成功响应且内容存在为最低可访问判据,其他引擎需各自真实 ready 信号。
- 公共字段:status=successproject_id 必填;creative_task_id 与 project_id 相同;run/turn ID 为 nullsource 为实际发起模块。
- propertiespreview_source=user / agent / auto_restorepreview_version 为该实例实际服务的项目 revision 字符串;ready_duration_ms 可选。
- 当前代码只启动监听、写 Running、返回 URL,不能直接产生本事件。探测失败或版本漂移不产生 ready;不记录本地 URL、端口或绝对路径。
- 去重事实键:项目 + 预览实例 ID + preview_version + event_name;轮询与重复回调不重复。同版本真正重开预览可产生新事件,漏斗按任务去重。
- ready 只证明可访问,不证明 JS 无异常、游戏通关或用户满意。仅策划文档项目不强行产生游戏预览事件。
### 6.13 project_save
- 触发:一次面向用户成果的逻辑保存操作已全部成功落盘,包括手动保存、业务自动保存或完整 checkpoint;多文件保存按一次操作记,不按底层 write 次数记。
- 公共字段:status=successproject_id 必填;creative_task_id 与 project_id 相同;run/turn ID 为 nullsource 为实际保存模块。
- propertiessave_source=manual / auto / checkpointrevision_id 可选,取本次保存结果对应的真实 revision。
- Agent 会话检查点、审计记录、埋点写入、配置保存、云快照同步不算项目保存。独立素材导出也不自动等于项目保存。
- 无变化的自动保存不记;用户显式保存成功可记,但不能同时伪造 revision。失败或部分写入不记成功。
- 去重事实键:项目 + 保存操作 ID + event_name。自动保存不表示用户认可;看板必须按 save_source 区分。
### 6.14 design_progress_snapshot
- 用途:记录采集时实际观察到的策划进度,不是审批操作流水,也不证明文件内容已改变。
- 唯一事实来源:对应项目 `.agent/design-agent/session.json` 已成功持久化的状态。不读取 `.debug`,不解析自然语言,不上传整个 session 文件。
- 触发:项目打开成功后采初始快照;阶段推进、提交审批、审批通过/拒绝、进入或结束等待澄清的状态成功保存后触发;切换或关闭项目时尽力补采。没有固定采集定时器,不等 5 分钟才读取。
- 普通模型输出、工具调用、对话保存、重试计数变化,若未改变白名单进度状态,不产生本事件。没有策划会话的项目跳过,不创建伪会话或默认初始阶段。
- 公共字段:status=nullsource=design_agentproject_id 必填,校验会话 projectId 与项目 manifest 一致;creative_task_id=project_idagent_run_id 与 agent_turn_id 为 null。editor_session_id 仍是编辑器会话,不使用策划 sessionId 替代。
- event_time 是成功读取该快照的观察时间,不回填为阶段真实转换时间。用户归属按触发上下文冻结:业务推进使用该次执行的用户,打开/关闭观察使用该操作的用户;不把快照中的历史阶段声称为当前观察者完成。
properties 白名单:
| 字段 | 类型 | 来源与语义 |
| --- | --- | --- |
| design_session_id | string | 原 sessionId,用于区分同项目重置后的策划会话;不改变项目目标 ID |
| current_phase | string | 原 currentPhase,仅 concept / top_design / architecture / systems / tdd / consultant |
| approved_phases | string[] | 原 approvedPhases;按固定阶段顺序规范化,无重复;不从 current_phase 推算 |
| pending_approval | object/null | 原 pendingApproval 提取 request_id、phase;没有待审批时为 null |
| pending_clarification | object/null | 原 pendingClarification 仅提取 request_id;不采问题正文和选项;没有时为 null |
| capture_reason | string | project_open / state_persisted / project_leave,表示观察触发方式,不冒充具体审批动作 |
- 不采 history、messages、commands、工具参数/结果、Prompt、错误正文、产物正文或路径。类型异常、未知阶段、项目身份不匹配或读取失败时静默跳过并计数,不填默认值。
- 比较范围为同平台、同用户、同编辑器会话、同项目与同 design_session_id 下最近一次已接收快照。比较 current_phase、approved_phases、pending_approval、pending_clarification;排除 event_time、capture_reason 及文件更新时间,避免普通会话写入反复触发。
- 第一次观察记录一次;同观察范围内状态相同则不重复。新编辑器会话或新用户需要自己的初始观察。只与相邻快照比较,不做永久内容去重:A→B→A 必须可记录三次,新的审批 request_id 也构成变化。
- 快照事件使用独立 event_id;重复回调的相邻相同状态被抑制,已排队事件的后续落盘/上传重试沿用原 ID。队列拒绝时不更新“已接收快照”,以便后续触发仍有机会采集。
- 读取和投递不阻塞正常业务。采集必须绑定触发时的项目路径和身份,不能在执行异步读取时改用当前选中项目。异步读取时状态可能已经推进,事件诚实记录实际读到的最新状态,不伪造漏掉的中间审批/澄清动作;本方案不承诺完整操作审计。
- 关闭项目不会删除已进入应用级队列的快照;整个客户端退出则尽力提前封存,强退仍适用未落盘丢数限制。
## 7. 本地存储、恢复与去重
### 7.1 位置与文件格式
使用客户端已解析的应用级配置目录下 `analytics/`;正式 Windows 默认基于 `%APPDATA%/world.genarrative.ai-game-creator/`,显式 config-dir 覆盖时随之变化。不放在项目目录或 `.debug` 中。
```text
analytics/
instances/<editor_session_id>/
session.json 本地生命周期标记和恢复所需关联
batches/<batch_id>/
meta.json 最小路由、幂等与清理元数据
events.jsonl 每行一条事件的明文批次,封存后不可变
```
batch_id 为随机 UUID;每个实例由单个后台写入器负责。批次在内存中组装,在同文件系统的临时批次目录写 meta.json 与 events.jsonl,写入完成后原子发布整个目录。未来上传只处理已发布批次,不读取在写临时目录。账号或目标平台变化时切新批次。
已确认时间条件:从当前批次第一条事件进入开始计时,5 分钟到期后由后台写入器封存落盘;后续事件进入新批次,没有事件时不产生空批次。仍建议批次达到 500 条或 1 MiB 序列化事件大小时提前封存,两个批量上限尚待评审。正常退出尽力封存,不新增阻塞退出的等待。
这些是本地写入参数,与未来 15 分钟上传周期独立。封存前原始事件只存在有界内存,强杀或断电可能损失最近约 5 分钟尚未封存的数据;后台调度或写盘延迟可能扩大窗口,不承诺严格的最大丢数时长。实现时作为内部常量,不增加用户设置界面。
session.json 保存会话 owner(含可验证的实例身份)、原用户/平台归属、lifecycle_stateactive / closed / incomplete)、未闭合 focus 及必要的幂等事实关联;原子替换。生命周期状态与会话开始、身份/focus 变化、正常退出等实际节点一起由后台写入,不设周期检查点。
下次启动识别旧实例退出后,只将符合第 6.3 节条件的 active 会话改为 incompleteclosed 和 incomplete 保持不变。可以保存本地 incomplete_detected_at 表示发现时间,它不是退出时间,不进入公共事件字段。记录缺失或损坏时不猜测重建完整会话。正常退出标记和事件均为尽力持久化,缺失可能来自写盘失败,不能据此确定为崩溃。
session.json 是本地恢复元数据,不是待上传事件;事件文件不能代替其中的实例存活身份和生命周期标记。用户下次启动前没有机会作出的标记,不视为已经完成。
### 7.2 去重与重放
- 第 6 节的事实键是本地逻辑幂等依据,不新增到公共 envelope。首次接收事实时分配 event_id;队列、批次及恢复保留同一身份。
- 普通重放从已有业务操作/任务记录取得关联 ID,不按相同正文去重;相同文本可能是两次合法操作。
- 幂等关联持久化与写入器串行执行。meta.json 保存批次的 event_id 清单和事实键摘要到 event_id 的映射,不复制事件正文。恢复从已发布批次元数据重建索引;批次成功发布但索引尚未更新时,不能重新生成 UUID 追加同一事实。
- 无法证明原事件身份时不补造历史事件,保留丢数限制;不为每条埋点给业务事务增加同步磁盘门禁。
- 重启保留完整已发布批次,清理未发布临时批次。读取时逐行解析并校验事件及元数据一致性;截断、坏行或身份冲突导致的损坏批次隔离并计数,不清空其他批次,也不自动改写事件或重新生成 event_id。
- meta.json 记录格式版本、批次身份、路由、创建时间、事件数量及幂等关联。事件白名单校验不等于防篡改认证;后续服务端仍须独立鉴权和校验。
- 正常退出标记缺失只是“未闭合”,不能证明具体退出原因;后续 incomplete 标记也不得被解释为已确定的 crash。
### 7.3 容量与生命周期
已确认:本地埋点队列及相关索引整体最多 20 MiB、最多保留 7 天。超过 7 天的批次按期清理;总量超过 20 MiB 时,从最旧封存批次开始清理及其专属索引,直到回到上限内。任一条件达到即执行,未上传批次也适用。清理记录丢弃条数;写入器轮转当前批次后仍无法满足上限则丢弃新事件。损坏文件同样计入限额,不无限隔离积压。
清理只作用于埋点队列及相关索引,不删除项目、Agent 对话、业务恢复记录或创作成果。清理由后台执行,失败静默,不阻塞正常客户端操作。
实例生命周期标记、打开历史索引与批次幂等映射均需有界清理;索引过期后不承诺识别无限久远的重放或首次打开。不可因此将未知 is_first_open 填 true。项目级首次创作提交标记跟随项目业务记录,不随埋点批次保留期清理。
本阶段没有成功上传清理,正常数据只因容量/保留期策略清理;不得启动一个空上传器并将数据标成“已上传”。这也意味着本阶段积累的数据不保证一直保留到上传功能上线。
### 7.4 数据最小化与文件边界
- 本地 events.jsonl 为可直接读取的明文,不承诺文件保密或防用户修改。本阶段不引入加密库、密钥配置或文件加密开关。
- 事件只包含本方案白名单字段;session.json 与 meta.json 只保存路由、实例身份、生命周期、幂等和清理所需元数据。
- 不采集 Prompt、对话正文、工具参数/结果、代码、文件正文、Token、Cookie、API Key 或异常堆栈;不因取消加密扩大采集范围。
- 正式事件写入 analytics 队列,不在普通应用日志、诊断包中重复输出完整事件正文。目录沿用现有应用私有目录权限。
- 后续网络传输仍使用 HTTPS,并由主站验证用户身份与字段。文件不加密不等于取消传输安全或服务端校验;本阶段无上传行为。
## 8. 为后续主站入库预留的合同
### 8.1 保持事件与传输分离
本地事件 envelope 保持第 4 至 6 节合同,不添加 uploaded、retry_count、last_upload_error 等会随传输变化的业务字段。批次目标、发送进度与确认信息留在传输元数据。
后续批量请求固定包含 schema_version(整数 1)、batch_id(批次 UUID)、destination_origin(规范平台 origin)、user_id(真实用户 ID 字符串或 JSON null)、events(从 JSONL 解析得到的事件对象数组)。全批事件用户必须一致;本地路由只作发送提示,不能作为服务端归属的唯一证据。
后续客户端通过 HTTPS 上传 JSON 批次,传输体类型为 application/json;保持各事件的原 ID、时间和字段值。本阶段不实现该请求,不虚构 URL 或物理表名。
为简化客户端文件管理,后续保留整批确认语义:服务端只有在全批事件已可靠保存,或按 event_id 去重确认内容相同时,才返回 `acknowledged_batch_ids`。客户端核对响应来自预期主站且 batch_id 属于本次请求后,删除对应整个批次目录。
部分入库后失败时不确认整批;客户端保留原事件批次并在后续重发全批,由服务端按 event_id 去重。无需为了全批确认强制采用一个数据库大事务,但必须可靠判断全批完成。相同 event_id 不同内容为冲突,不能覆盖原记录或谎报确认。
本版不实现逐事件确认后的部分文件重写。永久无效批次的拒收/过期清理不得记为上传成功,也不能因为 HTTP 200、收到字节或仅解析成功就删除本地数据。
### 8.2 主站接收的最低语义
- 按 schema_version + event_name 校验字段类型、必填项、枚举、事件与 status 的组合;properties 不作为任意 JSON 垃圾桶。
- 任务相关事件要求 creative_task_id == project_id;项目目标首次提交只能按已确认的项目首次提交事实记录,不能在接收端把每个新用户的第一次消息再算成新目标。
- 限制请求体积与事件数量,校验 JSON 格式、批次身份和字段版本;解析或校验失败不确认该批次。请求鉴权和 HTTPS 独立于本地文件存储格式。
- user_id 与鉴权主体必须一致;项目 ID 是客户端本地项目的关联标识,不因此赋予主站项目权限。
- event_id 是全局唯一去重键。导入时保留原 ID、发生时间、关联字段和客户端版本,不重生成事件身份。
- 服务端单独生成 received_at,用于观察延迟;不覆盖 event_time。
- 不要求会话、任务、run 与 revision 按顺序到达,不因父事件丢失或批次乱序拒绝所有子事件;关联不完整需可识别。
- 查询维度需要支持 user_id、project_id、creative_task_id、agent_run_id、event_name、event_time 和 client_version。主站物理表、索引和现有 tracking 适配在后续阶段核查后确定。
- 本地匿名事件、未知平台、未知 schema 版本不能静默改写归属或格式后入库。
- 未来每 15 分钟触发后台上传、同一客户端同时最多一个上传任务;成功清理、失败静默、不阻塞退出。重试策略、请求上限和超时在上传里程碑定义。
### 8.3 可用指标的限制
- 匿名事件不纳入按用户的留存;稳定用户也要等主站接收链路验证后才能出正式留存。
- 创作目标数就是项目目标数,不是消息数或执行次数;目标首次提交漏斗按有 creative_task_submit 的项目去重。旧项目缺失首次提交事件时不回填,应明确其不在该漏斗 cohort 中。
- 同目标可能跨天、跨会话、跨账号并经历许多 run。回访创作可由后续 run、revision、project_open 观察,不能只用首次 creative_task_submit 判断用户是否继续创作;每会话目标首次提交数不再代表每会话交互次数。
- 人工修改、预览和保存归入相同项目目标,但不能据此宣传为 Agent 导致的成果;run 的 output_change_detected 仍需要本次执行的真实变化证据。
- Game Agent 与 Design Agent 的预览适用性不同;不能把所有策划任务算入“游戏预览失败”的分母。
- 策划进度按 design_progress_snapshot 的阶段、已批准阶段与等待状态统计,不依赖文件 revision。打开旧项目得到的是一次当前观察,不能当成这次用户新完成阶段;不由观察时间计算精确阶段耗时或审批操作次数。
- 快照是进度观测,原方案的 revision 有效变化率不能把快照数加进分子;策划进度与 Game Agent 成果变化分别统计。
- 自动保存、checkpoint 与手动保存分开解释;focus 仅为前台时长。
- 只采集创建成功事件无法计算创建成功率;不采 task_type 就不能按任务类型分组;不能拿 agent_type 冒充 task_type。
- 取消、崩溃与未闭合 run 未纳入失败事件,本版完成率是已知终态样本的完成率,不是所有提交的完成率。
## 9. 合规事件示例
以下是字段形状示例,ID、版本和时间均为示例值,生产必须从对应事实取得。
```json
{
"schema_version": 1,
"event_id": "708cc064-e1ad-46d1-a26d-181432eef8aa",
"event_name": "agent_run_completed",
"event_time": "2026-09-21T10:30:00.000Z",
"user_id": "123",
"editor_session_id": "a5d5e098-e515-4b28-a27a-6018d47b55dd",
"project_id": "gameagent-example-project",
"creative_task_id": "gameagent-example-project",
"agent_run_id": "36c70186-93c7-4e2d-ad07-80d6b91a1371",
"agent_turn_id": null,
"status": "success",
"error_code": null,
"source": "design_agent",
"client_version": "0.1.0",
"properties": {
"agent_type": "design_agent",
"run_source": "user_submit",
"duration_ms": 32000,
"retry_index": 0,
"output_change_detected": false,
"end_reason": "waiting_for_user"
}
}
```
该例表示:本次策划执行正常停在等待用户澄清,已确认没有成果变化;不表示整个任务完成,不记录 revision_id。
## 10. 实施拆分与验收依据
本节仅列待评审的里程碑范围,不授权编码,不创建上传实现。
| 里程碑 | 范围 | 退出判据 |
| --- | --- | --- |
| A 数据合同与本地队列 | 强类型事件、捕获身份、JSONL 批次、有界异步写入与恢复、静默失败 | 字段校验、JSONL 读取、跨账号、不阻塞、损坏隔离、容量与幂等测试通过 |
| B 会话、窗口与项目 | start/end/focus/create/open,以及下次启动的本地 incomplete 标记 | 多窗口、重载、最小化、正常退出、旧实例存活识别、异常未闭合、项目重复打开测试与桌面 smoke |
| C 两类 Agent 与成果链路 | submit/run、策划进度快照、已有真实 revision、preview/save | 成功失败与等待、持久化触发快照与去重、重试关联、无变化、版本漂移、真实访问与保存边界全部可核对;不新建策划文件 revision 机制 |
| 后续独立阶段 | 上传主站与查询 | 另立规范;不在本版实施范围 |
实施前为选定里程碑生成单独实现计划,前一个里程碑评审/验收后再推进下一项。
最小验收矩阵:
| 场景 | 必须成立的结果 |
| --- | --- |
| 正常完整创作 | 读取本地 JSONL 批次得到原方案完整链路;事件 ID 各自唯一,关联 ID 同语义一致 |
| 策划澄清/审批/继续 | 同一 task、多次 run;等待不误报失败,不重复 task_submit |
| 策划状态保存与采集 | 保存成功后读取白名单;保存失败不伪造新状态;普通对话保存而阶段状态不变时不重复 |
| 策划项目 5 分钟内打开又关闭 | 打开时采初始快照,期间相关持久化触发采集;切换/关闭尽力补采,队列不随项目关闭消失 |
| 策划快照 A→B→A 与连续相同状态 | 前者三个观察都可记录;后者去重;采集时间仅为观察时间,不冒充审批发生时间 |
| 策划文件缺失、损坏、身份不符或快速切项目 | 静默跳过,不填伪默认阶段;异步结果不绑定到当前其他项目或其他用户 |
| 项目重开、改名、移动、切账号及策划转开发 | project_id 不变时 creative_task_id 恒定且等于 project_id;首次提交不因批次删除再产生 |
| 不同项目与旧项目首次观测 | 不同 project_id 对应不同目标;旧项目不补造历史首次提交,后续事件仍可关联目标 |
| 自动请求重试/用户重试 | 前者原 run,后者新 runretry_index 口径一致 |
| 同请求重放/双回调 | 同事实不重复;故意重复相同文本的新操作不被误去重 |
| 纯聊天/相同内容保存 | 不生成有效 revisionoutput_change_detected 不误填 true |
| 失败前已有文件变化 | 可同时有 revision 和 run_failed,不因失败隐藏真实变更 |
| URL 返回但入口失败 | 没有 preview_ready |
| 保存失败/部分提交 | 没有 project_save success |
| A 发起后切 B | A 在途结果不归 B,B 的新操作归 B;平台分区不串 |
| 匿名后登录 | 历史匿名事件不回填用户;focus 归属正确切段 |
| 多窗口/最小化/重载 | 同实例不重复 session_start,前台并集不重计 |
| 强杀与下次启动 | 确认旧实例退出且旧会话未闭合后标记 incomplete;不伪造结束时间、run 失败、完整前台时长或 timeout 事件 |
| 旧实例仍运行、身份不确定或 PID 复用 | 不误标存活或归属不明的旧会话;再次启动不重复改变已确定的 incomplete 状态 |
| 队列满/只读目录/磁盘失败 | 正常创建、对话、保存仍继续,无弹窗;内部可观察丢弃计数 |
| 批次发布中断与重启 | 保留完整已发布事件批次,未发布临时目录可清理;不重新生成已发布事件 ID |
| JSONL 截断、坏行或身份冲突 | 损坏批次隔离并计数,不改写事实、不清空其他批次、不阻塞业务 |
| 数据最小化 | 正式事件和元数据符合白名单,日志不重复完整事件;无 Prompt、凭据或工具正文等禁止字段 |
| 本阶段运行 | 不因埋点发起 HTTP,不创建 15 分钟上传定时器或周期会话检查点,不补发 timeout,不删除“假确认”数据;本地批次封存定时不受影响 |
主要代码核查入口(相对仓库根目录):
- 生命周期与配置:`apps/ai-game-creator-shell/src-tauri/src/main.rs``config.rs``platform_session.rs`
- 项目创建与打开:`apps/ai-game-creator-shell/src-tauri/src/commands.rs``apps/ai-game-creator-shell/src/App.tsx`
- Direct 执行与审计:`apps/ai-game-creator-shell/src-tauri/src/agent/direct_runtime/user_input.rs``agent/direct_runtime/mod.rs``agent/direct_codex_audit.rs`
- Design 执行与持久化:`apps/ai-game-creator-shell/src-tauri/src/agent/design_runtime.rs``agent/runtime_protocol/design_session.rs``agent/design_tools.rs`
- revision`apps/ai-game-creator-shell/src-tauri/src/agent/runtime_actions/project_gates.rs`,结合各实际写入调用方。
- 预览与保存:`apps/ai-game-creator-shell/src-tauri/src/preview.rs``ui_editor/persistence.rs``project/checkpoint.rs`
上述为定位线索,不表示这些函数已经完成埋点接入;正式实现必须再次核对当时源码。
## 11. 需要评审的口径与原需求差异
| 项目 | 本草案建议 | 未确认时的限制 |
| --- | --- | --- |
| 目标边界(已确认) | 用户已确认一个项目就是一个目标;方案据此令 creative_task_id=project_id | 不再需要消息意图分类或新增目标选择交互 |
| creative_task_submit 次数(草案建议) | 每个项目目标首次受理创作请求时一次,之后通过 run 表达继续执行 | 目标数不代表消息数;缺历史提交的旧项目不得伪造首次提交 |
| 异常会话处理(已确认) | 下次启动确认旧实例已退出且未正常结束,标记本地 incomplete;本版不采集 session_timeout | 无周期检查点、无超时阈值、无补造退出/前台时长;未再次启动则保持未闭合 |
| 等待用户(已确认) | completed + end_reason,后续继续为新 run | run 完成不是任务完成 |
| 取消与崩溃 run(已确认) | 本版不映射到失败事件 | 成功率仅覆盖已知正常/失败终态 |
| is_first_open | 本安装当前账号可观察范围,未知为 null | 不能声称全平台首次打开 |
| output_change_detected / duration_ms | 证据不足允许 null | 原方案未明确可空性,不能用 false/0 伪造确定值 |
| 策划进度(已确认) | 从持久化 session 提取白名单快照,打开/相关状态保存成功后触发,切换或关闭尽力补采,无变化去重 | 无定期采集;5 分钟仅用于封存;不能冒充完整操作流水或文件变更 |
| revision | Game Agent 与其他已有正式 revision 路径按真实变化采集;策划进度用快照,不为埋点新建策划 revision | 不把阶段、审批或快照当作文件 revision;剔除纯元数据同步 |
| 本地保留(已确认) | 最多 7 天或总量 20 MiB,任一条件达到即清理;超量先清最旧批次 | 未上传数据也会清理,本版不保证保留至上传功能上线;不删除项目及 Agent 业务数据 |
| 本地封存时间(已确认) | 当前批次首条事件进入后 5 分钟封存;无事件不生成空批次 | 与 15 分钟上传独立;强退可能丢失尚未落盘的一批事件 |
| 本地批量上限(草案建议) | 达到 500 条或 1 MiB 序列化事件大小提前封存 | 两个上限尚待评审,不影响已确认的 5 分钟时间条件 |
| 字段补充 | schema_version、focus_interval_id、run end_reasonincomplete 仅是本地生命周期状态 | 需与后续接收端按同版合同实现,不任意扩展 properties,不将本地状态冒充产品事件 |
| 本地格式(已确认) | 本阶段不加密,使用明文 JSONL 批次 | 无密钥前置要求;原字段白名单与数据最小化要求不变 |
| 未来确认删除 | 整批确认、整批删除,失败重发原事件批次 | 服务端仍需逐事件去重;暂不增加逐事件部分重写机制 |
## 12. 本次方案验证状态
已做:原产品方案逐项对照;核对现役 Direct/Design 持久化、项目创建、revision、预览、UI 保存与 checkpoint 的源码入口。本文新增合同和参数均以草案标识,不作为已经上线的事实。
本次将需求来源和当前技术方案纳入仓库并更新团队文档入口,不修改业务代码、数据库或网络行为;未运行客户端功能测试、真实窗口 smoke、主站 API 或数据库验证。文档编码、示例 JSON 与仓库文档检查结果随交付说明提供。
@@ -0,0 +1,431 @@
> 文档状态:`historical`(原始需求存档,仅用于来源追溯,不作为当前实施依据)
归档日期:2026-09-21。下方保留原始正文;其中目标边界、异常会话、上传阶段等口径已由后续决策调整。当前合同见[客户端本地埋点与主站入库契约](./【技术方案】客户端本地埋点与主站入库契约-2026-09-21.md)。
# Game Agent 埋点设计方案|早期地基版 v1.0
状态:正式方案;待技术负责人拆解实施
日期:2026-09-05
适用产品:当前 Game Agent 桌面端编辑器 / 项目工作台
## 1. 方案目的
第一阶段不建设完整数据平台,也不一次性覆盖所有细粒度编辑动作。本方案只解决三个问题:
1. 能不能知道用户进入了编辑器、创建或打开了哪个项目。
2. 能不能把一次创作任务和 Agent 执行、项目变化、预览结果串起来。
3. 能不能判断用户是否回到同一个项目继续创作。
本版本新增**编辑器前台时长**。它表示编辑器窗口处于前台/获得焦点的累计时间,不等于用户持续操作,也不等于真实编辑时长。本版本仍不统计编辑器活跃编辑时长和单个项目完整创作时长。
核心分析对象从旧版的“浏览/消费行为”切换为当前产品的“持续创作行为”。
## 2. 当前用户路径
```text
进入编辑器
→ 创建项目 / 打开已有项目
→ 提交一次创作任务
→ Agent 执行
→ 用户澄清、确认或追加指令
→ 文件、代码或资源发生有效变化
→ 项目产生 revision
→ 预览就绪
→ 用户试玩或继续修改
→ 保存并退出
→ 之后重新打开同一项目继续创作
```
第一阶段不要求把 Asset Canvas、Resource Editor、UI Editor 的每一个点击都拆成独立事件;先通过项目、任务、运行、revision 和前台时长关系判断用户是否真的在使用创作工作台。
## 3. 第一阶段事件清单
| 事件 | 所在环节 | 触发条件 | 可回答的问题 |
|---|---|---|---|
| `editor_session_start` | 进入编辑器 | 编辑器启动并完成可用初始化 | 有多少编辑器会话、用户从哪里开始 |
| `editor_session_end` | 离开编辑器 | 正常退出或明确关闭编辑器 | 正常结束的会话数;粗略会话时长 |
| `session_timeout` | 异常离开 | 超过约定时间没有心跳或前台状态 | 哪些会话可能异常中断;不可替代真实退出 |
| `editor_focus_start` | 进入前台 | 编辑器窗口获得焦点并处于可交互前台 | 用户把多少时间留在编辑器前台 |
| `editor_focus_end` | 离开前台 | 编辑器失去焦点、最小化或退出 | 前台时长区间和累计前台时长 |
| `project_create_success` | 创建项目 | 项目创建成功且拿到稳定 `project_id` | 创建项目人数、创建成功率 |
| `project_open` | 打开项目 | 项目被成功加载并进入工作区 | 回访项目数、项目复访率 |
| `creative_task_submit` | 发起创作 | 用户提交一次可执行的创作请求 | 用户发起了多少次真实创作任务 |
| `agent_run_completed` | Agent 执行结束 | 一次 Agent run 正常完成 | Agent 任务完成率、耗时、重试情况 |
| `agent_run_failed` | Agent 执行结束 | 一次 Agent run 明确失败 | 失败率、错误类型、失败后的修复行为 |
| `project_revision_created` | 产生有效变化 | 项目产生可识别的新 revision | Agent 或人工操作是否真正改变了项目 |
| `preview_ready` | 预览 | 当前项目预览达到可打开/可运行状态 | 有多少项目走到可预览;从任务到预览的转化 |
| `project_save` | 保存 | 用户或系统完成一次项目保存 | 用户是否保存成果;保存与继续创作关系 |
说明:`project_revision_created` 是项目变化事件,不等于用户满意;`preview_ready` 是技术/产品中间成功,不等于用户完成试玩或认可结果。
## 4. 公共事件字段
每条正式产品事件使用统一 envelope。`properties` 只放该事件特有字段,不重复创造新的顶层 ID。
| 字段 | 类型 | 是否必填 | 字段说明 |
|---|---|---:|---|
| `event_id` | string | 是 | 单条事件唯一 ID,用于去重;建议 UUID |
| `event_name` | string | 是 | 事件英文名,如 `creative_task_submit` |
| `event_time` | datetime | 是 | 事件发生时间,统一 ISO 8601;不要只记录上传时间 |
| `user_id` | string/null | 条件必填 | 稳定用户标识;没有登录用户时明确为空,不用设备 ID 冒充 |
| `editor_session_id` | string | 是 | 一次编辑器打开到结束/超时的会话 ID |
| `project_id` | string/null | 条件必填 | 当前项目的稳定 ID;编辑器入口事件可以为空 |
| `creative_task_id` | string/null | 条件必填 | 一次用户创作任务的 ID;任务相关事件必须携带 |
| `agent_run_id` | string/null | 条件必填 | 一次 Agent 执行的 ID;仅 Agent run 相关事件填写 |
| `agent_turn_id` | string/null | 否 | Direct 的底层 turn 技术记录 ID;不能替代 `agent_run_id` |
| `status` | string/null | 条件必填 | `success``failed``timeout``cancelled` 等有限枚举 |
| `error_code` | string/null | 失败时必填 | 稳定错误码;不要把整段异常堆栈当作分析字段 |
| `source` | string | 是 | 事件来源,如 `editor``supervisor``direct``asset_canvas``ui_editor``manual``system` |
| `client_version` | string | 是 | 客户端/编辑器版本,用于按版本比较问题 |
| `properties` | object | 是 | 事件专属属性;允许为空对象 |
### 4.1 ID 语义规则
- `editor_session_id`:编辑器会话,不能使用 Runtime 的 `sessionId`
- `creative_task_id`:用户的一次创作意图,可能包含多次 Agent run 和多轮追加指令。
- `agent_run_id`:一次可独立判断成功/失败的 Agent 执行。Direct 需要单独生成,不能把 `clientTurnId` 直接当作 run ID。
- `agent_turn_id`:底层技术 turn 记录,用于排错和技术审计,不直接作为产品任务口径。
- `project_id`:项目身份,优先使用 manifest 中稳定的项目 ID,不使用路径作为长期主键。
## 5. 各事件最小字段
### 5.1 编辑器会话
`editor_session_start`
```text
entry_source
first_project_id
client_version
```
`editor_session_end` / `session_timeout`
```text
end_reason
session_duration_ms(若可可靠计算)
last_project_id
```
`editor_focus_start`
```text
focus_reason
active_project_id
```
`editor_focus_end`
```text
blur_reason
focus_duration_ms(若可可靠计算)
active_project_id
```
前台时长计算规则:同一 `editor_session_id` 下,将成对的 `editor_focus_start``editor_focus_end` 区间相加。正常退出时补齐最后一个区间;崩溃、断电或强制结束造成的未闭合区间必须标记为不完整,不估算为完整前台时长。
### 5.2 项目
`project_create_success`
```text
project_template_id(如有)
creation_source
```
`project_open`
```text
open_source
is_first_open
```
`project_revision_created`
```text
revision_id
revision_source
change_kind
files_changed_count(如可得)
```
`project_save`
```text
save_source
revision_id(如有)
```
`revision_source` 建议至少使用:`agent``asset_canvas``resource_editor``ui_editor``manual_edit``system_projection`
### 5.3 创作任务与 Agent
`creative_task_submit`
第一阶段只要求带上:
```text
creative_task_id
project_id
source
```
不记录完整自然语言 prompt,也不要求第一阶段记录任务类型、输入方式、指令长度或附件信息。上述属性属于后续需要分析任务结构时再增加的可选字段。
`agent_run_completed` / `agent_run_failed`
```text
agent_type
run_source
duration_ms
retry_index
output_change_detected
revision_id(如已产生)
```
这里的 `agent_run_id` 只是一次 Agent 执行的技术关联 ID,不代表要记录每次执行的提示词内容。完成事件只能说明执行状态。`output_change_detected` 和后续 `project_revision_created` 用于区分“跑完了但没改变项目”。
### 5.4 预览
`preview_ready`
```text
preview_source
preview_version
ready_duration_ms(从触发构建到就绪,如可得)
```
第一阶段的 `preview_ready` 必须有明确技术触发条件,例如预览服务确认可访问或本地运行状态确认 ready;不能用“返回了 URL”直接代替。
## 6. 可以看的数据
### 6.1 创作漏斗
```text
编辑器进入
→ 创建/打开项目
→ 提交创作任务
→ Agent 完成
→ 项目产生 revision
→ 预览就绪
→ 保存
→ 后续重新打开项目
```
可计算:
- 编辑器到项目创建/打开转化率。
- 项目到首次创作任务转化率。
- 创作任务提交率:有项目用户中发生 `creative_task_submit` 的用户数 / 有项目用户数。
- Agent 完成率:`agent_run_completed` /`agent_run_completed` + `agent_run_failed`)。
- 有效变化率:产生 `project_revision_created` 的任务数 / 创作任务数。
- 预览到达率:产生 `preview_ready` 的任务数 / 创作任务数。
- 保存率:产生 `project_save` 的项目用户数 / 产生 revision 的项目用户数。
### 6.2 创作行为
第一阶段可以看:
- 用户每次会话提交多少创作任务。
- 一个项目累计发生多少次任务、run 和 revision。
- Agent 完成后是否真的产生项目变化。
- 失败后是否重试、追加指令或重新打开项目。
- 用户是一次性尝试,还是回到同一个项目继续创作。
- 不同来源、版本、任务类型的成功率差异。
第一阶段暂时不能可靠看:
- 编辑器活跃时长。
- 完整项目创作总时长。
- 用户是否满意或接受 Agent 结果。
- 可靠的试玩成功率。
- 仅凭这些事件直接得到 D1/D3/D7 留存,除非先确认 `user_id` 稳定且会话事件可靠落库。
## 7. 留存、LTV 与 ARPU 的当前口径
### 7.1 留存
当前先定义“创作者回访留存”,不定义泛产品活跃留存:
```text
某 cohort 用户在 D0 发生 project_create_success 或 creative_task_submit
在 D1/D3/D7 再次发生 project_open、creative_task_submit 或 project_revision_created
```
公式:
```text
Dk 创作者留存率 = D0 cohort 中在第 k 天至少发生一次创作相关事件的用户数 / D0 cohort 用户数
```
前提是 `user_id` 稳定、事件可靠落库、日期按统一时区计算。当前代码审计结论是这些条件尚未全部确认,因此先把公式写入方案,不把结果宣称为已可用。
### 7.2 LTV 与单用户 ARPU
埋点本身不能产生 LTV 或 ARPU。需要另外存在可靠的订单/扣费/退款事实表,并用 `user_id` 关联。
```text
ARPU = 统计周期内总收入 / 统计周期内活跃用户数
```
如果看创作者商业价值,可另算:
```text
创作者 ARPU = 统计周期内创作者收入 / 统计周期内发生创作行为的去重用户数
```
```text
LTV = 用户在定义生命周期内的累计净收入 / cohort 用户数
```
其中净收入应扣除退款、赠送额度和必要的渠道/支付成本,具体财务口径需要业务和财务确认。当前早期地基埋点只负责提供用户行为侧的 cohort 和创作分群,不负责替代收入系统。
## 8. 第一阶段建议看板
只建议做四组:
1. **基础使用**:编辑器会话数、创建项目用户数、打开项目用户数、项目复访数。
2. **创作漏斗**:任务提交、Agent 成功/失败、revision、preview ready、保存。
3. **失败与恢复**:失败错误码、失败后重试率、失败后产生 revision 的比例。
4. **回访创作**:D1/D3/D7 创作者回访,按任务类型、客户端版本、入口来源分组。
不要在第一阶段做几十个按钮点击看板,也不要把技术 JSONL、Runtime 状态和正式产品事件混成一张业务报表。
## 8.1 编辑时长的边界
本版本已经纳入前台时长事件,并区分三种时长:
- **会话时长**`editor_session_start``editor_session_end`,包含用户离开电脑或切到其他窗口的时间,不等于编辑时长。
- **前台时长**`editor_focus_start``editor_focus_end` 的累计时间。
- **活跃编辑时长**:前台期间发生有效编辑、任务提交、预览、保存等行为的累计时间。
本版本只承诺会话时长和前台时长两个粗粒度指标;即使记录了 `focus_duration_ms`,也不能把它解释成编辑器活跃时长。
前台时长的解释限制:
- 编辑器在前台但用户没有操作,仍会被计入。
- 多窗口、系统锁屏、远程桌面或窗口状态异常时,可能出现边界误差。
- 前台时长适合看停留和使用深度,不适合直接作为生产效率指标。
## 8.2 数据可靠性原则
已确认采用:
- 事件先写本地短暂 outbox。
- 网络恢复后自动重试上传。
- 服务端或接收端使用 `event_id` 去重。
- 关闭、断网、崩溃导致的可能丢数需要在技术验收中明确记录。
## 9. 当前已收口的产品口径
根据当前讨论,本版本采用以下口径:
1. “创作成功”采用分层口径:`agent_run_completed` 表示执行完成,`project_revision_created` 表示项目发生有效变化,`preview_ready` 表示达到可预览状态;第一阶段不加入用户满意/接受结果事件。
2. `creative_task_id` 表示用户一次创作意图;第一阶段不记录完整 Prompt,也不拆分 Prompt 内容。
3. 第一阶段不加入 `task_type``input_mode`、指令长度和附件属性,避免早期方案过重。
4. 事件允许先写本地短暂 outbox;网络恢复后自动重试;接收端按 `event_id` 去重。
5. 创作者 D1/D3/D7 暂按 `project_open``creative_task_submit``project_revision_created` 作为回访事件,但只有在 `user_id` 和数据落库可靠后才正式出数。
6. 编辑器前台时长纳入本版本;编辑器活跃编辑时长留到后续阶段。
## 9.1 仍需你确认的一项产品边界
只剩一个可能影响报表口径的问题:用户对同一个创作目标进行澄清或追加指令时,是否始终沿用同一个 `creative_task_id`。本方案默认沿用同一个任务 ID,只有用户明确开始新的创作目标时才生成新的任务 ID。
如果你没有特别异议,后续按这个默认口径执行即可;其余未收口项属于技术实现确认,不需要你继续定义。
## 10. 需要 master 开发对话回答的技术问题
开发对话只回答代码事实和实现成本:
- 是否已有服务端 analytics 接收接口和正式数据落点。
- 若没有,第一阶段事件落本地 outbox、现有服务端 tracking,还是其他已有入口。
- Tauri/Rust 是否能统一生成 `event_id``event_time``project_id``client_version`
- 当前能否稳定取得 `user_id`
- Direct 是否需要新增独立 `agent_run_id`
- `editor_session_id``creative_task_id` 是否能在现有生命周期生成。
- `project_revision_created``preview_ready` 的可靠触发点在哪里。
- 是否需要本地缓存、重试和去重;哪些异常场景会丢数。
- 每个第一阶段事件对应的代码文件、触发函数、测试方式和估算成本。
前台时长还需要确认:
- Tauri 当前是否能可靠监听窗口 focus、blur、minimize、restore 和退出事件。
- 窗口失焦后是否立即落一条 `editor_focus_end`,还是由统一会话管理器补齐。
- 锁屏、系统休眠、崩溃和强制结束时,如何标记未闭合前台区间。
- 多窗口场景是否存在;如存在,`editor_session_id` 是按应用实例还是按窗口生成。
## 11. 第一阶段验收标准
技术实现完成后,至少能够用一条测试链路证明:
```text
editor_session_start
→ editor_focus_start
→ project_create_success
→ creative_task_submit
→ agent_run_completed 或 agent_run_failed
→ project_revision_created(如果确实发生变化)
→ preview_ready(如果确实达到 ready
→ project_save
→ editor_focus_end
→ editor_session_end
```
并满足:
- 同一次链路中的 ID 能串联。
- 重复上传不会制造重复事件。
- Agent 失败不会被记成成功。
- Agent 完成但没有项目变化时,不能伪造 `project_revision_created`
- 预览 URL 返回但不可访问时,不能伪造 `preview_ready`
- 关闭、断网、崩溃等场景的丢数风险已明确记录。
- 能按 `user_id``project_id``creative_task_id``client_version` 做基本筛选。
- 正常切换到其他窗口时,能闭合前台区间并计算 `focus_duration_ms`
- 最小化、恢复、正常退出至少有明确的 focus 结束/重新开始行为。
- 崩溃或强制结束不会伪造一条完整的前台时长;未闭合区间必须可识别。
## 12. 对抗性自检审查
### 12.1 是否把技术完成误当创作成功
没有。方案明确区分 Agent 执行完成、项目产生 revision、预览就绪。三者分别是执行层、项目变化层和可预览层,不代表用户满意。
### 12.2 是否把前台时长误当编辑时长
没有。事件名、字段名和看板解释统一使用“前台时长”;用户没有操作但窗口保持前台的时间会被计入,并在文档中标明限制。
### 12.3 是否记录得过细、造成第一阶段过重
当前 P0 不记录完整 Prompt、任务类型、输入方式、指令长度和附件属性。保留的是会话、项目、任务、Agent 状态、revision、预览、保存和前台区间,属于基础漏斗与创作回访所需的最小集合。
### 12.4 前台事件是否会制造大量噪音
会比核心业务事件多,但仍是成对的窗口状态事件,不是按键或鼠标级事件。前台事件只用于时长聚合,不作为单独的产品成功指标。
### 12.5 断网、退出和崩溃是否会导致数据不可信
不能完全消除,但通过本地 outbox、重试和 `event_id` 去重降低风险。未闭合的 focus 区间必须标记不完整,不把估算时间写成事实。
### 12.6 是否能直接计算 D1/D3/D7、LTV、ARPU
不能直接保证。留存依赖稳定 `user_id` 和可靠落库;LTV/ARPU 还依赖订单、扣费、退款和净收入事实表。当前方案只提供行为 cohort 和创作者分群基础。
### 12.7 是否仍有未收口问题
有,但已经集中到技术实现确认,不影响产品方案定稿:
- 当前服务端 analytics 接口和正式落点是否存在。
- Direct 是否能在现有生命周期生成独立 `agent_run_id`
- `project_revision_created``preview_ready` 的实际可靠触发点。
- Tauri focus/blur 等窗口事件在当前多窗口、锁屏、休眠和崩溃场景下的行为。
- `user_id` 是否稳定,以及匿名用户后续是否需要身份合并。
这些不是继续扩展事件的理由,而是技术负责人需要逐项确认的实现事实。
## 13. 当前结论
这套早期地基版埋点足以回答:用户有没有进入编辑器、在前台停留了多久、有没有创建项目、有没有发起创作、Agent 是否完成、项目是否真的变化、是否走到预览、是否回到同一项目继续创作。
它暂时不能回答:用户是否喜欢结果、是否完成试玩、编辑器真正活跃了多久、完整 LTV/ARPU,以及在用户身份和数据落库尚未确认前的可靠 D1/D3/D7 留存。
因此本方案的产品层已经基本收口。下一步是把第 10 节交给最新 master 开发对话核实,再由技术负责人拆成最小实现任务;如果技术核实发现 focus/blur 触发不稳定,只需要调整前台时长实现方式或降级为会话时长,不需要推翻整个埋点方案。
@@ -51,6 +51,8 @@
以下文档明确是历史记录、实施记录、专利材料或问题记录:
- `docs/technical/【需求来源】GameAgent埋点设计原始方案-2026-09-05.md`
- `docs/【实施记录】SFX生成优化V2.0T6测试与发布门禁-2026-08-07.md`
- `docs/【专利交底】一种极低成本快速生成高质量2D小游戏高一致性美术素材的解决方案-2026-05-25.md`
- `docs/technical/【问题记录】DirectProject客户端Skill自然语言触发能力缺口-2026-09-01.md`