主站对AGC请求头做特殊处理和记录 #246

Merged
kdletters merged 22 commits from feat/agc_call_rec into master 2026-09-03 15:33:27 +08:00
Owner
No description provided.
lhk229 changed title from WIP: 添加客户端埋点统计 #225 to WIP: 添加客户端埋点统计 2026-09-02 15:56:35 +08:00
lhk229 added 1 commit 2026-09-02 16:02:04 +08:00
阶段1接入AGC标记解析
Project CI / Repository checks (pull_request) Successful in 2m44s
Project CI / Frontend tests (pull_request) Successful in 3m5s
Project CI / Backend tests (pull_request) Successful in 6m44s
Project CI / Native shell tests (pull_request) Successful in 19m41s
2296f79fdc
在统一 API tracking middleware 中解析 X-Genarrative-Client。

将有效 marker 传入 TrackingEventDraft,供后续 metadata 合并使用。

增加大小写、空白、未知值和非法文本的定向测试。
lhk229 added 3 commits 2026-09-02 18:19:01 +08:00
接入 AuthenticatedAccessToken 与 ExternalApiPrincipal 的 tracking 主体归属。

让 External API Key User scope 使用 owner_user_id,并保持原有账号态语义。

在既有 route metadata 中追加 client=agc,保留原有字段和资产嵌套信息。

增加主体归属、scope 和 metadata 合并定向测试。
补齐账号态和 External v1 路由 tracking 规范

复用资产详细事件并传递 AGC 标记,避免重复记录

修正 External API Key 主体归属和动态路由归一化
阶段4验证埋点持久化与后台读取
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
c75c61521d
补充 tracking draft、runtime input 和 outbox round-trip 验证

覆盖 SpacetimeDB mapper 与 metadata object 校验

补充账号态和 External API Key 后台 tracking readback 回归
lhk229 added 1 commit 2026-09-02 18:19:16 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Successful in 3m18s
Project CI / Frontend tests (pull_request) Successful in 3m40s
Project CI / Native shell tests (pull_request) Successful in 18m23s
Project CI / Backend tests (pull_request) Failing after 8m16s
91e1c80976
lhk229 self-assigned this 2026-09-02 18:20:18 +08:00
lhk229 added 2 commits 2026-09-02 18:41:47 +08:00
增加 2xx 成功和 4xx/5xx 失败状态 tracking 测试

验证 AGC 标记不绕过鉴权且不污染敏感 metadata

复核业务路由、排除路由和已有事件幂等语义
修复日登录埋点构造器缺失
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
0a06b49880
恢复 TrackingEventDraft::user 用户态构造器

补充用户 scope 与主体字段的回归测试
lhk229 added 1 commit 2026-09-02 18:42:02 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Successful in 2m53s
Project CI / Frontend tests (pull_request) Successful in 3m28s
Project CI / Backend tests (pull_request) Successful in 7m21s
Project CI / Native shell tests (pull_request) Successful in 19m41s
5aa38d9f3d
lhk229 added 1 commit 2026-09-02 19:56:30 +08:00
完成Issue225阶段6验收与交接
Project CI / Repository checks (pull_request) Successful in 2m55s
Project CI / Frontend tests (pull_request) Successful in 3m34s
Project CI / Native shell tests (pull_request) Successful in 17m6s
Project CI / Backend tests (pull_request) Successful in 7m5s
c127cf2f99
更新Issue225实施方案与分阶段验收计划为阶段0至6完成

补充最终门禁、48个定向测试证据与tracking_event落点说明

