# DeepSeek Harness 的 LoongSuite 可观测插件
[English](README.md) | 简体中文
`@loongsuite/dsh-plugin` 是面向
[DeepSeek Harness](https://github.com/deepseek-ai/deepseek-harness)(`dsh`)的独立开源可观测
插件。它观测 DSH 原生的会话、Agent 循环、LLM 流和工具生命周期,将其转换为 OpenTelemetry
GenAI Trace 与 Metric,并通过标准 OTLP/HTTP Protobuf 上报到任意兼容后端。
LoongSuite 是基于 OpenTelemetry 的开源可观测采集体系。本仓库是它面向 DSH 的原生集成。插件**不依赖也不
要求安装 LoongSuite Pilot**,不需要 sidecar、本地 JSONL 采集点,也不绑定任何厂商的后端。
> 当前状态:稳定的 `0.1.x` 版本。可从 npm 或 DSH 插件市场安装 `@loongsuite/dsh-plugin`。
一轮 DSH 对话通过 OTLP 上报到自建 Langfuse:4 个 react step、每次调用的耗时与 token、
web_search 失败后回退到 bash,以及 ENTRY span 上的 GenAI 语义属性。
该截图显式开启了正文采集,默认是关闭的。
## 数据模型
```text
DSH session/event + llm/stream
│
▼
生命周期协调器
│
▼
LoongSuite GenAI OTel 工具库
│
▼
私有 TracerProvider + MeterProvider
│ OTLP/HTTP Protobuf
▼
任意 OpenTelemetry 兼容后端
```
DSH 的每一轮对话生成一条调用链:
```text
ENTRY
└── AGENT
└── STEP
├── LLM
└── TOOL
```
每次真实 LLM 调用都会生成独立的 `LLM` span,因此重试会保留为同一个 STEP 下的多次尝试。工具调用
通过 DSH call ID 与结果关联。错误、中止、不完整的流以及插件卸载都会以错误状态关闭未结束 span,不会
留下悬挂链路。Subagent 会话生成独立 trace,并携带 DSH 父会话和委派层级属性。
开启正文采集时,`ENTRY` 和 `AGENT` 的输入消息只包含本轮 `source.kind=user` 的直接输入。Runtime
快照、Agent 指令、Skill Catalog、Goal 和 Coordinator relay 等 DSH 合成上下文仍作为实际模型请求的
一部分完整保留在 `LLM` span 上,但不会被表述为用户原始输入。
插件还会上报标准的 `gen_ai.client.operation.duration` 与 `gen_ai.client.token.usage` 指标。它不
上报 OpenTelemetry Log,可与独立的 DSH 日志导出插件同时使用。
GenAI Invocation 构建与语义属性由
[`@loongsuite/otel-util-genai`](https://www.npmjs.com/package/@loongsuite/otel-util-genai) SDK 提供。
## 兼容范围
| 组件 | 支持范围 | 已完整验证版本 |
| --- | --- | --- |
| DeepSeek Harness | `>=0.1.0-rc.6 <0.2.0` | `0.1.0-rc.6` 的 headless 与 Web profile |
| Node.js | `>=22.19.0` | macOS 上的 `22.19`、`24.19` 和 `25.9` |
不支持早于 `0.1.0-rc.6` 的 DSH RC 版本。每个插件版本都以 DSH 当前最新发布版为准进行验证,
不会仅凭 bundle 能成功组合就宣称具备完整运行时兼容性。
## 安装与使用
手上还没有 OTLP 后端的话,[`examples/quickstart`](examples/quickstart/README.zh-CN.md) 会起一个本地
Jaeger 后端,三条命令看到第一条调用链。
在需要观测的每个 DSH profile 中安装插件:
```sh
dsh plugin --profile web add @loongsuite/dsh-plugin
dsh plugin --profile headless add @loongsuite/dsh-plugin
```
本地开发时,把包名换成本仓库的绝对路径:
```sh
dsh plugin --profile web add /absolute/path/to/dsh-plugin
```
设置服务名和 OTLP/HTTP Collector 地址,然后照常启动该 profile:
```sh
export OTEL_SERVICE_NAME=dsh-agent
export OTEL_EXPORTER_OTLP_ENDPOINT=http://localhost:4318
export OTEL_EXPORTER_OTLP_HEADERS='authorization=Bearer%20your-token'
dsh --profile web
# 或:dsh --profile headless "总结这个工作区"
```
共享 endpoint 会自动补成 `/v1/traces` 与 `/v1/metrics`。未配置 endpoint 时,导出器使用
OpenTelemetry 标准默认值。
## 插件配置
大部分部署只需环境变量。也可以修改 `$DSH_HOME/profiles//cordis.patch.yml`(默认位于
`~/.dsh`)中的插件配置:
```yaml
- id: loongsuite-observability
config:
endpoint: http://localhost:4318
serviceName: dsh-agent
headers:
authorization: Bearer your-token
resourceAttributes:
deployment.environment.name: development
captureContent: false
exportMetrics: true
```
显式插件配置的优先级高于环境变量。
如果不想修改 profile,可在启动 DSH 前通过 GenAI 正文采集模式开启:
```sh
export OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT=SPAN_ONLY
dsh --profile web
```
| 配置项 | 默认值 | 含义 |
| --- | --- | --- |
| `enabled` | `true` | 不卸载 bundle,直接停止采集。 |
| `endpoint` | 未设置 | OTLP/HTTP 公共基地址;插件自动追加 signal 路径。 |
| `traceEndpoint` / `metricEndpoint` | 未设置 | 完整的单 signal 地址;优先于 `endpoint`。 |
| `headers` | `{}` | 同时添加到两个导出器的请求头。 |
| `serviceName` | `OTEL_SERVICE_NAME` 或 `deepseek-harness` | OpenTelemetry `service.name`。 |
| `resourceAttributes` | `{}` | 额外的字符串类型 Resource 属性。 |
| `captureContent` | 环境变量配置或 `false` | 上报提示词、回复、工具定义、参数和结果正文。 |
| `contentMaxChars` | `128000` | 每个正文属性序列化后保留的最大字符数。 |
| `exportMetrics` | 环境变量配置或 `true` | 上报 LLM 耗时和 token 指标。 |
| `maxExportBatchSize` | `512` | 每批最多上报的 span 数。 |
| `maxQueueSize` | `2048` | 最多排队的 span 数,不能小于 batch size。 |
| `traceExportIntervalMs` | `5000` | Trace 批量导出间隔。 |
| `metricExportIntervalMs` | `60000` | Metric 导出间隔。 |
| `exportTimeoutMs` | `30000` | OTLP 导出超时。 |
| `debug` | `false` | 通过 DSH logger 输出额外的插件生命周期诊断。 |
支持以下 OpenTelemetry 标准环境变量:
- `OTEL_EXPORTER_OTLP_ENDPOINT`
- `OTEL_EXPORTER_OTLP_TRACES_ENDPOINT` 和 `OTEL_EXPORTER_OTLP_METRICS_ENDPOINT`
- `OTEL_EXPORTER_OTLP_HEADERS`
- `OTEL_EXPORTER_OTLP_TRACES_HEADERS` 和 `OTEL_EXPORTER_OTLP_METRICS_HEADERS`
- `OTEL_SERVICE_NAME`
- `OTEL_RESOURCE_ATTRIBUTES`
- `OTEL_INSTRUMENTATION_GENAI_CAPTURE_MESSAGE_CONTENT`(未显式配置 `captureContent` 时,`SPAN_ONLY` 或 `SPAN_AND_EVENT` 开启 span 正文)
- `OTEL_METRICS_EXPORTER`(未显式配置 `exportMetrics` 时,`none` 关闭指标导出)
Header 与 Resource 值使用标准的逗号分隔、百分号编码 `key=value` 格式。
## 隐私与运行时行为
默认不采集正文:提示词、回复、工具 schema、参数和结果不会进入 span,但结构元数据和 token 用量仍会
上报。启用 `captureContent`,或将正文采集环境变量设为 `SPAN_ONLY` / `SPAN_AND_EVENT` 后,源码、凭据、
个人数据或其他敏感内容可能被发送到已配置后端;启用前应先确认后端的留存和访问控制策略。如果某个
profile 必须忽略进程环境并始终禁止正文采集,请显式配置 `captureContent: false`。
插件持有私有 OpenTelemetry Provider,不会替换 DSH 或其他库的全局 Provider;监听器和 Provider
也会随 DSH 插件生命周期释放和 flush。插件附加到已运行或 HMR 重载的 profile 时,只接管现有会话的
身份,并从下一次原生 `turn/start` 开始采集,不会重放历史事件或生成重复链路。
## 开发
需要 Node.js 22.19 或更高版本以及 pnpm。
```sh
pnpm install
pnpm run check
pnpm test
pnpm run build
pnpm pack
```
实现约束与发布检查清单见[贡献指南](CONTRIBUTING.zh-CN.md)。
## 许可证
[Apache-2.0](LICENSE)