Langfuse 本地 AI 调试台怎么给团队验收我用 cpolar 开了一个短时观测入口AI Demo 跑通以后最容易吵起来的不是模型能不能回答而是“这次请求到底发生了什么”。产品看到一句回答测试看到一个失败用例后端看到接口超时算法同事想看 prompt 和 token。只靠终端日志大家很快就开始互相截图、互相猜。我现在的做法是本地用 Docker 起一套 Langfuse把测试请求打成 trace验收时只用 cpolar 给 Langfuse Web 面板开一个临时 HTTPS 地址。团队看同一组脱敏 trace确认 prompt、响应、耗时、token 和错误日志。验收结束关掉 cpolar公网入口立刻回收。这篇不写大而全的 AI 平台搭建只把一个闭环跑通Langfuse 本地部署、最小 trace 上报、团队远程看面板、cpolar 短时 HTTPS 验收、安全边界和排错。适用场景这套流程用在 AI Demo 已经能调用模型但还没进入正式环境的阶段效果很直接。比如你本机跑了一个客服问答、RAG 检索、提示词改写、Agent 工具调用接口已经能返回结果。问题是团队验收时不能只看“最终回答”还要看这次调用用了哪个 prompt、输入输出是什么、耗时卡在哪里、失败原因是模型报错还是业务代码报错。可以用在这些场景里本地 AI Demo 需要产品、测试、后端一起验收调用链Prompt 改动后要把新旧 trace 放在面板里对照测试同事要复现异常输入、超时、空回复、JSON 解析失败后端同事要确认 token 用量、延迟、错误日志是否完整公司内网机器能跑 Docker但外部协作成员打不开本地面板验收数据已脱敏只展示测试用户、测试 prompt、测试输出重点不是把 Langfuse 长期挂公网。重点是本地观测面板只在验收窗口短时间开放验收完马上关。最终效果跑完后你会得到三样东西Langfuse 本地面板http://localhost:3000一条能在 Langfuse 里看到的测试 tracecpolar 临时 HTTPS 地址形如https://xxxx.cpolar.top团队验收链路是这样团队成员浏览器 | | HTTPS v cpolar 临时公网地址 | | 隧道转发 v 本机 Langfuse Web 面板 :3000 | v 查看脱敏 trace / prompt / latency / token / error你在本机继续跑 AI Demo 和 Langfuse。团队成员打开 cpolar 地址登录 Langfuse 面板后查看同一批 trace。验收会议里不用再贴十几张日志截图。大家直接在 Langfuse 里点开一次请求看输入、输出、耗时、metadata、level、status message问题归因会快很多。环境准备本文命令以 macOS / Linux 为例。Windows 用户放到 WSL2 里执行也能跑。需要准备DockerDocker Compose v2GitPython 3.10cpolar 客户端和 authtoken先检查 Docker 和 Gitdocker --version docker compose version git --version检查 Pythonpython3 --version准备工作目录mkdir -p ~/ai-observe-demo cd ~/ai-observe-demo如果你的机器内存比较紧先关掉不用的容器。Langfuse v3 的 Docker Compose 会带 Web、Worker、Postgres、ClickHouse、Redis、MinIO 等组件本地调试能跑但别拿这套单机 Compose 当高可用生产方案。Docker 启动 LangfuseLangfuse 官方仓库已经提供 Docker Compose 文件。本地试跑最省事的方式就是直接拉仓库。cd ~/ai-observe-demo git clone https://github.com/langfuse/langfuse.git cd langfuse启动前先生成几组本地 secret。下面这段命令只是在终端里生成随机字符串方便你替换配置里的默认值openssl rand -hex 32 openssl rand -hex 32 openssl rand -hex 32打开docker-compose.yml把里面明显的默认密钥替换掉。不同版本字段会有变化看到NEXTAUTH_SECRET、SALT、ENCRYPTION_KEY、数据库密码、ClickHouse 密码这类字段都换成自己的值。本地教程里偷懒不换密钥短时间也许看不出问题但一旦你后面用 cpolar 暴露面板默认密钥就是很糟糕的习惯。启动 Langfusedocker compose up -d查看容器状态docker compose ps看 Web 容器日志docker compose logs -f langfuse-web等日志里出现服务 ready 相关信息后打开http://localhost:3000第一次进入面板按页面提示注册本地管理员账号。然后创建一个项目比如Project name: local-ai-acceptance进入项目后在项目设置里创建 API Keys。你会拿到LANGFUSE_PUBLIC_KEYpk-lf-xxxx LANGFUSE_SECRET_KEYsk-lf-xxxx LANGFUSE_HOSThttp://localhost:3000这三个值后面会给最小示例使用。接入最小示例先生成一条 trace为了让教程可以直接跑我这里不用真实模型 API。先写一个“假 LLM 调用”重点验证 Langfuse trace 能不能上报。新建一个目录cd ~/ai-observe-demo mkdir -p langfuse-client-demo cd langfuse-client-demo python3 -m venv .venv source .venv/bin/activate pip install -U langfuse写环境变量文件cat .env EOF LANGFUSE_PUBLIC_KEY替换成你的_public_key LANGFUSE_SECRET_KEY替换成你的_secret_key LANGFUSE_HOSThttp://localhost:3000 EOF让 shell 读取它set -a source .env set a写一个最小脚本demo_trace.pycat demo_trace.py PY import os import time from langfuse import Langfuse langfuse Langfuse( public_keyos.environ[LANGFUSE_PUBLIC_KEY], secret_keyos.environ[LANGFUSE_SECRET_KEY], hostos.environ.get(LANGFUSE_HOST, http://localhost:3000), ) trace langfuse.trace( namelocal-ai-acceptance-demo, user_idtest-user-001, session_idacceptance-session-20260714, input{ question: 用户问订单 12345 为什么还没发货, env: local-demo, }, metadata{ app: customer-service-demo, git_branch: feature/langfuse-trace, tester: qa-local, }, tags[local, acceptance, cpolar-demo], ) start time.time() prompt 你是客服助手。请根据订单状态给出简短回复。 订单号12345 订单状态仓库已打包等待快递揽收 # 这里模拟一次 LLM 返回先不依赖任何外部模型服务 answer 订单 12345 已完成打包正在等待快递揽收。揽收后会自动更新物流信息。 time.sleep(0.8) latency_ms int((time.time() - start) * 1000) trace.generation( namemock-llm-call, modelmock-gpt-local, inputprompt, outputanswer, usage{ input: 42, output: 31, total: 73, }, metadata{ latency_ms: latency_ms, temperature: 0.2, }, ) trace.update( output{answer: answer}, metadata{result: accepted, latency_ms: latency_ms}, ) langfuse.flush() print(ftrace sent, latency_ms{latency_ms}) PY运行python demo_trace.py看到类似输出trace sent, latency_ms801回到 Langfuse 面板进入项目的 Traces 页面应该能看到local-ai-acceptance-demo。点进去后能看到trace 名称user_id / session_idinput / outputgeneration 名称prompt 内容mock 模型名token usagelatency metadatatags到这里本地 trace 链路已经通了。再加一条失败 trace验收时更有用只看成功请求没什么价值。团队验收时失败请求反而更能说明观测是否完整。再写一个失败脚本demo_error_trace.pycat demo_error_trace.py PY import os from langfuse import Langfuse langfuse Langfuse( public_keyos.environ[LANGFUSE_PUBLIC_KEY], secret_keyos.environ[LANGFUSE_SECRET_KEY], hostos.environ.get(LANGFUSE_HOST, http://localhost:3000), ) trace langfuse.trace( namelocal-ai-acceptance-error-demo, user_idtest-user-002, session_idacceptance-session-20260714, input{question: 请查询订单空字符串}, metadata{app: customer-service-demo, case: empty_order_id}, tags[local, acceptance, error-case], ) trace.event( namevalidate-input, input{order_id: }, output{ok: False, reason: order_id is empty}, levelERROR, status_message订单号为空业务校验未通过, ) trace.update( output{error: order_id is empty}, metadata{result: rejected}, ) langfuse.flush() print(error trace sent) PY运行python demo_error_trace.py回到 Langfuse把 tags 过滤到error-case就能看到失败样例。验收时让测试同事重点看这一条输入是否脱敏、错误原因是否明确、错误等级是否正确、业务字段是否足够定位问题。接真实 OpenAI 兼容接口时怎么写上面的 mock 示例用来验证链路。接真实模型时思路不变在发起 LLM 请求前后把 prompt、输出、usage、耗时写进 generation。下面是一个 OpenAI 兼容接口的写法。你可以接 OpenAI、通义兼容接口、硅基流动或者公司内部网关。pip install -U openai langfuseimport os import time from openai import OpenAI from langfuse import Langfuse client OpenAI( api_keyos.environ[OPENAI_API_KEY], base_urlos.environ.get(OPENAI_BASE_URL), ) langfuse Langfuse( public_keyos.environ[LANGFUSE_PUBLIC_KEY], secret_keyos.environ[LANGFUSE_SECRET_KEY], hostos.environ.get(LANGFUSE_HOST, http://localhost:3000), ) trace langfuse.trace( namereal-llm-call-demo, user_idtest-user-003, input{question: 用一句话解释 Langfuse trace 的价值}, tags[real-llm, acceptance], ) messages [ {role: system, content: 你是一个技术文档助手回答要短。}, {role: user, content: 用一句话解释 Langfuse trace 的价值}, ] start time.time() resp client.chat.completions.create( modelos.environ.get(OPENAI_MODEL, gpt-4o-mini), messagesmessages, temperature0.2, ) latency_ms int((time.time() - start) * 1000) answer resp.choices[0].message.content usage resp.usage trace.generation( namechat-completion, modelos.environ.get(OPENAI_MODEL, gpt-4o-mini), inputmessages, outputanswer, usage{ input: usage.prompt_tokens if usage else 0, output: usage.completion_tokens if usage else 0, total: usage.total_tokens if usage else 0, }, metadata{latency_ms: latency_ms}, ) trace.update(output{answer: answer}) langfuse.flush() print(answer)这里有个小细节不要把真实用户手机号、身份证、合同内容、内部系统 token 原样写进 trace。验收样例用测试数据线上数据做脱敏字段。Langfuse 做 LLM 可观测很顺手但观测系统本身也会沉淀敏感信息。这个边界必须提前说清楚。观测面板验收看什么Trace 能上报以后就可以准备团队验收了。别把链接一丢就结束最好提前准备一个检查清单。我一般让团队看这些项验收项在 Langfuse 里怎么看请求是否进入系统Traces 列表能看到目标 trace输入是否脱敏点开 trace看 input 和 generation input输出是否符合预期看 trace output 和 generation outputPrompt 是否是当前版本看 generation input 或 metadata 里的 git_branchtoken 是否有记录看 generation usage耗时是否可解释看时间线和 latency metadata失败是否能定位看 level、status_message、error-case tag是否能按用户或会话追踪看 user_id、session_id是否能按版本筛选看 tags、metadata、branch 信息验收时可以准备三类 trace成功请求证明主链路通业务失败比如参数为空、知识库无命中、权限不足模型失败比如模型接口超时、JSON 输出不合法、内容被拦截这样产品、测试、后端看到的是同一套证据而不是各看各的日志。用 cpolar 临时开放 Langfuse HTTPS 面板本地面板只能你自己打开。团队远程验收时用 cpolar 把localhost:3000临时映射成 HTTPS。安装 cpolar 后先登录并配置 authtoken。token 在 cpolar 控制台里复制cpolar authtoken 替换成你的_cpolar_authtoken启动临时隧道cpolar http 3000终端会输出一个公网地址类似Forwarding https://xxxx.cpolar.top - http://localhost:3000把这个 HTTPS 地址发给团队成员。对方打开后看到的就是你本机的 Langfuse Web 面板。这里有几个操作我会固定做给团队单独创建验收账号不共用你的管理员账号验收前确认 trace 数据是脱敏样例只开放3000Web 面板端口不开放 Postgres、ClickHouse、Redis、MinIO、模型服务、公司内网 API验收窗口结束后立刻CtrlC停掉 cpolar如果你使用的是固定域名隧道也要给它明确的使用窗口。别因为“链接还能打开”就一直挂着。安全边界这几条别偷懒Langfuse 面板里会沉淀 prompt、用户输入、模型输出、token 用量、错误信息、内部接口路径。它不是一个普通静态页面。所以 cpolar 在这里的角色是“短时验收入口”不是长期公网发布方案。我会按下面这几条做只使用测试项目不把生产 trace 混进验收项目prompt、用户输入、模型输出提前脱敏API Key、Cookie、Authorization Header 不写入 trace创建单独验收账号验收后禁用或删除只开放 Langfuse Web 端口3000数据库、ClickHouse、Redis、MinIO、模型网关全部留在内网cpolar 终端窗口保持可见验收结束马上关闭验收结束后清理测试 trace 或删除测试项目还有一点很容易忽略截图也算数据外发。如果团队要把 Langfuse 页面截图贴到群里先确认里面没有真实用户问题、内部 prompt、供应商 Key、订单号、手机号、邮箱、合同文本。很多泄露不是发生在公网入口而是发生在“随手截个图”。常见问题和排错1.docker compose up -d后面板打不开先看容器状态cd ~/ai-observe-demo/langfuse docker compose ps再看 Web 日志docker compose logs --tail100 langfuse-web如果 Web 容器不断重启通常是 secret、数据库连接、端口占用、依赖容器没起来。先确认3000没被占用lsof -i :3000被占用就停掉旧服务或者调整 Compose 里的端口映射。2. Python 脚本运行成功但 Langfuse 看不到 trace按这个顺序查echo $LANGFUSE_HOST echo $LANGFUSE_PUBLIC_KEY echo $LANGFUSE_SECRET_KEY确认LANGFUSE_HOST是http://localhost:3000然后确认脚本最后执行了langfuse.flush()Langfuse SDK 会异步发送数据不 flush 的话短脚本结束太快trace 还没来得及发出去。还要检查 API Key 属于当前项目。很多人会在 Langfuse 里建两个项目然后拿 A 项目的 Key 去 B 项目里找 trace当然找不到。3. token usage 为空mock 示例里我手动写了 usage。真实模型接口里要看供应商是否返回 usage 字段。OpenAI 兼容接口一般会返回resp.usage.prompt_tokens resp.usage.completion_tokens resp.usage.total_tokens如果你的网关没返回 usage可以先在 metadata 里记录估算值或者让网关层补齐 token 统计。验收时要把“真实统计”和“估算统计”区分清楚。4. trace 里时间不对本地容器、浏览器、服务器时区不一致时列表时间看起来会很怪。先看宿主机时间date再看容器时间docker exec -it langfuse-langfuse-web-1 date容器名以docker compose ps输出为准。验收结论里统一写北京时间或 UTC别让大家用各自电脑显示的时间互相对。5. cpolar 地址能打开但登录后页面异常先确认本地http://localhost:3000是否正常。如果本地都异常先修 Langfuse。如果本地正常cpolar 页面异常检查隧道是不是映射到了正确端口cpolar http 3000不要映射到数据库端口也不要映射到 ClickHouse、Redis、MinIO。团队只需要看 Web 面板。6. 团队成员能看到不该看的 trace这是权限和数据隔离问题不是 cpolar 的问题。处理方式很直接新建一个专门验收项目只导入或生成脱敏 trace不要把生产项目拿来共享。验收账号只给这个项目权限验收完禁用账号、删除测试项目或清理 trace。7. 端口 3000 被别的服务占了查占用lsof -i :3000停掉占用进程或者修改 Compose 端口映射。比如把宿主机端口换成3100ports: - 3100:3000然后本地访问http://localhost:3100cpolar 也跟着换cpolar http 3100关闭公网入口和清理现场验收结束后我会按这个顺序收尾。停掉 cpolar在运行 cpolar 的终端按 CtrlC确认外部 HTTPS 地址已经打不开。如果只是结束远程验收Langfuse 可以继续留在本机。但如果这次只是临时演示也可以停掉整套容器cd ~/ai-observe-demo/langfuse docker compose down如果要连测试数据一起清理执行前先确认没有要保留的 tracedocker compose down -v-v会删除 Compose 创建的数据卷Postgres、ClickHouse、MinIO 里的本地数据都会被清掉。这个命令用于临时演示环境不要在需要保留数据的机器上随手敲。最后把验收账号禁用或删除把临时 API Key 作废把发到群里的 cpolar 链接标记为已关闭。写在最后Langfuse 的价值不只是“看日志”。它真正解决的是 AI 应用验收里的证据问题一次回答背后的 prompt、输入、输出、耗时、token、错误能不能被团队一起看见。cpolar 在这个流程里也不是为了把内网服务永久暴露出去而是给本地调试台开一个短时、可回收的 HTTPS 验收窗口。我的经验是只要把脱敏 trace 准备好再把安全边界讲清楚一场 AI Demo 验收会顺很多。大家不用围着截图猜问题直接点开 trace 看事实。验收完记得关入口。这一步看着简单但它决定了“临时分享”到底是工程流程还是安全事故的开头。