Agent 从 Demo 走向生产,真正卡人的往往不是模型选得不好,而是没人说得清线上一次回答到底经历了什么:走了几轮工具调用?用的是哪个版本的提示词?token 烧在哪一步?Langfuse 就是解决这类问题的开源可观测与评估平台——它把链路追踪(Trace)、提示词版本、数据集评估和成本延迟分析放进同一个控制台。本篇带你从零接上第一条 trace,再走到「观测 → 评估 → 回归」的闭环。
📊 本教程适合:准备把 Agent 推上生产、需要排障与回归能力的开发者/小团队。可选两种部署:Langfuse Cloud 免费额度,或本地 Docker 自托管。不需要 GPU,它只做观测,不做模型推理。
先对齐:Langfuse 到底解决什么
| 能力 | 一句话说明 |
|---|---|
| Trace 链路追踪 | 把一次请求里的模型调用、工具调用、子步骤串成一棵树,看清每一步 |
| Prompt 版本管理 | 提示词改动可追踪,出问题能定位到「哪一版」 |
| 数据集与评估 | 把真实案例沉淀成回归集,批量跑分,防止改一处坏一处 |
| 成本与延迟 | 按调用维度看 token 与耗时,找出最贵/最慢的那一步 |
它的定位是应用层的观测底座:你不需要把模型注册进去,只要在调用前后上报数据即可。
Step 1:选部署方式
1 云免费额度 or 本地自托管
# 方式 A:Langfuse Cloud(零运维,有免费额度)
# 注册后建项目,直接拿 key,跳到 Step 2
# 方式 B:Docker 自托管(数据留在自己机器上)
git clone https://github.com/langfuse/langfuse.git
cd langfuse
docker compose up -d
# 默认 Web 控制台在 http://localhost:3000
# 底层组件:PostgreSQL(事件)、ClickHouse(分析)、Redis(队列)、MinIO(对象存储)
💡 自托管不需要 GPU,主要资源消耗在数据库与 Web 服务上。个人开发者最轻量的用法就是 Docker 拉起一个实例,业务代码通过 SDK 接入。
Step 2:拿到 Key,配好环境变量
2 三个变量就够
pip install langfuse
# .env(不要提交到 git)
LANGFUSE_SECRET_KEY=sk-lf-...
LANGFUSE_PUBLIC_KEY=pk-lf-...
LANGFUSE_BASE_URL=https://cloud.langfuse.com
# 其它区域端点(按你的账号区域选择):
# 美国: https://us.cloud.langfuse.com
# 日本: https://jp.cloud.langfuse.com
端点别填错。区域选错会出现「数据上报成功但控制台看不到」的情况。自托管则把 LANGFUSE_BASE_URL 指向你自己的地址(如 http://localhost:3000)。
Step 3:最小接入——先看到第一条 Trace
3 只改一行 import
如果你用的是 OpenAI SDK,Langfuse 提供了一个「即插即用」的封装:把 import 换掉,其余写法不变,调用就会被自动记录。
# 原来是:from openai import OpenAI
# 现在换成 Langfuse 的封装(行为一致,额外记录每次调用)
from langfuse.openai import openai
completion = openai.chat.completions.create(
name="first-trace", # 给这次调用起个名字,便于检索
model="gpt-4o", # 模型名以官方文档为准
messages=[
{"role": "system", "content": "你是一个只输出计算结果的计算器。"},
{"role": "user", "content": "1 + 1 = ?"},
],
metadata={"env": "local"}, # 自定义元数据,方便筛选
)
print(completion.choices[0].message.content)
跑完去控制台看 Trace 列表,应该能看到这一条,点进去能看到输入、输出、模型、token 与耗时。
Step 4:给自定义函数加嵌套 Span
4 把「一次 Agent 运行」串成一棵树
真实 Agent 里除了模型调用,还有检索、工具、后处理。用 @observe() 装饰器可以把它们都挂进同一条链路,形成父子 Span:
from langfuse import Langfuse
from langfuse.decorators import observe, langfuse_context
langfuse = Langfuse() # 读取环境变量里的 key 与端点
@observe(as_type="generation")
def call_llm(messages, model="gpt-4o"):
"""被装饰后,输入/输出/耗时/token 会被自动捕获。"""
resp = openai.chat.completions.create(model=model, messages=messages)
langfuse_context.update_current_observation(
model=model,
usage={
"input": resp.usage.prompt_tokens,
"output": resp.usage.completion_tokens,
},
)
return resp.choices[0].message.content
@observe()
def run_agent(user_input: str):
"""顶层函数:它下面的所有 @observe 调用都会成为子 Span。"""
langfuse_context.update_current_trace(
user_id="user-123",
session_id="session-abc", # 同一会话的多轮会归到一起
tags=["production"],
)
answer = call_llm([{"role": "user", "content": user_input}])
return answer
if __name__ == "__main__":
print(run_agent("用一句话解释什么是 RAG"))
🔎 有了
session_id 和 tags,你就能按「会话」或「环境」筛选排查。生产排障时,最常用的动作就是「找到某个用户的那次会话,逐 Span 看它在哪一步跑偏」。Step 5:Prompt 版本管理 + 数据集回归
5 把调试变成可复用的资产
调试时踩过的坑,应该变成下次的测试用例。Langfuse 的路径是:
- 把提示词放到控制台管理:改一次生成一个版本,线上引用具体版本;出问题时能精确回滚到「上一版」。避免「改了 prompt 但没人记得改了啥」。
- 建数据集:把「信息缺失」「含无关材料」「来源冲突」这几类难例各存几条,作为回归集。
- 批量跑评估:改动前后各跑一遍同一数据集,对比答案质量、token、延迟,用数据决定是否上线。
数据合规提醒:Trace 里会包含真实的用户输入与模型输出,可能含个人信息。上线前务必评估:哪些字段需要脱敏、trace 保留多久、谁能查看。若涉及受监管数据,先确认平台的数据驻留区域与合规选项是否符合你的要求。
Step 6:生产上线检查清单
6 自托管从「能跑」到「能扛」
| 检查项 | 动作 |
|---|---|
| 🔐 默认密钥 | 上线前必须改掉 compose 里的 NEXTAUTH_SECRET、SALT、ENCRYPTION_KEY 与数据库密码 |
| 📉 采样率 | 高流量场景把采样率降到 0.1 左右,控制 trace 量,别把存储打爆 |
| 🗄️ 存储 | 长期运行建议换托管 Postgres / ClickHouse,替代本地数据卷 |
| 🐞 调试出口 | 稳定后移除 OTel Collector 配置里的 debug exporter |
| 🔄 回滚 | 镜像打 tag 留档,回滚只需换 tag 重启 |
🧩 如果你的应用已经在输出 OpenTelemetry span,可以直接走 OTEL 接入,无需改动业务代码;Langfuse 也提供对常见框架(OpenAI、LangChain、Vercel AI SDK、Claude Agent SDK 等)的集成。
常见问题速查
| 现象 | 大概率原因 & 解决 |
|---|---|
| 控制台看不到 trace | 区域端点填错、key 未生效,或程序退出太快没来得及上报(结束前 flush) |
| 多轮对话散成很多条 | 没设 session_id,把它设成同一个值即可归组 |
| Span 没有嵌套关系 | 子函数没有加 @observe(),或不在父函数的调用链里 |
| 成本统计不准 | 自定义模型没上报 usage,或模型定价未配置 |
| 自托管启动失败 | 端口占用或数据卷权限问题,先看容器日志再重试 |
📌 一句话总结:可观测不是「加个日志」,而是让每一次 Agent 运行都可解释、可比较、可回归。先接上 Trace 看清现状,再把难例沉淀成数据集,你就有了把 Agent 推上生产的底气。