Files
deepseek-harness/.agents/notes/implemented/feature/2026-07-31-web-telemetry-default-mount.zh.md
fz 193d918856 Merge remote-tracking branch 'origin/master' into codex/fix-headless-sigint
# Conflicts:
#	apps/cli/README.i18n.yaml
#	apps/cli/README.md
#	apps/cli/README.zh.md
#	apps/cli/config/base.cordis.yml
2026-08-04 15:18:48 +08:00

4.8 KiB
Raw Blame History

Agent Note: dsh web 组合默认挂载会话遥测(OTel 上报)

Status: implemented

English | 中文

Problem

遥测 seam 与 OTel backend(revival Note)自完成以来从未接入任何部署组合:没有 roster 行、没有开关、没有节奏口径,内部部署对用户会话零可观测。需要一个部署决策:哪些 surface 上报、报到哪、什么节奏、怎么关、CI 怎么隔离。

Decision

dsh 共享 base(apps/cli/config/base.cordis.yml)默认挂载 telemetry-otel 行,内置生产 endpoint,因此 Web 与 headless 都会上报;原始配置命令也会先挂载该行,再应用其必需的部署 overlay。这是内部测试期的部署立场——有 endpoint 就上报,用户可通过环境变量退出。Web 与 headless 在 SIGINT/SIGTERM 时使用有界、可升级的进程关闭控制器,在启动器 5 秒上限到期前,先给后端 3 秒关闭截止时间完成排空。

决策项 取值 理由
挂载面 base.cordis.yml(原始配置 + Web + headless) 所有加载共享 base 的配置树采用同一个部署立场;原始配置 overlay 决定该部署是否创建会话
endpoint DSH_TELEMETRY_OTLP_URL,缺省 https://harness-telemetry.deepseeksvc.com/v1/logs 内部 collector;env 覆盖供本地/联调
退出开关 DSH_TELEMETRY_DISABLED 非空(含 0/false)即关 隐私向开关取「宁关勿误开」;行级 disable 只能在 AppCLIEntry 的 patch 层做(config 无 disable 语义,且必须先于 exporter.url 的加载期校验生效)
上报节奏 processor.scheduledDelayMillis: 10000(10s/批) 流式回流,非退出才报;崩溃至多丢最后一个未导出间隔
退出 drain 上界 exporter.timeoutMillis: 1000 + maxExportBatchSize: 2048(== maxQueueSize) + exportTimeoutMillis: 1500 + shutdownTimeoutMillis: 3000 collector 不可达的常规故障会在约 1s 内放行:timeoutMillis 是单次 socket 超时与重试 deadline,使用与队列等大的单批可避免依次排空导致耗时倍增。由 DSH 管理的 3s 外层上限覆盖 SDK 先执行的无界 forceFlush() 等待,即传输 Promise 始终无法取得 socket 的情况。
压缩 compression: gzip 事件 body 含全文,跨机房带宽
CI 隔离 全部 8 个 GitHub workflow 顶层 env: DSH_TELEMETRY_DISABLED: '1' CI 启动 web 组合的所有通道(e2e/snapshot/built smoke)不得向生产 endpoint 泄测试会话

集成测试 apps/cli/tests/telemetry-web.e2e.ts(keyless)钉住部署级行为:测试内 OTLP collector + mock LLM,真启动 dsh web,断言 ledger 覆盖、seq 单调、chunk 首条投影、以及 SIGINT drain 后 ops shutdown 标记到达。

Alternatives considered

默认不挂载,部署方自行加行(SDK 立场的延续)。 否决于当前阶段:本仓的 web/headless 组合就是内部部署本身,「上报默认开」是这个部署的产品要求;SDK 立场仍由 seam 包保持(不挂 = 零外发)。

开关做成 config 字段而非 env patch。 不可行:cordis 行没有 config 层的 disable 语义,且 exporter.url 校验在插件构造期 fail-loud,开关必须在 Loader 之前生效——AppCLIEntry patch 层是唯一落点。

退出时 Promise.race 兜底超时。 最初暂缓,是因为 SDK 参数看似已经将后端排空耗时限制在约 1.5-3s(通常 <100ms),实测 SIGINT 到退出耗时 110ms-1.1s。后来在 Linux 沙箱中复现并证明,BatchLogRecordProcessor.shutdown() 可能在 exporter.forceFlush() 中永久等待,无法进入受 exportTimeoutMillis 限制的完成 Promise。因此,CLI 关闭修复 既为这一特定缺口增加 3 秒后端上限,也为整棵插件树增加 5 秒进程级上限和重复信号退出途径。

Consequences

  • 无本地 collector 的开发者跑 dsh web 会对生产 endpoint 每 10s 发一次 POST(联不通则静默失败,OTel diag logger 未注册);本地开发设 DSH_TELEMETRY_DISABLED=1 或 DSH_TELEMETRY_OTLP_URL 指本地。
  • 当前零脱敏规则挂载:导出即原始捕获副本(用户/助手消息全文、工具参数与结果、system prompt、session.cwd 本地路径)。跨信任边界前必须挂 telemetry/record 规则——脱敏规则、其余身份 Resource 维度(hostname/surface;匿名 user id 已由匿名用户 id Note落地)、使用数据 metrics 轨是本决策明确的后续工作。
  • 复用这棵树的测试载具(如 apps/web/tests/scaffold.ts)须显式关停该行,否则 fixture 会话会流向 env 里碰巧存在的 collector。