本节摘要:离线回归集再全,也照不到线上的真实长尾——观测平台补上这一端。Langfuse 是开源、可自托管的 LLM 工程平台(官方支持 Docker / Kubernetes / VM 自托管,另有托管云),核心是两件事:trace 采集与在线 evals。trace 模型是三层嵌套树:一次用户请求记为一个 trace,内部每步操作是 span(检索、工具调用、业务逻辑),模型调用是更特殊的 generation(带 prompt、输出、token 用量与延迟)——session 与 user 字段把多次请求串成会话与用户画像。在线 evals 的分数挂在 trace 上,来源三路:用户反馈(界面上的 👍/👎 写成 score)、模型裁判评分器(对 trace 的输入输出按第 5 章 rubric 批量打分写回)、规则/统计(脚本对 trace 字段做计算,如"输出是否为空")。三路分数在同一个分析视图里随时间排布,就是第 8 章监控面板的数据底座;trace 分层抽样又反哺第 3.1 节的回归集——离线与在线从此闭环。本节代码均标注「写法示意,以官方文档为准」。
阅读完本节,你应当能够:
trace(一次用户请求:input = 用户问题,output = 最终回答) ├── span: 检索(query 改写 → 向量召回 → 重排) ← 业务步骤 │ └── span: 向量召回(top-k=5, 耗时 120ms) ├── span: 工具调用(查订单接口) ← 外部依赖 └── generation: LLM 调用 ← 模型调用(最特殊的 span) prompt 版本 = prompt-v12,模型 = xxx-snapshot token: 输入 1200 / 输出 350,延迟 1.8s session: 会话聚合(同一用户的多轮) user: 用户维度聚合(脱敏 ID)
三个字段是评测的眼睛:usage(token 与延迟——第 4.4 节成本三角的原始数据)、metadata(自定义标签:场景、路由分支——第 7.3 节 PSI 分桶的依据)、prompt 版本 / 模型标识(第 3.4 节版本化在线上的兑现:指标异动时可下钻到版本对比)。
# langfuse_instrument.py(写法示意,以官方文档为准) from langfuse import get_client, observe langfuse = get_client() # 凭环境变量读取 host / public key / secret key @observe() # 外层函数 → trace(自动捕获输入输出与耗时) def answer(question: str) -> str: docs = retrieve(question) return draft_answer(question, docs) @observe() # 内层函数 → span(调用层级即嵌套层级) def retrieve(question: str) -> list[str]: return ["退款政策:5 个工作日原路退回…"] # 示意 @observe(as_type="generation") # 模型调用 → generation(记录 prompt 版本与 usage) def draft_answer(question: str, docs: list[str]) -> str: return call_llm(question, docs) # 示意:换成厂商 SDK if __name__ == "__main__": print(answer("退款多久到账?")) langfuse.flush() # 短生命周期进程退出前冲刷缓冲
三条纪律:PII 脱敏在前(第 3.1 节同款——trace 是新的影子日志);采样控制成本(全量记 trace、按比例挂重评分器,示意:裁判只抽 1%~5%);版本字段必填(prompt 版本、模型快照写进 metadata,否则第 8 章的版本下钻无从谈起)。
# langfuse_scores.py(写法示意,以官方文档为准) from langfuse import get_client langfuse = get_client() trace_id = "当前 trace 的 ID" # 第 1 路:用户反馈(前端 👍/👎 回调里调用) langfuse.create_score(trace_id=trace_id, name="user-feedback", value=1, data_type="NUMERIC") # 1 赞 / 0 踩 # 第 2 路:模型裁判评分器(离线调好的第 5 章裁判,对抽样的 trace 批量跑) verdict = judge(question, answer, reference) # 5.1 节六件套裁判 langfuse.create_score(trace_id=trace_id, name="judge-rubric", value=verdict["score"] / 2, data_type="NUMERIC", comment=verdict["reason"]) # 第 3 路:规则/统计(便宜且确定的先跑) score = 0.0 if answer.strip() == "" else 1.0 # 空输出检测(示意) langfuse.create_score(trace_id=trace_id, name="non-empty", value=score, data_type="NUMERIC")
三路的分工与第 2.3 节判定分流同构:规则最便宜跑全量,裁判贵跑抽样,"用户反馈"是人工的众包形态——每一路都是带时间的分数流,汇进同一张按日聚合的看板(第 8.2 节监控面板直接消费)。裁判评分器通常由定时任务驱动:拉取近期 trace → 抽样 → 跑第 5 章裁判 → 分数写回——官方把这类任务称为 evaluation 的模型裁判方式,平台侧也有托管配置。
线上 trace ──分层抽样(3.1 影子日志配方)──▶ 回归集新样本(3.1/3.4 入库升版本) ▲ │ │ ▼ 在线评分器 ◀──离线调优(5 章裁判 + 4 章指标校准)──离线回归(6.1 Promptfoo)
两个方向的节奏(观点):
| 部署形态 | 适合 | 注意 |
|---|---|---|
| 自托管(Docker/K8s/VM,官方支持) | 数据敏感、内网合规 | 运维成本自担:升级、备份、容量 |
| 托管云 | 快速起步、小团队 | 数据出境与 DPA 条款要先过法务 |
无论哪种形态,评测侧的红线不变:PII 进 trace 前脱敏;裁判评分器用到的样本同样遵守第 3.1 节三条纪律(旁路不改线、脱敏先行、元数据随行)。
到此,离线(Promptfoo)与在线(Langfuse)两端都装上了。但工具市场远不止这两家——pytest 风格的 DeepEval、RAG 专项的 RAGAS、LangChain 系的 LangSmith……下一节横向比较六家主流工具,给出选型决策树。