导语:在上一章,我们建立了评估体系的“理论大厦”。但理论需要工具来落地。如果说开发 AI 应用像是在造一架精密的飞机,那么没有追踪和可观测性工具,就相当于在没有仪表盘和黑匣子的情况下“盲飞”。这无疑是危险的。Langfuse 就是为我们这架“AI 飞机”量身打造的仪表盘和黑匣子。它是一个开源的、强大的 LLM 应用可观测性平台。本章将是一个纯粹的上手实战教程,我们将从零开始,在本地部署 Langfuse 服务,并学会如何将其集成到我们的 Python 代码中,用它强大的追踪(Tracing)能力,像 X-Ray 一样透视我们 Agent 的每一次“心跳”和“思考”,为后续的评估、调试和优化提供最精细入微的数据支持。
目录
- Langfuse 是什么?为什么选择它?
- 开源 vs. 闭源 (LangSmith)
- 可观测性的三大支柱:日志 (Logs)、追踪 (Traces)、指标 (Metrics)
- Langfuse 的核心功能:追踪、评估、Prompt 管理、成本分析
- 第一步:启动你的 Langfuse 本地服务
- 环境要求:Docker 和 Docker Compose
- 从官方仓库下载
docker-compose.yml - 一行命令启动 Langfuse:
docker-compose up -d - 访问 Langfuse UI:
http://localhost:3000 - 创建项目并获取你的 API Keys
- 第二步:SDK 集成,为你的代码安装“探针”
- 环境准备:
pip install langfuse - 获取 API Keys:从 Langfuse UI 中找到
LANGFUSE_PUBLIC_KEY,LANGFUSE_SECRET_KEY, 和LANGFUSE_HOST。 - 基本用法:
- 初始化
Langfuse客户端 - 使用
langfuse.trace()创建一个追踪包裹 - 在
trace中创建span(跨度)和generation(生成)
- 初始化
- 环境准备:
- 第三步:与 LangChain 的“魔法”集成
- Langfuse 对 LangChain 的一等支持
- 代码实战:
- 设置环境变量
- 创建
Langfuse客户端 - 使用
LangfuseCallbackHandler - 将
handler传入 LangChain 的Runnable调用中 - 神奇发生:LangChain 的每一步都会被自动追踪!
- 第四步:漫游 Langfuse UI——你的“AI 作战指挥室”
- Traces 视图:搜索和筛选你的所有 Agent 调用记录。
- Trace 详情页:
- 调用层级(Nesting):清晰地看到
AgentExecutor->ChatOpenAI->Tool Call的完整调用链。 - 输入/输出:查看每一步精确的 Prompt、
tool_calls和最终输出。 - 性能与成本:查看每一步的耗时、Token 数量和预估费用。
- 调用层级(Nesting):清晰地看到
- Generations 视图:专注于 LLM 的生成,方便对比不同模型、不同 Prompt 的效果。
- Sessions 视图:查看某个特定用户(
thread_id)的完整对话历史。 - Scores 和 Datasets:我们将在后续章节深入的功能。
- 调试实战:用 Langfuse 解决一个“神秘”的 Bug
- 问题:一个 Agent 在某些情况下返回了完全不相关的内容。
- 使用 Langfuse:
- 通过
user_id或thread_id在 Traces 视图中定位到出错的会话。 - 打开 Trace 详情,检查 Supervisor Agent 的输入
messages,发现上下文中包含了来自之前完全不同任务的干扰信息。 - 定位根因:确认是记忆管理策略的问题,没有在不同任务之间正确地隔离会话。
- 解决:修复会话管理逻辑。
- 通过
- 总结:告别“盲飞”,拥抱“数据驱动”
1. Langfuse 是什么?为什么选择它?
Langfuse 是一个为 LLM 应用量身打造的开源可观测性平台。可以把它看作是 AI 领域的 Datadog 或 Sentry。
- 开源 vs. 闭源 (LangSmith)
- LangSmith 是 LangChain 官方提供的闭源可观测性平台,功能强大,与 LangChain 生态结合紧密。
- Langfuse 作为开源替代品,提供了与 LangSmith 高度相似的核心功能,但它允许你私有化部署,将所有敏感的生产数据保留在自己的服务器上,这对于许多企业来说至关重要。同时,它的集成方式更通用,不局限于 LangChain。
- 可观测性的三大支柱
- 日志 (Logs):记录发生了“什么事”。(
print或loguru) - 指标 (Metrics):记录可聚合的“数字”。(如延迟、成本、错误率)
- 追踪 (Traces):记录一个请求的完整端到端调用链,告诉你“事情是如何发生的”。
- Langfuse 的核心就是追踪(Tracing)。它将一次复杂的 Agent 调用(包含多次 LLM 调用和工具调用)关联到一个 Trace 中,让你能一目了然地看到整个过程。
- 日志 (Logs):记录发生了“什么事”。(
2. 第一步:启动你的 Langfuse 本地服务
Langfuse 官方推荐使用 Docker Compose 进行部署,这非常简单。
环境要求
- 确保你的电脑上已经安装并运行了 Docker 和 Docker Compose。
获取并启动
-
创建一个新目录,比如
langfuse-deployment,然后进入该目录。mkdir langfuse-deployment cd langfuse-deployment -
下载官方的
docker-compose.yml文件。curl -o docker-compose.yml https://langfuse.com/docker-compose.yml -
启动服务。
docker-compose up -d-d参数让服务在后台运行。- 这个命令会拉取 Langfuse server, Langfuse UI, PostgreSQL 数据库和 Clickhouse 数据库的镜像,并启动它们。整个过程可能需要几分钟。
-
验证。等待所有容器都启动后,在浏览器中访问
http://localhost:3000。- 你应该能看到 Langfuse 的登录/注册界面。首次使用,你需要创建一个新的账户。
创建项目并获取 API Keys
- 登录后,系统会引导你创建一个新的项目(Project),给它取个名字,比如
trip-genius-dev。 - 进入项目后,导航到 Settings -> API Keys。
- 在这里,你会看到你的 Public Key (pk-...) 和 Secret Key (sk-...)。这两个 Key 是你的代码与 Langfuse 服务通信的凭证。
- 同时记下 Server URL,对于本地部署,它就是
http://localhost:3000。
3. 第二步:SDK 集成,为你的代码安装“探针”
现在,我们要在 Python 代码中集成 Langfuse 的 SDK。
环境准备
pip install langfuse
基本用法
让我们来看一个不依赖任何框架的纯 Python 示例,来理解 Langfuse 的核心概念。
# simple_langfuse_example.py
import os
from langfuse import Langfuse
from datetime import datetime
# 1. 初始化 Langfuse 客户端
# 强烈建议使用环境变量来配置
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-..." # 替换成你的 Public Key
os.environ["LANGFUSE_SECRET_KEY"] = "sk-..." # 替换成你的 Secret Key
os.environ["LANGFUSE_HOST"] = "http://localhost:3000"
langfuse = Langfuse()
# 2. 创建一个 Trace (追踪)
# Trace 是一次完整的端到端请求,比如用户的一次提问
trace = langfuse.trace(
name="weather-forecast-trace",
user_id="user-001",
session_id="session-xyz", # 用于关联同一用户的多次对话
tags=["development", "example"]
)
# 3. 在 Trace 中创建一个 Span (跨度)
# Span 代表 Trace 中的一个具体步骤,比如一次工具调用
with trace.span(name="fetch-weather-data") as span:
# ... 这里是你的业务逻辑,比如调用一个外部 API ...
import time; time.sleep(1)
span.end(output={"city": "SF", "temp": 75})
# 4. 在 Trace 中创建一个 Generation (生成)
# Generation 专用于记录 LLM 的调用
generation = trace.generation(
name="summarize-weather",
model="gpt-3.5-turbo",
model_parameters={"max_tokens": 100},
start_time=datetime.now(),
input=[{"role": "user", "content": "Summarize: SF, 75 degrees"}],
usage={"prompt_tokens": 10, "completion_tokens": 5}
)
time.sleep(1)
generation.end(output={"role": "assistant", "content": "It's 75 degrees in SF."})
# 5. 确保所有数据都被发送
langfuse.flush()
print("Trace sent to Langfuse. Check http://localhost:3000")
- Trace: 最高层级的对象,代表一次完整的交互。
- Span: Trace 内部的一个有名字、有耗时的操作单元。
- Generation: 一种特殊的 Span,专门用于记录 LLM 的调用,有
model,usage,input,output等专用字段。
运行这段代码后,回到 Langfuse UI,你就能在 Traces 页面看到一条名为 weather-forecast-trace 的新记录。点进去,你会看到一个包含 fetch-weather-data 和 summarize-weather 两个步骤的、有层级关系的调用链。
4. 第三步:与 LangChain 的“魔法”集成
手动创建 span 和 generation 虽然灵活,但比较繁琐。幸运的是,Langfuse 对 LangChain 提供了“魔法般”的无缝集成。你几乎不需要改变你的 LangChain 代码。
代码实战
让我们回到 2.2 节的 multi_tool_agent.py,并为它加上 Langfuse 追踪。
# multi_tool_agent_with_langfuse.py
# ... (保留所有之前的 import 和代码) ...
from langfuse.callback import LangfuseCallbackHandler
# --- 1. 设置环境变量 (或者在代码中配置) ---
os.environ["LANGFUSE_PUBLIC_KEY"] = "pk-..."
os.environ["LANGFUSE_SECRET_KEY"] = "sk-..."
os.environ["LANGFUSE_HOST"] = "http://localhost:3000"
# --- 2. 创建 LangfuseCallbackHandler ---
# 你可以为 handler 指定一些元数据,它们会被附加到所有追踪上
langfuse_handler = LangfuseCallbackHandler(
# public_key=..., secret_key=..., host=... # 也可以在这里传入
session_id="multi-tool-agent-session-001"
)
# ... (保留 app = workflow.compile() 之前的代码) ...
# --- 3. 在调用时传入 handler ---
app = workflow.compile()
inputs = {"messages": [HumanMessage(content="What is the weather in SF?")]}
# 将 handler 传入 config 中
# 神奇的事情即将发生!
result = app.invoke(inputs, config={"callbacks": [langfuse_handler]})
print(result)
# 如果是 stream
# for output in app.stream(inputs, config={"callbacks": [langfuse_handler]}):
# ...
发生了什么?
LangfuseCallbackHandler 利用了 LangChain 的 Callbacks 机制。Callbacks 是 LangChain 系统中的一套事件监听器。当 LangChain 内部发生特定事件时(如 on_chain_start, on_llm_end, on_tool_start),它会自动通知所有注册的 Callback Handler。
LangfuseCallbackHandler 正是这样一个监听器。它监听到这些事件后,会自动地、在后台为你创建对应的 Langfuse Trace, Span, Generation,并将它们关联起来。
你只需要在调用 .invoke 或 .stream 时,将 handler 传入 config={"callbacks": [...]},其他所有事情——追踪的开始和结束、层级关系的建立、输入输出的捕获、性能和成本的计算——都由 Langfuse 全自动完成了!
运行完这段代码后,再去 Langfuse UI 看看。你会看到一条极其详尽的 Trace,它完美地复现了 LangGraph Agent 的每一步执行流程,从 agent 节点到 action 节点,再到 ChatOpenAI 的调用和 search 工具的执行,一切都清晰可见。
5. 第四步:漫游 Langfuse UI——你的“AI 作战指挥室”
- Traces 视图:这是你的主仪表盘。你可以像在 IDE 中搜索代码一样,通过
user_id,session_id,tags, 或 Trace 的名字来搜索你想要找的某次 Agent 执行记录。 - Trace 详情页:这才是 Langfuse 的精华。
- 左侧的层级视图:清晰地展示了 Agent 的调用栈。对于 LangGraph,你会看到图的节点(
agent,action)作为父Span,而节点内部的 LLM 调用和工具调用则作为子Span或Generation。 - 右侧的详情面板:点击任意一个
Span或Generation,右侧会立即显示其详细信息:- Input/Output: 对于 LLM Generation,你可以看到完整的
messages输入和模型返回的content/tool_calls。 - Metadata: 耗时(Latency)、模型名称(Model)、Token 数量(Usage)、预估成本(Cost),一应俱全。
- Input/Output: 对于 LLM Generation,你可以看到完整的
- 左侧的层级视图:清晰地展示了 Agent 的调用栈。对于 LangGraph,你会看到图的节点(
- Sessions 视图:通过
session_id将一个用户的多次连续对话(Traces)串联起来,让你能以对话的视角回顾整个交互历史。
6. 调试实战:用 Langfuse 解决一个“神秘”的 Bug
假设用户报告:“我问旅小智厦门的天气,它却告诉我现在是夏天”。这是一个典型的、难以通过 print 调试的 AI 行为问题。
使用 Langfuse 的流程:
- 定位:向用户索要这次对话的大致时间或
session_id。在 Langfuse Traces 视图中,通过时间范围和session_id筛选,快速定位到这次出错的 Trace。 - 审查:点击进入 Trace 详情页。
- 分析调用链:你看到
TravelPlannerAgent 调用了CityExpert。点击CityExpert这个Span。 - 检查输入:在右侧面板,你检查
CityExpert的输入messages。你发现传入的input是:“请告诉我关于‘夏天’的信息”。 - 回溯:这很奇怪。你回到上一级的
TravelPlanner节点,检查它的 输出。你发现它在tool_calls中,确实是错误地指示CityExpert去搜索“夏天”,而不是“厦门”。 - 检查思考过程:你检查
TravelPlanner的 输入,发现用户的原始请求是“我想去厦门玩,现在是夏天,适合吗?”。 - 定位根因:问题清楚了。
TravelPlanner的 LLM 在解析这个复合问题时,发生了“注意力漂移”,错误地将句子中的“夏天”当作了核心查询对象,而忽略了“厦门”。 - 解决:这是一个 Prompt Engineering 问题。你可以在
TravelPlanner的 System Prompt 中加入一条指令:“你的任务是规划旅行,当用户同时提到地点和时间时,地点是你的核心任务对象。” - 验证:部署新的 Prompt 后,重新运行相同的测试用例,并在 Langfuse 中观察新的 Trace,确认
TravelPlanner这次正确地指示CityExpert去搜索“厦门”。
没有 Langfuse 这样精细的 Trace 视图,想要定位这种隐藏在复杂调用链深处的 AI 行为问题,几乎是不可能的。
7. 总结:告别“盲飞”,拥抱“数据驱动”
通过本章的学习,我们已经成功地为我们的 AI 应用安装了必不可少的“黑匣子”和“仪表盘”。
Langfuse 的追踪能力,将原本模糊、不可知的 Agent 内部工作流,以一种高度结构化、可视化的方式呈现在我们面前。它让我们得以:
- 精细调试:快速定位到复杂调用链中的错误根源。
- 性能分析:识别出应用中的性能瓶颈。
- 成本控制:精确地追踪每一次调用的 Token 消耗和费用。
我们已经从“盲目开发”迈向了“可观测驱动的开发”。拥有了 Langfuse 提供的坚实数据基础,我们才真正有能力在接下来的章节中,去进行更高级的、数据驱动的评估和优化。