Langfuse 使用教程(结合 DeerFlow / ontology-harness)

仓库 AgentOS/ontology-harness · 分支 main
依据 commit 3916a98d7a1d3aa6e96a622a69dcd525bfc69a5f(短 SHA 3916a98)· 日期 2026-10-29(Asia/Shanghai)
重点:Trace 挂载与脱敏降噪、提示词本地 / 远端与 sync 发布(以 agent/tracing.py、agent/prompt_assembly.py 等源码为准)

一句话: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_TRACINGfalse是否启用追踪(DeerFlow 启用列表含 langfuse 时才初始化客户端)
LANGFUSE_PUBLIC_KEY(空)公钥,形态如 pk-lf-...
LANGFUSE_SECRET_KEY(空)密钥,形态如 sk-lf-...
LANGFUSE_BASE_URL(空)Langfuse 实例地址(自建或云)
LANGFUSE_TRACING_ENVIRONMENTdev上报环境名(用于筛选 / tag)
LANGFUSE_TIMEOUT300超时(秒,见 env 模板)
LANGFUSE_PROMPT_LOCALtruetrue 时只用仓库 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.py lifespan:在日志配置、读 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_idlangfuse_session_id
user_idlangfuse_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)

  1. 先 read_prompt_file(agent_name) 读仓库(启动校验与失败回退都靠它)
  2. 若 local_only 或 LANGFUSE_PROMPT_LOCAL 为真 → 直接返回仓库文本
  3. 否则三项凭据齐全时 get_prompt(name, label=PROMPT_LABEL 或 production, type="text", …)
  4. 拉不到 / 异常 / 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. 本地开发推荐流程

  1. 默认本地提示词:保持 LANGFUSE_PROMPT_LOCAL=true,改 agent/prompts/*.md 即可验证文案
  2. 需要 Trace 时:LANGFUSE_TRACING=true,填 PUBLIC_KEY / SECRET_KEY / BASE_URL(勿提交),确认 shell 无陈旧 export 盖住 .env
  3. 不需要 Trace 时:保持 LANGFUSE_TRACING=false;提示词仍可只走本地
  4. 要测远端提示词:PROMPT_LOCAL=false,凭据齐全;首次或缺版本时跑 sync_prompts.py,已有差异加 --update,升 production 加 --promote
  5. 在 Langfuse UI 查 trace:用 Run 的 trace_id(即 run_id 的 UUID hex)搜索;也可按 session(thread_id)、user(user_id)、tag(含 ontology-wj、环境、模型)筛选
  6. 关进程:依赖 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、降噪、callbackagent/tracing.py
挂 callback、metadata、跑图粘合services/agent.py 的 _prepare
lifespan 里何时 setup / shutdownmain.py
提示词读本地 / 拉远端 / 回退agent/prompt_assembly.py
各 agent 提示词正文agent/prompts/<agent_name>.md
发布 / 更新 / promotescripts/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 原理笔记。

返回课程首页