Langfuse 的核心抽象只有一个:Trace。可观测是「看 Trace」,评估是「给 Trace 打 Score」,Prompt 管理是「让生成 Trace 的那段提示词可版本化」。三大能力不是三个独立产品,而是围绕同一份数据的三种视角——这正是它和「日志工具 + 评估工具 + 配置中心」拼盘方案的本质区别。
它到底解决什么问题
把一个 demo 级 LLM 应用推向生产时,你会连续撞上三堵墙:链路是黑盒,效果没法量化,成本不透明。Langfuse 的设计就是逐个拆掉这三堵墙。
黑盒链路
- 典型表现
- 一个 RAG / Agent 请求内部有检索、重排、多次 LLM 调用,线上偶发变慢或答非所问,但你只看得到最终输出,定位不到是哪一步、哪个 prompt、哪段输入出了问题。
- 判断标准
- 能在 UI 里点开任意一条线上请求,逐层展开它的 span 与 generation,看到每一步的输入/输出/耗时/模型。
- 解决方向
- 用 Tracing:@observe 装饰器或 langfuse.openai 自动把每次调用记成嵌套的 span / generation。
效果不可量化
- 典型表现
- 改了 prompt 或从 gpt-4o 换到别的模型,凭感觉觉得「好像好了一点」,但拿不出数字,也无法回归验证。
- 判断标准
- 同一批输入能跑出可对比的 score(如准确率、相关性、用户点赞率),并能在 Dataset 实验里横向对比两个版本。
- 解决方向
- 用 Scores + Datasets:手动打分 / LLM-as-judge / 用户反馈三种来源统一成 score,挂在 trace 上。
成本不透明
- 典型表现
- 月底拿到 provider 账单是一个总数,分不清是哪个功能、哪个用户、哪条链路烧掉的 token。
- 判断标准
- 能按 user / session / 功能维度看 token 与花费分布,定位高成本路径。
- 解决方向
- Langfuse 对每个 generation 自动按模型定价计算 token 与 cost,在 Dashboard 聚合。
架构全景:从 SDK 到 Web UI
Langfuse 是典型的「客户端 SDK + 服务端摄入 + 双存储 + Web UI」架构。关键是要记住:SDK 到服务端是异步、批量、单向的上报,你的应用主链路不等待 Langfuse 的网络往返。
- Instrumentation 层(SDK):Python SDK(@observe 装饰器 / langfuse.openai 直替 / LangChain CallbackHandler)、JS/TS SDK。它们在你应用进程里把调用记录成 trace/span/generation,缓存进本地队列。
- Ingestion API:SDK 后台线程把队列里的事件 批量、异步 POST 到
/api/public/ingestion,失败自动重试,应用退出时 flush。 - 队列与 Worker:服务端把摄入事件入队(自托管用 Redis),由 Worker 异步消费、解析、计算 token 与 cost。
- 存储双写:分析型大表(traces / observations / scores)落 ClickHouse(列式,扛聚合查询);项目、用户、API key、prompt 等元数据落 Postgres。对象(如大 payload)走 S3 兼容存储。
- Web UI:Dashboard(用量/成本/延迟指标)、Tracing(链路下钻)、Prompts(版本与 label)、Evaluations(datasets 与 scores)从存储读取并展示。
一条请求如何变成一条 Trace(数据流)
[你的应用进程]
│ ① 调用被 SDK 拦截 / 装饰
▼
┌──────────────────────────┐
│ Langfuse SDK │
│ trace → span → generation│ ② 记录输入/输出/耗时/模型
│ 写入本地内存队列 │
└──────────────┬───────────┘
│ ③ 后台线程:批量 + 异步 + 重试(不阻塞主链路)
▼ POST /api/public/ingestion
┌──────────────────────────┐
│ Langfuse Server │
│ Ingestion API → 队列 │ ④ 入队
│ Worker 消费 │ ⑤ 解析 + 按模型定价算 token/cost
└───────┬───────────┬──────┘
│ │
▼ ▼
ClickHouse Postgres ⑥ 双写:分析大表 / 元数据
(traces, (projects,
observations, prompts,
scores) users, keys)
│ │
└─────┬─────┘
▼
┌───────────────┐
│ Web UI │ ⑦ Dashboard / Tracing / Prompts / Evals
└───────────────┘
30 秒感受:一条 Trace 是怎么产生的
下面是能直接跑通的最小示例。把它当作「架构图里 ①②③ 步」的代码版——你会在第 4 章 quickstart-sdk 里展开细节,这里先建立体感。
# 安装 Python SDK(v3,基于 OpenTelemetry);带 openai 集成
pip install "langfuse>=3.0.0" openai
# 三个环境变量来自 Langfuse 项目设置页(云版 cloud.langfuse.com 或自托管实例)
export LANGFUSE_PUBLIC_KEY="pk-lf-xxxxxxxx"
export LANGFUSE_SECRET_KEY="sk-lf-xxxxxxxx"
export LANGFUSE_HOST="https://cloud.langfuse.com" # 自托管换成你的地址
export OPENAI_API_KEY="sk-..."
# 方式 A:用 @observe 装饰器,把整段函数变成一个 trace,内嵌一个 generation
from langfuse import observe, get_client
# 关键:从 langfuse.openai 导入,而不是直接 import openai
# 这个 drop-in 替换会自动把每次 chat.completions 记成 generation(含 token/cost)
from langfuse.openai import openai
@observe() # 装饰器把这个函数包成一个 trace 的根节点
def answer_question(question: str) -> str:
completion = openai.chat.completions.create(
model="gpt-4o-mini",
messages=[
{"role": "system", "content": "你是一个简洁的助手。"},
{"role": "user", "content": question},
],
# name 会成为这条 generation 在 UI 里的显示名,便于检索
name="answer-llm-call",
)
return completion.choices[0].message.content
if __name__ == "__main__":
print(answer_question("用一句话解释什么是可观测性"))
# SDK 默认异步批量上报;脚本/短任务退出前必须 flush,否则事件可能没发出去
langfuse = get_client()
langfuse.flush() # 阻塞直到本地队列清空(长驻服务里通常无需手动调用)
# 运行后到 Web UI 的 Tracing 页,就能看到一条名为 answer_question 的 trace
Langfuse vs LangSmith vs Phoenix:怎么选
三者都做 LLM 可观测,但定位不同。一句话区分:Langfuse 是开源 + 可自托管 + 框架中立的全平台;LangSmith 是 LangChain 官方的托管闭源方案,和 LangChain/LangGraph 生态贴得最紧;Phoenix(Arize)是开源、强调本地探索与 OpenTelemetry 原生的可观测/评估工具。
| 维度 | Langfuse | LangSmith | Phoenix (Arize) |
|---|---|---|---|
| 开源 / 自托管 | ✅ 开源(MIT 核心),可完整 Docker 自托管 | ❌ 闭源,主要托管 SaaS | ✅ 开源,可本地/自托管 |
| 能力范围 | 可观测 + 评估 + Prompt 管理 一体 | 可观测 + 评估 + Prompt + 数据集,生态最全 | 可观测 + 评估为主,Prompt 管理较轻 |
| 框架绑定 | 中立:原生 SDK + 集成 LangChain/LlamaIndex 等 | 与 LangChain/LangGraph 深度耦合 | 中立,OpenTelemetry 原生 |
| 数据主权 | 自托管时数据完全在自己手里 | 数据在 LangChain 托管(可申请自托管,企业版) | 自托管时数据在自己手里 |
| 典型选择理由 | 要开源/数据合规/不想锁定生态 | 已重度使用 LangChain,要官方一体化 | 偏研究/调试,想要 OTel 原生与轻量本地 |
✓推荐做法
- 把 Tracing 当成 LLM 应用的「最低门槛基建」,在接入第一天就装上,而不是等出了线上事故再补
- 短脚本结尾 flush,长驻服务依赖 SDK 后台自动上报
- 选型时先确认是否有「数据必须自托管/可审计」的硬约束,这一条往往直接决定 Langfuse vs LangSmith
✗不推荐
- 不要把 Langfuse 当成同步日志库——它是异步上报,别在主链路里阻塞等它返回
- 不要因为现在只用 OpenAI 就觉得「框架中立」无所谓,半年后接入新框架时锁定的代价很高
- 不要在没有 Score 和 Dataset 的情况下凭感觉评价 prompt 改动效果
⚠常见误区
- 误以为 HTTP 200 就代表数据已落库:摄入是异步队列,真正可见要等 Worker 消费完
- 自托管时只起了 Postgres 忘了 ClickHouse/Redis,导致 Tracing 页空白或报错
- 把 LANGFUSE_HOST 留成默认云地址,结果数据上报到了 cloud.langfuse.com 而不是自己的实例
接入后能在 Web UI 看到第一条带 token/cost 的 trace,且确认数据落在你预期的(云 or 自托管)实例上。
你无法改进你看不见、量不准、算不清成本的系统。Langfuse 做的就是先让 LLM 应用变得「可见、可量、可算」。
— 本 Wiki 编者按
下一章进入动手环节:搞清楚 云版 vs Docker 自托管 各自的取舍,并把一个可用的 Langfuse 实例跑起来——这是后续所有 SDK 接入和实验的前提。