4.6 KiB
4.6 KiB
Agent Note: telemetry 匿名用户 id($DSH_HOME/.userid)与 OTel Resource user.id
Status: implemented
English | 中文
Problem
session telemetry 已默认挂载(默认挂载 Note),但 OTel Resource 只有 service.name/service.version,没有任何用户级标识——接收端无法按用户聚合、无法数活跃用户。此前唯一相关口径是一条未实现的「hostname/本机 IP 哈希派生 user.id」裁定;dsh-sdk 工具链另有自用的匿名 id($DSH_HOME/telemetry.json),但那是 launcher 回流的私有事实,与 OTel 回流无关。需要给 OTel 回流一个语义干净的匿名用户身份。
Decision
getOrCreateAnonymousUserId() 返回 $DSH_HOME/.userid(resolveDshHome 解析,$DSH_HOME > ~/.dsh)中的裸 UUID 行,首用生成随机 UUID v4 并落盘;后端构造时把它作为 Resource 的 user.id(OTel semconv 标准用户属性)随每批导出携带一次。原始实现位于 session-telemetry-otel,因为当时不存在第二个真实消费方。/feedback 后来成为该消费方,因此共享 id 决策将所有权移交给 @deepseek-ai/dsh-user-id,但不改变本 Note 记录的存储、匿名、并发与丢失语义。dsh-sdk launcher telemetry 继续使用自己独立的匿名 id 存储(telemetry.json),与此身份无关。
| 裁定 | 取值 | 理由 |
|---|---|---|
| id 来源 | 随机 UUID v4,绝不从 hostname/网络地址/git remote 派生 | 派生 id 可反查,「匿名」名不副实 |
| 存储形态 | .userid 裸 UUID 行 + 换行,无 JSON 包装 |
身份是独立事实,不挂在某条 telemetry 链路的文件命名/格式下 |
| 读写形态 | 同步 IO + 进程内按解析后文件路径 memo | TelemetryOtel 构造函数是同步的(async 迫使插件装载改形);一进程一次盘 IO,运行中删文件不影响本进程 |
| 并发首启 | wx 独占写裁决,落败方重读胜者 id |
覆盖常见并发(重读撞进胜者建档-写入微秒窗仍可能各持一 id 一次运行,下次启动收敛到落盘值——telemetry 级后果,接受) |
| 丢失语义 | 文件被删 → 下次启动换新 id,接受丢失 | 匿名身份无恢复价值;可恢复性要求派生材料,与匿名冲突 |
| 写失败 | best-effort 返回内存 id | telemetry 永不因 home 只读被阻塞 |
| 上报位置 | Resource 属性,非逐条 attributes | 每批一次即够接收端按 Resource 维度聚合;逐条注入要动 seam 约定且涨 wire 体积 |
| semconv 依赖 | 不引 @opentelemetry/semantic-conventions 包 |
一个字符串常量不值一个依赖 |
| 落点 | @deepseek-ai/dsh-user-id,由 OTel 后端与 /feedback 共享 |
第二个真实消费方已经出现;直接反馈不能依赖导出后端 |
| 单独开关 | 无 | 任一消费方都可创建该身份;DSH_TELEMETRY_DISABLED 会停止遥测上报,但不会禁用反馈确认 |
Alternatives considered
| 被拒 | 一句话理由 |
|---|---|
| hostname/IP 哈希派生 id(此前口径) | 可反查即非匿名;随机 UUID 语义干净,用户裁决取代 |
| user.id 放每条 record 的 attributes(Claude Code 形态) | 要动 session-telemetry seam 约定或逐条注入,wire 体积涨;Resource 每批一次已满足聚合 |
在 /feedback 需要该 id 之前抽取共享包(初版实现) |
当时唯一的真实消费方是 OTel 后端;只有直接反馈需要同一个关联 id 后,抽取才具备依据 |
| 复用 telemetry.json 不新建文件 | 文件名/JSON 格式把身份挂在 launcher 链路命名下;OTel 回流身份是独立事实 |
| AppCLIEntry 读好 id 经 config patch 注入 | 每个 surface 入口都要接线;config 里传运行时事实与部署配置混淆 |
挂进 @deepseek-ai/dsh-paths |
paths 是纯路径计算零 IO;带持久化的身份能力会污染包边界 |
Consequences
- 一个
$DSH_HOME在 OTel 回流中是一个稳定用户;不同 home 在构造上就是不同用户,无跨 home 关联机制。 - OTel 回流与
/feedback共享.userid;launcher 回流仍使用telemetry.json,无法与前两者关联。 - 删除
.userid即重置身份(下次启动生效);home 不可写时每进程各自持有一个内存 id 直至恢复可写。 - 默认挂载 Note 的身份 follow-up 中「匿名用户 id」项由本决定关闭;hostname/surface 维度与脱敏规则、usage-metrics track 仍是待办。