第一次接入观测平台,只验证一件事:业务代码运行一次以后,页面上能看到一条结构正确的 Trace,其中包含真实的模型调用。先跑通这条最小链路,再接多步骤 Agent、成本分析和评估。
这一章使用 Python 和手工埋点,call_model() 代表项目原有的模型调用函数。Langfuse 也提供 OpenAI、LangChain 等集成,它们能自动采集更多字段。手工方式更适合第一次学习,因为每个数据对象从哪里来都看得见。
先决定数据发到哪里
Langfuse Cloud 和 Self-hosted 共享主要工作流,但功能还会受到 Cloud 上线节奏、自托管 Server 版本以及 OSS/Enterprise 许可证影响。部署责任只是其中一个差异。
使用 Cloud 时,官方负责运行平台,接入团队负责埋点设计、上传内容和项目权限。学习、个人试验或早期 PoC 通常从 Cloud 开始,这样可以先验证 Trace 是否有用,而不是先维护数据库和队列。
Self-hosted 把平台运行也交给团队。它适合数据必须留在指定网络、需要企业身份系统、存储位置或审计策略必须自主控制的情况。控制力的代价是持续运维。当前 Langfuse 自托管架构包含 Web、Worker、Postgres、ClickHouse、Redis/Valkey 和 Blob Storage;Docker Compose 面向本地、测试和低规模使用,不具备高可用、横向扩展和备份能力。生产环境的部署与治理会在提高篇展开。
如果公司政策禁止把 Prompt、模型输出或客户数据发送到外部服务,就不要为了练习绕过政策。可以使用隔离后的脱敏样本,或者在内部搭建测试实例。Cloud 与 Self-hosted 都不能替代数据分类:部署在自己的网络里,也不表示数据已经自动脱敏或权限天然正确。
创建项目与凭证
进入 Langfuse 后先创建 Project。Project 是 SDK 和公共 API 使用的认证边界,一组 API Key 只属于一个项目。初期不必按微服务拆项目,否则同一次业务链路容易被分散。只有数据需要独立授权、独立保留或独立核算时,拆分才有明确理由。
在 Project settings → API Keys 生成 public key 和 secret key。Secret key 不应写入代码、截图或前端页面;开发环境放入不会提交的 .env,生产环境交给公司的密钥管理服务。
LANGFUSE_PUBLIC_KEY="pk-lf-***"
LANGFUSE_SECRET_KEY="sk-lf-***"
LANGFUSE_BASE_URL="https://<your-langfuse-host>"
LANGFUSE_TRACING_ENVIRONMENT="development"
LANGFUSE_BASE_URL 必须指向项目所在的 Cloud 区域或自托管实例。Host 与密钥不匹配时,即使每个值看起来都合法,认证仍会失败。模型供应商的 API Key 和 Langfuse Key 是两套凭证:前者允许调用模型,后者允许发送观测数据,不能混用。
环境字段在这里就应设置好。测试 Trace 进入 development,生产流量进入 production。把两者混在一起,后面看到的请求量、成本和错误率都没有可靠口径。
先做连接检查
安装 Python SDK 后,让客户端从环境变量读取配置:
pip install langfuse
from langfuse import get_client
langfuse = get_client()
if not langfuse.auth_check():
raise RuntimeError("Langfuse 认证失败,请检查 Host 与项目密钥")
auth_check() 通过,只说明当前进程能够使用这组凭证连接项目。它没有验证数据边界、Trace 设计或页面筛选,也不能证明后续异步上报一定成功。
在写第一条 Trace 前,顺手检查这几个问题:当前项目是否对应目标业务;运行进程实际读取的是哪套环境变量;输入输出是否包含个人信息或客户机密;团队是否知道哪些字段不能上传。这里花十分钟,比数据进入平台后再清理省事得多。
记录一次业务请求
用户输入仍是“昨天哪个广告计划表现异常?”为了保持示例聚焦,我们只记录一个根步骤和一次模型调用。检索和工具调用会在下一章加入。
from langfuse import get_client, propagate_attributes
langfuse = get_client()
question = "昨天哪个广告计划表现异常?"
messages = [{"role": "user", "content": question}]
with langfuse.start_as_current_observation(
as_type="span",
name="answer-campaign-question",
input={"question": question},
) as root:
with propagate_attributes(
user_id="employee_731",
session_id="campaign-review-8f31",
tags=["campaign-review", "manual-instrumentation"],
metadata={
"tenant_id": "tenant_42",
"data_source": "ads_api"
},
):
with root.start_as_current_observation(
as_type="generation",
name="generate-campaign-answer",
model="your-model-name",
input=messages,
) as generation:
answer = call_model(messages)
generation.update(output=answer)
root.update(output={"answer": answer})
# Notebook、测试或仍会继续运行的进程,可主动发送当前批次。
langfuse.flush()
外层 span 表示这次业务处理,内层 generation 表示真实的模型请求。父子关系保留了“这次答案由哪次模型调用产生”。propagate_attributes() 在当前 Trace 范围内写入用户、Session、标签和 metadata,并让内部 Observation 继承这些属性。
示例没有伪造 Token。若模型 SDK 返回 usage,应记录真实的输入、输出 Token;如果使用 Langfuse 官方支持的模型或框架集成,通常可以自动采集模型、用量和参数。根据文本长度自行估算,再把估算值冒充供应商用量,会让成本分析失去可信度。
your-model-name 也应替换为实际模型标识。Langfuse 需要根据模型名称和 usage 匹配价格;名称错误时,Trace 仍可能上报成功,但费用无法正确计算。
页面上应该看到什么
运行代码后打开 Traces 页面,找到 answer-campaign-question。进入详情,沿着执行树核对:
- 根节点的输入问题与最终答案存在;
- 内部有一条
generate-campaign-answer,类型是 Generation; - Generation 显示真实模型名称、输入消息和输出;
userId、sessionId、环境、tags 与 metadata 符合预期;- 父子关系、时间顺序和状态没有异常。
若只能看到 Generation,却没有清楚的业务根步骤,模型调用脱离了产品语境。若只有根 Span,看不到 Generation,通常是模型调用没有放入内层上下文,或者自动集成没有正确初始化。第一次验收看结构,不追求字段数量。
页面没有数据时,沿发送路径排查
观测数据经过一条明确的路径:业务代码创建事件,SDK 写入本地队列,后台按时间或数量组成批次,发送到 Host,服务端鉴权接收,页面再按项目、环境和时间范围展示。按这条路径排查,比反复刷新页面有效。
先确认自己看的是正确项目和环境,时间范围覆盖刚才的测试,页面没有残留过窄的名称、标签、用户或 Score 筛选。然后检查当前进程实际读取的 public key、secret key、Base URL 和 environment。不要只看 .env 文件,容器、IDE 与部署平台未必加载了它。
接着确认埋点代码真的执行。给测试 Trace 一个容易识别的名称,检查函数是否在创建 Observation 前就提前返回或抛错。打开 SDK 调试日志时要避免输出 secret key 和敏感业务内容。
auth_check() 失败,通常回到 Host、密钥和项目。认证通过但发送报连接超时、DNS、证书、代理或防火墙错误,则检查网络路径。不要通过关闭证书校验来换取“先看到数据”。
最后再看异步队列。Langfuse SDK 默认在后台批量发送,避免每个埋点都阻塞业务请求。长驻 Web 服务一般会自行发送,页面晚几秒出现并不异常;脚本、测试、Serverless Function 和短生命周期容器可能在队列清空前结束。
首次接入先记住两个动作:Notebook 中为了马上查看结果,可以调用 flush();命令行脚本准备永久退出时,应按所用 SDK 调用 shutdown() 或对应的异步关闭方法。flush() 发送当前批次后客户端仍可继续使用,不是完整退出协议。OpenTelemetry 的 forceFlush() 及生产环境的送达边界放到第 11 章统一讨论。
如果数据仍未出现,保留测试时间、Trace 名称、SDK 版本和发送日志,再交给平台维护者。到了这一步,问题已经从模糊的“页面没数据”缩小为项目配置、事件生成、鉴权、网络或队列中的某一段。
第一条 Trace 的验收结果应当具体到:谁在什么环境发起了请求,调用了哪个模型,模型看到了什么,又返回了什么。任一项无法从页面确认,都说明最小链路还没有真正跑通。
参考资料
系列目录:AI 应用可观测性与评估
