大模型应用能从 demo 走到生产,关键往往不是模型选得不好,而是没人能说清楚线上一次回答到底经历了几轮工具调用、Prompt 用的是哪个版本、Token 烧在哪一步。Langfuse 就是解决这类问题的开源可观测平台,它把 Trace、Span、Generation、Score、数据集和 Prompt 版本管理放在同一个 Web 控制台里,适合做大模型 Agent 的调试、评估和性能优化。
这篇文章会从一个真实可跑的落地路径展开:先快速过一遍 Langfuse 的核心能力和部署门槛,然后演示自托管部署、SDK 接入、Agent 链路追踪、提示词版本管理、基于数据集的批量评估,再到 REST API 和自动化回归。全文以本地测试环境为准,代码会尽量贴近官方 SDK 写法,版本差异会单独说明。
在开始之前先给结论:Langfuse 不需要 GPU,也不吃显存,它主要做观测和分析,实际资源消耗在数据库和 Web 服务上。对个人开发者和中小团队来说,最轻量的用法是 Docker 一键拉起一个自托管实例,然后通过 Python SDK 把业务代码里的关键调用接进去,剩下的事情都交给控制台。
1. Langfuse 核心能力速览
| 能力项 | 说明 |
|---|---|
| 项目性质 | 开源的 LLM 可观测性与评估平台 |
| 核心功能 | Trace 链路追踪、Prompt 版本管理、数据集管理、离线/在线评估、成本与延迟分析 |
| 支持接入方式 | Python SDK、TypeScript SDK、REST API、OpenTelemetry、LangChain 等框架回调 |
| 部署方式 | Docker Compose 自托管、官方云服务、本地开发模式 |
| 硬件要求 | 无 GPU 要求,普通服务器或开发机即可运行 |
| 主要存储组件 | PostgreSQL(事件数据)、ClickHouse(分析数据)、Redis(队列缓存) |
| 适合场景 | 智能体评估、线上问题定位、Prompt 调优、成本与 Token 监控、批量回归测试 |
Langfuse 的定位不是模型部署工具,而是一个应用层观测和分析底座。你不需要把自己的模型注册进去,只需要在应用的推理调用前后上报数据,它就能把这些数据串成结构化链路。
从技术选型的角度,Langfuse 的价值在于把“调试”从日志里解放出来。普通日志只能看到一条一条孤立文本,而 Langfuse 可以看到一次任务里从用户输入到 Agent 规划、工具返回、再到大模型最终生成的全过程。
2. 为什么智能体落地后必须做可观测与评估
传统 Web 服务的问题定位很简单,接口报错了看状态码和堆栈基本就能定位。智能体的失败方式完全不同,即使整个调用链 HTTP 状态都是 200,Agnet 仍然可能因为检索结果不好、工具参数传错、模型过度推理而给出一个错误答案。
这时候就需要三类数据:
第一类是调用链路数据。一次智能体任务可能包含 Intent 识别、知识库检索、工具调用、回答生成多个环节。没有链路追踪,出问题之后根本不知道应该优化哪一个环节。
第二类是版本数据。同一个 Prompt 改过三次,线上跑的是第二版,离线实验用的是第三版,最后表现不一致,这在实际开发中非常常见。Langfuse 的 Prompt 版本管理正好能把版本和 Trace 绑定在一起,后续复盘时可以精确还原某一次生成使用的是哪个版本。
第三类是评估结果数据。大模型输出没有确定性的断言可以参考,必须用评分函数或 LLM-as-a-Judge 来做质量度量。评估不能只跑一次,要把评估结果沉淀下来,形成样本库,每次 Prompt 或模型变化后重新跑一遍回归。
Langfuse 的设计本质上是把这三种数据统一存储,并提供了 UI、API 和 SDK 三个维度的使用入口。接下来先从部署开始,把服务先跑起来。
3. Langfuse 本地部署与启动
3.1 环境准备
自托管 Langfuse 的硬件要求不高,一台 2 核 4G 内存的 Linux 服务器或者本机 Docker 环境都可以,磁盘取决于你的数据量。日常测试预留 20G 以上更稳妥。
部署前确认环境里已经安装:
- Docker 20.10 以上
- Docker Compose 2.x
- 可访问的浏览器
Windows 环境建议直接使用 Docker Desktop,Linux 环境直接安装 Docker Engine 即可。以下命令以 Linux 和 macOS 终端为准。
3.2 通过 Docker Compose 启动
Langfuse 官方仓库提供了完整的 Docker Compose 编排文件,包含 Web 服务、PostgreSQL、ClickHouse、Redis 和反向代理等组件。
git clone https://github.com/langfuse/langfuse.git cd langfuse docker compose up -d首次启动会拉取镜像并初始化数据库,耗时取决于网络状况,通常在几分钟内完成。启动完成后:
docker compose ps看到服务状态为 running 后,在浏览器访问:
http://localhost:3000首次进入页面时会要求注册一个管理员账号,这个账号用于登录控制台。如果端口 3000 被占用,可以修改 docker-compose 中 Web 服务的端口映射,比如把3000:3000改成13000:3000。
3.3 创建 API 密钥
登录控制台之后,在项目设置中创建 API Key,会得到两个关键值:
- Public Key,作为请求标识
- Secret Key,作为请求签名
这两个值不要提交到 Git 仓库。SDK 和 REST API 调用时都会用到,建议放到本地环境变量或配置中心里。
到这里服务已经可用,接下来要做的就是让业务应用开始向 Langfuse 上报 Trace 数据。
4. SDK 接入:让 Langfuse 开始记录智能体调用
4.1 安装依赖
Python 项目安装 SDK:
pip install langfuse如果你的智能体基于 LangChain,还需要额外安装:
pip install langchain langchain-openai4.2 初始化客户端
SDK 初始化时需要三个配置项:
import os from langfuse import Langfuse langfuse = Langfuse( public_key=os.getenv("LANGFUSE_PUBLIC_KEY", "pk-lf-xxxx"), secret_key=os.getenv("LANGFUSE_SECRET_KEY", "sk-lf-xxxx"), host=os.getenv("LANGFUSE_HOST", "http://localhost:3000") )如果是云服务版本,host 换成 Langfuse 控制台对应的 API 地址即可。
4.3 使用装饰器追踪代码
Langfuse Python SDK 提供了一个非常轻量的装饰器@observe。在函数上加上装饰器后,每次函数调用会自动生成一个 span,一个完整调用链最外层就是 trace。
from langfuse.decorators import observe import openai client = openai.OpenAI() @observe() def ask_llm(question: str) -> str: response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是一个智能助手。"}, {"role": "user", "content": question} ] ) return response.choices[0].message.content在@observe()装饰的函数内部调用 openai SDK 时,Langfuse 会自动捕获大模型调用,并以 generation 的形式挂到当前 trace 下。运行一次后去 Langfuse 控制台刷新 Trace 页面,就能看到本次调用记录。
4.4 为 Agent 任务增加业务信息
纯装饰器只能记录函数名和参数,生产环境还要记录用户 ID、会话 ID、任务类型等业务信息。Langfuse 提供了运行时上下文,可以在@observe函数内额外写入元数据。
from langfuse.decorators import observe, langfuse_context @observe() def run_agent(user_id: str, question: str) -> str: langfuse_context.update_current_trace( name="customer-service-agent", user_id=user_id, session_id="session-2026-001", metadata={ "scene": "after_sales", "env": "test" } ) return ask_llm(question)这样在控制台的 Trace 详情页里,可以按用户 ID、会话 ID 或 metadata 内容筛选,问题定位效率会高很多。
5. 复杂 Agent 链路追踪:LangChain 场景接入
如果你的智能体已经基于 LangChain / LangGraph 实现,通常不需要自己手写 span,Langfuse 提供了对应回调处理器,能自动捕获 Agent 的每一步执行过程。
5.1 标准回调方式
以 LangChain 的 AgentExecutor 为例:
from langfuse.callback import CallbackHandler from langchain_openai import ChatOpenAI from langchain.agents import create_tool_calling_agent, AgentExecutor from langchain.tools import tool lanfuse_handler = CallbackHandler( public_key="pk-lf-xxxx", secret_key="sk-lf-xxxx", host="http://localhost:3000" ) @tool def get_weather(city: str) -> str: """获取指定城市的天气信息。""" return f"{city} 今日天气晴朗,温度 20 摄氏度。" llm = ChatOpenAI(model="gpt-4o-mini", temperature=0) agent = create_tool_calling_agent(llm, [get_weather]) executor = AgentExecutor(agent=agent, tools=[get_weather]) result = executor.invoke( {"input": "北京今天天气如何?适合穿什么出门?"}, config={"callbacks": [lanfuse_handler]} )执行完成后,打开 Langfuse 控制台,就能看到整个 Agent 的 Trace 结构:
- Agent 的规划过程
- 工具名称和输入输出
- 大模型推理的 Token 消耗
- 每步执行耗时
- 最终回答内容
如果某一个工具返回了异常内容,直接在 UI 中点击对应 span 就能看到完整的原始输出,不需要再去翻日志。对多工具 Agent 来说,这是最高效的手段。
5.2 多轮对话与 Session 绑定
多轮对话场景下,不要把每一轮用户输入都当成独立 trace,而是尽量让它们属于同一个 session。LangChain 场景可以在 invoke 配置中带上 session_id:
executor.invoke( {"input": "那明天呢?"}, config={ "callbacks": [lanfuse_handler], "run_id": "session-2026-001" } )Langfuse 控制台会把同一 session 下的多次 trace 组织起来,方便按会话维度回放用户和智能体的完整交互。
6. 大模型观测:不是只看日志,而是看指标
接入 Trace 之后,Langfuse 的观测页面会持续积累几类指标:
- 调用总量:按模型、按操作类型、按用户维度统计
- Token 消耗:输入 Token、输出 Token、总量和对应成本
- 推理延迟:单次生成耗时和端到端耗时
- 错误率:SDK 上报时的错误状态和异常信息
- 评分分布:人工打标和自动评估的分数变化
这些指标可以在控制台直接看,也可以把筛选结果导出。排查线上问题时,可以先看错误率和大延迟集中在哪个 Agent 环节,再进入相应 Trace 查看具体输入输出,最后定位到 Prompt 或工具参数问题。
这与传统 APM 的用法类似,区别是大模型观测需要结合文本内容和语义信息才能判断问题,所以 Langfuse 把 trace、prompt、token、score 放在同一个详情页,尽可能减少跳转成本。
7. 提示词版本管理:Prompt 调试与变更追踪
Prompt 调优是智能体迭代过程中最频繁的操作。Langfuse 的 Prompt Management 提供了在线编辑、版本发布、版本对比和代码拉取能力。
7.1 在控制台创建 Prompt
在 Langfuse UI 的 Prompts 页面中新建一个 Prompt,命名为agent-system-prompt,填写系统提示词内容,并发布为 v1。后续再次编辑并发布,版本号自动变更。
7.2 代码中获取 Prompt
Python SDK 获取指定名称 Prompt,并读取其最新版本:
prompt = langfuse.get_prompt("agent-system-prompt") messages = [ { "role": "system", "content": prompt.compile(variables={"user_name": "张三"}) }, { "role": "user", "content": question } ]Prompt 支持变量模板,prompt.compile()方法会把模板变量渲染成最终文本。
7.3 让 Trace 关联 Prompt 版本
在调用大模型时,最好把 Prompt 版本信息同步写入 generation:
@observe(as_type="generation") def call_with_prompt(question: str): prompt = langfuse.get_prompt("agent-system-prompt", version=None) response = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": prompt.compile()}, {"role": "user", "content": question} ] ) return response之后在 Trace 详情里,可以看到这条 generation 引用的 Prompt 名称和版本号。一旦发现某个版本上线后结果变差,可以直接对比旧版本的输出,快速回滚。
8. 智能体评估落地:从评估函数到批量评测
Langfuse 的价值不只是观测历史调用,还能推动质量回归。智能体评估可以理解为把一批“输入 + 期望输出”放到数据集里,然后用评分规则批量打分。
8.1 创建评估数据集
dataset = langfuse.create_dataset( name="customer-service-eval", description="售后客服智能体回归数据集" ) dataset.create_item( input={"question": "我的订单还没收到,应该怎么办?"}, expected_output="回答中应包含查询物流状态的建议或操作步骤。" ) dataset.create_item( input={"question": "产品有问题想退货,流程是什么?"}, expected_output="回答中应包含退货申请的方式、时限和注意事项。" )数据集里的每一个 item 会保留独立版本,后续更新数据不会影响历史结果。
8.2 实现简单评估函数
最简单的评估函数可以检查输出文本中是否包含期望关键词,也可以写一个基于规则的打分函数:
def contains_key_points(output: str, expected_output: str) -> dict: output_clean = output.replace(",", "").replace("。", "") expected_clean = expected_output.replace(",", "").replace("。", "") matched = sum(1 for point in expected_clean if point in output_clean) total = max(len(expected_clean), 1) return { "score": matched / total, "label": "pass" if matched / total > 0.6 else "fail", "metadata": { "matched_count": matched, "expected_length": len(expected_clean) } }这种函数的好处是脱离大模型依赖,执行速度快,适合在 CI 或批量任务中每天跑。
8.3 执行批量评估
在 Langfuse SDK 中执行评估,需要把 dataset 与评估函数关联。不同 SDK 版本对evaluate入口的写法有差异,建议使用前先查看当前版本的官方示例。下面是一种接近官方 API 的写法:
from langfuse.evaluations import evaluate evaluate( dataset=dataset, evaluation=contains_key_points, run_name="rule-based-regression", )执行完成后,控制台的 Dataset 页面会显示本次 run 的评分结果,包括每个样本的详细分数、标注信息和整体均分。
8.4 用 LLM-as-a-Judge 做更细粒度的评估
规则评估无法覆盖生成内容质量。更常见的做法是让一个强大的模型扮演评委,对智能体输出打分。这里用一个通用示例:
def llm_as_judge(output: str, expected_output: str) -> dict: judge_prompt = f""" 你是一个智能体评估专家。 请根据以下标准判断回答是否合格: 标准:{expected_output} 智能体回答: {output} 请只返回 JSON,格式为: {{"score": 0 到 1 之间的数字, "reason": "评分理由"}} """ judge_response = client.chat.completions.create( model="gpt-4o-mini", messages=[{"role": "user", "content": judge_prompt}], response_format={"type": "json_object"} ) content = judge_response.choices[0].message.content import json result = json.loads(content) return { "score": result.get("score", 0), "label": "pass" if result.get("score", 0) >= 0.7 else "fail", "metadata": {"reason": result.get("reason", "")} }LLM-as-a-Judge 更适合评估回答的相关性、完整性和语气。执行时会消耗一定 Token,建议批量任务控制在合理规模内,并且在跑批量之前先手动验证 5 到 10 条样本,避免评委 Prompt 本身产生系统性偏差。
8.5 线上分数采集
除了离线评估集,Langfuse 还支持人工评分。在控制台 Trace 详情页可以直接给某一条回答打分,也可以把人工评分结果与线上反馈同步。后续分析时可以同时看自动评估和人工评分,两者一致性高说明自动评估可信,两者差异大则需要重新校准评估标准。
9. 接口 API 与自动化批量任务
很多团队的评估流程希望接入自己的任务系统,比如每天晚上从业务库拉取一批样本,调用评估函数后把结果写回 Langfuse。这时候可以使用 REST API。
9.1 API 认证方式
Langfuse REST API 使用 HTTP Basic Auth,用户名是 Public Key,密码是 Secret Key。请求地址为部署实例的/api/public路径。
LANGFUSE_PUBLIC_KEY="pk-lf-xxxx" LANGFUSE_SECRET_KEY="sk-lf-xxxx" LANGFUSE_HOST="http://localhost:3000"9.2 使用 curl 创建 Generation
curl -X POST "$LANGFUSE_HOST/api/public/generations" \ -u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "name": "llm-call", "model": "gpt-4o-mini", "input": "用户问题:我的订单还没收到", "output": "建议您先查看物流轨迹,并联系客服获取最新状态。", "usage": { "input": 120, "output": 80, "unit": "TOKENS" } }'创建成功后,接口会返回该 generation 的 ID,可以在后续请求中用于追加 score。
9.3 创建 Score
curl -X POST "$LANGFUSE_HOST/api/public/scores" \ -u "$LANGFUSE_PUBLIC_KEY:$LANGFUSE_SECRET_KEY" \ -H "Content-Type: application/json" \ -d '{ "traceId": "对应 trace id", "name": "quality", "value": 0.9, "comment": "回答完整,步骤清晰" }'9.4 Python 批量评估流水线
下面给出一个可扩展的批量评估骨架,读取本地 JSONL 测试集,逐条调用大模型,然后把 generation 和 score 写入 Langfuse:
import json import time import requests from requests.auth import HTTPBasicAuth LANGFUSE_HOST = "http://localhost:3000" AUTH = HTTPBasicAuth("pk-lf-xxxx", "sk-lf-xxxx") def run_eval_line(line: dict): question = line["question"] expected = line["expected"] import openai client = openai.OpenAI() resp = client.chat.completions.create( model="gpt-4o-mini", messages=[ {"role": "system", "content": "你是售后客服助手。"}, {"role": "user", "content": question} ] ) output = resp.choices[0].message.content score = 1.0 if expected.strip() in output else 0.0 langfuse_payload = { "name": "batch-eval", "model": "gpt-4o-mini", "input": question, "output": output, "metadata": {"dataset_line": line.get("id")} } resp = requests.post( f"{LANGFUSE_HOST}/api/public/generations", auth=AUTH, json=langfuse_payload, timeout=30 ) resp.raise_for_status() return output, score if __name__ == "__main__": with open("./eval_samples.jsonl", encoding="utf-8") as f: lines = [json.loads(line) for line in f if line.strip()] for idx, line in enumerate(lines[:20]): try: output, score = run_eval_line(line) print(f"{idx}: score={score}") except Exception as exc: print(f"{idx}: failed, {exc}") time.sleep(1)批量任务建议分三块:样本准备、调用执行、结果回写。 Langfuse 的 API 本身不限制写入数量,但过多并发请求可能影响自托管服务端性能,因此在开发阶段先限制并发数。
10. 性能优化与成本优化建议
Langfuse 本身不会提升模型响应速度,但观测数据能帮助你找到最值得优化的部分。
10.1 延迟优化
从 Trace 中看每一步耗时,如果发现工具调用是瓶颈,就优化工具接口;如果发现上下文太长,就减少历史消息数量或做上下文压缩;如果发现模型本身响应慢,可以尝试降低输出 Token 上限或换一个更快的轻量模型。
10.2 Token 成本优化
Langfuse 会对每个 generation 统计输入 Token 和输出 Token。当某个环节的输入 Token 异常高时,往往是因为把大量无关文档塞进了上下文。可以基于这些数据调整检索策略,比如限制每个知识块大小、提高检索阈值、在进入大模型前过滤低分文档。
10.3 错误与重试策略
当 Trace 页面出现大量超时或异常 generation 时,不建议直接调高重试次数,而是先看异常原因。如果是上游模型限流,增加指数退避;如果是工具输出格式错误,需要检查工具描述和参数解析逻辑;如果是上下文超长,优先压缩输入而不是盲目增加 max_tokens。
11. Langfuse 常见问题与排查方法
| 问题现象 | 可能原因 | 排查方式 | 解决方案 |
|---|---|---|---|
| 安装后无法访问 3000 端口 | Docker 服务未启动或端口冲突 | 执行docker compose ps查看状态 | 修改端口映射后重启服务 |
| SDK 上报后控制台看不到 Trace | Public Key / Secret Key 配置错误 | 查看应用日志中是否有 401/403 | 重新生成 API Key 并更新环境变量 |
| Trace 能看到,但内部没有 generation | 大模型 SDK 未被当前环境自动捕获 | 在函数里手动创建 generation 或升级 SDK 版本 | 使用@observe(as_type="generation")显式包装 |
| 控制台时间范围选择后数据为空 | 服务器时区与浏览器时区不一致 | 调整时间范围或用 UTC 时间查看 | 在代码中统一上报 UTC 时间戳 |
| Docker 重启后数据丢失 | Docker Volume 未挂载或误删容器 | 检查 compose 文件中的 volume 配置 | 确保数据目录持久化到宿主机 |
| 批量评估任务卡住 | 单条调用超时或外部 API 限流 | 检查日志中的阻塞点 | 增加超时时间和失败重试机制 |
| 数据分析页响应慢 | 数据量增长过快或查询范围过大 | 缩短时间范围或减少字段 | 定期归档历史数据,避免无限制增长 |
如果你在接入过程中遇到 SDK 函数签名差异,最直接的方法是去查看当前安装版本的官方文档。
pip show langfuse根据返回的版本号,再搜索对应版本的 API 说明。Langfuse 新版迭代速度较快,不同小版本的评估接口入口确实有变化。
12. 最佳实践与部署建议
12.1 开发阶段就接入
不要等到线上出问题再补观测。开发一个 Agent 后的第一件事不是跑完整流程,而是先接入 Langfuse,保证每个环节的输入输出都能被追踪。
12.2 字段脱敏
线上 Trace 中可能包含用户手机号、地址等敏感信息。Langfuse 默认会记录原始输入输出,在接生产流量前,需要在 SDK 层面对上报字段做脱敏或过滤,避免个人信息落库。
12.3 控制数据保留周期
Langfuse 的 ClickHouse 会存储大量明细数据,长期运行后磁盘占用会持续增加。建议在部署时配置数据保留策略,例如只保留最近 90 天的原始 Trace,分析报表可以单独导出归档。
12.4 建立评估回归基线
维护一个评估数据集本身就是一项长期投入。数据集不需要特别大,但必须覆盖高频场景和曾经出过错的案例。每次修改 Prompt、切换模型、调整工具后,都跑一次同样的评估集,用分数变化做回归判断。
12.5 权限与访问边界
如果 Langfuse 部署在公司内网,控制台不要默认开放到公网。可以在反向代理层增加身份认证,只允许开发和运维人员访问。API Key 也要按环境隔离,测试环境和生产环境使用不同的 Key。
13. 总结与下一步实际操作建议
Langfuse 最值得尝试的核心点不是单一的 Trace 追踪,而是 Prompt 版本管理、Trace 关联和数据集评估这三块拼在一起形成的闭环能力。真正用它调试一次线上 Agent 问题后,你会明显感觉到排查路径比翻日志直观得多。
第一次上手时,不用急着把所有代码都接入。建议先部署一个本地测试实例,用一个小型回收客服或知识问答 Agent 跑通以下链路:
- 先接入一个
@observe函数,确认控制台能看到 Trace - 为整个 Agent 增加用户 ID 和 session 关联,确认过滤可用
- 然后在控制台发布一个 Prompt v1,代码里使用
get_prompt拉取 - 最后准备 10 到 20 条测试用例,执行一次批量评估
跑通这条链路之后,你就具备了一套基础的大模型应用可观测与智能体评估体系。后面可以考虑继续扩展的方向有三个:接入更多框架的自动埋点、建立每天自动跑评估的 CI 流水线、把评估结果与线上告警打通。