← 返回列表
✓ 可直接安装
为 DeepSeek Harnessdsh提供 Langfuse 可观测性:把每个 turn 导出为…
自动检查通过:npm 包已发布且 engines 声明满足基线(声明 Node ^22.19 || >=24);该结论来自程序自动检查,未经人工实机验证。 · 最近上游提交 2026/9/10 · 已提供中文文档
DeepSeek Harness (dsh) 的 Langfuse 可观测性:将 agent 会话作为 OpenTelemetry trace 树(GenAI semconv)导出到 Langfuse 的 OTLP 端点
综合分
38.8
GitHub 分
38.8
用户评分
—
★ Stars
13
周下载量
—
安装插件(需先安装 dsh CLI 引擎:npm install -g @deepseek-ai/dsh)
dsh plugin --profile web add dsh-plugin-langfusenpm 包 dsh-plugin-langfuse 已校验归属本仓库,走 npm 安装最省事
数据截至 2026/9/17(元数据每日更新 · 实装验证按队列轮转,单条结论的验证时间见上方)
安装兼容性检查✓ 自动检查通过
以下结论由程序自动检查 npm 包、engines 声明与入口文件得出,未做人工实机验证——能装不等于用着没问题。
✓npm 包dsh-plugin-langfuse @ 0.6.0
✓Node 引擎要求 ^22.19 || >=24 · 基线 Node 22.19 满足
✓dsh CLI 依赖未声明 dsh 版本约束
✓入口文件main/exports/bin 已声明
验证方式:npm registry 存在性 + package.json 静态校验 · 最后验证 2026/9/18 02:17:23
依赖的 DSH / Cordis 模块
@deepseek-ai/dsh-command-feedback@deepseek-ai/dsh-commands@deepseek-ai/dsh-llm@deepseek-ai/dsh-session@deepseek-ai/dsh-session-telemetry@deepseek-ai/schemastery@deepseek-ai/dsh-message-feedback@deepseek-ai/cordis@deepseek-ai/cordis-plugin-loader@deepseek-ai/cordis-plugin-logger-console@deepseek-ai/dsh-agent-loop@deepseek-ai/dsh-agent-loop-testkit用户评分
还没有人投票,来当第一个
订阅周报,不错过优质插件更新
每周一封 · 高评分插件 + 新用户活动
README
dsh-plugin-langfuse
为 DeepSeek Harness(dsh)提供 Langfuse 可观测性:把每个 turn 导出为 OpenTelemetry trace(模型 step → generation、工具调用 → tool span),按 session 分组,把 canonical feedback 记录成 Langfuse Score,并保留 fork/subagent 血缘。
这是一个社区插件(dsh-plugin topic),不属于官方仓库。它实现 harness 的公开 telemetry seam(@deepseek-ai/dsh-session-telemetry),作为官方 OTLP-logs 导出器之外的另一个后端。
安装
以下命令假设已安装 dsh CLI。如果你是从官方仓库源码 checkout 运行 harness,把每条命令改为在 checkout 根目录执行 pnpm dsh …(先跑它的 pnpm run build)—— 命令相同,profile 也是同一个 web。
作为 profile bundle 安装(包内附带 cordis.patch.yml patch 层):
dsh plugin --profile web add dsh-plugin-langfuse
export LANGFUSE_PUBLIC_KEY=pk-lf-…
export LANGFUSE_SECRET_KEY=sk-lf-…
可选,默认 https://cloud.langfuse.com(EU 区);注意插件读取的是
LANGFUSE_HOST,而不是 Langfuse SDK 惯用的 LANGFUSE_BASE_URL
export LANGFUSE_HOST=https://us.cloud.langfuse.com
dsh web # 即 dsh --profile web 的别名
附带的 patch 会禁用 base profile 的 session-telemetry-otel 行(telemetry seam 每个 context 只接受一个后端;重复加载会抛错),并在存在 Langfuse key 时以 FULL 模式挂载本后端,否则为 DISABLED。两个项目密钥都存在时,它还会启用 feedback Score。设置 LANGFUSE_TELEMETRY_MODE=FEEDBACK_ONLY 可将共享收窄为反馈门控释放。
bundle 层和环境变量都在启动时读取:安装后必须重启已在运行的实例,且启动 shell 里要带上这些变量。dsh --profile web --dump-config 可以不启动就查看组合结果 —— 应出现一个 # == dsh-plugin-langfuse 层,它 patch 掉 base 的 telemetry 行并新增 env 驱动模式的 session-telemetry-langfuse 行。跑完下一轮对话后,trace 出现在 LANGFUSE_HOST 所指区域的 Langfuse 控制台 —— 密钥按区域隔离,US 区项目在 EU 控制台上什么都看不到。dsh plugin --profile web remove dsh-plugin-langfuse 会同时移除依赖和对应的层。
也可以作为显式 cordis.yml 行挂载:
- id: session-telemetry-langfuse
name: dsh-plugin-langfuse
config:
mode: FULL # FULL | FEEDBACK_ONLY | DISABLED(默认)
exporter: # 原样透传给 SDK 的 OTLP/HTTP trace exporter
url: https://cloud.langfuse.com/api/public/otel/v1/traces
auth:
publicKey: !!js process.env.LANGFUSE_PUBLIC_KEY
secretKey: !!js process.env.LANGFUSE_SECRET_KEY
feedbackScores: # 可选;显式配置时默认关闭
enabled: true
url: https://cloud.langfuse.com/api/public/scores
maxQueueSize: 256
requestTimeoutMillis: 3000
content: # 可选:隐私/内容控制
turnInputMode: user # none | user | user-and-context
cwdMode: omit # omit | basename | full
toolMetaAllowlist: [] # 精确匹配 tool-result meta 顶层键
metadata: # 可选:Langfuse 静态分组
environment: production
tags: [dsh]
health:
warningIntervalMillis: 60000
maxErrorChars: 1000
processor: {} # 可选;原样透传给 BatchSpanProcessor
shutdownTimeoutMillis: 3000
配置
| 字段 | 含义 |
|---|---|
| mode | FULL 实时导出每个会话;FEEDBACK_ONLY 仅在用户记录反馈时重放并导出 canonical 会话日志;DISABLED(默认)不构造任何东西,没有数据离开进程。FULL 是本插件的持续追踪模式;Harness 0.1.5 官方 OTLP-logs 后端仅提供 FEEDBACK_ONLY 和 DISABLED。 |
| exporter | 完整的 OTLPExporterNodeConfigBase 对象,传给 OTLP/HTTP trace exporter。非 DISABLED 模式下 url 必填,且必须是完整的 traces 路径(…/api/public/otel/v1/traces)。插件默认附带 x-langfuse-ingestion-version: 4 请求头——缺少它新 span 不会实时进入 Langfuse 的 v4 数据模型。无论显式条目来自普通 exporter.headers 对象还是 HeadersFactory 的返回值,均按任意大小写识别并优先采用。 |
| auth | Langfuse 项目密钥对,转换为端点的 Basic-auth 请求头。与显式的 exporter.headers authorization 互斥;上传模式要求两者恰好提供其一。 |
| correlation | 宿主身份关联:userId/sessionId 以 langfuse.user.id/langfuse.session.id 盖在每个导出 span 上,让嵌入方宿主的 trace 和本插件的 trace 归入同一个 Langfuse user/session。见与嵌入宿主关联。 |
| feedbackScores | 可选:把 feedback/record 导出为会话级 TEXT Score,把 feedback/message-put 导出为 CATEGORICAL 消息评价,并同步修改与删除。enabled 默认 false;url 必须是完整的 …/api/public/scores 路径。maxQueueSize 默认 256,requestTimeoutMillis 默认 3000。有界内存队列与 trace 故障隔离,并在 shutdown 时 best-effort 排空。bundle profile 在两个项目密钥都存在时启用它。 |
| content | 导出内容策略。turnInputMode 默认 user(只聚合真人消息);user-and-context 还包括插件注入上下文,none 省略根 input。cwdMode 默认 omit;basename 只导出末级目录,full 导出完整路径。toolMetaAllowlist 默认为空,只允许明确列出的 tool/result.meta 顶层键。 |
| metadata | 可选的 Langfuse 静态 environment 与 tags,传播到每个 observation 以支持 v4 查询。environment 遵循 Langfuse 的小写 a-z0-9-_ 格式、不能以 langfuse 开头且最长 40 字符;最多 50 个 tag,每个最长 200 字符。 |
| health | 投递诊断。warningIntervalMillis 默认 60000,用于持续失败告警限频(0 表示首次告警后不再重复);maxErrorChars 默认 1000,作用于凭据/URL 清洗后的错误文本。这些设置不会增加重试或改变 SDK 缓冲语义。 |
| processor | 原样透传给 BatchSpanProcessor(scheduledDelayMillis、maxQueueSize、maxExportBatchSize 等);批处理、重试、丢失策略均为 SDK 的文档化行为。 |
| maxAttributeChars | 每个 span 属性的序列化 payload 上限(默认 32768);超长部分以 …[clipped] 标记裁剪,canonical 会话日志保留完整字节。 |
| shutdownTimeoutMillis | 插件持有的 SDK shutdown 排水外层截止时间(默认 3000)。 |
错误配置在插件加载时即失败:exporter URL 缺失/畸形/非 http(s)、凭据缺失、双重鉴权歧义、非正的 maxExportBatchSize(SDK 会在 shutdown 时挂死)、非法的 correlation/content/metadata/health、启用 Score 却没有合法 URL/队列/超时、未知 mode,全部在构造任何传输之前抛出。
投递状态
LangfuseSessionTelemetryBackend.status() 同步返回脱离内部状态的快照:整体与分通道状态、trace 批次/span 成功失败数、连续失败数、最近时间、已清洗的最近错误,以及 Score 的 queued/delivered/dropped/skipped/failed 计数。状态包括 disabled、starting、healthy、degraded、stopped;Score 始终是独立通道。OTel SDK 不公开 BatchSpanProcessor 队列深度,因此 traces.queuedBySdk 明确为 unknown。首次失败、限频后的持续失败和恢复也会写日志,且不会带 Authorization 或 Langfuse key。
在标准 Harness 交互 profile 中,可以直接在对话界面查看同一份快照:
/langfuse status
/langfuse status --json
第一种格式适合人工阅读;--json 返回稳定 envelope,包含插件版本、会话分享策略和完整的 status() 快照。该命令只读本地状态:不会访问 Langfuse、重试投递、检查凭据或强制执行 SDK flush。只要 profile 组合了 Harness commands 服务(包括标准 web profile)就会注册该命令;未组合这一可选服务的纯 telemetry/headless context 仍能正常加载后端,只是不注册命令。starting 表示尚无 trace 导出批次完成,并不表示命令正在探测 endpoint。即使处于 DISABLED 模式,只要命令服务存在也会注册,因此可以用 /langfuse status 确认当前没有分享任何数据。
与嵌入宿主关联
把 dsh 运行时嵌入自身、且已向同一 Langfuse 项目发送自有 trace 的宿主应用,可以操控本插件的身份标识,让两套视图归入同一个 Langfuse user/session——宿主通常在 spawn 运行时进程时以环境变量注入自己的 id:
config:
correlation:
userId: !!js process.env.HOST_USER_ID
sessionId: !!js process.env.HOST_SESSION_ID
- 解析出的 langfuse.session.id/langfuse.user.id 会盖在每个导出 span 上——turn、generation、tool、compaction——因为 Langfuse v4 的查询模型按 observation 而非仅按 trace 过滤与聚合(属性传播合约)。
- sessionId 默认取 dsh session id;原始 dsh session id 始终以 dsh.session.id 留在每个逻辑根上——这是回查 $DSH_HOME/sessions 本地日志的指针。
- 按轮动态覆盖:turn/start record 上携带的 langfuse.user.id/langfuse.session.id 属性覆盖该轮的静态配置——部署方通过 session-telemetry/record waterfall listener 注入。快照在 turn/start 时锁定;之后 record 上的身份属性一律忽略。优先级:record 属性 > correlation 配置 > dsh session id。
- 动态映射必须可从 dsh session id 确定性重建,且至少存活到该会话不再可能触发 FEEDBACK_ONLY 重放为止——否则重放出的树会带上与实时捕获不同的身份。
- 静态 correlation 值不经过脱敏 waterfall:waterfall 只变换 record,而这些值从不途经 record。
- 投递语义不变:correlation 是身份而非去重——重复仍然可能(见决策 5)。
Langfuse 中会看到什么
| dsh 会话事件 | Langfuse 概念 |
|---|---|
| session(session.id) | session(每个导出的 observation/span 都带 langfuse.session.id) |
| turn/start / turn/end | trace 根 observation(root span;错误结束原因置 span 状态为 ERROR) |
| step/start / step/end + request/header + request/context + assistant/message | generation —— 模型、provider、安全的请求参数/context window、输出、规范的 gen_ai.usage. token(input/output/cache-read/cache-creation/reasoning);最新一条 assistant message 同时成为根 observation 的整体输出。中断输出会保留部分内容,并在两层 observation 标记 dsh.assistant.interrupted=true,但不会把中断误判为错误 |
| llm/retry / llm/retry-started | 现有 generation 上的结构化 scheduled/started event,包含 retry id/attempt/policy/delay 与裁剪后的失败详情;当前 Harness 事件合约没有逐 attempt usage 生命周期,因此不伪造 generation |
| assistant/message.stream | 从已提交消息的原始流时间提取首 token,写入 langfuse.observation.completion_start_time |
| assistant/attempt | 在 step generation 上记录有限的 attempt 元数据;已知 usage 仅累计一次,并保留最终结果 |
| system/message | 仅记录事件类型和序号,不导出系统提示词正文 |
| tool/call + tool/result | tool span(参数为 input,完整 result content 数组为 output,结构化 error name/code/outcome,isError → 状态 ERROR;私有 meta 除非 allowlist 明确允许,否则省略) |
| approval/asked + approval/decided | 可计时的 approval 内部 span;callId 能解析时挂在对应 tool 下,否则挂在当前 generation/turn;未闭合审批强制以 ERROR 收尾 |
| user/message | 按 content.turnInputMode 聚合为根 observation input;同时保留已弃用的 trace input,以兼容旧版 evaluator |
| session/title / subagent/descriptor / agent-preset/selected | 会话语义状态,用于当前/后续 observation 的 langfuse.trace.name 与安全浏览 metadata,不改变稳定 span name 或 ID |
| session/end-seed | 在 seed 边界关闭继承但缺少配对 end 的 compaction,并标记 incomplete/ERROR |
| feedback/record | 把 waterfall 后的文本写为会话级 dsh_user_feedback TEXT Score,保留分类;无文本反馈仍可触发回放,但不生成 TEXT Score |
| feedback/message-put / feedback/message-delete | 按所属 session 和 message 派生稳定 ID,导出会话级 dsh_message_feedback CATEGORICAL 评价(positive / negative),修改与删除沿用相同 ID |
| fork child session | 独立的 child turn trace,并带可查询的 parent/seed metadata;进程内仍保留父 turn context 时附加指向它的 OTel Link |
| agent-error ops 记录 | 开放 turn 上的 agent-error span event + 状态 ERROR |
| compaction/start + compaction/summary + compaction/end | 一个覆盖完整压缩事务的 generation;能找到所属 turn 时作为其子节点,否则成为稳定的独立 trace;包含 provider/model/usage 与被遮蔽范围、事件数、token 数统计 |
| compaction/prune | 带裁剪范围、事件数和 token 数统计的时间点 span event |
| 其他所有事件类型(todo、plan、hooks、插件事件) | 开放 turn 上的时间点 span event |
Token 计量遵循 OpenTelemetry GenAI 的 inclusive-total 契约。DSH 报告的是互斥输入 buckets(inputTokens 仅包含未缓存输入),因此导出的 gen_ai.usage.input_tokens 会重建为 inputTokens + cacheReadTokens + cacheWriteTokens;cache read/write 与 reasoning 继续作为规范的明细属性。Langfuse 随后只需执行一次归一化,即可得到互斥 usage buckets。
架构决策
1. 实现 telemetry seam 后端,而非在 agent-loop 或 LLM 层埋点
Harness 的规则是 model-visible ⟺ logged:所有进入模型请求的内容都可以从 canonical 会话日志重建,且新行为以插件形式落在文档化扩展点上,绝不改 agent-loop。telemetry seam(@deepseek-ai/dsh-session-telemetry)正是为"把会话记录交给上报 SDK"而建的扩展点。实现它的 SessionTelemetryBackend,免费且保证一致地获得:
- 捕获所有模型可见内容 —— 包括本包从未听说过的 subagent、workflow、compaction 和插件事件;这里指采集完整性,不代表导出每个 body 字段;
- session-telemetry/record 脱敏 waterfall(部署自装的清洗规则作用于导出副本;canonical 日志永不改写);
- FEEDBACK_ONLY 同意语义(用户记录反馈前不出境任何数据,且只有已提交的 canonical 事件才算同意);
- handoff cursor、adoption 扫描和 teardown 排水。
直接在 LLM adapter 或 agent loop 埋点意味着重复实现上述全部、与日志漂移,并在 loop 变更时立即破裂。
2. 用原生 OTel traces SDK,而非 Langfuse SDK —— 根源是信号类型不匹配
官方 session-telemetry-otel 后端喂不了 Langfuse:它导出 OTLP logs,而 Langfuse 的 OTLP 端点(/api/public/otel)只接受 traces,走 OTLP/HTTP(JSON 或 protobuf;不支持 gRPC),Basic 鉴权。这个不匹配——而不是缺一个 URL——正是本插件存在的原因。
Trace 管线使用原生 OTel traces SDK(BasicTracerProvider → BatchSpanProcessor → OTLPTraceExporter),与官方后端同一 SDK 家族、同一配置面;属性遵循 OTel GenAI 语义约定加 Langfuse 文档化的 langfuse. 属性映射。Feedback Score 使用小型原生 HTTP transport,而不再初始化第二套 tracing SDK,因此可以复用异步/自定义鉴权合约,并隔离 trace 与 Score 的故障。将来可在不改变 telemetry seam 或公开配置的前提下,把该内部 transport 换成 Langfuse SDK。
3. 折叠投影 —— 因为 seam 交来扁平流,而 Langfuse 需要树
seam 的记录与会话日志事件一一对应;Langfuse 需要 trace → observation 层级。SessionSpanFolder 是按 (session.id, turn, step, compactionId) 键控的状态机,把记录折叠进开放的 OTel span。契约关键的选择:
- 时间戳取 canonical 记录或内嵌 stream 的原始时间,绝不取回放时的墙钟,因此实时捕获与 FEEDBACK_ONLY 的 canonical 日志重放产出完全相同的树(span 起止时间显式指定 —— OTel API 支持历史时间戳)。
- V3 capture 与 canonical 事件一一对应。原始 delta 保存在 assistant/message.stream 或 assistant/attempt.stream 中;插件读取时间、结束状态和 usage,不导出完整原始流。completion_start_time 对应已提交消息,失败 attempt 的首 token 时间单独保留为事件元数据。空或被脱敏的流不补造时间。
- 边界及工具事件使用 seam 映射的 severity。模型 attempt 的结果从内嵌 finish 记录读取,只有最后一次结算决定 generation 是否失败,成功重试不会保留中间失败状态。
- tool span 是其 step 的 generation span 的子节点:harness 定义 step 为一次模型请求加上它调用的工具 —— tool/call 与 tool/result 都落在 step 边界之内 —— 因此 generation span 在时间上包含它的工具执行。step 已不再开放的调用(崩溃窗口重放)回退挂到 turn span。
- 整轮 input/output 按 Langfuse v4 合约放在根 observation 上:user/message 提供 input;每条完成的 assistant message 覆盖 output,因此 turn 结束时保留最后一条回复。已弃用的 langfuse.trace.input/output 别名仅用于兼容旧版 trace-level evaluator。
- 未知事件类型落为开放 turn 上的 span event —— 事件词汇表是 merge-extensible 的,丢弃未知类型会悄悄稀释时间线。
- Compaction 是从 compaction/start 到 compaction/end 的单个事务 Generation。它的时长有意包含 provider 调用外围的编排时间,不标成纯模型延迟。compaction/summary 用压缩摘要、provider/model/usage 与聚合后的 shadow 统计补全 span;provider rawOutput 和完整 shadowedSeqs 列表永不导出。与其配对的替换型 user/message(source.plugin=compact)仍是模型可见上下文,但不会覆盖 turn 的真人输入。所属 turn 缺失时生成稳定的独立 trace;生命周期记录缺失或畸形时降级为时间点事件或 ERROR span,不伪造时长。
- 强制收尾扫描在三处关闭仍开放的 span(标记 dsh.force_ended):新 turn/start 到来而前一个 turn 未闭合、会话的 ops shutdown 记录、后端 shutdown —— teardown 绝不把已开始的 span 遗弃在 SDK 队列里。
4. 稳定身份、feedback Score 与 fork 血缘
- 分别对 (dsh session id, turn) 与 (dsh session id, compaction id) 做带版本的 SHA-256 派生,使实时导出与 FEEDBACK_ONLY 重放得到稳定的 32 位十六进制 Trace ID。合法的 W3C traceparent 仍优先用于分布式追踪;确定性 ID 保留为可查询 metadata。
- 会话反馈成为 dsh_user_feedback TEXT Score,消息评价成为携带 dshMessageId 元数据的 dsh_message_feedback CATEGORICAL Score。两者共用有界单 worker 队列;修改与删除排在先前重试之后。评价关联到解析出的 Langfuse session,不伪造 observation。备注截断至 500 字符,保留分类。
- 每个 child turn 都带直接父 session、seed boundary 和可解析的父 Trace ID metadata。若有界进程内 registry 仍保存已完成父 turn 的根 SpanContext,child root 还会携带一个 OTel Link。父 context 缺失、淘汰或跨进程时降级为 metadata 和 dsh.lineage.linked=false,绝不伪造 context。
5. 投递语义:at-most-once handoff,可能重复
采集 cursor 标记的是已交接而非已送达;崩溃时留在 SDK 批处理队列里的数据会丢失;无 cursor 的重新收养(热重载)可能重发前缀,产生重复 span。接收端以 langfuse.session.id + dsh.turn + dsh.event.seq 关联。持久化 outbox 有意不做,与 seam 自身的立场一致。
6. 什么数据离开本机
上传模式下,span 属性携带用户与助手消息内容、工具参数与结果、compaction 摘要与聚合 shadow 统计、模型/用量元数据,以 session-telemetry/record waterfall 的返回值为准。Compaction provider rawOutput 和完整 shadowedSeqs 列表会被明确排除。本插件不带任何脱敏规则;导出跨越信任边界的部署需自行挂载 waterfall listener。插件不会主动添加 provider 凭据字段,但任意用户或工具正文仍需部署侧脱敏。序列化 payload 每属性按 maxAttributeChars 裁剪(默认 32768);canonical 日志保留完整字节。
Model Experience
无。本插件仅通过 telemetry seam 观察会话流并把折叠出的 span 交给 OTel SDK,从不向模型请求贡献任何内容。
KV Cache 影响
无。本插件既不组装也不发送 provider 请求。
测试
npm run typecheck && npm run typecheck:tests # 源码及全部测试夹具类型检查
npm test # 单元:status 命令 + 折叠投影 + 配置 fail-loud 路径
npm run build && npm run test:e2e # REAL composition:经 Loader 启动真实 dsh 应用
(mock 模型 + 真实 bash 往返),断言 mock Langfuse
collector 在 wire 上实际收到的 OTLP payload
npm run test:e2e:cloud # opt-in 真实 Langfuse 往返;从 .env 加载凭据
npm run test:package # npm pack + 空 consumer 安装/import + bundle 组合
e2e 沿用官方仓库的 REAL-composition 模式(@deepseek-ai/dsh-app-boot + @deepseek-ai/dsh-loader-smoke):fixture cordis.yml 加载构建产物 lib/index.js —— 与部署加载的是同一个文件 —— 断言针对 wire(包括 retry payload、approval/tool-error metadata、完整独立 compaction 与 seed-boundary orphan compaction),而非内部实现。
status 命令测试覆盖人工/JSON 格式、严格参数处理、Harness 命令注册、recordInput: false、disabled 模式诊断,以及缺少可选命令服务的 headless 组合。另一个本地 e2e 使用真实 OTLPTraceExporter 与 BatchSpanProcessor,依次触发 HTTP 503 和 200,并验证公开 backend status 从 degraded 恢复为 healthy;backend 单元测试还会独立锁定 observer 到 status() 的接线。
npm run test:e2e:cloud 会加载 gitignored 的 .env,并且只运行 opt-in 的 Langfuse Cloud 往返测试。先把 .env.example 复制为 .env,再填入 LANGFUSE_PUBLIC_KEY、LANGFUSE_SECRET_KEY 和匹配区域的 LANGFUSE_HOST。测试会通过 v4 Observations API 校验根 input/output、usage、逐 observation 关联、parent/child metadata、独立 compaction 的身份/摘要/usage、approval outcome 与结构化 tool error,并通过 Scores API 回读 feedback;它还会验证 retry 生命周期不会生成重复 generation。retry event payload 本身由本地 raw-OTLP wire 测试锁定,因为 Observations API 返回 observation,但不返回其内嵌的 OTel span event。通用的 npm run test:e2e 在这些变量已导出时仍会执行 Cloud case,否则自行跳过。设置 LANGFUSE_REQUIRE_TOTAL_COST=1 后,还会要求测试夹具中 deepseek-v4-flash step generation 的 totalCost 为有限正数;LANGFUSE_E2E_COST_MODEL 可选择采用相同价格的隔离测试别名。这是对测试项目 Langfuse 模型计价配置的 opt-in 验证,插件本身不会硬编码价格。
冷反馈 E2E 使用真实 JSONL persistence、message-feedback 服务及构建后的插件,验证历史回放、HTTP 上的评价创建/修改/删除顺序、重复抑制和写句柄释放。单元回归额外覆盖空/仅分类反馈、恢复及 fork、伪造事件、waterfall 脱敏、失败/重试/取消 attempt 和 V3 Score 身份。Cloud E2E 还会读回 CATEGORICAL 消息评价的创建与修改,并验证相同稳定 Score ID 的删除。
版本兼容
DeepSeek Harness 处于 developer preview,无兼容承诺;本插件精确锁定 @deepseek-ai/dsh- 版本。
| dsh-plugin-langfuse | @deepseek-ai/dsh- |
|---|---|
| 0.1.x | 0.1.0-rc.6 |
| 0.2.x | 0.1.0-rc.6 |
| 0.3.x | 0.1.0-rc.7 |
| 0.4.x | 0.1.0-rc.8 |
| 0.5.0 | 0.1.0-rc.8 |
| 0.5.1 | 0.1.1-rc.1 |
| 0.5.2 | 0.1.2-rc.1 |
| 0.6.0 | 0.1.5-rc.1 |
独立的 Upstream compatibility canary 工作流会把所有 @deepseek-ai/ 依赖解析到最新发布版本。Pull request 与 main push 只做预警;每周定时和手动触发严格失败,并依次运行源码及测试类型检查、单测、构建、REAL-composition e2e 与 package smoke。失败运行会保留解析后的 manifest 和 lockfile,便于复现。
从 0.5.2 升级
Harness 与插件需使用匹配版本;0.6.0 面向 @deepseek-ai/dsh- 0.1.5-rc.1。通过 dsh plugin --profile web add dsh-plugin-langfuse@0.6.0 更新 bundle,带上 Langfuse 环境变量重启 Harness,再检查 dsh --profile web --dump-config 和 /langfuse status --json。源码用户先构建匹配的 Harness 版本,再以 pnpm dsh … 执行相同命令。Harness 0.1.2-rc.1 应继续使用插件 0.5.2。
Harness 负责迁移 Session V3 并保留旧代日志;旧版 runtime 无法读取升级后的日志。插件仅消费当前 canonical 事件,不改写存储日志。turn/compaction Trace ID 算法保持原样。会话 TEXT Score ID 现在包含来源格式版本,避免迁移重排 event seq 后发生身份冲突;以前导出的反馈在迁移回放后可能出现为另一个 Score。旧的双参数 createDshFeedbackScoreId API 保留原固定向量。
FEEDBACK_ONLY 回放恢复历史直到本次新的 canonical 反馈,包括仅分类或空的会话反馈,以及消息评价变更。冷会话评价通过 feedback/committed 处理:复制借用快照后仅排队导出,不再次打开写句柄,也不等待网络。fork 继承事件不会被导出为子会话自己的 observations 或 Scores,继承或外部会话反馈也不能授权子会话采集。FULL 跟随实时事件并处理冷反馈,但不会自动补传所有恢复日志。DISABLED 不构建 exporter。
有界的 1,024 个 session/format 交接游标用于抑制后端单次生命周期内的重复冷快照。它不持久化,只代表已交接而非已送达;淘汰、重启、HMR 或迁移后可能重放数据。消息评价 upsert/delete 以所属 session/message 派生稳定 ID。不提供持久化 outbox 或跨进程 exactly-once 保证。
Generation 时长仍覆盖整个 step,包含重试等待与工具执行。失败 attempt 与已提交消息中的已知 usage 各累计一次;消息顶层 usage 优先于内嵌流中的副本。中间失败不会把后续成功的 generation 标成 ERROR;最终模型错误或无消息结算标为 ERROR,用户取消以 aborted 记录并保留部分输出,不把正常停止视为服务故障。系统提示词正文和原始流不进入导出属性;turnInputMode: none 仅省略根 input,不会关闭助手或工具输出。
已知限制与延后工作
- 消息评价为会话级 Score,在元数据中保留原始 message id;尚未关联到单个 Langfuse observation,不伪造 observation id。
- 不保证 UI 渲染 OTel Link:parent/seed metadata 是稳定且可通过 API 查询的 lineage 合约;Langfuse 未必把 Link 显示成可点击边。
- 无持久化投递(决策 5):OTel batch 与 Score 队列都在内存中,进程崩溃可能丢失已接收但未 flush 的数据。
- 每个 context 只能有一个后端:同时运行 Langfuse 和官方 OTLP-logs 后端需要上游 seam 演进出 multi-sink。
- 辅助 LLM 调用尚未映射为 generation:title/search 请求事件缺少完整配对的 completion/failure/usage 生命周期,本插件不会伪造 observation。
- 通用轮次外事件仍仅留在日志:title/preset/descriptor 会保留为语义状态,但插件不会创建长寿命 session trace,也不会为任意点事件逐条创建 trace。
许可证
MIT扫码进群