私有部署与观测

Langfuse

通过 OpenTelemetry 桥接层,把每次查询的 LLM 调用、工具执行与子 Agent 链路上报到你自己的 Langfuse 实例。未配置密钥时所有追踪函数为 no-op,零开销;上传前对敏感字段、路径与文件内容做自动脱敏。

  • OpenTelemetry
  • 自托管 / 云
  • 自动脱敏

这是什么

Langfuse 是一个开源的 LLM 可观测性平台,用于追踪、监控和调试 AI 应用的请求链路。 千手大师通过 OpenTelemetry (OTel) 桥接层把它接入查询流程,实现三类观测:

  • LLM 调用追踪:记录每次 API 请求的模型、Provider、输入 / 输出与 Token 用量(含缓存读写拆分)。
  • 工具执行追踪:记录每个工具调用的名称、输入、输出、耗时与错误标记。
  • 多 Agent 追踪:主 Agent 与 AgentTool 启动的子 Agent 各自拥有独立 Trace。

启用开关由 isLangfuseEnabled() 判定:同时存在 LANGFUSE_PUBLIC_KEY 与 LANGFUSE_SECRET_KEY 时开启, 否则全部追踪函数直接返回 null 或跳过,等价于零开销。

生命周期在启动流程内完成:src/entrypoints/init.ts 启动时调用 initLangfuse() 创建 LangfuseSpanProcessor 与 BasicTracerProvider,并通过 registerCleanup(shutdownLangfuse) 在进程退出时强制 flush 并关闭。依赖库为 @langfuse/otel、@langfuse/tracing 与 @opentelemetry/sdk-trace-base。

