Langfuse 使用教程(结合 DeerFlow / ontology-harness)
一句话:Langfuse 在 ontology-harness 里同时承担 Trace 可观测 与 提示词版本管理。底层跑图仍是 DeerFlow;Harness 在图根挂 CallbackHandler、固定 trace_id = run_id 的 hex,并用仓库文件 / Langfuse text prompt 装配系统提示词。
相关阅读:ontology-harness 实现原理学习笔记(Run / 中间件 / DeerFlow 边界)。下文不写任何真实密钥;示意形态用 pk-lf-... / sk-lf-...,本地请只放在被 gitignore 的 .env。
1. 这门课学什么
学完你能回答:
- 开 / 关追踪、开 / 关远端提示词各改哪几个环境变量,二者是否互相牵连
- 为什么
setup_tracing()必须早于进程里任何Langfuse(...),否则会拒绝启动 - 一次 Run 的开跑 / 续跑 / 重试 / 继续如何落进同一条 Langfuse trace
- 仓库
agent/prompts/*.md怎样经sync_prompts.py进 Langfuse,运行时怎样回退
分工 DeerFlow 提供跑图、checkpoint、上游 tracing 开关与 metadata 构建器;Harness 额外做 mask / 降噪 / 固定 trace id、提示词 registry(一 agent 一文件)、以及 tag ontology-wj。
2. 环境变量怎么配
来源:仓库根 env.example(复制为 .env;真实值不入库)。与 Langfuse 相关的项如下(值留空或占位,勿粘贴真实密钥):
| 变量 | 本地模板默认 | 作用 |
|---|---|---|
LANGFUSE_TRACING | false | 是否启用追踪(DeerFlow 启用列表含 langfuse 时才初始化客户端) |
LANGFUSE_PUBLIC_KEY | (空) | 公钥,形态如 pk-lf-... |
LANGFUSE_SECRET_KEY | (空) | 密钥,形态如 sk-lf-... |
LANGFUSE_BASE_URL | (空) | Langfuse 实例地址(自建或云) |
LANGFUSE_TRACING_ENVIRONMENT | dev | 上报环境名(用于筛选 / tag) |
LANGFUSE_TIMEOUT | 300 | 超时(秒,见 env 模板) |
LANGFUSE_PROMPT_LOCAL | true | true 时只用仓库 agent/prompts/<agent>.md,不拉远端 |
LANGFUSE_PROMPT_LABEL | (注释掉,缺省 production) | 远端拉取用的 label |
可分开 LANGFUSE_PROMPT_LOCAL 不影响追踪凭据:注释写明「追踪仍用下面的凭据」。本地默认 PROMPT_LOCAL=true,改提示词文件即可热生效(见第 5 节中间件);要看 Trace 再单独开 LANGFUSE_TRACING 并填三项凭据。
.env 覆盖 main.py 在任何 deerflow import 之前 load_dotenv(..., override=True)。交互 shell 里旧的 export LANGFUSE_* 否则会盖住 .env,表现为 tracing「已启用」却 401、trace 静默丢失。
3. 启动顺序铁律
Langfuse 客户端按 public_key 单例:第一次 Langfuse(...) 的参数(含 mask)生效,之后的构造都拿同一实例。
agent/tracing.py的setup_tracing()创建带mask/mask_otel_spans/should_export_span的客户端- 若发现
client._mask is not mask,抛RuntimeError("Langfuse 客户端在 setup_tracing() 之前已被初始化,mask 未生效")—— 拒绝启动 main.pylifespan:在日志配置、读 config、池校验、记忆校验、upgrade_db()之后立刻setup_tracing(),再setup_mcp()与构造各 Service(含可能触发 DeerFlow 记忆抽取的路径)- 进程退出时 lifespan 的
finally调shutdown_tracing()冲刷缓冲
# main.py lifespan(节选,顺序以源码为准)
configure_logging()
app_config = get_app_config()
check_pool_size(app_config)
check_memory_config(app_config)
upgrade_db()
setup_tracing() # 要早于进程里任何 Langfuse(...)
await setup_mcp()
# …再构造 AgentService / MemoryService 等
4. Trace 怎么串起来(DeerFlow 集成)
4.1 核心模块:agent/tracing.py
| 符号 | 做什么 |
|---|---|
setup_tracing() | 按启用列表初始化带 mask 的客户端;未开 tracing 返回 None(@cache) |
tracing_callbacks(trace_id) | 返回挂在图根的 [CallbackHandler(..., trace_context={"trace_id": trace_id})];未启用则 [] |
mask | 上报前把凭据类字段值换成 [REDACTED](键保留);思考等内容原样 |
should_export_span | 没改状态(output 空)的中间件钩子不上报,避免一轮二十来个空节点盖住模型 / 工具 |
mask_otel_spans | 根节点(名字等于 trace 名)标为 agent,其余图节点标为 chain,Agent Graph 只画真正的 agent |
SECRET_KEYS 集合(小写匹配):password、token、access_token、api_key、authorization、database_url、secret_key。
4.2 services/agent.py 的 _prepare
对话 / 续跑不走公开的 DeerFlowClient.stream,而是直接调 agent 的 stream,自行挂 callbacks,并调用 DeerFlow 的 deerflow.tracing.inject_langfuse_metadata 写入 metadata。字段映射(Run / 配置 → Langfuse):
| 来源 | Langfuse / 元数据键 |
|---|---|
thread_id | langfuse_session_id |
user_id | langfuse_user_id |
| assistant_id = agent 名 | langfuse_trace_name(根节点 run_name 同名,供 mask_otel_spans 识别) |
| 环境 / 模型等 | tags(上游会带 env、model 等);Harness 额外加 ontology-wj |
run_id(UUID) | deerflow_trace_id = run_id.hex;即 Run 对象上的 trace_id |
同一 Run 一条 trace 开跑、续跑、重试、继续共用 tracing_callbacks(trace_id(run_id)) —— 拿 Run 的 hex 就能在 Langfuse UI 打开整条执行。DeerFlow 的 run_id 就是 harness 的 run_id(见 AGENTS.md)。
- 思考进 Langfuse:callback 在中间件之前看到模型原始响应(含
reasoning_content/ thinking 块),原样上报便于排查;客户端侧仍丢掉模型层 reasoning 事件 - 模型侧:以
attach_tracing=False创建,避免绕过图根 callback 另报一份
图 A · 追踪挂载时序(lifespan → 跑图 → 上报)。
sequenceDiagram autonumber participant Boot as lifespan participant TR as tracing.py participant AS as agent._prepare participant G as Agent图 participant LF as Langfuse Boot->>TR: setup_tracing(mask…) Note over Boot,TR: 必须早于任何 Langfuse(...) AS->>TR: tracing_callbacks(run_id.hex) AS->>AS: inject_langfuse_metadata AS->>G: stream + CallbackHandler G-->>LF: spans(mask / 降噪后) Note over G,LF: 思考可见;凭据 [REDACTED]
5. 提示词怎么管
5.1 一 agent 一文件
- 仓库路径:
agent/prompts/<agent_name>.md(文件名 =normalize_agent_name后的名字) - Langfuse 上同名 text prompt
- 五个 agent 启动时缺文件或空文件 → 拒绝启动
5.2 build_system_prompt(agent/prompt_assembly.py)
- 先
read_prompt_file(agent_name)读仓库(启动校验与失败回退都靠它) - 若
local_only或LANGFUSE_PROMPT_LOCAL为真 → 直接返回仓库文本 - 否则三项凭据齐全时
get_prompt(name, label=PROMPT_LABEL 或 production, type="text", …) - 拉不到 / 异常 / fallback → 仓库文件
以源码为准 get_prompt(..., cache_ttl_seconds=3, fetch_timeout_seconds=3, max_retries=0, fallback=text)。旧文档 / AGENTS.md 正文曾写「客户端缓存 60 秒」——当前源码是 3 秒,本文按 prompt_assembly.py 书写。
5.3 scripts/sync_prompts.py
# 首次发布(远端不存在则 create,并标 production)
python scripts/sync_prompts.py
# 已有不同版本:默认跳过;要推仓库内容加 --update
python scripts/sync_prompts.py --update
# 本次新建版本升为 production
python scripts/sync_prompts.py --update --promote
- 需要
LANGFUSE_PUBLIC_KEY/SECRET_KEY/BASE_URL - 脚本里
Langfuse(..., tracing_enabled=False),只碰提示词 API - 内容相同则跳过;已有不同版本且未
--update则打印提示并跳过
5.4 运行时装配
SystemPromptMiddleware 每次调模型前重新装配:改本地文件后下一次模型调用即生效(无需为提示词重启;改技能白名单等另当别论)。
图 B · 提示词拉取与回退。
flowchart TD
A["build_system_prompt(agent)"] --> B["读仓库 agent/prompts/name.md"]
B --> C{"local_only 或 PROMPT_LOCAL?"}
C -->|是| D["返回仓库文本"]
C -->|否| E{"三项凭据齐全?"}
E -->|否| D
E -->|是| F["get_prompt label=production 等"]
F -->|成功且非空| G["返回远端文本"]
F -->|失败 / fallback / 空| D
6. 本地开发推荐流程
- 默认本地提示词:保持
LANGFUSE_PROMPT_LOCAL=true,改agent/prompts/*.md即可验证文案 - 需要 Trace 时:
LANGFUSE_TRACING=true,填PUBLIC_KEY/SECRET_KEY/BASE_URL(勿提交),确认 shell 无陈旧export盖住.env - 不需要 Trace 时:保持
LANGFUSE_TRACING=false;提示词仍可只走本地 - 要测远端提示词:
PROMPT_LOCAL=false,凭据齐全;首次或缺版本时跑sync_prompts.py,已有差异加--update,升 production 加--promote - 在 Langfuse UI 查 trace:用 Run 的
trace_id(即run_id的 UUID hex)搜索;也可按 session(thread_id)、user(user_id)、tag(含ontology-wj、环境、模型)筛选 - 关进程:依赖 lifespan 的
shutdown_tracing()冲刷,避免尾部 span 丢在缓冲里
7. 和纯 DeerFlow 上游的关系
上游 DeerFlow 已有
LANGFUSE_TRACING等配置入口CallbackHandler+ metadata builders(session / user / name / tags)- 公开客户端流式路径上的 tracing 挂载
Harness 额外做的
mask脱敏 +should_export_span降噪 +mask_otel_spans校正观测类型- 固定
trace_id = run_id.hex,开跑/续跑/重试/继续同条 - 不走公开
DeerFlowClient.stream,在_prepare自挂 callback +inject_langfuse_metadata - 提示词 registry:仓库文件 + sync +
PROMPT_LOCAL/ label - tag 额外加
ontology-wj - 启动强制:mask 未生效则拒绝启动
8. 怎么读代码 / 相关文件地图
| 要搞清的事 | 打开 |
|---|---|
| 追踪初始化、mask、降噪、callback | agent/tracing.py |
| 挂 callback、metadata、跑图粘合 | services/agent.py 的 _prepare |
| lifespan 里何时 setup / shutdown | main.py |
| 提示词读本地 / 拉远端 / 回退 | agent/prompt_assembly.py |
| 各 agent 提示词正文 | agent/prompts/<agent_name>.md |
| 发布 / 更新 / promote | scripts/sync_prompts.py |
| 环境变量模板 | env.example |
| 约定总表(权威口径) | AGENTS.md(与源码冲突时以源码函数签名与字面量为准) |
建议顺序:env.example → main.py(lifespan)→ agent/tracing.py → services/agent.py(_prepare)→ agent/prompt_assembly.py → scripts/sync_prompts.py。整体产品边界见 ontology-harness 原理笔记。