Files
deepseek-harness/.agents/notes/implemented/feature/2026-08-11-deepseek-request-user-id-header.zh.md
T
2026-08-11 21:01:36 +08:00

3.8 KiB

Agent Note: DeepSeek 请求用户与会话身份头部

Status: implemented

English | 中文

问题

当调用方提供 GenerateOptions.sessionId 时,直连 DeepSeek 请求已携带 x-deepseek-harness-session-id,让提供方侧支持与诊断可以关联同一对话中的多个轮次。但请求缺少跨会话的稳定身份,而 harness 已为遥测与反馈持久化匿名用户 id。另行生成 id 会破坏关联;把它放进提供方无关的归属辅助函数,则会让每个 HTTP 适配器都发送稳定的逐用户标识。

用户 id 是传输元数据,不是模型输入。它不得进入请求体、提示词、token 计量、KV cache 身份或会话日志。发送目标是适配器解析后的 baseURL,既可能是 DeepSeek 自身,也可能是配置的网关,因此必须明确隐私边界。

决策

dsh-llm-deepseek 在凭据解析成功后发出的每个提供方请求上发送 x-deepseek-harness-user-id。该值来自 @deepseek-ai/dsh-user-id,因此与同一 $DSH_HOME 的 OpenTelemetry Resource user.id 及 /feedback 确认一致。适配器继续仅在存在 GenerateOptions.sessionId 时发送 x-deepseek-harness-session-id;普通 agent、标题生成与压缩请求由 agent loop 提供当前持久化 Session.id。

插件在凭据解析成功后惰性获取用户 id,并在该插件实例内缓存。缺少凭据不会创建 .userid;即使设置了 DSH_TELEMETRY_DISABLED,首个已授权的提供方请求仍可能创建它。直连适配器构造函数接收 resolveUserId 依赖,使线路行为可在单元测试中保持确定性。

两个头部都是发送到解析后 baseURL 的模型不可见 HTTP 元数据。它们不在 JSON 请求体中,也不会成为模型可见输入或会话事件。配置的网关会收到它们。遥测共享只控制遥测导出,不会禁用提供方请求身份。

验证

  • mock 提供方断言已授权请求携带 getOrCreateAnonymousUserId() 返回的同一用户 id,并在未提供会话 id 时省略会话头部。
  • 会话身份线路测试断言两个头部都存在,并原样保留传入的会话 id。
  • 直连适配器测试断言每条 stream 仅解析一次用户 id,keyless 配置测试则证明凭据失败不会创建 .userid。
  • 真实 Loader 组合测试断言组装后的插件使用共享 user-id 包,而非测试专用值。
  • 无需修改 keyless snapshot,因为这些头部不是模型可见或用户可见的 transcript 内容。

考虑过的替代方案

已否决 原因
把 id 加进通用 attributionHeaders() 该辅助函数是提供方无关且静态的;加入逐用户值会把它发送给无关提供方,并违反其应用身份隐私契约
在 cordis.yml 中配置固定自定义头部 部署配置无法推导当前会话 id,且会把稳定身份暴露为可变配置,而不是使用其所属运行时契约
生成 DeepSeek 专用用户 id 提供方请求将无法与同一 harness home 的遥测和反馈关联
随遥测共享关闭该头部 提供方请求身份与遥测导出的接收方和目的不同;共用开关会掩盖真实隐私边界
把 id 放进 OpenAI 兼容的 user 或 metadata 请求字段 body 字段可能影响提供方 schema、日志、缓存、token 化或模型可见重建;HTTP 元数据可保留预期边界

后果

  • DeepSeek 支持可以通过一个匿名 harness-home id 跨会话关联请求,并通过持久化 session id 关联同一对话。
  • 首个已授权 DeepSeek 请求可独立于遥测导出创建 $DSH_HOME/.userid。
  • 自定义 DeepSeek 网关会收到稳定用户 id 与可用的会话 id,因此运维方必须将配置的 baseURL 视为身份接收方。
  • 请求体、提示词、token 数、KV cache 身份和会话日志保持不变。