主题
阶段 2:Langfuse 核心概念
预计学习时间:1 天
生活类比主线:Langfuse = 餐厅后厨的「一单到底」监控系统
目录
1. 概念总览
Langfuse 把应用运行数据组织成三层结构:
Session(可选,多轮对话)
└── Trace(一次请求/操作)
└── Observation(请求内的每一步,可嵌套)
└── Observation(子步骤)
└── ...餐厅类比:
| Langfuse 概念 | 餐厅类比 | 说明 |
|---|---|---|
| Session | 一桌客人从进店到离店的整场体验 | 多轮交互的容器 |
| Trace | 一张订单(从下单到出餐) | 单次操作的完整记录 |
| Observation | 做菜的每个步骤(切菜、炒菜、装盘) | 有起止时间的工作单元 |
| Generation | 特别标记的「大师傅炒菜」步骤 | 专指 LLM 调用 |
| Tool | 「去冷库取食材」 | 外部 API / 函数调用 |
2. Trace:一张完整订单
2.1 定义
Trace 代表一次完整的用户请求或业务操作。
例如:
- 用户问 chatbot 一个问题 → 1 条 Trace
- 用户点击「生成周报」→ 1 条 Trace
- Claude Code 里你发一条消息、AI 完整回复 → 1 条 Trace(本仓库的实现方式)
2.2 Trace 包含什么
Trace: "用户问:北京明天天气怎么样?"
├── input: 用户原始问题
├── output: 最终回复给用户的内容
├── user_id: "user_123"(可选)
├── session_id: "sess_abc"(可选)
├── tags: ["weather-bot", "prod"]
├── metadata: {"app_version": "1.2.0"}
├── latency: 总耗时
└── observations: [ ... 所有子步骤 ... ]2.3 生活例子:点外卖
你在 App 里点了一份黄焖鸡米饭,这一单就是一条 Trace:
Trace ID: order_20260605_001
输入:黄焖鸡米饭 × 1,少辣
输出:预计 30 分钟送达
用户:张三
总耗时:28 分钟不管中间经历了「接单 → 备菜 → 炒菜 → 配送」,对外就是一单。
3. Observation:做菜的每一步
3.1 定义
Observation 是 Trace 内部的一个工作步骤,有明确的开始和结束时间,可以嵌套。
Trace: 回答天气问题
│
├── Observation (span): "解析用户意图" 50ms
│
├── Observation (tool): "调用天气 API" 200ms
│ input: {city: "北京", date: "明天"}
│ output: {temp: 28, weather: "晴"}
│
├── Observation (retriever): "检索知识库" 80ms
│
└── Observation (generation): "GPT-4o 生成回复" 1200ms
model: gpt-4o
input: [system prompt + 天气数据]
output: "北京明天晴,28°C..."
usage: {input: 320, output: 45, total: 365}3.2 Observation vs Trace
| Trace | Observation | |
|---|---|---|
| 粒度 | 一整次请求 | 请求内的一步 |
| 数量关系 | 1 个 Trace 含多个 Observation | 多个 Observation 属于 1 个 Trace |
| 类比 | 一整张订单 | 订单里的每个工序 |
3.3 嵌套关系
Observation 可以父子嵌套,形成树状结构:
Agent 决定调用工具
└── Tool: 查数据库
└── Span: SQL 执行就像做黄焖鸡:
主工序:烹饪
├── 子工序:焯水
├── 子工序:炒糖色
└── 子工序:炖煮4. Session:一桌客人的整场饭局
4.1 定义
Session 把多条相关的 Trace 归到同一个会话,适合多轮对话场景。
Session: "sess_abc"(用户张三的聊天)
├── Trace 1: "你好" → "您好,有什么可以帮您?"
├── Trace 2: "查订单" → "您有 2 笔待发货订单..."
└── Trace 3: "第一笔什么时候发?" → "预计明天..."4.2 生活类比
- Trace = 你向服务员提的一个具体问题
- Session = 你在这家餐厅坐了一晚上的所有问答
没有 Session 也能用 Langfuse,但多轮 Agent / Chatbot 强烈建议设置 session_id,否则无法按对话聚合分析。
4.3 什么时候需要 Session
| 场景 | 是否需要 Session |
|---|---|
| 单次问答 API | 可选 |
| 多轮 Chatbot | ✅ 推荐 |
| Claude Code 编程助手 | ✅ 一个 coding session |
| 批量离线任务 | 通常不需要 |
5. Observation 类型详解
Langfuse 支持多种 Observation 类型,方便在 UI 里过滤和分析:
| 类型 | 含义 | 生活类比 | 典型字段 |
|---|---|---|---|
span | 普通工作单元 | 切菜、装盘 | name, input, output, duration |
generation | LLM 调用 | 大师傅炒菜(核心环节) | model, prompt, completion, usage, cost |
tool | 工具/API 调用 | 去冷库取食材 | tool name, input, output |
retriever | 检索/RAG | 查菜谱本 | query, documents |
agent | Agent 决策步骤 | 主厨决定做什么菜 | plan, actions |
chain | 链式编排步骤 | 传菜窗口交接 | pipeline step |
embedding | 向量化调用 | 把食材切标准化 | model, usage |
event | 瞬时事件 | 火警铃响了一下 | name, metadata |
guardrail | 安全护栏 | 食材安检 | check result |
5.1 Generation 为什么特殊
Generation 是 LLM 应用的「主角」。它额外记录:
python
{
"model": "gpt-4o",
"model_parameters": {"temperature": 0.7, "max_tokens": 1024},
"usage_details": {
"input": 500,
"output": 120,
"total": 620
},
"cost_details": {
"input": 0.0025,
"output": 0.0012,
"total": 0.0037
}
}普通 span 不自动算 token 和费用;generation 会。
5.2 类型怎么选
简单原则:
- 调了 LLM →
generation - 调了外部 API / 函数 →
tool - 查了向量库 →
retriever - Agent 在「思考下一步」 →
agent - 其他内部逻辑 →
span
6. 属性传播机制
Trace 上设置的属性会自动传播到其下所有 Observation:
python
trace = langfuse.trace(
name="weather-query",
user_id="user_123", # ← 传播到所有子 observation
session_id="sess_abc", # ← 传播
tags=["prod", "v2"], # ← 传播
metadata={"region": "cn"} # ← 传播
)生活类比:订单上贴了「VIP 客户」「包间 8 号」标签,后厨每个工序的单据上都会自动带上这些标签,不用每道工序手写一遍。
常用属性
| 属性 | 用途 |
|---|---|
user_id | 按用户查问题、算人均成本 |
session_id | 聚合多轮对话 |
tags | 环境、功能模块、实验分组 |
metadata | 任意 JSON,如 app 版本、实验 ID |
release | 发布版本号 |
version | Prompt 版本 |
7. 数据在 UI 里长什么样
打开 Langfuse Cloud → Traces 页面,你会看到:
┌─────────────────────────────────────────────────────────────┐
│ Traces │
├──────────┬──────────┬────────┬────────┬───────────────────┤
│ Time │ Name │ Latency│ Tokens │ User │
├──────────┼──────────┼────────┼────────┼───────────────────┤
│ 10:00:01 │ chat-turn│ 2.3s │ 1,240 │ user_123 │
│ 10:00:15 │ chat-turn│ 1.8s │ 890 │ user_123 │
└──────────┴──────────┴────────┴────────┴───────────────────┘点开一条 Trace,左侧是树状时间线:
chat-turn (2.3s)
├── parse-intent (50ms)
├── tool:weather-api (200ms)
└── generation:gpt-4o (1.8s) ← 点击可看 Prompt/Completion右侧显示选中节点的 input/output、token、metadata。
8. 常见问题 QA
Q1: Trace 和 OpenTelemetry 的 Trace 是一回事吗?
基本是。Langfuse Trace 与 OTel Trace 共享同一个 trace_id。详见 3_langfuse_and_opentelemetry.md。
Q2: 一次 LLM 调用应该建 Trace 还是 Generation?
- 整个用户请求 → 建 Trace
- 其中的 LLM 调用 → 在 Trace 下建 Generation
不要每个 LLM 调用都单独建 Trace,否则无法看完整业务链路。
Q3: Observation 的 input/output 会无限大吗?
SDK 和平台都有截断策略。生产环境应对超大 payload 做截断或脱敏(见 7_best_practices.md)。
Q4: 没有 LLM 的纯业务逻辑需要记录吗?
建议记录关键步骤为 span,方便定位「慢在哪」。但不必逐行代码都 trace。
9. 学习检查
- [ ] 能画出 Session → Trace → Observation 三层结构
- [ ] 能说出 Generation 和 Span 的三个区别
- [ ] 能举例说明何时用
toolvsretriever类型 - [ ] 理解
user_id/session_id的传播行为 - [ ] 已在 Langfuse UI 中点开一条 Trace 并对照本文概念