固化Issue225交付评论和Issue226客户端交接及后续风险记录
lhk229 added 1 commit 2026-09-02 20:51:48 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Successful in 2m47s
Project CI / Frontend tests (pull_request) Successful in 3m21s
Project CI / Backend tests (pull_request) Successful in 6m6s
Project CI / Native shell tests (pull_request) Successful in 15m59s
1aba9c843c
lhk229 marked the pull request as ready for review 2026-09-02 21:15:07 +08:00
lhk229 requested review from kdletters 2026-09-02 21:16:29 +08:00
Author
Owner
  1. P1:新增 route spec 会无条件记录非 AGC 请求
    位置:
  • tracking.rs
  • tracking.rs
    record_route_tracking_event_after_success 当前只判断:
    resolve_route_tracking_spec(...)
    should_record_route_tracking(status, &spec)
    没有要求 client_marker == Some(TrackingClientMarker::Agc)。
    而本分支新增了大量 route spec,例如:
  • /api/editor/projects
  • /api/editor/images/generations
  • /api/assets/read-url
  • /api/runtime/external-generation/jobs/{id}
  • 对应的 /api/external/v1/* 路径
    所以,未携带 X-Genarrative-Client: agc 的普通网页请求,只要返回 2xx,也会新增 tracking event;区别只是 metadata 中没有 client 字段。
    这会带来两个后果:
  1. 非 AGC 请求的 tracking 覆盖面和写入量扩大;
  2. tracking_daily_stat 中相关 event key 的统计口径发生变化。
    这与方案文档中“追加 AGC 标记,但不改变现有统计口径”的描述不完全一致:

需产品决策:是否只记录AGC请求?

1. P1:新增 route spec 会无条件记录非 AGC 请求 位置: - [tracking.rs](C:\\projects\\narrative\\Genarrative\\server-rs\\crates\\api-server\\src\\tracking.rs) - [tracking.rs](C:\\projects\\narrative\\Genarrative\\server-rs\\crates\\api-server\\src\\tracking.rs) record_route_tracking_event_after_success 当前只判断: resolve_route_tracking_spec(...) should_record_route_tracking(status, &spec) 没有要求 client_marker == Some(TrackingClientMarker::Agc)。 而本分支新增了大量 route spec,例如: - /api/editor/projects - /api/editor/images/generations - /api/assets/read-url - /api/runtime/external-generation/jobs/{id} - 对应的 /api/external/v1/* 路径 所以,未携带 X-Genarrative-Client: agc 的普通网页请求,只要返回 2xx,也会新增 tracking event;区别只是 metadata 中没有 client 字段。 这会带来两个后果: 1. 非 AGC 请求的 tracking 覆盖面和写入量扩大; 2. tracking_daily_stat 中相关 event key 的统计口径发生变化。 这与方案文档中“追加 AGC 标记,但不改变现有统计口径”的描述不完全一致: - [Issue225 实施方案](C:\\projects\\narrative\\Genarrative\\local-docs\\【实施方案】Issue225-AGC主站请求标记埋点统计-2026-09-02.md) - [Issue225 实施方案](C:\\projects\\narrative\\Genarrative\\local-docs\\【实施方案】Issue225-AGC主站请求标记埋点统计-2026-09-02.md) 需产品决策:是否只记录AGC请求?
lhk229 added 1 commit 2026-09-02 21:48:44 +08:00
修复Issue225仅记录AGC请求
Project CI / Repository checks (pull_request) Successful in 3m4s
Project CI / Frontend tests (pull_request) Successful in 4m16s
Project CI / Backend tests (pull_request) Successful in 7m2s
Project CI / Native shell tests (pull_request) Successful in 14m30s
d1b2aea4a4
新增AGC-only route tracking门禁

保持既有route和手工资产事件统计语义

清理TrackingEventDraft冗余marker状态

补充定向测试并同步Issue225文档
lhk229 added 1 commit 2026-09-03 10:40:32 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
d8667ac2bb
lhk229 added 2 commits 2026-09-03 10:44:58 +08:00
新增build_router到tracking outbox的成功链路回归

断言AGC标记、实际路由和用户主体归属

同步Issue225实施与验收文档
Merge remote-tracking branch 'origin/feat/agc_call_rec' into feat/agc_call_rec
Project CI / Frontend tests (pull_request) Successful in 2m50s
Project CI / Repository checks (pull_request) Successful in 2m32s
Project CI / Backend tests (pull_request) Successful in 7m7s
Project CI / Native shell tests (pull_request) Successful in 19m30s
9ad5782eba
lhk229 removed review request for kdletters 2026-09-03 11:35:31 +08:00
lhk229 marked the pull request as work in progress 2026-09-03 11:35:33 +08:00
lhk229 added 1 commit 2026-09-03 11:57:21 +08:00
修复AGC登录埋点用户归属
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
fda1bb6097
登录成功 handler 通过 response extension 传递已验证用户主体
tracking middleware 仅对合法 AGC marker 使用登录主体
新增密码、手机号和未标记登录回归测试
同步 Issue225 技术方案、长期决策记录和文档索引
lhk229 added 1 commit 2026-09-03 11:58:12 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Successful in 2m13s
Project CI / Frontend tests (pull_request) Successful in 2m55s
Project CI / Backend tests (pull_request) Successful in 6m39s
Project CI / Native shell tests (pull_request) Successful in 17m33s
f28e4b52e7
kdletters reviewed 2026-09-03 12:04:29 +08:00
kdletters left a comment
Member

审查 head sha f28e4b52e7fa218f1ddc05fef2183d49f5d1e094:未发现明确问题。

审查 head sha f28e4b52e7fa218f1ddc05fef2183d49f5d1e094:未发现明确问题。
Member

代码审查(head sha f28e4b52e7fa218f1ddc05fef2183d49f5d1e094):未发现明确问题。已执行 api-server cargo check,并运行与 AGC 埋点相关的 15 个测试,全部通过。

代码审查(head sha f28e4b52e7fa218f1ddc05fef2183d49f5d1e094):未发现明确问题。已执行 api-server cargo check,并运行与 AGC 埋点相关的 15 个测试,全部通过。
lhk229 added 1 commit 2026-09-03 12:56:25 +08:00
修复AGC资产读取埋点用户归属
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Frontend tests (pull_request) Has been cancelled
5e90add1a0
资产读取路由保留可选鉴权并传递已验证 Bearer 主体到响应扩展

补充资产读取主体传递与匿名边界测试

同步 Issue225 技术方案和项目决策记录
lhk229 marked the pull request as ready for review 2026-09-03 12:56:45 +08:00
kdletters added 1 commit 2026-09-03 12:57:50 +08:00
Merge branch 'master' into feat/agc_call_rec
Project CI / Repository checks (pull_request) Failing after 59s
Project CI / Frontend tests (pull_request) Successful in 3m0s
Project CI / Backend tests (pull_request) Successful in 6m40s
Project CI / Native shell tests (pull_request) Failing after 18m17s
4d8a0603a0
kdletters reviewed 2026-09-03 13:04:30 +08:00
kdletters left a comment
Member

代码审查(head sha 4d8a0603a0ccf2809e005d957f4b098fc5845653):未发现明确问题。基于 base 025f627297 的真实 diff 审查已完成;干净 worktree 中执行 cargo check --locked -p api-server -j 1 通过。

代码审查(head sha 4d8a0603a0ccf2809e005d957f4b098fc5845653):未发现明确问题。基于 base 025f6272970cace6670776375f788776c5e85fc0 的真实 diff 审查已完成;干净 worktree 中执行 cargo check --locked -p api-server -j 1 通过。
Member

代码审查(head sha 4d8a0603a0ccf2809e005d957f4b098fc5845653):未发现明确问题。已审查 base 025f627297 到当前 head 的真实 diff;并在干净 worktree 执行 cargo check --locked -p api-server -j 1,编译通过。

代码审查(head sha 4d8a0603a0ccf2809e005d957f4b098fc5845653):未发现明确问题。已审查 base 025f6272970cace6670776375f788776c5e85fc0 到当前 head 的真实 diff;并在干净 worktree 执行 `cargo check --locked -p api-server -j 1`,编译通过。
lhk229 added 3 commits 2026-09-03 13:28:54 +08:00
恢复独立 platform-agent 的 workspace exclude 边界

同步 Repository checks #5755 根因与验证记录
Merge remote-tracking branch 'origin/feat/agc_call_rec' into feat/agc_call_rec
Project CI / Frontend tests (pull_request) Has been cancelled
Project CI / Backend tests (pull_request) Has been cancelled
Project CI / Native shell tests (pull_request) Has been cancelled
Project CI / Repository checks (pull_request) Has been cancelled
7ef30cc997
lhk229 added 1 commit 2026-09-03 13:38:14 +08:00
修复 Native shell 过期文件清单
Project CI / Repository checks (pull_request) Successful in 2m33s
Project CI / Frontend tests (pull_request) Successful in 2m54s
Project CI / Backend tests (pull_request) Successful in 7m2s
Project CI / Native shell tests (pull_request) Successful in 20m16s
bea820da57
移除已退役 H5 QR 扫码组件和 HostBridge wrapper 的检查引用

清理根 Vitest include 中已删除的个人中心测试路径

补充 Native shell 检查清单维护决策记录
Member

代码审查(head sha bea820da57bc280d11b722752a36aa9b14f0b496):未发现明确问题。

代码审查(head sha bea820da57bc280d11b722752a36aa9b14f0b496):未发现明确问题。
kdletters reviewed 2026-09-03 13:45:49 +08:00
kdletters left a comment
Member

代码审查(head sha bea820da57bc280d11b722752a36aa9b14f0b496):未发现明确问题。已基于 base 025f627297 执行真实 diff 审查;api-server tracking 相关测试 5 项全部通过。

代码审查(head sha bea820da57bc280d11b722752a36aa9b14f0b496):未发现明确问题。已基于 base 025f6272970cace6670776375f788776c5e85fc0 执行真实 diff 审查;api-server tracking 相关测试 5 项全部通过。
Author
Owner

AGC 调用记录的最终落点是主站 SpacetimeDB 的 tracking_event 表,不是在 AGC 客户端本地单独建表。

链路大致是:

AGC 请求
  ↓
X-Genarrative-Client: agc
  ↓
api-server tracking middleware
  ↓
生成 TrackingEventInput
  ↓
SpacetimeDB procedure
  ↓
tracking_event 表

表中的核心字段包括:

event_id
event_key
scope_kind
scope_id
user_id
owner_user_id
profile_id
module_key
metadata_json
occurred_at

AGC 标识放在 metadata_json 里,例如:

{
  "route": "/api/editor/projects",
  "method": "GET",
  "status": 200,
  "operation": "listEditorProjects",
  "client": "agc"
}

相关代码位置:

需要区分两种写入路径:

  1. 普通路由埋点

默认先写入 api-server 本机的临时 outbox:

server-rs/.data/tracking-outbox/

文件是 NDJSON 格式,通常会出现:

active.ndjson
sealed-*.ndjson

后台 worker 批量调用 record_tracking_events_and_return 写入 tracking_event。成功入库后对应 sealed 文件会被删除。因此这个目录只是可靠投递缓冲,不是最终数据存储。

  1. 详细业务埋点

例如资产详细事件、登录事件或其他显式 tracking 事件,会直接调用 record_tracking_event_and_return 写入 SpacetimeDB;如果 outbox 不可用,普通路由事件也会回退到同步直写。

写入 tracking_event 后,SpacetimeDB 还会同步更新 tracking_daily_stat,它是按事件和业务日聚合的统计表;原始调用记录仍然在 tracking_event

后台读取入口是:

GET /admin/api/tracking/events

后台查询实际执行的是:

SELECT event_id, event_key, scope_kind, scope_id, day_key,
       user_id, owner_user_id, profile_id, module_key,
       metadata_json, occurred_at
FROM tracking_event
...

所以目前查看 AGC 调用记录时,重点是查看 tracking_event.metadata_json 中是否存在:

"client": "agc"

目前后台接口支持按事件 key、用户、scope 和日期筛选,但还没有单独的 client=agc 查询参数;需要从返回的 metadata_json 中识别 AGC 标记。

AGC 调用记录的最终落点是主站 SpacetimeDB 的 `tracking_event` 表,不是在 AGC 客户端本地单独建表。 链路大致是: ```text AGC 请求 ↓ X-Genarrative-Client: agc ↓ api-server tracking middleware ↓ 生成 TrackingEventInput ↓ SpacetimeDB procedure ↓ tracking_event 表 ``` 表中的核心字段包括: ```text event_id event_key scope_kind scope_id user_id owner_user_id profile_id module_key metadata_json occurred_at ``` AGC 标识放在 `metadata_json` 里,例如: ```json { "route": "/api/editor/projects", "method": "GET", "status": 200, "operation": "listEditorProjects", "client": "agc" } ``` 相关代码位置: - 请求头解析和 tracking middleware:[app.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs:237) - 组装 tracking input:[tracking.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking.rs:1048) - 调用 SpacetimeDB:[runtime.rs](C:/projects/narrative/Genarrative/server-rs/crates/spacetime-client/src/runtime.rs:1341) - `tracking_event` 表定义:[profile.rs](C:/projects/narrative/Genarrative/server-rs/crates/spacetime-module/src/runtime/profile.rs:80) - 表写入和幂等处理:[profile.rs](C:/projects/narrative/Genarrative/server-rs/crates/spacetime-module/src/runtime/profile.rs:7524) 需要区分两种写入路径: 1. 普通路由埋点 默认先写入 api-server 本机的临时 outbox: ```text server-rs/.data/tracking-outbox/ ``` 文件是 NDJSON 格式,通常会出现: ```text active.ndjson sealed-*.ndjson ``` 后台 worker 批量调用 `record_tracking_events_and_return` 写入 `tracking_event`。成功入库后对应 sealed 文件会被删除。因此这个目录只是可靠投递缓冲,不是最终数据存储。 2. 详细业务埋点 例如资产详细事件、登录事件或其他显式 tracking 事件,会直接调用 `record_tracking_event_and_return` 写入 SpacetimeDB;如果 outbox 不可用,普通路由事件也会回退到同步直写。 写入 `tracking_event` 后,SpacetimeDB 还会同步更新 `tracking_daily_stat`,它是按事件和业务日聚合的统计表;原始调用记录仍然在 `tracking_event`。 后台读取入口是: ```text GET /admin/api/tracking/events ``` 后台查询实际执行的是: ```sql SELECT event_id, event_key, scope_kind, scope_id, day_key, user_id, owner_user_id, profile_id, module_key, metadata_json, occurred_at FROM tracking_event ... ``` 所以目前查看 AGC 调用记录时,重点是查看 `tracking_event.metadata_json` 中是否存在: ```json "client": "agc" ``` 目前后台接口支持按事件 key、用户、scope 和日期筛选,但还没有单独的 `client=agc` 查询参数;需要从返回的 `metadata_json` 中识别 AGC 标记。
lhk229 added a new dependency 2026-09-03 15:25:17 +08:00
Author
Owner
<html>

PR246 的核心是 Issue #225:主站侧把 AGC 调用记录下来,并补齐来源与用户归属。它依赖已经合入的 #226 提供 X-Genarrative-Client: agc,但 PR246 本身不负责给 AGC 客户端注入 Header

可以把它理解为:

#226 负责“客户端打标签”;PR246 负责“主站识别、记录、归属和查询这个标签”。

一、PR246 具体做了什么

1. 在主站统一解析 AGC 标记

在主站 tracking middleware 中识别:

X-Genarrative-Client: agc

只有值经过空白裁剪后严格等于小写 agc 才认为是 AGC 请求。

Header 只用于判断“是否来自 AGC”,不参与身份认证,也不会根据 Header 推导用户 ID。

主要代码:

  • [tracking.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking.rs)
  • [app.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs)

2. 给主站请求增加统一的 route tracking 记录

PR246 建立了主站请求的显式 route tracking spec,覆盖主要 AGC 调用类别:

  • 登录和账号相关请求;
  • Profile、API Key、充值等账号操作;
  • 资产读取;
  • 编辑器项目和素材库;
  • 图片、视频、音频、背景移除等生成请求;
  • runtime 外部生成任务查询;
  • /api/external/v1/* 外部编辑器接口。

记录条件不是“所有 HTTP 请求”,而是:

  1. 路由在显式白名单中;
  2. 请求返回成功状态,即 2xx;
  3. 对标记为 AGC-only 的路由,还必须携带合法 AGC Header;
  4. /admin/* 等后台路由不进入用户 route tracking。

因此这是“显式路由清单”,不是全局无差别记录。

3. 将 client: "agc" 写入已有 tracking metadata

典型记录会包含:

{
  "route": "/api/editor/images/generations",
  "method": "POST",
  "status": 202,
  "operation": "generateEditorImage",
  "client": "agc"
}

没有新增 agc_xxx 的平行事件体系,而是复用现有 event key 和 metadata 结构。

4. 补齐账号态用户归属

对于已经有认证主体的请求:

  • Bearer access token:
    • user_id = 登录用户;
    • owner_user_id = 登录用户;
    • User scope 的 scope_id = 登录用户。
  • External API Key:
    • owner_user_id = API Key 对应 owner;
    • 按现有语义,user_id 可以为空。
  • 管理员请求:
    • 继续使用管理员自己的审计主体和既有审计链路。

对于登录成功请求,进入 handler 时还没有 access token extension,因此 PR246 增加了一个请求级的一次性主体传递:

登录 handler 验证成功
  → 从认证结果取得真实 user.id
  → 写入 response extension
  → tracking middleware 读取
  → 生成真实用户归属的登录 route event

涉及:

  • [password_entry.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/password_entry.rs)
  • [phone_auth.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/phone_auth.rs)
  • [app.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs)

不会从响应体、Cookie 或 Header 解析用户身份,也不会把 token 写入埋点。

5. 修复资产读取请求的用户归属

/api/assets/read-url/api/assets/read-bytes 同时支持:

  • 匿名读取公开素材;
  • 登录用户读取私有素材。

所以不能直接改成强制 Bearer 鉴权。PR246 保留原来的可选鉴权逻辑:

optional_access_token_from_headers
  → 校验成功
  → 读取素材
  → 将已验证 AuthenticatedAccessToken 放入 response extensions
  → tracking middleware 归属真实用户

结果是:

  • AGC + 有效 Bearer + 私有素材:记录真实用户;
  • AGC + 无 Bearer + 公开素材:仍记录匿名;
  • 无效 Bearer:仍返回 401,不降级为匿名;
  • 不记录 Authorization Header、access token、Cookie。

涉及:

  • [assets.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/assets.rs)

6. 接通持久化链路

tracking event 的完整链路变成:

HTTP 请求
  → route tracking
  → TrackingEventDraft
  → RuntimeTrackingEventInput
  → tracking outbox
  → SpacetimeDB tracking_event
  → 后台 tracking 查询接口

PR246 没有新增 tracking 表,而是复用现有 tracking_event

outbox 可以在 SpacetimeDB 暂时不可用时先落本地文件,之后由后台 flush。相关代码:

  • [tracking_outbox.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking_outbox.rs)
  • [commands.rs](C:/projects/narrative/Genarrative/server-rs/crates/module-runtime/src/commands.rs)
  • [runtime.rs](C:/projects/narrative/Genarrative/server-rs/crates/spacetime-client/src/active/mapper/runtime.rs)

后台已有的 tracking readback 逻辑也增加了 AGC metadata、user/owner 字段的回归验证。

7. 保持 daily_login 原有语义

PR246 没有给 daily_login 追加 client: "agc"

原因是 daily_login 是:

用户 + 北京时间业务日

的幂等事件。如果把 AGC 来源写进去,会出现:

  • 先网页登录、后 AGC 登录:AGC 来源可能因为幂等被跳过;
  • 先 AGC 登录、后网页登录:当天事件可能永久显示为 AGC;
  • 同一天不同来源无法表达多次事实。

因此:

  • daily_login 继续只表示“用户当天完成过登录”;
  • 查询“哪些用户通过 AGC 登录”,使用带 metadata.client = "agc" 的登录 route event。

8. 增加定向测试

覆盖内容包括:

  • AGC Header 解析;
  • route tracking spec;
  • metadata 构造;
  • 用户、owner、scope 归属;
  • 登录成功主体传递;
  • 资产读取主体传递;
  • tracking outbox round-trip;
  • module mapper;
  • 后台 readback;
  • 真实 build_router middleware 链路;
  • AGC 标记不绕过鉴权;
  • 非 AGC、匿名素材和外部 API Key 边界。

二、做了和没做的区别

维度 | PR246 合入后 | 不做 PR246 / 合入前 -- | -- | -- 能否识别 AGC 请求 | 主站可从 Header 识别,并写入 metadata.client = "agc" | 主站无法稳定区分 AGC、网页或其他调用来源 路由记录 | 显式白名单路由的成功请求会生成 route event | 只有原有零散/手工埋点,很多 AGC 调用没有统一 route 记录 AGC-only 路由 | 未携带 AGC 标记的普通请求不记录这些 AGC 专用事件 | 容易把普通网页请求混入 AGC 统计,或无法建立清晰边界 已登录普通请求 | 可按 Bearer 用户归属 | 可能只能记录匿名或依赖各 handler 原有埋点 AGC 登录成功 | 登录 route event 可归属到真实登录用户 | route event 通常是 anonymous,只能看到“有 AGC 登录”,无法知道是谁 AGC 读取私有素材 | user_id、owner_user_id、scope_id 可归属真实 Bearer 用户 | 高概率记录成匿名,无法回答谁读取了素材 External API Key 请求 | 保留 API Key owner 归属语义 | 外部调用和账号态调用的统计边界不清晰 公开素材匿名读取 | 仍保持匿名,不强制登录 | 原有公开素材行为不变 数据持久化 | 通过 outbox 写入已有 tracking_event,后台可查询 | 即使业务请求成功,也缺少可靠的 AGC 来源和归属记录 数据库结构 | 不新增表、字段、migration、binding | 不会有新的数据库结构变化,但也没有新增的 AGC 统计能力 业务响应 | 不改变登录响应、Cookie、token、状态码和权限逻辑 | 业务本身大多仍能工作,但可观测性和归因缺失 失败请求 | 通用 route tracking 只记录成功响应;认证失败不会伪造成功事件 | 仍然不会因为 AGC 标记绕过鉴权,但缺少成功链路记录

三、哪些事情 PR246 没有做

以下内容不属于 PR246:

  1. 不负责 AGC 客户端注入 Header

    这是 #226 的职责,包括 TS fetchClientHttp、Rust 主站 client factory、default headers、同源重定向边界等。

  2. 不是所有主站 HTTP 请求都自动记录

    只有显式登记在 route tracking spec 中的路由才会记录,且主要记录成功请求。

  3. 不记录 OSS、签名 URL、Provider、搜索、loopback 等非主站业务请求

    这些不是主站用户行为 route tracking 的目标。

  4. 不回填历史 tracking 数据

    旧记录缺少可信 AGC 来源或真实用户信息,PR246 不做猜测性补写。

  5. 不新增 agc_login_success 事件

    继续复用现有登录 route event。

  6. 不修改认证协议

    不改变 access token、refresh token、Cookie、登录响应体或鉴权流程。

  7. 不修改 SpacetimeDB schema

    没有新增表、字段、migration 或 bindings。

  8. 不改后台 UI

    主要是保证现有 tracking readback 能正确解析和展示新增 metadata、user、owner 信息。

四、当前 PR 中的附带维护性修复

当前分支相对 origin/master 还包含两项与 Issue225 业务逻辑无关、但为 CI 修复所做的维护:

  • 恢复 server-rs/Cargo.tomlcrates/platform-agent 的 workspace exclude,修复 Rust workspace 格式检查;
  • 清理 scripts/check-native-shells.mjsvitest.config.ts 中已经删除组件和测试的旧引用,修复 Native shell 检查。

另外,当前分支 diff 中还存在若干 local-docs 验收材料。它们不是运行时代码,也不应作为团队长期交接依据;正式可见的方案和决策已经写入:

  • [Issue225 登录成功 AGC 用户归属方案](C:/projects/narrative/Genarrative/docs/technical/【后端架构】Issue225登录成功AGC用户归属修复方案-2026-09-03.md)
  • [共享决策记录](C:/projects/narrative/Genarrative/docs/project-memory/shared-memory/decision-log.md)

总结

PR246 的实际价值不是改变 AGC 的业务功能,而是补上主站侧的“来源识别—请求记录—用户归属—持久化—后台查询”闭环:

以前:
AGC 请求能调用主站,但主站记录里看不出来源,部分请求只能看到匿名

现在:
AGC 请求能被识别,成功调用可记录到 tracking_event,
账号态能归属真实用户,管理员可以按 client / route / user / owner 查询

同时它保持了非 AGC 请求、匿名公开素材、External API Key、daily_login 和认证协议的原有边界。

</html>
<html> <body> <!--StartFragment--><div><div><div><div><p>PR246 的核心是 <strong>Issue #225:主站侧把 AGC 调用记录下来,并补齐来源与用户归属</strong>。它依赖已经合入的 #226 提供 <code dir="ltr">X-Genarrative-Client: agc</code>,但 <strong>PR246 本身不负责给 AGC 客户端注入 Header</strong>。</p><p>可以把它理解为:</p><blockquote><p>#226 负责“客户端打标签”;PR246 负责“主站识别、记录、归属和查询这个标签”。</p></blockquote><h2>一、PR246 具体做了什么</h2><h3>1. 在主站统一解析 AGC 标记</h3><p>在主站 tracking middleware 中识别:</p><pre dir="ltr"><code>X-Genarrative-Client: agc</code></pre><p>只有值经过空白裁剪后严格等于小写 <code dir="ltr">agc</code> 才认为是 AGC 请求。</p><p>Header 只用于判断“是否来自 AGC”,不参与身份认证,也不会根据 Header 推导用户 ID。</p><p>主要代码:</p><ul><li><span data-prompt-link-label="tracking.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking.rs">[tracking.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking.rs)</span></li><li><span data-prompt-link-label="app.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs">[app.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs)</span></li></ul><h3>2. 给主站请求增加统一的 route tracking 记录</h3><p>PR246 建立了主站请求的显式 route tracking spec,覆盖主要 AGC 调用类别:</p><ul><li>登录和账号相关请求;</li><li>Profile、API Key、充值等账号操作;</li><li>资产读取;</li><li>编辑器项目和素材库;</li><li>图片、视频、音频、背景移除等生成请求;</li><li>runtime 外部生成任务查询;</li><li><code dir="ltr">/api/external/v1/*</code> 外部编辑器接口。</li></ul><p>记录条件不是“所有 HTTP 请求”,而是:</p><ol start="1"><li>路由在显式白名单中;</li><li>请求返回成功状态,即 2xx;</li><li>对标记为 AGC-only 的路由,还必须携带合法 AGC Header;</li><li><code dir="ltr">/admin/*</code> 等后台路由不进入用户 route tracking。</li></ol><p>因此这是“显式路由清单”,不是全局无差别记录。</p><h3>3. 将 <code dir="ltr">client: "agc"</code> 写入已有 tracking metadata</h3><p>典型记录会包含:</p><pre dir="ltr"><code>{ "route": "/api/editor/images/generations", "method": "POST", "status": 202, "operation": "generateEditorImage", "client": "agc" }</code></pre><p>没有新增 <code dir="ltr">agc_xxx</code> 的平行事件体系,而是复用现有 event key 和 metadata 结构。</p><h3>4. 补齐账号态用户归属</h3><p>对于已经有认证主体的请求:</p><ul><li>Bearer access token:<ul><li><code dir="ltr">user_id</code> = 登录用户;</li><li><code dir="ltr">owner_user_id</code> = 登录用户;</li><li>User scope 的 <code dir="ltr">scope_id</code> = 登录用户。</li></ul></li><li>External API Key:<ul><li><code dir="ltr">owner_user_id</code> = API Key 对应 owner;</li><li>按现有语义,<code dir="ltr">user_id</code> 可以为空。</li></ul></li><li>管理员请求:<ul><li>继续使用管理员自己的审计主体和既有审计链路。</li></ul></li></ul><p>对于登录成功请求,进入 handler 时还没有 access token extension,因此 PR246 增加了一个请求级的一次性主体传递:</p><pre dir="ltr"><code>登录 handler 验证成功 → 从认证结果取得真实 user.id → 写入 response extension → tracking middleware 读取 → 生成真实用户归属的登录 route event</code></pre><p>涉及:</p><ul><li><span data-prompt-link-label="password_entry.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/password_entry.rs">[password_entry.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/password_entry.rs)</span></li><li><span data-prompt-link-label="phone_auth.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/phone_auth.rs">[phone_auth.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/phone_auth.rs)</span></li><li><span data-prompt-link-label="app.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs">[app.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/app.rs)</span></li></ul><p>不会从响应体、Cookie 或 Header 解析用户身份,也不会把 token 写入埋点。</p><h3>5. 修复资产读取请求的用户归属</h3><p><code dir="ltr">/api/assets/read-url</code> 和 <code dir="ltr">/api/assets/read-bytes</code> 同时支持:</p><ul><li>匿名读取公开素材;</li><li>登录用户读取私有素材。</li></ul><p>所以不能直接改成强制 Bearer 鉴权。PR246 保留原来的可选鉴权逻辑:</p><pre dir="ltr"><code>optional_access_token_from_headers → 校验成功 → 读取素材 → 将已验证 AuthenticatedAccessToken 放入 response extensions → tracking middleware 归属真实用户</code></pre><p>结果是:</p><ul><li>AGC + 有效 Bearer + 私有素材:记录真实用户;</li><li>AGC + 无 Bearer + 公开素材:仍记录匿名;</li><li>无效 Bearer:仍返回 401,不降级为匿名;</li><li>不记录 Authorization Header、access token、Cookie。</li></ul><p>涉及:</p><ul><li><span data-prompt-link-label="assets.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/assets.rs">[assets.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/assets.rs)</span></li></ul><h3>6. 接通持久化链路</h3><p>tracking event 的完整链路变成:</p><pre dir="ltr"><code>HTTP 请求 → route tracking → TrackingEventDraft → RuntimeTrackingEventInput → tracking outbox → SpacetimeDB tracking_event → 后台 tracking 查询接口</code></pre><p>PR246 没有新增 tracking 表,而是复用现有 <code dir="ltr">tracking_event</code>。</p><p>outbox 可以在 SpacetimeDB 暂时不可用时先落本地文件,之后由后台 flush。相关代码:</p><ul><li><span data-prompt-link-label="tracking_outbox.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking_outbox.rs">[tracking_outbox.rs](C:/projects/narrative/Genarrative/server-rs/crates/api-server/src/tracking_outbox.rs)</span></li><li><span data-prompt-link-label="commands.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/module-runtime/src/commands.rs">[commands.rs](C:/projects/narrative/Genarrative/server-rs/crates/module-runtime/src/commands.rs)</span></li><li><span data-prompt-link-label="runtime.rs" data-prompt-link-href="C:/projects/narrative/Genarrative/server-rs/crates/spacetime-client/src/active/mapper/runtime.rs">[runtime.rs](C:/projects/narrative/Genarrative/server-rs/crates/spacetime-client/src/active/mapper/runtime.rs)</span></li></ul><p>后台已有的 tracking readback 逻辑也增加了 AGC metadata、user/owner 字段的回归验证。</p><h3>7. 保持 <code dir="ltr">daily_login</code> 原有语义</h3><p>PR246 <strong>没有给 <code dir="ltr">daily_login</code> 追加 <code dir="ltr">client: "agc"</code></strong>。</p><p>原因是 <code dir="ltr">daily_login</code> 是:</p><pre dir="ltr"><code>用户 + 北京时间业务日</code></pre><p>的幂等事件。如果把 AGC 来源写进去,会出现:</p><ul><li>先网页登录、后 AGC 登录:AGC 来源可能因为幂等被跳过;</li><li>先 AGC 登录、后网页登录:当天事件可能永久显示为 AGC;</li><li>同一天不同来源无法表达多次事实。</li></ul><p>因此:</p><ul><li><code dir="ltr">daily_login</code> 继续只表示“用户当天完成过登录”;</li><li>查询“哪些用户通过 AGC 登录”,使用带 <code dir="ltr">metadata.client = "agc"</code> 的登录 route event。</li></ul><h3>8. 增加定向测试</h3><p>覆盖内容包括:</p><ul><li>AGC Header 解析;</li><li>route tracking spec;</li><li>metadata 构造;</li><li>用户、owner、scope 归属;</li><li>登录成功主体传递;</li><li>资产读取主体传递;</li><li>tracking outbox round-trip;</li><li>module mapper;</li><li>后台 readback;</li><li>真实 <code dir="ltr">build_router</code> middleware 链路;</li><li>AGC 标记不绕过鉴权;</li><li>非 AGC、匿名素材和外部 API Key 边界。</li></ul><h2>二、做了和没做的区别</h2><div><div><div> 维度 | PR246 合入后 | 不做 PR246 / 合入前 -- | -- | -- 能否识别 AGC 请求 | 主站可从 Header 识别,并写入 metadata.client = "agc" | 主站无法稳定区分 AGC、网页或其他调用来源 路由记录 | 显式白名单路由的成功请求会生成 route event | 只有原有零散/手工埋点,很多 AGC 调用没有统一 route 记录 AGC-only 路由 | 未携带 AGC 标记的普通请求不记录这些 AGC 专用事件 | 容易把普通网页请求混入 AGC 统计,或无法建立清晰边界 已登录普通请求 | 可按 Bearer 用户归属 | 可能只能记录匿名或依赖各 handler 原有埋点 AGC 登录成功 | 登录 route event 可归属到真实登录用户 | route event 通常是 anonymous,只能看到“有 AGC 登录”,无法知道是谁 AGC 读取私有素材 | user_id、owner_user_id、scope_id 可归属真实 Bearer 用户 | 高概率记录成匿名,无法回答谁读取了素材 External API Key 请求 | 保留 API Key owner 归属语义 | 外部调用和账号态调用的统计边界不清晰 公开素材匿名读取 | 仍保持匿名,不强制登录 | 原有公开素材行为不变 数据持久化 | 通过 outbox 写入已有 tracking_event,后台可查询 | 即使业务请求成功,也缺少可靠的 AGC 来源和归属记录 数据库结构 | 不新增表、字段、migration、binding | 不会有新的数据库结构变化,但也没有新增的 AGC 统计能力 业务响应 | 不改变登录响应、Cookie、token、状态码和权限逻辑 | 业务本身大多仍能工作,但可观测性和归因缺失 失败请求 | 通用 route tracking 只记录成功响应;认证失败不会伪造成功事件 | 仍然不会因为 AGC 标记绕过鉴权,但缺少成功链路记录 </div></div></div><h2>三、哪些事情 PR246 没有做</h2><p>以下内容不属于 PR246:</p><ol start="1"><li><p><strong>不负责 AGC 客户端注入 Header</strong></p><p>这是 #226 的职责,包括 TS <code dir="ltr">fetchClientHttp</code>、Rust 主站 client factory、default headers、同源重定向边界等。</p></li><li><p><strong>不是所有主站 HTTP 请求都自动记录</strong></p><p>只有显式登记在 route tracking spec 中的路由才会记录,且主要记录成功请求。</p></li><li><p><strong>不记录 OSS、签名 URL、Provider、搜索、loopback 等非主站业务请求</strong></p><p>这些不是主站用户行为 route tracking 的目标。</p></li><li><p><strong>不回填历史 tracking 数据</strong></p><p>旧记录缺少可信 AGC 来源或真实用户信息,PR246 不做猜测性补写。</p></li><li><p><strong>不新增 <code dir="ltr">agc_login_success</code> 事件</strong></p><p>继续复用现有登录 route event。</p></li><li><p><strong>不修改认证协议</strong></p><p>不改变 access token、refresh token、Cookie、登录响应体或鉴权流程。</p></li><li><p><strong>不修改 SpacetimeDB schema</strong></p><p>没有新增表、字段、migration 或 bindings。</p></li><li><p><strong>不改后台 UI</strong></p><p>主要是保证现有 tracking readback 能正确解析和展示新增 metadata、user、owner 信息。</p></li></ol><h2>四、当前 PR 中的附带维护性修复</h2><p>当前分支相对 <code dir="ltr">origin/master</code> 还包含两项与 Issue225 业务逻辑无关、但为 CI 修复所做的维护:</p><ul><li>恢复 <code dir="ltr">server-rs/Cargo.toml</code> 中 <code dir="ltr">crates/platform-agent</code> 的 workspace exclude,修复 Rust workspace 格式检查;</li><li>清理 <code dir="ltr">scripts/check-native-shells.mjs</code> 和 <code dir="ltr">vitest.config.ts</code> 中已经删除组件和测试的旧引用,修复 Native shell 检查。</li></ul><p>另外,当前分支 diff 中还存在若干 <code dir="ltr">local-docs</code> 验收材料。它们不是运行时代码,也不应作为团队长期交接依据;正式可见的方案和决策已经写入:</p><ul><li><span data-prompt-link-label="Issue225 登录成功 AGC 用户归属方案" data-prompt-link-href="C:/projects/narrative/Genarrative/docs/technical/【后端架构】Issue225登录成功AGC用户归属修复方案-2026-09-03.md">[Issue225 登录成功 AGC 用户归属方案](C:/projects/narrative/Genarrative/docs/technical/【后端架构】Issue225登录成功AGC用户归属修复方案-2026-09-03.md)</span></li><li><span data-prompt-link-label="共享决策记录" data-prompt-link-href="C:/projects/narrative/Genarrative/docs/project-memory/shared-memory/decision-log.md">[共享决策记录](C:/projects/narrative/Genarrative/docs/project-memory/shared-memory/decision-log.md)</span></li></ul><h2>总结</h2><p>PR246 的实际价值不是改变 AGC 的业务功能,而是补上主站侧的“来源识别—请求记录—用户归属—持久化—后台查询”闭环:</p><pre dir="ltr"><code>以前: AGC 请求能调用主站,但主站记录里看不出来源,部分请求只能看到匿名 现在: AGC 请求能被识别,成功调用可记录到 tracking_event, 账号态能归属真实用户,管理员可以按 client / route / user / owner 查询</code></pre><p>同时它保持了非 AGC 请求、匿名公开素材、External API Key、<code dir="ltr">daily_login</code> 和认证协议的原有边界。</p></div></div></div></div><!--EndFragment--> </body> </html>
lhk229 changed title from 添加客户端埋点统计 to 主站对AGC请求头做特殊处理和记录 2026-09-03 15:28:02 +08:00
kdletters merged commit 8b4c3792c7 into master 2026-09-03 15:33:27 +08:00
kdletters deleted branch feat/agc_call_rec 2026-09-03 15:33:27 +08:00
Sign in to join this conversation.