前置条件

  • 一个 Langfuse 实例:可以在自己的 Docker / Kubernetes 上自部署,也可以使用官方 Langfuse Cloud(https://cloud.langfuse.com,提供免费测试额度)。
  • 一对 API 密钥:在实例的 Project Settings → API Keys 页面获取 Public Key(pk-...)与 Secret Key(sk-...)。 两个都要提供,只配置其一会保持 no-op。
  • 服务地址:默认指向 Langfuse Cloud;自部署时需要把 LANGFUSE_BASE_URL 指向你的实例。
  • 网络出口可达:运行环境需能访问所配置的 Langfuse 主机。

安装 / 启用

无需安装任何 CLI 插件,配置环境变量即可启用。

  1. 获取 Public Key 与 Secret Key

    在 Langfuse 实例的 Project Settings → API Keys 页面创建密钥对,记录公钥与密钥。

  2. 设置环境变量

    两个密钥必须同时存在。可在真实 shell 环境中导出,或写入用户级 ~/.claude/settings.json 的 env 字段(受信任源,启动早期注入), 也可写入 ~/.claude.json。

    Terminal
    LANGFUSE_PUBLIC_KEY=pk-xxx \
    LANGFUSE_SECRET_KEY=sk-xxx \
    LANGFUSE_BASE_URL=https://cloud.langfuse.com \
    qsdashi
  3. 验证生效

    启动后会创建 Processor(debug 日志输出 [langfuse] Initialized with LangfuseSpanProcessor), 每轮对话创建 Trace([langfuse] Trace created: ...), 在 Langfuse 面板即可看到对应 Track / Session。

!
不要把密钥放在项目级 settings.json

Langfuse 在启动早期初始化,早于「信任建立后」的完整 settings 环境注入。 放在项目级 .claude/settings.json 的 env 会太晚, 当轮不生效。请使用真实 shell 环境变量、~/.claude/settings.json 的 env(userSettings 属受信任源,早期注入)或 ~/.claude.json。

配置

全部通过环境变量控制,均无对应的 CLI 子命令。

环境变量 默认值 说明
LANGFUSE_PUBLIC_KEY 无(必填) Langfuse 公钥,缺失则不启用
LANGFUSE_SECRET_KEY 无(必填) Langfuse 密钥,缺失则不启用
LANGFUSE_BASE_URL https://cloud.langfuse.com 服务地址,自部署时改为你的实例地址
LANGFUSE_TRACING_ENVIRONMENT development 环境标签,用于面板筛选
LANGFUSE_FLUSH_AT 20 批量发送的 span 数量阈值
LANGFUSE_FLUSH_INTERVAL 10 定时刷新间隔(秒)
LANGFUSE_EXPORT_MODE batched 导出模式:batched(批量)或 immediate(即时)
LANGFUSE_TIMEOUT 5 请求超时(秒)
LANGFUSE_USER_ID 无 指定 Trace 的用户标识,用于面板按用户聚合;解析顺序为 显式 username > LANGFUSE_USER_ID > 账号邮箱 > 设备 ID
i
release 版本号自动注入

Processor 的 release 字段取自构建常量 MACRO.VERSION, 无需手动配置,用于在面板中按版本对比。

追踪层级与脱敏

数据流从一个根 Trace 展开,子节点为 LLM generation 与 tool span:

Trace 结构
Trace (Agent Span)                  <- createTrace() / createSubagentTrace()
  ├── Generation (LLM 调用)         <- recordLLMObservation()
  ├── tools (批量工具 span)          <- createToolBatchSpan() / endToolBatchSpan()
  │     ├── Tool Observation        <- recordToolObservation()
  │     └── Tool Observation
  └── ...

主 Agent 每次 query() 调用(即用户一次对话 turn)创建一个 agent 类型的根 Span,名称为 agent-run 或 agent-run:<querySource>, 元数据含 provider、model、agentType: "main", 并携带 Session ID 以支持按会话聚合。子 Agent 通过 createSubagentTrace() 创建独立 Trace,名称为 agent:<agentType>。

LLM generation 名称按 Provider 映射:

Provider Generation 名称
firstPartyChatAnthropic
bedrockChatBedrockAnthropic
vertexChatVertexAnthropic
foundryChatFoundry
openaiChatOpenAI
geminiChatGoogleGenerativeAI
grokChatXAI

每个工具调用记录为 tool 类型的 Span,名称为工具名(如 FileEditTool、BashTool),元数据含 toolUseId 与 isError;失败时附加 level: ERROR。Trace 结束时若状态为 interrupted 或 error,会分别标记 WARNING / ERROR。

所有上传数据在发送前经 sanitize.ts 处理:

范围 脱敏策略
敏感字段(全局) 匹配 api_key / token / secret / password / credential / auth_header 等关键字的字段值替换为 [REDACTED]
Home 路径 将 /Users/xxx、C:\Users\xxx 等替换为 ~ (同时覆盖 file_path / path / directory)
FileReadTool / FileWriteTool / FileEditTool 内容整体遮蔽,仅保留字符数:[file content redacted, N chars]
BashTool / PowerShellTool 输出截断至 500 字符,超出追加 [truncated]
ConfigTool / MCPTool 输出完全遮蔽
其他工具 原样保留(仍经过全局字段与路径脱敏)

convert.ts 另将内部 Message 类型转换为 Langfuse 期望的 OpenAI 兼容格式: text → text part、thinking → thinking part、 tool_use → tool call、tool_result → tool 消息, 图片与文档退化为 [image] / [document: name] 占位标记。

常用命令与参数

Langfuse 集成没有独立的 CLI 子命令,启用与调参完全依赖环境变量。相关「参数」为追踪 命名的行为约定与关键调用函数:

项 说明
initLangfuse() 启动时调用,创建 Processor 与 Provider;已有实例则直接返回
isLangfuseEnabled() 检查双密钥是否齐备;各追踪函数据此快速短路
flushLangfuse() 强制 flush 当前队列(如 Trace 结束、进程退出前调用)
shutdownLangfuse() flush 后关闭 Processor,经 cleanup 钩子在退出时执行
agent-run[:source] 主 Agent 根 Trace 名称
agent:<agentType> 子 Agent 独立 Trace 名称
tools 并发工具批次的父 span 名称,元数据含 toolNames / toolCount / batchIndex
*
flush 时机

批量模式下(默认 batched),span 达到 LANGFUSE_FLUSH_AT 数量或 经过 LANGFUSE_FLUSH_INTERVAL 秒才发送。每次对话 turn 结束时会主动 flushLangfuse(),进程退出时也会强制 flush,因此不会丢失尾部数据。

实战示例

写入用户级 settings.json 长期生效(受信任源,启动早期注入):

~/.claude/settings.json
{
  "env": {
    "LANGFUSE_PUBLIC_KEY": "pk-xxx",
    "LANGFUSE_SECRET_KEY": "sk-xxx",
    "LANGFUSE_BASE_URL": "https://cloud.langfuse.com",
    "LANGFUSE_TRACING_ENVIRONMENT": "production"
  }
}

即时模式(每个 span 立即发送,适合调试,吞吐略低):

Terminal
LANGFUSE_EXPORT_MODE=immediate qsdashi

自部署 Langfuse(Docker 方式仅供参考,实际部署请对照 Langfuse 官方文档):

Terminal
docker run -d \
  --name langfuse \
  -p 3000:3000 \
  -e DATABASE_URL=postgresql://... \
  langfuse/langfuse:latest

# 随后把 LANGFUSE_BASE_URL 指向该实例
LANGFUSE_BASE_URL=http://localhost:3000 qsdashi
!
「Trace 转数据集」不是 CLI 功能

把一次 Trace 保存为数据集(Dataset)是 Langfuse 平台侧的能力,需要在 Langfuse 界面中操作。千手大师只负责把链路数据上报上去,不提供对应的命令行子命令。

常见问题排错

!
配置了密钥但面板里没有数据

最常见原因是把密钥写进了项目级 .claude/settings.json:该来源的 env 在信任建立后才完全注入,而 Langfuse 已在启动早期初始化,当轮不会生效。 改用真实 shell 环境变量、~/.claude/settings.json 或 ~/.claude.json。

!
只设置了其中一个密钥

isLangfuseEnabled() 要求 LANGFUSE_PUBLIC_KEY 与 LANGFUSE_SECRET_KEY 同时存在。 任一缺失时追踪函数直接短路,日志会提示 No keys configured, running in no-op mode。

!
数据有明显延迟

默认导出模式为 batched,按 LANGFUSE_FLUSH_AT(默认 20 个 span) 或 LANGFUSE_FLUSH_INTERVAL(默认 10 秒)触发。需要实时观看时改为 LANGFUSE_EXPORT_MODE=immediate,或调低刷新阈值与间隔。

!
自部署实例连接失败

确认 LANGFUSE_BASE_URL 指向的地址从运行环境可达,且协议(http / https)与端口正确; 超时由 LANGFUSE_TIMEOUT(默认 5 秒)控制,网络不通时会看到 [langfuse] Init failed 或 Flush error 日志,不影响主流程。

*
担心源码 / 输出外泄

上传前统一经过脱敏:文件读写内容整体遮蔽只留字符数、Shell 输出截断 500 字符、 Config / MCP 输出完全遮蔽、敏感字段替换为 [REDACTED]、Home 路径替换为 ~。 但仍建议自部署实例并限制网络出口,避免把密钥交给不可信服务。