资讯动态

AI Agent 生产级可观测性实战:Langfuse 追踪、评测与落地

发布时间:2026/10/5 8:46:25 来源:尧图企业网站定制
1. 为什么“能跑通”和“能上线”之间隔着一整套可观测体系我最早接触 AI Agent 是在一个内部知识库问答项目上当时用 LangChain 串了一个检索加生成的链路本地跑得挺顺Demo 演示也过了。结果一上预发环境问题全冒出来了用户问同一个问题有时候答得挺好有时候答非所问偶尔整个请求卡住十几秒不返回更头疼的是出了这些问题我根本不知道是哪一步出了岔子——是检索召回的内容不对还是提示词拼接出了问题还是模型本身返回了异常还是工具调用超时了。日志里只有一行“request finished”什么有价值的信息都没有。这就是典型的“模型调用”思维和“工程化”思维之间的差距。你写一个chain.invoke()能跑通那叫模型调用但你要让一个 AI Agent 系统在生产环境里稳定服务成百上千的并发请求还能在出问题时快速定位、在效果下降时快速归因那就必须有一套完整的可观测体系。Langfuse 就是在这个位置上进入我的视野的。它解决的核心问题很明确把 AI Agent 系统里每一次请求的完整链路——从用户输入、提示词组装、检索召回、模型调用、工具执行到最终输出——全部记录下来并且以结构化的方式呈现出来让你能像看分布式追踪一样看每一次 Agent 的执行过程。同时它还提供了评测、数据集、提示词管理等配套能力把“观测”和“迭代”串成了一条线。这篇文章适合谁看如果你正在用 LangChain、LangGraph、Spring AI 或者自己手写的 Agent 框架搭系统已经过了“能跑通”的阶段开始面对“效果不稳定”“排查困难”“不知道怎么评测”这些问题那这篇内容应该能帮到你。我会从整体设计思路讲到具体落地细节包括参数配置、埋点方式、常见坑和排查技巧尽量做到你看完就能在自己的项目里复现。2. 整体设计思路为什么选 Langfuse以及它在架构里站什么位置2.1 从“日志”到“追踪”可观测性的三个层次在讲 Langfuse 具体怎么用之前我想先把“可观测性”这件事拆清楚。很多团队一开始的做法是打日志每次模型调用前后各打一行记录输入输出。这属于第一个层次日志级别。它能告诉你“发生了什么”但没法告诉你“为什么发生”因为日志是扁平的、离散的缺少上下文关联。第二个层次是指标级别比如统计每分钟请求数、平均延迟、Token 消耗量、错误率。这些指标能帮你发现“系统整体是不是有问题”但没法帮你定位“具体是哪一次请求、哪一个环节出了问题”。第三个层次才是追踪级别也就是 Langfuse 主要解决的问题。它把一次 Agent 执行拆解成一棵调用树根节点是一次完整的请求子节点是各个步骤检索、提示词组装、模型调用、工具执行每个节点都有自己的输入、输出、耗时、元数据。这样你就能精确地看到这次请求慢是因为检索花了 3 秒还是模型花了 8 秒这次回答不对是因为召回的文档本身就不相关还是模型没有正确使用召回内容。提示不要跳过日志和指标直接上追踪。日志和指标是基础追踪是在它们之上的结构化增强。三者配合使用效果最好。2.2 为什么是 Langfuse 而不是自己造轮子我一开始也想过自己搭一套追踪系统无非就是记录每次调用的输入输出存到数据库里再做个界面展示。但真正动手才发现有几个问题是自己造轮子很难绕过的。第一是与主流框架的集成成本。LangChain、LangGraph、OpenAI SDK、LlamaIndex 这些框架都有自己的回调机制你要自己对接每一个工作量不小。Langfuse 已经把这些集成做好了基本上改几行配置就能接入。第二是数据模型的合理性。一次 Agent 执行不是简单的“请求-响应”而是嵌套的、有层次的。Langfuse 的 Trace、Span、Generation 三层数据模型刚好对应了这种结构用起来很自然。第三是评测和数据集能力。光有追踪还不够你还需要基于追踪数据做评测——比如把某次失败的请求加入数据集跑一批测试用例看新版本有没有改善。这部分 Langfuse 也提供了省了很多事。第四是部署灵活性。Langfuse 支持云端和自托管两种方式对于数据敏感的场景可以自己部署这点在选型时很重要。2.3 在 Agent 架构中的位置从架构上看Langfuse 是横切在整个 Agent 系统之上的一个观测层。它不参与业务逻辑不改变请求路径只是在关键节点上“旁路”收集数据。典型的接入方式有两种一种是通过框架的回调机制自动埋点比如 LangChain 的 CallbackHandler另一种是在自己的代码里手动创建 Span 和 Generation适合自研框架或者需要精细控制的场景。我个人的建议是能用自动埋点的地方就用自动埋点减少侵入性自动埋点覆盖不到的关键业务节点再手动补充。两者结合既保证了覆盖率又保证了关键信息的完整性。3. 核心概念拆解Trace、Span、Generation 到底怎么理解3.1 用一个生活类比把三层结构讲透Langfuse 的数据模型其实不复杂但第一次接触容易懵。我用一个类比来解释把一次 Agent 执行想象成你去餐厅吃饭。Trace就是你这次用餐的完整记录——从你进门到离开整个过程。它有一个唯一的 ID关联了这次用餐的所有信息。Span是用餐过程中的一个个环节——点菜是一个 Span等菜是一个 Span吃饭是一个 Span结账是一个 Span。每个 Span 有自己的开始时间和结束时间可以嵌套比如“等菜”里面还可以细分“厨房备菜”和“上菜”。Generation是一种特殊的 Span专门用来记录模型调用。它比普通 Span 多了几个关键字段用的什么模型、输入了多少 Token、输出了多少 Token、花了多少钱。这就像餐厅里专门记录“这道菜用了什么食材、多少克、成本多少”一样。理解了这三层你就能明白为什么 Langfuse 的界面看起来是一棵树——因为 Agent 的执行本身就是树状的。3.2 每个字段背后的工程意义很多人接入 Langfuse 之后只填了最基本的输入输出其他字段都空着。这样用其实浪费了它一半的价值。我来说说几个关键字段为什么值得填。Name给每个 Span 起一个有意义的名字。不要用默认的“Chain”“Tool”这种泛泛的名字而是用“retrieve_knowledge”“format_prompt”“call_llm”这种能一眼看出在干什么的名字。排查问题时名字比 ID 有用得多。Input / Output这是最核心的字段但要注意脱敏和截断。用户输入里可能包含敏感信息模型输出可能很长。我的做法是输入保留原文但做敏感字段替换输出超过一定长度就截断并标记。Metadata这个字段最容易被忽略但价值很高。你可以把这次调用用的提示词版本、检索到的文档 ID、工具调用的参数、用户的会话 ID 都塞进去。排查问题时这些元数据往往比输入输出更能说明问题。Level标记这次调用的严重程度DEBUG、DEFAULT、WARNING、ERROR。这样在界面上可以快速筛选出有问题的调用。Status Message出错时把错误信息填进去比在日志里翻半天强。3.3 Trace 的上下文传递一个容易被忽略的点是 Trace 的上下文传递。在一个多轮对话或者多步骤 Agent 里你需要把同一个 Trace ID 贯穿始终否则每次调用都是独立的 Trace串不起来。在 LangChain 里通过CallbackHandler的trace_id参数可以指定在自研框架里你需要自己维护一个上下文变量在每个步骤开始时把 Trace ID 传下去。我一般会在请求入口处生成一个 Trace ID然后通过 context 或者参数传递的方式带到每个子调用里。注意Trace ID 的生成要保证全局唯一建议用 UUID 或者带时间戳的雪花 ID。不要用自增 ID分布式环境下会冲突。4. 实操落地从零接入 Langfuse 的完整流程4.1 部署方式选择与初始化配置Langfuse 有两种使用方式云端版和自托管版。云端版注册就能用适合快速验证和小团队自托管版需要自己部署适合数据敏感或者需要深度定制的场景。自托管最简的方式是用 Docker Compose。官方提供了 compose 文件包含 Langfuse Server、PostgreSQL 和 ClickHouse用于存储追踪数据。我实测下来单机 4 核 8G 的配置跑中小规模流量没问题。部署命令大致如下git clone https://github.com/langfuse/langfuse.git cd langfuse docker compose up -d启动后访问 3000 端口就能看到界面。第一次登录需要创建组织和项目然后拿到 Public Key 和 Secret Key这两个 Key 后面接入时要用。环境变量配置是关键一步我一般会在项目里建一个.env文件LANGFUSE_PUBLIC_KEYpk-lf-xxxxxxxx LANGFUSE_SECRET_KEYsk-lf-xxxxxxxx LANGFUSE_HOSThttp://localhost:3000提示Secret Key 不要硬编码在代码里也不要在前端暴露。它相当于密码泄露了别人就能往你的项目里写数据。4.2 LangChain / LangGraph 场景的自动埋点如果你用的是 LangChain 或 LangGraph接入是最省事的。只需要在调用链里传入CallbackHandlerfrom langfuse.callback import CallbackHandler langfuse_handler CallbackHandler( public_keypk-lf-xxxxxxxx, secret_keysk-lf-xxxxxxxx, hosthttp://localhost:3000, trace_nameknowledge_qa, session_iduser_session_123, user_iduser_456 ) result chain.invoke( {question: 什么是向量数据库}, config{callbacks: [langfuse_handler]} )这样一次调用就会自动生成一个 Trace里面的每个 Chain、每个 LLM 调用、每个 Tool 调用都会自动变成 Span 或 Generation。你不需要改任何业务逻辑。LangGraph 的场景稍微特殊一点因为它是图结构执行节点之间的跳转比较多。Langfuse 对 LangGraph 的支持是通过CallbackHandler自动识别节点边界每个节点会生成一个 Span。我实测下来节点名称会自动带上但如果你想让名称更可读可以在节点函数里手动更新当前 Span 的名称。4.3 自研框架的手动埋点如果你用的是 Spring AI、自己手写的 Agent 框架或者 Rust 写的 Agent那就需要手动埋点。手动埋点的核心是三个 API创建 Trace、创建 Span、创建 Generation。以 Python 为例最简的手动埋点长这样from langfuse import Langfuse langfuse Langfuse( public_keypk-lf-xxxxxxxx, secret_keysk-lf-xxxxxxxx, hosthttp://localhost:3000 ) # 创建根 Trace trace langfuse.trace( nameagent_execution, user_iduser_456, session_idsession_123, metadata{agent_version: v1.2} ) # 创建一个 Span 表示检索步骤 retrieval_span trace.span( nameretrieve_knowledge, input{query: 什么是向量数据库} ) docs retrieve_documents(什么是向量数据库) retrieval_span.end(output{doc_count: len(docs), doc_ids: [d.id for d in docs]}) # 创建一个 Generation 表示模型调用 generation trace.generation( namegenerate_answer, modelgpt-4, input{prompt: prompt, documents: docs}, model_parameters{temperature: 0.7, max_tokens: 500} ) answer call_llm(prompt) generation.end( outputanswer, usage{input: 1200, output: 350, total: 1550} ) trace.update(output{answer: answer})手动埋点的好处是控制精细你可以决定哪些步骤值得记录、记录哪些字段。坏处是代码侵入性比较强每个关键节点都要加代码。我的经验是核心链路手动埋点边缘逻辑用自动埋点或者干脆不埋。4.4 关键参数配置与性能考量接入 Langfuse 之后有一个问题必须考虑它会不会拖慢我的主流程Langfuse 的 SDK 默认是异步上报的也就是说它不会阻塞你的业务逻辑。但如果你用的是同步模式或者网络不稳定确实可能影响性能。我的建议是第一开启批量上报。Langfuse SDK 支持把多个事件攒一批再发减少网络请求次数。配置项一般是flush_at和flush_interval前者是攒够多少条发一次后者是最长等多久发一次。我一般设flush_at10、flush_interval1兼顾实时性和性能。第二设置合理的超时和重试。网络抖动是常态不要让上报失败影响业务。SDK 一般有超时配置设个 2-3 秒就够了失败了就丢弃不要重试太多次。第三采样。如果流量特别大全量上报成本很高可以按比例采样。比如只上报 10% 的请求但保证错误请求 100% 上报。Langfuse 支持在创建 Trace 时通过sampling参数控制。第四注意数据量。每次请求都上报完整的输入输出数据量增长很快。ClickHouse 虽然能扛但存储成本还是要考虑的。我的做法是输入输出做截断元数据精简定期清理老数据。5. 评测与数据集让可观测性真正产生价值5.1 为什么光有追踪还不够追踪解决的是“发生了什么”的问题但没法回答“这样好不好”。你看到一次请求的完整链路知道检索召回了 5 篇文档、模型生成了 300 字回答但你不知道这 5 篇文档是不是真的相关、这 300 字回答是不是真的准确。这就是评测要解决的问题。Langfuse 的评测能力建立在追踪数据之上你可以把某次追踪的输入输出加入数据集然后跑一批测试用例用评分函数给每个输出打分最后对比不同版本的表现。5.2 数据集构建的实操方法数据集的质量直接决定评测的价值。我一般从三个来源构建数据集第一是线上真实请求。从追踪数据里筛选出有代表性的请求加入数据集。特别是那些出过问题的请求一定要加进去作为回归测试用例。第二是人工构造的边界用例。比如空输入、超长输入、包含特殊字符的输入、多轮对话的上下文切换等。这些线上不一定经常出现但一旦出现就容易出问题。第三是标注数据。如果条件允许让领域专家对一批输入输出做人工标注作为评测的“标准答案”。这部分成本高但价值也最高。在 Langfuse 里创建数据集的代码大致如下dataset langfuse.create_dataset(nameqa_evaluation_v1) dataset.create_item( input{question: 什么是向量数据库}, expected_output向量数据库是一种专门用于存储和检索向量数据的数据库..., metadata{category: concept, difficulty: easy} )5.3 评分函数的设计与选择评分函数是评测的核心。Langfuse 支持几种常见的评分方式人工评分在界面上手动打分适合小规模、高质量的评测。规则评分用代码写规则比如检查输出是否包含关键词、长度是否在范围内、格式是否符合要求。适合有明确标准的场景。模型评分用另一个模型来给输出打分比如让 GPT-4 判断回答是否准确、是否相关。适合开放式问题但要注意模型评分本身也有偏差。自定义评分通过 API 上报评分适合有自己评测体系的团队。我一般会组合使用规则评分做快速筛选模型评分做初步判断人工评分做最终确认。三层过滤下来既能保证覆盖率又能保证准确性。5.4 从评测结果到迭代闭环评测不是终点迭代才是。跑完评测后你会得到一份报告哪些用例通过了、哪些没通过、平均分是多少、和上一个版本比是涨了还是跌了。如果发现某个版本效果下降你可以回到追踪数据里找到那些失败的用例看它们的完整链路定位是哪个环节出了问题。是提示词改了导致模型理解偏差是检索策略调整导致召回质量下降还是模型版本升级导致输出风格变化定位到问题后修改、重新评测、对比、上线形成一个闭环。这个闭环跑得越快你的 Agent 系统迭代速度就越快。6. 常见问题与排查技巧实录6.1 追踪数据不完整或丢失这是最常见的问题。表现是明明调用了模型但 Langfuse 界面上看不到对应的 Generation或者 Trace 有但 Span 缺失。排查思路分三步。第一步检查 SDK 初始化是否正确Key 和 Host 有没有填错。第二步检查网络是否通特别是自托管场景容器之间能不能互相访问。第三步检查是不是异步上报还没 flush 就进程退出了。Python SDK 在进程退出时不会自动 flush需要手动调用langfuse.flush()。提示在 Serverless 或者短生命周期进程里一定要在返回响应前手动 flush否则数据会丢。6.2 性能下降与上报阻塞如果接入 Langfuse 后发现接口响应变慢大概率是上报阻塞了主流程。排查方法是看 SDK 是不是同步模式以及网络延迟高不高。解决办法前面提过开启批量上报、设置超时、采样。另外如果 Langfuse 服务本身负载高也会导致上报变慢这时候可以考虑加机器或者升级配置。6.3 敏感信息泄露风险追踪数据里可能包含用户隐私、业务机密。如果 Langfuse 是云端版数据会传到第三方风险更高。我的做法是第一在埋点时就做脱敏手机号、身份证号、邮箱这些用正则替换掉。第二敏感字段不记录比如密码、Token。第三如果数据特别敏感用自托管版数据不出内网。第四设置数据保留策略定期清理老数据。6.4 多轮对话的 Trace 串联多轮对话场景下每一轮都是一个独立的请求但它们在业务上是关联的。如果不做处理Langfuse 里会看到很多独立的 Trace没法串起来看。解决办法是用session_id。在创建 Trace 时传入同一个session_idLangfuse 就会把这些 Trace 归到同一个会话下。界面上可以按会话查看很方便。6.5 常见问题速查表问题现象可能原因排查方法解决方案追踪数据缺失SDK 未 flush检查进程退出前是否调用 flush手动 flush 或改用长生命周期进程接口变慢上报阻塞查看 SDK 是否同步模式开启批量上报、设置超时数据敏感未脱敏检查埋点字段正则替换、字段过滤、自托管会话串不起来未传 session_id检查 Trace 创建参数统一传入 session_id评测结果偏差大评分函数不合理抽样人工复核调整评分规则或组合多种评分存储增长快全量上报查看数据量趋势采样、截断、定期清理6.6 几个我踩过的坑第一个坑是在异步任务里用同步 SDK。有一次我在 Celery 任务里用 Langfuse结果发现数据上报特别慢后来才发现是同步 SDK 在异步环境里阻塞了事件循环。换成异步 SDK 或者放到线程池里执行就好了。第二个坑是Trace 嵌套层级太深。有一次我把每个函数调用都埋了点结果一个 Trace 里有上百个 Span界面上看起来一团糟。后来我调整了策略只埋关键节点层级控制在 5 层以内清晰多了。第三个坑是忘记设置环境区分。开发、测试、生产的数据混在一起排查问题时很干扰。后来我在 Trace 的 metadata 里加了env字段界面上可以按环境筛选清爽很多。7. 一些关于工程实践的体会Langfuse 这类工具的价值不在于它本身有多复杂而在于它强迫你把 Agent 系统当成一个正经的软件系统来对待。以前你可能觉得“模型调用嘛输入输出对了就行”但真正上线之后你会发现输入输出只是冰山一角水面下还有检索质量、提示词版本、工具稳定性、并发控制、成本控制一大堆问题。我现在的习惯是任何一个新的 Agent 功能上线前先确保 Langfuse 埋点到位然后跑一轮评测确认基线。上线后每天看一次追踪数据重点关注错误率和延迟的异常波动。每周跑一次回归评测确认效果没有退化。这套流程跑下来心里踏实很多。另外说一个容易被忽略的点追踪数据本身就是宝贵的资产。它记录了用户真实的使用场景比任何人工构造的测试用例都真实。我经常从追踪数据里挖掘出一些意想不到的用例反过来丰富我的评测数据集。这个正向循环一旦建立起来Agent 系统的迭代速度会有明显提升。最后分享一个小技巧Langfuse 的界面支持按 metadata 筛选你可以把业务相关的标签比如用户等级、问题类型、渠道来源塞进 metadata然后在界面上按这些标签筛选分析。比如你想看“VIP 用户的问题回答质量是不是比普通用户差”直接筛一下就能对比比写 SQL 查日志方便多了。

读完文章,也想定制专属网站?

尧图设计师 24 小时内与您沟通定制方案

免费获取报价 →
↑