ARTICLE DETAIL

资讯详情

深耕郑州网站建设与运营推广的一线实战洞察。

Langfuse实战:从零搭建LLM应用追踪与调试平台

Langfuse实战:从零搭建LLM应用追踪与调试平台 这类工具最值得先看的不是功能列表而是能不能在普通环境里稳定跑起来以及接入现有项目时会不会引入一堆依赖和配置问题。Langfuse 是一个专门用于追踪、调试和优化大语言模型LLM应用的开源平台它能帮你把模型调用、用户输入、输出、耗时、成本甚至中间步骤都记录下来形成可视化的链路。对于正在开发或已经上线 AI 应用、智能客服、内容生成工具的团队来说这解决了“黑盒”问题——你不再需要到处打日志来猜为什么这次回答不好或者成本突然飙升。我建议先从最小样例开始把本地环境搭起来跑通一个最简单的追踪示例。这比直接看文档要快能立刻知道它到底在记录什么。之后再考虑如何把它接入到你现有的 Spring Boot、Node.js 或者直接用 SDK 的项目里。整个过程我会拆成四步先搞定基础环境数据库和 Langfuse 服务再理解核心概念和追踪方式然后动手接入一个真实项目最后聊聊生产环境落地时那些容易踩坑的细节。下面按实际落地顺序拆一遍。1. 先确认环境本地跑通需要什么云服务又怎么选在动手写一行代码之前得先把 Langfuse 的服务跑起来。它本身是一个服务端应用需要数据库PostgreSQL和对象存储可选用于存文件来持久化数据。对于本地开发和测试用 Docker Compose 是最快最省事的方式。1.1 本地开发环境Docker Compose 一键启动如果你只是想快速体验或者用于内部开发测试Docker Compose 方案足够了。你需要确保本地已经安装了 Docker 和 Docker Compose。首先创建一个工作目录比如langfuse-demo然后在里面创建docker-compose.yml文件。你可以直接从 Langfuse 官方仓库获取最新的配置但为了稳定我建议先固定一个版本。下面是一个简化但可用的配置示例version: 3.8 services: postgres: image: postgres:15-alpine environment: POSTGRES_USER: langfuse POSTGRES_PASSWORD: langfuse POSTGRES_DB: langfuse volumes: - postgres_data:/var/lib/postgresql/data healthcheck: test: [CMD-SHELL, pg_isready -U langfuse] interval: 10s timeout: 5s retries: 5 langfuse: image: langfuse/langfuse:latest depends_on: postgres: condition: service_healthy environment: DATABASE_URL: postgresql://langfuse:langfusepostgres:5432/langfuse NEXTAUTH_SECRET: your-super-secret-nextauth-secret-at-least-32-characters-long NEXTAUTH_URL: http://localhost:3000 S3_ENDPOINT: http://minio:9000 S3_ACCESS_KEY_ID: minio S3_SECRET_ACCESS_KEY: minio123 S3_BUCKET_NAME: langfuse S3_REGION: us-east-1 S3_USE_SSL: false ports: - 3000:3000 volumes: - langfuse_data:/home/langfuse/data minio: image: minio/minio:latest command: server /data --console-address :9001 environment: MINIO_ROOT_USER: minio MINIO_ROOT_PASSWORD: minio123 volumes: - minio_data:/data ports: - 9000:9000 - 9001:9001 volumes: postgres_data: langfuse_data: minio_data:这个配置启动了三个服务PostgreSQL: 作为主数据库。Langfuse Server: 主应用服务默认端口 3000。MinIO: 一个开源的 S3 兼容对象存储用于存储追踪过程中可能产生的文件如上传的文档、生成的图片等。对于纯文本追踪MinIO 不是必须的但官方配置通常包含它。启动命令很简单cd langfuse-demo docker-compose up -d等所有容器都启动成功后可以通过docker-compose logs -f查看日志在浏览器打开http://localhost:3000。第一次访问会进入初始化页面让你创建第一个用户管理员和第一个项目。完成这一步本地环境就准备好了。注意NEXTAUTH_SECRET环境变量必须是一个足够长的随机字符串用于加密会话。你可以用openssl rand -base64 32命令生成一个。1.2 生产或长期使用环境更稳妥的部署选择如果你打算在团队内长期使用或者用于准生产环境直接docker-compose up就不太合适了。你需要考虑数据持久化、备份、升级和网络安全性。方案一自托管增强版你可以基于上述 Docker Compose 文件但做以下调整数据卷映射到主机路径将postgres_data、langfuse_data、minio_data这些匿名卷改为绑定挂载到主机特定目录方便备份和管理。volumes: - ./data/postgres:/var/lib/postgresql/data - ./data/langfuse:/home/langfuse/data - ./data/minio:/data使用独立的 PostgreSQL 和 S3 服务如果团队已有现成的 PostgreSQL 数据库如 RDS、云数据库和 S3 存储如 AWS S3、MinIO 集群可以直接在environment中配置对应的连接字符串和密钥并移除postgres和minio服务。配置反向代理和 HTTPS使用 Nginx 或 Caddy 为localhost:3000配置域名和 SSL 证书。方案二使用官方云服务 (Langfuse Cloud)对于不想维护基础设施的团队Langfuse 也提供了云托管版本。你只需要注册账号创建一个项目就能获得LANGFUSE_SECRET_KEY和LANGFUSE_PUBLIC_KEY直接在代码中使用。这对于快速启动和中小型项目来说管理成本最低。选择哪种方案取决于你的团队规模、运维能力和数据合规要求。1.3 环境检查清单在进入下一步之前确保以下几点服务可访问http://localhost:3000能打开并成功创建了项目和用户。拿到密钥在 Langfuse 项目设置中找到 “API Keys” 部分。你会需要LANGFUSE_PUBLIC_KEY用于客户端 SDK和LANGFUSE_SECRET_KEY用于服务端或需要写权限的操作。本地部署的密钥在项目设置里生成云服务在创建项目后自动提供。知道项目 ID创建项目时指定的 ID在 SDK 初始化时会用到。环境就绪后我们来看 Langfuse 里最核心的几个概念这决定了你怎么设计追踪代码。2. 理解核心概念Trace、Span、Generation 和 Event 到底记什么很多文档一上来就列概念但如果不结合场景很容易看晕。我习惯用一个最简单的 AI 问答流程来串讲这些概念比如“用户提问 - 调用 OpenAI API - 返回答案”。Trace追踪代表一次完整的、端到端的执行过程。比如处理一次用户提问的全流程就是一个 Trace。它是最高层级的容器有一个唯一的traceId。你可以把 Trace 想象成一次“会话”或“事务”的完整记录。Span跨度代表 Trace 中的一个逻辑操作单元。比如“验证用户输入”、“检索相关文档”、“调用大模型生成”、“后处理答案”都可以是独立的 Span。Span 可以嵌套形成树状结构清晰地展示出父任务和子任务的关系。Generation生成这是 Langfuse 为 LLM 调用专门设计的一类 Span。它特指一次向大模型如 GPT-4、Claude、本地模型发起请求并得到响应的过程。Generation 会自动记录输入prompt、输出completion、使用的模型、令牌用量、耗时和成本如果配置了单价。这是最常用、信息最丰富的记录类型。Event事件用于记录一些简单的、点状的信息比如“用户点击了按钮”、“缓存命中”、“开始处理文件”。它不像 Span 那样有明确的开始和结束时间更像一个打点日志。为什么这么设计因为 LLM 应用很少是单次 API 调用就结束的。一个复杂的流程可能包含意图识别 - 数据库查询 - 构建 Prompt - 调用 LLM - 结果格式化 - 安全检查。用 Trace 包住整个流程用 Span/Generation 拆解每个步骤你就能在 Langfuse 的界面上清晰地看到整个链路的耗时分布、成本构成以及哪一步出了问题。理解了这些我们来看如何用代码把它们记录下来。Langfuse 提供了多种集成方式我会从最简单的 SDK 开始。3. 项目接入实战从 Python/Node.js SDK 到 Spring Boot 集成接入的核心就是在你的应用代码中在关键位置插入 Langfuse SDK 的调用发送追踪数据到 Langfuse 服务器。我们分语言和场景来看。3.1 Python SDK 基础接入Python SDK 可能是使用最广泛的。首先安装pip install langfuse接下来在你的代码中初始化客户端并创建一个最简单的 Trace 和 Generation。假设我们有一个函数调用 OpenAIimport os from langfuse import Langfuse from openai import OpenAI # 1. 初始化 Langfuse 客户端 # 密钥从环境变量读取更安全 langfuse Langfuse( public_keyos.getenv(“LANGFUSE_PUBLIC_KEY”), secret_keyos.getenv(“LANGFUSE_SECRET_KEY”), host“http://localhost:3000” # 如果是云服务用 https://cloud.langfuse.com ) # 2. 创建一次 Trace trace langfuse.trace( name“user-question-answering”, user_id“user-123”, metadata{“channel”: “web”} ) try: # 3. 在 Trace 下创建一个 GenerationLLM调用 generation trace.generation( name“call-gpt-4”, model“gpt-4”, model_parameters{“temperature”: 0.7, “max_tokens”: 500}, input“请用中文解释一下量子计算的基本原理。” ) # 4. 模拟调用 OpenAI API client OpenAI(api_keyos.getenv(“OPENAI_API_KEY”)) start_time time.time() response client.chat.completions.create( model“gpt-4”, messages[{“role”: “user”, “content”: generation.input}], temperature0.7, max_tokens500 ) end_time time.time() answer response.choices[0].message.content # 5. 更新 Generation记录输出和详细信息 generation.end( outputanswer, usage{ “input”: response.usage.prompt_tokens, “output”: response.usage.completion_tokens, “total”: response.usage.total_tokens }, duration(end_time - start_time) * 1000 # 毫秒 ) # 6. 标记整个 Trace 成功结束 trace.update(outputanswer) except Exception as e: # 7. 如果出错记录错误信息 trace.update(metadata{“error”: str(e)}) generation.end(metadata{“error”: str(e)}) raise finally: # 8. 确保数据发送出去 langfuse.flush()这段代码做了几件关键事环境变量管理密钥和主机地址最好通过环境变量配置不要硬编码。Trace 创建trace()方法开启一个追踪上下文。user_id和metadata可以帮助你后续按用户或维度筛选。Generation 记录在调用 LLM 前用trace.generation()开始记录调用完成后用generation.end()记录输出、用量和耗时。这自动在界面上生成了一条清晰的 LLM 调用记录。错误处理在异常捕获中更新 Trace 和 Generation 的元数据记录错误信息。数据刷新langfuse.flush()会确保缓冲的数据被发送到服务器。在生产代码中SDK 通常有后台线程定期刷新但显式调用或在请求结束时调用更稳妥。跑通这个例子后打开 Langfuse 界面在 “Traces” 页面应该能看到这条记录。点进去可以看到 Generation 的详情包括输入、输出、令牌数和耗时。3.2 Node.js/TypeScript SDK 接入对于 Node.js 项目流程类似。先安装 SDKnpm install langfuse # 或 yarn add langfuse # 或 pnpm add langfuse然后是在代码中集成import { Langfuse } from “langfuse”; // 初始化 const langfuse new Langfuse({ publicKey: process.env.LANGFUSE_PUBLIC_KEY, secretKey: process.env.LANGFUSE_SECRET_KEY, baseUrl: process.env.LANGFUSE_HOST || “http://localhost:3000”, }); // 在异步函数中记录 async function answerQuestion(question, userId) { const trace langfuse.trace({ name: “user-question-answering”, userId: userId, }); const generation trace.generation({ name: “call-openai”, model: “gpt-4”, input: question, }); try { // 模拟调用 OpenAI const startTime Date.now(); const response await openai.chat.completions.create({...}); const endTime Date.now(); const answer response.choices[0].message.content; generation.end({ output: answer, usage: response.usage, duration: endTime - startTime, }); trace.update({ output: answer }); } catch (error) { generation.end({ metadata: { error: error.message } }); trace.update({ metadata: { error: error.message } }); throw error; } finally { await langfuse.shutdownAsync(); // 确保数据发送 } }Node.js SDK 的 API 与 Python 非常相似注意shutdownAsync方法用于优雅关闭并发送剩余数据。3.3 Spring Boot 项目集成对于 Java/Spring Boot 项目Langfuse 没有官方的 Java SDK但你可以通过两种方式接入方式一使用 HTTP API 直接调用Langfuse 提供了清晰的 REST API。你可以在 Spring Boot 中创建一个 Service 组件使用RestTemplate或WebClient来发送追踪数据。这需要你手动构建 JSON 请求体但控制更灵活。API 文档可以在你的 Langfuse 实例的/api路径下找到如http://localhost:3000/api。方式二使用社区或自己封装的 SDK你可以寻找社区维护的 Java 客户端或者基于 HTTP API 封装一个简单的客户端。核心逻辑是在你的 LLM 调用或业务关键方法前后调用这个客户端记录 Trace 和 Generation。一个简单的 Spring Boot 组件示例Component public class LangfuseClient { private final String publicKey; private final String secretKey; private final String host; private final RestTemplate restTemplate; public LangfuseClient(Value(“${langfuse.public-key}”) String publicKey, Value(“${langfuse.secret-key}”) String secretKey, Value(“${langfuse.host:http://localhost:3000}”) String host) { this.publicKey publicKey; this.secretKey secretKey; this.host host; this.restTemplate new RestTemplate(); this.restTemplate.getInterceptors().add((request, body, execution) - { request.getHeaders().set(“Authorization”, “Bearer ” secretKey); return execution.execute(request, body); }); } public void createTrace(String traceId, String name, String userId) { // 构建请求体调用 /api/traces 端点 // ... } public void createGeneration(String traceId, String generationId, MapString, Object input) { // 调用 /api/generations 端点 // ... } public void updateGeneration(String generationId, MapString, Object output, MapString, Object usage) { // 调用 PATCH /api/generations/{generationId} 端点 // ... } }然后在你的 Service 类中注入这个LangfuseClient在调用 AI 服务前后记录数据。虽然比 Python/Node.js 麻烦但对于 Java 技术栈的项目是可行的路径。3.4 使用 Decorators/装饰器简化代码Python如果你觉得在每个函数里手动写trace.generation()太繁琐Langfuse Python SDK 提供了observe()和score()装饰器可以自动追踪函数执行。这特别适合包装你的 LLM 调用函数或工具函数。from langfuse.decorators import observe, langfuse_context # 装饰一个普通的工具函数 observe() def retrieve_context(query: str): # 模拟检索 time.sleep(0.1) return [“文档1内容”, “文档2内容”] # 装饰一个 LLM 调用函数并自动捕获输入输出 observe(as_type“generation”) # 指定为 generation 类型 def call_llm(prompt: str, model: str “gpt-3.5-turbo”): # 这里调用真实的 LLM API response openai.chat.completions.create(...) return response.choices[0].message.content # 在主流程中使用 observe() # 追踪整个主流程 def answer_question(question: str): context retrieve_context(question) # 这个调用会被自动追踪为子 span final_prompt f“基于以下上下文{context}\n\n问题{question}” answer call_llm(final_prompt, model“gpt-4”) # 这个调用会被自动追踪为 generation return answer # 调用前需要设置当前 trace langfuse_context.set_current_trace(langfuse.trace(name“decorator-demo”)) result answer_question(“什么是机器学习”) langfuse.flush()使用装饰器后代码干净很多Langfuse 会自动记录函数的输入、输出、耗时和异常。在界面上你会看到一个树状结构的 Trace清晰地展示了answer_question-retrieve_context-call_llm的调用链。这是实现低侵入性监控的推荐方式。基础接入跑通后我们来看看如何利用 Langfuse 提供的丰富功能真正解决开发和运维中的实际问题。4. 不止于记录利用评分、对比、数据集和监控告警如果 Langfuse 只是另一个日志系统那价值就有限了。它的强大之处在于围绕“追踪数据”构建了一整套分析、评测和优化工具。4.1 人工评分与反馈收集你可以在 Langfuse 界面上对任何一条 Trace 或 Generation 进行手动评分Score。比如客服主管可以查看 AI 客服的回答并给出“相关性”、“准确性”、“友好度”的分数。这些分数会被记录并与对应的 Trace 关联。更强大的是你可以通过 SDK 在应用中集成反馈收集。例如在聊天界面添加“点赞/点踩”按钮用户点击后调用 SDK 记录一个分数# 用户对某次回答给出反馈 trace.score( name“user-feedback”, value1, # 1 表示正面-1 表示负面 comment“回答非常准确解决了我的问题。”, user_id“end-user-456” # 反馈用户ID )这些分数数据是后续评估模型表现、优化 Prompt 的黄金标准。4.2 对比实验与 Prompt 管理当你调整了 Prompt 模板、换了模型、或者修改了检索策略怎么知道新版本更好Langfuse 的“对比”功能让你能并排查看不同版本处理同一个问题的全过程。具体做法是在记录 Trace 时通过metadata或tags字段标记版本号或实验组trace langfuse.trace( name“qa-with-new-prompt”, metadata{“prompt_version”: “v2.1”, “model”: “gpt-4-turbo”}, tags[“experiment-a”] )然后在 Langfuse 界面的 “Traces” 页面你可以按prompt_version或tags进行筛选并选择两条 Trace 进行对比。界面上会高亮显示差异比如哪个步骤耗时变长了哪个 Generation 的令牌用量增加了输出质量有何不同。这是做 A/B 测试和迭代优化的利器。4.3 数据集管理与版本化评测Langfuse 允许你创建“数据集”Datasets。你可以将一些典型的用户问题或测试用例导入为一个数据集。然后针对这个数据集运行你的 AI 应用不同版本自动产生一批 Trace。之后你可以利用前面提到的“评分”功能手动或通过一些自动化的评测脚本例如调用另一个 LLM 作为裁判为这批 Trace 的产出打分。Langfuse 会汇总这些分数给出每个版本在数据集上的整体表现报告。这样每次代码或 Prompt 更新后你都能有一个量化的指标来衡量是进步还是退步。4.4 监控、告警与成本分析当应用上线后你需要关注异常和成本。Langfuse 的“监控”模块可以帮助你设置告警规则例如当最近1小时内错误率Trace 中标记 error 的比率超过 5% 时发送告警到 Slack 或邮件。分析令牌消耗和成本如果你在 Generation 中正确记录了usage和model并且配置了各模型的单价在项目设置中Langfuse 会自动计算每次调用的成本并展示每日、每周的成本趋势。这对于控制预算至关重要。追踪延迟查看 P50、P95、P99 的响应时间定位性能瓶颈。这些功能需要你在记录数据时尽可能提供完整的信息如usage、model并在 Langfuse 后台进行相应配置。5. 生产落地避坑指南从开发到上线的关键检查点把 Langfuse 集成到开发环境跑通 Demo 是一回事把它用到生产环境支撑每天数万甚至数百万的调用是另一回事。下面是我从几次落地过程中总结出来的关键检查点。5.1 性能与可靠性考量异步与非阻塞确保 SDK 的数据上报是异步的不会阻塞主业务逻辑。Python 和 Node.js 的官方 SDK 默认使用后台线程/进程进行批量发送但你需要了解flush()和shutdown()的时机避免在应用关闭时丢失数据。采样率控制在生产环境你可能不需要记录每一条请求尤其是流量巨大的场景。可以在 SDK 初始化时设置采样率或者根据 Trace 的属性如特定用户、特定功能动态决定是否记录。这能大幅减轻后端存储压力和网络开销。# 示例仅记录 10% 的请求或记录特定用户的全部请求 def should_sample(trace_name, user_id): if user_id in [“important-user-1”, “important-user-2”]: return True return random.random() 0.1数据量控制避免记录过于庞大的input或output比如整个文档内容。可以考虑只记录摘要、哈希或前 N 个字符。Langfuse 对单条数据大小有限制过大的数据会导致上传失败。错误处理与降级Langfuse 服务端可能暂时不可用。你的 SDK 调用应该被妥善的 try-catch 包裹确保即使追踪失败也不会影响核心业务功能。可以考虑将失败的数据写入本地日志或队列稍后重试。5.2 数据安全与隐私敏感信息脱敏用户的身份证号、手机号、地址等个人敏感信息绝对不要明文记录在input、output或metadata中。可以在发送到 Langfuse 之前进行脱敏处理如替换为[REDACTED]或哈希值。访问控制妥善保管LANGFUSE_SECRET_KEY。这个密钥拥有向项目写入数据的权限。不要把它放在前端代码或客户端环境中。对于前端应用应该通过你自己的后端服务来中转追踪数据或者使用具有更小权限的密钥。网络隔离如果你的 Langfuse 服务部署在内网确保生产服务器能够访问它而外部互联网不能。如果使用云服务确认数据传输是加密的HTTPS。5.3 运维与维护数据库清理策略追踪数据会快速增长。需要定期清理旧数据或者按时间分区。Langfuse 本身不提供自动清理你需要自己在 PostgreSQL 上设置定时任务或者定期归档/删除旧数据。升级计划关注 Langfuse 的版本更新。升级前先在测试环境验证兼容性特别是数据库 schema 的变更。备份数据后再进行生产环境升级。监控 Langfuse 本身监控 Langfuse 服务本身的健康状态CPU、内存、磁盘、PostgreSQL 的连接数和慢查询。确保这个监控工具自己不会成为单点故障。5.4 团队协作与流程项目与权限划分在 Langfuse 中可以为不同的产品线、不同的环境开发、测试、生产创建不同的项目。利用好项目和成员权限功能让不同团队的人只能看到自己相关的数据。命名规范为trace.name、generation.name建立团队规范。例如“chat-completion-v1”、“document-summarization”。一致的命名能让筛选和分析变得更容易。将 Langfuse 纳入开发流程在代码评审中检查新增的 AI 调用是否添加了合适的追踪。将 Langfuse 的评测数据作为模型或 Prompt 迭代的验收依据之一。我个人更建议先把单任务跑稳再考虑批量和接口。对于 Langfuse 这类工具真正落地时最该盯住的不是功能列表而是输入格式、资源占用和失败重试。先从一个小而具体的场景比如一个关键的问答接口开始集成验证整个数据流从代码到界面是否通畅再逐步推广到其他模块。踩过几次之后我发现很多问题不是工具能力不够而是前置环境和输入材料没有处理干净。
返回列表