用 Langfuse 打造终极 LLMOps 体系——在 Langfuse 中评估 LLM

0 阅读47分钟

引言

本章介绍 Langfuse 中的 Evaluation 功能,它允许我们同时使用自动化指标和人工反馈来评估回答质量。我们会覆盖一些定量指标,例如响应延迟、token 效率和单次查询成本;也会覆盖一些定性指标,例如相关性、连贯性、正确性和用户满意度。我们还将学习如何设置评估流水线,并从最终用户那里收集结构化反馈。本章重点在于构建一种结合自动化与人工判断的评估策略。我们也会看到如何运行实验来验证改进,并确保系统变更带来可衡量的收益。

结构

本章将覆盖以下主题:

Evaluation of LLM Applications
Online and Offline Evaluations in Langfuse
Offline Evaluation and Experimentation
Deployment and Live Monitoring
Online Evaluation and Feedback Integration
Core Concepts of Evaluation in Langfuse:
Scores
Evaluation Methods
Datasets
Experiments
Creating our First Dataset in Langfuse
What Makes a Good Dataset
Dataset Creation Using the UI/SDK
Synthetic Datasets
Automatic Trace Scoring Using LLM-as-a-Judge
Benefits of Using an LLM as a Judge
Setting up an Evaluator
Accessing Scores
Custom Evaluators
Manual Scoring
Human Annotations
Custom Scores
Running Experiments in Langfuse
Prompt Experiments
Experiments Using the SDK
Score Analytics

LLM 应用的评估

在第 4 章《Langfuse 入门》中,我们已经简要介绍过 Langfuse 的 Evaluation 功能。Langfuse 为评估和监控基于 LLM 的系统提供了一个稳健框架。它使团队能够在生产环境和测试环境中收集、可视化并分析定量和定性指标。

在深入这个功能之前,有必要先澄清:在 LLM 应用的语境中,我们所说的“评估”是什么意思。评估服务于多个目的:

性能验证:确保应用在已定义用例上按预期运行,例如准确回答问题或总结内容。
可靠性度量:判断模型在重复查询或轻微输入变化下,输出是否一致。
质量保障:评估语言连贯性、事实准确性,以及是否符合用户或组织标准。
风险缓解:识别潜在失败模式,例如幻觉、偏见或不安全输出。
优化反馈:提供可执行数据,用于改进提示词、微调模型或系统编排流水线。

当我们谈论评估一个 LLM 应用时,不能孤立地评估模型本身。我们必须评估整个系统,包括:

提示词设计和编排逻辑;
检索或上下文,如果适用;
模型输出本身;以及
用户交互反馈。

评估也应该与监控维度保持一致:功能性、非功能性、伦理和安全监控。

一个定义良好的评估框架,会回答三个基本问题:

应用是否在做它应该做的事?这些任务包括功能充分性、任务表现和语义准确性。
应用是否能在考虑延迟、成本和可扩展性的前提下,一致且高效地完成任务?
应用是否以负责任的方式运行,包括伦理对齐、安全、合规和公平性?

这些维度分别需要不同类型的指标和监控实践,其中许多都可以通过 Langfuse 的集成评估流水线、语义评分和反馈收集来支持。

通过把评估变成一个持续过程,我们可以开发出能够做到以下事情的系统:

早期漂移检测:在用户发现之前识别质量退化。
更快实验:用受控指标测试新的提示词或模型。
数据驱动优化:用可衡量趋势替代主观印象。
治理和可审计性:维护合规与伦理对齐的可追溯证据。
以用户为中心的改进:把真实世界反馈纳入监控循环。

Langfuse 中的在线评估与离线评估

如果回想第 4 章《Langfuse 入门》,评估大体可以分为离线评估和在线评估,分别在应用部署前和部署后执行。

image.png

图 7.1:如何持续改进 LLM 产品,Evidently AI
来源:www.youtube.com/watch?v=x9m…

这也在 LLM 应用开发生命周期中形成了一个持续循环。Langfuse 使用了这一过程的一个略微改造版本,但保留了评估的核心含义。

image.png

图 7.2:Langfuse 中的评估工作流
来源:langfuse.com/docs/evalua…

上图展示了一个持续反馈循环,说明 LLM 应用如何随着时间演进和改进。该图把它呈现为一个持续过程——一个连接开发、部署、监控和反馈的循环。

这个过程展示了数据和洞察如何在两个主要环境之间流动:离线实验和在线运行。二者共同形成一个闭环,确保 LLM 系统随着使用增长和条件变化持续适应。

离线评估与实验

我们通常从离线评估开始。这是一个团队可以安全、系统地进行实验的环境。在这里,LLM 应用会使用受控数据集、合成样例或人工整理的基准进行测试。因为输入和期望结果已知,我们可以计算一些定量性能指标,例如语义相似度、事实准确性或错误率。

这个阶段的重点是在部署前建立信心。我们可能会测试多个模型版本、提示词模板或检索策略,并衡量延迟、质量和成本之间的取舍。离线评估回答的问题包括:

模型是否正确遵循指令?
它的回答是否在语义上忠实于来源?
哪一种配置在质量和效率之间取得最佳平衡?

通过把模型与生产复杂性隔离开来,团队可以快速迭代,并识别最有希望部署的配置。

部署与实时监控

一旦应用在离线环境中表现良好,它就会进入生产环境。在生产中,真实用户会与应用交互,暴露出预先收集的数据集无法捕捉的行为和边缘案例。

在这个阶段,监控成为核心。延迟、token 消耗和错误频率等指标会被持续记录。更重要的是,用户交互本身会成为丰富的评估数据来源——通过显式评分、隐式反馈,例如重新提问或改写问题,以及会话结果来体现。

Langfuse 的 tracing 和 observability 功能让这一阶段变得具体:每一次模型调用、提示词输入和用户响应,都可以被追踪,并关联到性能和质量指标。

在线评估与反馈整合

第三个阶段通过把线上数据解释为评估输入来闭合循环。不同于使用静态基准的离线测试,在线评估使用生产数据——真实查询、真实用户反馈和真实世界条件——来动态衡量性能。

这个阶段有几个目的:

验证离线假设是否在真实使用中依然成立。
识别测试中不可见的新型失败或漂移。
捕捉真实的用户满意度和任务成功指标。

在实践中,这可能包括 A/B 测试两个提示词变体,比较不同模型的输出,或者用 evaluator LLM 自动给线上回答打分。这里收集到的洞察会直接反馈到离线评估阶段,用真实成功和失败样例丰富测试数据集。

对于现代 LLM 应用来说,这种持续循环非常重要,原因包括:

真实世界适应:离线评估无法预判所有可能输入。在线监控确保真实使用数据持续修正我们对性能的理解。
平衡优化:它支持一种整体方法——不仅优化准确性,也优化延迟、成本和用户满意度。
风险检测:来自生产环境的早期反馈,可以在风险扩大之前暴露回归、偏见或安全问题。
测试数据丰富:我们持续把真实世界使用数据,也就是非结构化数据,转化为可复用、带标签的结构化评估数据集,让未来离线测试更接近生产中的真实情况。
持续改进:最重要的是,它让产品随着模型、提示词和用户预期变化而持续演进。

Langfuse 中评估的核心概念

现在,我们来探索 Langfuse 定义的四个 Evaluation 核心概念:Scores、Evaluation Methods、Datasets 和 Experiments。这些抽象是开发时离线评估和生产时在线评估工作流的构建块。实践上,它们允许我们定义:我们在衡量什么(Scores)、如何衡量(Evaluation Methods)、基于什么数据衡量(Datasets),以及在什么上下文中衡量(Experiments)。

Scores

Langfuse 中的 Score,是对我们的应用或模型在某个特定任务或交互上表现如何进行评分的方式。Scores 提供了团队用来追踪改进或回归的可衡量信号。因为它们是 Langfuse 中的数据对象,所以可以被过滤、聚合、趋势化,并在不同版本或配置之间比较。Scores 把抽象的质量概念,连接到可以与应用运行数据一起存储的具体数值。

Score 可以绑定到单个 trace 或 observation。我们也可以对整个 trace session 进行评分。每个 score 只能引用这些实体中的一个,这意味着我们不能同时单独给一个 trace 和它下面的 observation 打同一个 score。

Langfuse 支持 numeric、categorical 或 boolean(true/false、0/1)类型的 scores。我们也可以创建自定义 score configs,来定义自己的评分指标结构。

Langfuse 中的 Experiments 也可以被评分,用于性能追踪。

Evaluation Methods

Langfuse 中的 Evaluation Methods,是产生 scores 的机制。换句话说,evaluation method 是一个函数或工具,它接收一个对象,例如 generation、trace 或 dataset item,并根据某种标准产生一个 score 或一组 scores。它们定义我们如何评价质量。Langfuse 当前支持 3 种 evaluation methods:

LLM-as-a-Judge:使用另一个 LLM 作为评估器,在幻觉、有帮助性或准确性等维度上给输出打分。
Human Annotations:由人工标注员手动审查回答或 traces,并基于人类判断产生 scores。
Custom Scores / Custom Methods:如果我们的领域有特殊需求,可以通过 API/SDK 实现自己的评估逻辑。

实践中,我们会在在线和离线评估阶段组合使用这些方法。后续章节会看到如何有效使用每种方法。

Datasets

在 Langfuse 中,Dataset 是一个结构化样例集合,包含输入和期望输出,主要用于离线评估。它相当于我们 LLM 应用的测试集或基准。

Datasets 提供受控条件,使我们可以系统比较模型版本、提示词变体或系统变更。它们充当评估中的 ground truth 一侧。当我们有期望输出时,就可以更可靠地衡量功能正确性。它们也提供可重复性,因为每次运行 dataset 时,我们都使用同一个 benchmark,从而可以随时间追踪回归和改进。

Langfuse 在创建 Dataset 方面也非常灵活。我们可以直接使用 UI 手动创建 items,也可以上传 CSV 文件。对于开发者而言,也可以通过 API/SDK 以编程方式创建 datasets 和 items。

Experiments

评估中的最后一个概念是 Experiment。它指的是在受控条件下,让我们的 LLM 应用运行在某个 dataset 上,并可选地应用 evaluation methods 来收集 scores 的过程。这个概念让我们能够并排比较不同模型、提示词和检索配置,追踪结果,并衡量影响。

我们可以在相同条件下运行同一个 dataset,因此结果是可比较的。

Experiments 本质上是前面三个概念之间的桥梁,帮助我们把评估工作流运营化。

到目前为止所覆盖概念的总结如下:

概念在评估工作流中的角色
Scores每次评估收集到的可衡量结果/指标
Evaluation Methods计算 scores 的机制
Datasets结构化输入集合,以及可选的期望输出
Experiments执行上下文:在 datasets 上运行应用 + methods + 捕获 scores

表 7.1:Langfuse 评估概念总结表

带着这些概念,我们现在开始准备运行实验,先在 Langfuse 中创建第一个 dataset。

在 Langfuse 中创建第一个 Dataset

我们的评估旅程从创建 validation/test dataset 开始。如前所述,Langfuse 中的 dataset 是一组结构化样例输入,理想情况下也包含期望输出。我们的系统会反复在这些样例上测试,以监控性能、检测回归并衡量改进。

什么构成一个好的 Dataset

为了从实验中获得最大价值,dataset 中的 items 必须可靠。创建真正有用的 dataset 时,可以考虑以下质量标准:

与用例相关

样例必须反映系统预期处理的真实任务。如果我们正在构建支持机器人,就应该包含真实用户问题,而不是通用提示词。

覆盖最重要的交互类型,例如常见查询、罕见但关键的支持问题,以及领域特定表达方式。

避免无关的通用 items——它们会稀释焦点,并可能给出误导性乐观评估。

难度多样性

标准场景,也就是“happy path”,系统应该能轻松成功。

边缘案例和棘手场景——模糊查询、多步推理、罕见领域术语。

失败/负面案例——系统可能表现吃力的 prompts,用于监控改进或回归。

清晰且无歧义的期望输出

如果包含 expected outputs,它们应该清楚定义,使比较有意义。如果我们依赖语义相似度,而不是 exact match,就需要定义如何衡量它。

对每个 item,确保 input 和 expected output 对齐,并映射到我们的评估方法,例如 prompt variables 与 item keys 匹配。

这种清晰性可以防止评估中的 false positives / false negatives,并提升可复现性。

可追溯到真实使用

纳入真实生产交互,尤其是系统失败或表现不足的案例。这些非常有价值,因为它们反映真实用户体验和领域挑战。

例如:选择一组导致负面用户反馈或高延迟的 prompts,补充 expected output,并跨版本追踪它们。

这种方法闭合了线上反馈与离线评估之间的循环。

可维护性和版本管理

随着系统变化,包括模型、提示词和检索逻辑,dataset 也应该演进。我们应该添加新 items,归档过时 items,并保留元数据来追踪 items 何时、为何改变。

维护 dataset 的 baseline 版本,这样就可以追踪回归。

足够规模和代表性

虽然一开始不需要成千上万条 items,但需要足够多样性来检测有意义差异。覆盖范围狭窄的小 dataset 会遗漏许多失败。

确保 dataset 能代表我们测试的输入光谱:不同长度、不同用户意图、不同领域复杂度。

关联评估上下文

每个 item 最好携带元数据,用于说明上下文,例如它是在什么 prompt 或模型版本下创建的,服务于什么业务场景,或者具备哪些标签,例如 “billing”、“technical support”、“escalation”。

这有助于后续过滤结果,例如:“提示词更新后,系统在 billing 相关问题上表现如何?”

使用 UI/SDK 创建 Dataset

现在终于到了创建第一个 dataset 的时候。最简单的开始方式是使用 UI。可以通过左侧导航点击 Datasets 来创建 datasets。如果还没有任何 datasets,应该会看到 + New dataset 选项。

image.png

图 7.3:使用 Langfuse UI 创建新 Dataset

要创建新 dataset,只需要一个名称。在名称中添加 / 会把 dataset 组织到文件夹中,推荐这样做,以便保持有序。在本例中,我们会创建一个名为 product_description_generator/happy_path 的新 dataset,用于测试前面章节中的 product_description_generator prompt。

Write a compelling and concise product description for an e-commerce listing.
Product Name: {{product_name}}
Key Features: {{features}}
Target Audience: {{audience}}
Write in a friendly, trustworthy tone. Limit your response to 90 words. End with a short call to action.

image.png

图 7.4:新的空 Dataset

完全相同的 dataset 也可以使用 SDK 创建:

from langfuse import get_client
from dotenv import load_dotenv

load_dotenv()
langfuse_client = get_client()

langfuse_client.create_dataset(
    name="product_description_generator/happy_path",
    description="Happy path use cases for the product_description_generator prompt",
    metadata={"version": 1, "created_by": "nikhilt"},
)

有几种方式可以向 dataset 添加 items:

上传 CSV:CSV 中每一行应包含三列:Input,用于传给 prompt 的输入变量;Expected output;以及可选 Metadata。
手动添加 items:如果 items 很少,并希望逐条添加,可以使用这种方式。这也是通过 UI 向已有 dataset 添加 items 的推荐方式。
使用 SDK:我们可以写代码把 items 添加到 dataset。
使用已有 traces:Langfuse 也允许我们把生产中已有 traces 添加到 datasets。后面会看到如何做到这一点。

对于 dataset item input,值应该是一个 JSON,包含 prompt 所有可能输入的 key-value pairs。Expected output 可以是 JSON,也可以是普通字符串。

image.png

图 7.5:向 Dataset 添加新 Item

目前 UI 不允许 expected output 是普通字符串。对于普通字符串输出,需要使用 SDK。Metadata 如果使用,也应该是 JSON。

我们也可以使用 SDK 添加 item:

langfuse_client.create_dataset_item(
    dataset_name="product_description_generator/happy_path",
    input={
        "product_name": "EcoSip Reusable Bamboo Travel Mug",
        "features": [
            "Made from 100% sustainable bamboo",
            "Double-wall insulation keeps drinks hot or cold for hours",
            "Leak-proof stainless steel lid",
            "Lightweight and dishwasher safe",
        ],
        "audience": "eco-conscious coffee and tea lovers on the go",
    },
    expected_output=(
        "Meet the EcoSip Reusable Bamboo Travel Mug — your stylish, sustainable way to enjoy drinks anywhere. "
        "Crafted from 100% bamboo with double-wall insulation, it keeps your coffee hot and your smoothies chilled for hours. "
        "The leak-proof stainless steel lid and lightweight design make it perfect for life on the move. Easy to clean, eco-friendly, "
        "and built to last — sip smart and reduce waste every day. Get yours now and travel green with EcoSip!"
    )
)

合成 Datasets

为测试和评估 LLM 应用创建高质量 datasets,通常是开发过程中最困难、最耗时的部分之一。LLM 应用处理的是开放式语言任务——摘要、推理、分类、生成——其中“ground truth”可能具有主观性,而且定义成本很高。

构建 LLM 评估数据集的挑战

Ground Truth 的歧义:许多 LLM 任务没有单一正确答案。例如,两个不同但同样优秀的产品描述,可能都满足 prompt 要求。定义什么叫“好”,需要人类判断、一致性和清晰评估标准。

人工标注成本高:收集带标签样例通常需要理解上下文、语气或领域的专家评审或标注员。这个过程慢、昂贵,并且很难扩展到大型评估集。

数据隐私和敏感性:使用真实世界数据,尤其是客户对话或支持日志,会带来隐私和合规挑战。这类数据必须在复用前匿名化或脱敏,从而增加 dataset 创建复杂度。

边缘案例覆盖有限:人工创建 datasets 往往过度代表常见或简单样例,而低估罕见、棘手的边缘案例。LLM 往往正是在这些案例中失败,例如模糊问题、否定表达或多步推理。

模型快速演进:随着提示词、模型版本和架构演进,过去构建的数据集可能不再反映应用的最新要求。这使得持续维护 dataset 变得困难。

合成 datasets 提供了一种可扩展且灵活的方式,来解决许多这类挑战。开发者不必只依赖手动收集样例,而可以使用 LLM 本身,或者其他自动生成流水线,创建模拟真实世界交互的合成输入输出对。Synthetic datasets 让我们能够更快、更安全、更大规模地测试 LLM 应用。通过调整 prompts、参数和上下文,我们可以快速生成数千个多样化样例,从而探索模型在不同语气、领域和边缘案例中的表现。一旦生成流水线建立起来,这些 datasets 就可以低成本生成,并随着系统演进持续更新。由于数据是机器生成的,它可以保持安全和合规,避免真实用户数据常见的隐私问题,同时仍然提供可定制 ground truths,用于准确评估。

我们会看到一个例子:如何使用 GPT 这样的 LLM,为 product_description_generator prompt 评估生成 datasets。我们会使用 SDK 把这些 items 添加到 dataset 中。

from langfuse import get_client
from langfuse.openai import openai
from dotenv import load_dotenv
import json

load_dotenv()
langfuse_client = get_client()

# 用于让 GPT 模型生成合成 dataset 样例的 system prompt
system_prompt = """
You are an AI assistant that generates high-quality synthetic dataset examples for evaluating product description generation models.
Your goal is to produce structured dataset items based on the given prompt template. Each dataset item should simulate a realistic e-commerce scenario with diverse product categories, features, and audiences.
Expected Output Format should be this JSON:
{
"input": {
"product_name": "…",
"features": ["…", "…", "…"],
"audience": "…"
},
"expected_output": "…"
}
"""

def generate_datatset_item(num_items=1):
    for i in range(num_items):
        # 我们要求 GPT 输出结构化 JSON
        completion = openai.chat.completions.create(
            model="gpt-4.1-mini",
            messages=[{"role": "system", "content": system_prompt}],
            response_format={"type": "json_object"},
        )
        dataset_item = json.loads(completion.choices[0].message.content)

        # 对每个 dataset item,在 Langfuse 中创建一个新的 dataset item
        langfuse_client.create_dataset_item(
            dataset_name="product_description_generator/happy_path",
            input=dataset_item["input"],
            expected_output=dataset_item["expected_output"],
        )

generate_datatset_item(10)

运行这段代码后,我们会在 dataset 中看到 10 个 dataset items。

image.png

图 7.6:使用合成 Dataset 创建方式创建新 Items

Langfuse 中还有许多创建 synthetic datasets 的方式。部分想法可以参考:

langfuse.com/guides/cook…

虽然 synthetic datasets 是加速早期开发的优秀方式,但它们应该被视为起点,而不是真实世界数据的替代品。它们帮助我们快速原型化、识别弱点,并在部署到生产前微调 prompts 或评分方法。然而,随着应用成熟,必须逐渐转向真实用户交互和真实数据样本来进行评估。真实样例能够捕捉合成数据无法完全复现的情况,确保我们的评估真正反映系统在实践中的表现。

使用 LLM-as-a-Judge 自动进行 Trace 评分

LLM-as-a-Judge 的概念,是利用一个模型根据定义好的标准,例如准确性、连贯性、语气或有帮助性,来评估和评分另一个模型,甚至它自己的回答。我们不再只依赖人工标注员,而是使用 LLM 的推理和理解能力,以结构化、可扩展的方式评估语言输出。这个模型充当评估器,解释 prompt 和 response,然后产生一个判断或分数,反映输出满足预期的程度。使用 LLM 作为 judge,可以让我们以更接近人类判断的方式评估质量。

随着应用演进,仅靠人工评估会变得不现实。人工审查昂贵、耗时,而且难以扩展到数千次交互。另一方面,LLM judge 可以实时评估输出,立刻反馈系统表现。它让我们能够持续、自适应地评估,并提供更深入洞察,判断模型是否在改进,或者是否随时间漂移。

使用 LLM 作为 Judge 的好处

可扩展且高效的评估:我们可以快速评估数千个模型输出,而不完全依赖人工审核。这使大规模评估可行,并支持持续监控模型质量。

大规模类人判断:LLM judge 可以捕捉语气、清晰度、相关性或事实准确性等特征,提供更接近人类质量感知的反馈。

一致性和可复现性:通过在评估中使用同一个 judging model,我们可以消除人工审核中常见的变异性和主观性,确保随时间保持一致且可重复的评分。

更快迭代和反馈循环:使用 LLM judge 可以加速实验周期。我们可以即时评估新的模型版本或提示词设计,早期检测回归,并在不等待人工标注的情况下优化系统。

负责任评估:我们可以设计 LLM judges,不只评估性能,也评估伦理和安全维度,例如偏见、包容性或有害内容,确保评估同时符合技术和社会责任目标。

Langfuse 将 LLM-as-a-judge 方法直接集成到评估工作流中,使我们可以用基于模型的 evaluators 自动给 traces 打分。配置完成后,Langfuse 可以运行一个 LLM,针对定义好的标准评估每条记录的 trace 或 output,并赋予数值或类别 score。这个评估在后台发生,因此不会影响应用性能。现在我们来看看如何用这种方法设置 evaluator。

设置 Evaluator

要设置 evaluator,可以从 UI 左侧导航进入 LLM-as-a-Judge 选项。如果还没有 evaluators,会看到 Create Evaluator 按钮。

image.png

图 7.7:为 Evaluator 选择默认模型

在选择 evaluator 之前,我们需要为项目设置默认模型。为此,选择右上角的铅笔图标。默认模型会被所有 automatic evaluators 用于每次评估。我们可以选择任何已配置的模型作为默认模型。使用此功能前,需要先设置 LLM Connection。关于如何设置,可以参考第 6 章《Langfuse 中的 Prompt Management》。所选择的默认模型应支持结构化输出和 function calling。

设置默认模型之后,我们可以选择 Langfuse 维护的 evaluators,也可以创建自己的 evaluator。第一个例子中,我们选择一个已有 evaluator,名为 Hallucination,用于计算 trace 的幻觉分数。

image.png

图 7.8:为 Hallucination 设置新的 Evaluator

Evaluator 可以设置在 Traces、Observations 或 Experiments 上。如果我们希望同一个 evaluator 同时用于两者,就需要设置两次 evaluator。稍后会看到如何为 experiments 设置 evaluator。设置 Traces 时,我们可以选择只对新 traces 运行 evaluator,也可以对过去已有 traces 运行评估。此外,还可以添加 filters,只挑选特定 traces 运行评估。

由于 LLM-as-a-Judge 技术耗时,并且每次评估都会产生 LLM 成本,我们可以对 traces 进行抽样,这会影响所有未来 traces。例如,50% 表示未来只有 50% 的 traces 会用这个 evaluator 评估。我们也可以添加延迟时间,单位为秒,即 trace 被记录后到评估开始之间的等待时间。这是为了确保评估不会在整个 trace 完整记录之前开始。对于长时间运行的 traces,应把这个值设得更高。

下一步要配置的是 trace 与 LLM evaluator 调用所使用 prompt 之间的变量映射。

image.png

图 7.9:Evaluator 的变量映射

对于 Langfuse 维护的 evaluators,已经有默认 prompt,可以直接使用而无需修改。这个 prompt 本质上是向 evaluator LLM 解释如何计算 score,这里是 Hallucination。虽然这个 prompt 对通用场景足够好,但在用于定制用例前,应该先更新它。

Langfuse 允许我们使用 trace/observation 的任意部分作为 prompt 中的变量。在这个例子中,有两个变量:query 和 generation,分别是 trace 的输入和输出。我们还可以看到包含变量映射的 prompt 预览,这就是将发送给 evaluator 的内容。使用 K 和 J 箭头,可以切换到上一条或下一条 trace 来查看预览。对于变量映射,Langfuse 支持 JsonPath 语法,以选择对象中的特定部分作为变量。一切设置完成后,点击 Execute 即可完成设置。设置 evaluator 后,可以再次进入 LLM-as-a-Judge 页面,查看正在运行的 evaluators。

image.png

图 7.10:正在运行的 Evaluators 列表

由于 evaluators 在后台运行,评估完成可能需要一段时间。在这个页面上,我们可以看到正在运行和已完成的评估,以及评估本身的总成本。

访问 Scores

设置 evaluators 后,Langfuse 会随着 traces 进入而自动开始收集 scores。访问被评估 traces 的 scores 有几种方式。

使用 Scores 区域

通过左侧导航进入 Scores 区域,可以看到所有计算出来的 scores 列表,包括 score 的 Value,以及 Comment 列中的评估解释。

image.png

图 7.11:Langfuse 中的 Scores

每次评估都可以从 judge LLM 得到一个 value 和一个关于该 score value 的解释。在这个页面上,也可以过滤 scores,并将它们下载为 CSV 文件。

从 Traces 页面查看

另一种在 trace 层面检查 scores 的方式,是导航到 trace 本身,可以从 Scores 页面或 Traces 页面进入。在单个 trace 中,可以点击 Scores 标签页,以表格形式查看该 trace 的所有 scores。这里同样可以使用 filters,也可以把 scores 导出为 CSV 文件。

在 traces 页面,Scores 也会以 badge 形式出现在 trace 名称下方。鼠标悬停在 badge 上,可以看到 score 的解释,也可以导航到 evaluation execution trace。

image.png

图 7.12:Trace 层面的 Scores

对 LLM judge 本身发起的调用,也可以作为 trace 在 Traces 页面查看。所有 evaluator traces 的 environment 都是 langfuse-llm-as-a-judge,可以用它来过滤这类 traces。随后,这些 traces 可以像普通 trace 一样查看,检查 evaluation 的推理过程和 score value。

image.png

图 7.13:Evaluation Trace

这条 trace 展示了对 judge 的确切调用,以及模型响应,包括 token 使用量、延迟和执行期间发生的任何错误。

自定义 Evaluators

Langfuse 提供了大量内置、即用型 evaluators,覆盖 LLM 常用指标。对于其中一些指标,它默认使用 Ragas 库提供 evaluators:

image.png

docs.ragas.io/en/stable/

图 7.14:Langfuse 与 Ragas 维护的 Evaluators

虽然这个列表很丰富,但有时我们仍然希望为 LLM-as-a-judge 方法创建自己的 evaluator。原因可能包括:

领域特定标准:我们可能需要衡量应用独有的质量,例如是否符合品牌语气、法律准确性或领域专业能力,而通用 evaluators 无法捕捉这些内容。

自定义输出格式:当模型生成 JSON、SQL 或代码等结构化输出时,定制 evaluator 可以确保正确性、格式和所需 schema 的遵循。

成本和效率优化:我们可以更新 evaluator 的 prompt,使其使用更少文本,从而降低成本和延迟,同时保持评估相关且聚焦。

多指标评估:有些应用需要在一次运行中评估多个方面,例如准确性、语气和清晰度。自定义 evaluator 可以把这些维度整合成一个连贯评分框架。

边缘案例评估:自定义 evaluators 可以帮助我们监控罕见或低频场景,例如模糊查询或矛盾输入。

要创建 custom evaluator,需要在 Set up evaluator 页面点击 + Create Custom Evaluator 按钮。

image.png

图 7.15:在 Langfuse 中创建 Custom Evaluator

从结构上看,Custom Evaluators 与内置 evaluators 相同。我们需要提供名称、带变量的主 prompt、评分解释,以及 range prompts,后者通常每个是一两句话。保存这个 evaluator 后,就可以像其他 evaluator 一样使用它。

手动评分

在评估 LLM 应用输出时,自动化指标无法完全替代人类洞察。Manual scoring,也称 Human Annotations,可以把人类判断纳入循环,评估那些难以可靠自动化的领域特定含义。

Human Annotations

Langfuse 通过 human annotation 工作流支持手动评分,使团队可以协作地对 traces、sessions 或 individual observations 添加 scores,从而形成 ground truth,帮助更广泛的评估策略。人工评分有很多有用原因。

理解上下文:人类评估者能够识别自动评分常常遗漏的细微之处。手动评分用于在评估中准确反映重要因素,例如客服回答的有效性、产品描述的清晰度,以及翻译的文化适当性。

构建可信 Benchmarks:经过人工审查的数据集会成为 benchmark,用于校准自动指标。这有助于让系统生成的评估与人类预期对齐,并为衡量未来改进提供可信基线。

管理新的或演进中的场景:当测试新用例,例如新产品类别、受众或语言时,预定义 evaluators 可能尚不存在。人工审核员可以弥补这个空白,在模型部署早期发现自动系统可能忽略的错误或不一致。

生成高质量评估数据:Manual annotations 会贡献到经过良好整理的数据集,用于回归测试和持续评估。因为这些 scores 反映真实人类判断,所以可以确保未来实验测试真正重要的内容。

开始使用 Annotations

和自动评分一样,annotations 可以添加在 Trace、Observation 或 Session 层面。在手动给对象评分之前,需要定义一个 score config,用于描述 score,并定义其参数,例如数据类型和允许值。要创建 score config,需要进入 Settings,然后选择 Scores/Evaluation。

image.png

图 7.16:添加新的 Score Config

在这个例子中,我们会添加一个名为 “Helpfulness” 的 score config,它是一个 1 到 10 之间的 numeric score。这个 score 将由人工标注员用于评价前面示例中生成产品描述的 helpfulness。

其他 score 类型包括 Categorical,值可以是固定标签;以及 Boolean,用于 0/1 或 True/False 类型的 score。我们可以为项目创建任意数量的 score configs。Score Configs 不能跨项目共享,因此如果需要在另一个项目中使用相同 scores,必须手动复制。

创建 score config 后,还需要创建一种叫 Annotation Queue 的东西。这些队列帮助团队以规模化方式管理和组织人工审核任务。我们可以把 traces、sessions 或 observations 分组到专门队列中进行审核。这些队列允许我们分配特定评分标准,追踪标注进度,并在审核员之间分配 items,使标注工作更容易协调。

Langfuse 中使用 Annotation Queues 的优势包括:

可以通过为每个队列分配特定 score configurations 和维度,确保多个审核员之间评分一致,从而支持标注标准化。

它们提供进度可见性,让我们可以监控哪些 items 已经被审核、由谁审核,以及哪些 items 仍然待处理,从而提高透明度。

Annotation Queues 支持可扩展协作,使多个审核员可以并行工作而不会产生冲突标注,从而更高效地处理大批量 items。

由于 items 会以队列形式批处理,审核吞吐量更高。我们可以更快完成审核周期,并更迅速地把已标注案例反馈回评估流水线。

要创建 queue,可以从左侧导航进入 Human Annotation,然后点击 + New queue

image.png

图 7.17:添加新的 Annotation Queue

在本例中,我们会创建一个 queue 来管理产品描述评分,并使用上面创建的 score config。

向 Queue 添加 Items

一旦有了 queue 和对应 score config,就可以开始把需要评分的对象填充到队列中,让人工评估者开始评分。向 queue 添加 items 有几种方式。

从 Tracing 导航中,我们可以在 trace detail view 使用选项把单个 trace 添加到 queue。这个选项位于右上角,紧邻 Annotate 按钮。

image.png

图 7.18:把 Trace 添加到 Annotation Queue

在这个视图中,我们也可以直接标注 Trace,而不把它添加到 queue。只需要选择 score config,并可选添加 comment。新添加的 score 随后会和该 trace 的其他 scores 一起出现。

image.png

图 7.19:向 Trace 添加 Score

除了逐个添加 traces,我们也可以选择多个 traces,然后使用 Actions 按钮,把它们全部添加到 queue。

image.png

图 7.20:批量把 Traces 添加到 Annotation Queue

上面的选项同样适用于给单个 Observations 评分,而不仅是 Traces。我们也可以从 Experiment runs 中添加对象进行标注,用于对两个 experiments 进行 side-by-side comparison。我们会在 Prompt Evaluation 部分看到如何做到这一点。下一节我们会看 Custom Scores,了解如何以更灵活方式给对象评分。

Custom Scores

在 Langfuse 中,custom scores 让我们能够通过 API 或 SDK 定义并接收自己的评估指标。这些是 score objects,可以是 Numeric、Categorical 或 Boolean,并附加到 traces、observations、sessions 或 dataset-run items 上。它们可以反映任何我们关心的事情,例如生成的 JSON 是否有效、回答是否遵循所需格式,或者 agent 中某个工具是否成功。因为 custom scores 完全由我们控制,我们可以设置前面看到的 score configs,并从几乎任何评估流水线摄入它们,无论是人工、自动化还是基于规则。

在 LLM-as-a-Judge 中,我们配置一个 LLM,根据某个指标,例如 helpfulness、correctness、hallucination 等来评估输出,并相应赋分。当我们需要可扩展的类人判断,并且希望减少人工工作时,这种方法很有用。Custom scores 则把评估逻辑从 LLM 转移到自定义逻辑。因此,custom scores 提供了更多控制,但除非我们自己构建,否则它们缺乏内置的类人细腻度。

Human Annotation 则涉及人工审核员基于判断、细微差别、语气和上下文来赋分,尤其适合自动系统难以处理的主观质量。Custom scores 也可以代表人类判断,但它们不像 Human Annotation 那样绑定到具体人工审核工作流。它们允许把任何来源,无论人类还是自动化,摄入评分流水线,而不仅限于人工审核员。

当我们需要对评估如何定义和衡量进行细粒度控制时,custom scores 尤其有价值。它们允许我们直接把自己的规则、业务逻辑或验证检查编码进评分,而不是依赖预构建 evaluators。

它们在以下场景中特别有用:

领域特定需求:当评估标准与应用内部目标或运营标准相关,例如验证合规话术、财务准确性或品牌对齐时,custom scores 允许我们精确表示这些指标。

结构化或非文本输出:许多 LLM 应用返回 JSON、SQL 或 API response 等格式的数据。在这些情况下,可以用 custom scores 以编程方式验证结构、正确性或执行成功,而语言类评估可能不够。

与现有系统集成:如果我们已经从下游来源收集指标,例如点击率、用户满意度标记或自动化测试结果,就可以把它们作为 custom scores 反馈到 Langfuse 中,与其他评估统一追踪。

精确性和可复现性:通过定义自己的 score schema、数据类型和可接受范围,可以确保评估随时间保持一致。这让比较模型版本、衡量进步,以及追踪评分逻辑变化变得更容易。

合规和透明性:对于在受监管或高信任环境中运行的团队,custom scores 能够完全展示评估决策如何产生。每条规则或评分标准都可以被显式记录,并与组织或法律标准对齐。

不同于自动化和手动评分技术,custom scoring 大多通过 SDK/API 完成。下面会看到一些示例。

示例 1:基于外部 API 调用响应给 Trace 打分

使用这种方法给 trace 打分,最简单的方式是在 trace 摄入时添加 score。这样,当方法完成时,trace 和 score 会立即出现在 UI 中。这也可以保证 score 总是与对象一起可用。

与手动评分技术类似,我们应该定义一个 score config,可以通过项目设置完成,这是推荐做法,也可以直接用 SDK 创建 scores。

from langfuse import get_client
from dotenv import load_dotenv
import requests

load_dotenv()
langfuse_client = get_client()

def get_api_success_score(status_code: int):
    return 1 if status_code == 200 else 0

def get_weather(city: str):
    """
    Fetch weather data for a given city using the wttr.in API.
    """
    with langfuse_client.start_as_current_span(name="get_weather") as span:
        # 更新 trace input
        span.update_trace(input=city)

        url = f"https://wttr.in/{city}?format=j1"

        try:
            response = requests.get(url, timeout=5)
            response.raise_for_status()
            data = response.json()
        except Exception as e:
            print(f"API call failed for {city}: {e}")

        # 更新 trace output
        span.update_trace(output=data)

        # 给 trace 打分
        span.score_trace(
            name="api_call_succeeded",
            value=get_api_success_score(response.status_code),
            data_type="BOOLEAN",
        )

get_weather("London")

这会创建一个新的 Boolean 类型 score,名为 api_call_succeeded,之后也可以被其他对象使用。Langfuse 允许用同一个 score name 多次给同一个对象打分,此时它会被存储为多值列表。这有助于观察同一对象上某个 score 的趋势。

在这个例子中,我们也直接在 score_trace 方法调用中用代码创建了 score object 本身。这不是最佳实践,因为它不会强制约束 score 如何被捕获。对于生产用例,应该先从 score config 开始,并用它给对象打分。

示例 2:检查 SQL 语法

假设我们正在创建一个 LLM 应用,它根据用户问题生成 SQL 查询。我们希望知道有多少比例的问题会产生可以成功执行的查询。

在这个例子中,我们会创建一个名为 “SQL Execution Success” 的 score config,然后在 score_trace 调用中使用 config ID。

from langfuse import get_client
from dotenv import load_dotenv

load_dotenv()
langfuse_client = get_client()

def check_sql_execution(sql_query: str) -> float:
    try:
        # 在这里针对数据库运行 query
        return 1.0
    except Exception as e:
        print(f"SQL Error: {e} when running {sql_query}")
        return 0.0

def generate_sql(question: str):
    """
    Generate an SQL statement based on the given question.
    """
    with langfuse_client.start_as_current_span(name="generate_sql") as span:
        # 更新 trace input
        span.update_trace(input=question)

        # 生成 SQL 的逻辑
        generated_sql_query = """
SELECT COUNT(*) AS total_orders
FROM orders
WHERE order_date>= DATE_TRUNC('month', CURRENT_DATE - INTERVAL '1 month')
AND order_date< DATE_TRUNC('month', CURRENT_DATE);
"""

        # 更新 trace output
        span.update_trace(output=generated_sql_query)

        # 给 trace 打分
        span.score_trace(
            name="SQL Execution Success",
            config_id="cmhxf7z9z0001qt07rta5bc7r",
            value=check_sql_execution(generated_sql_query),
        )

generate_sql(
    "How can I find the total number of orders placed by customers in the last month?"
)

config_id 可以在 Project Settings 的 Score Configs 区域找到。需要注意的是,在 score_trace 调用中,不会验证 config_idname 是否匹配。config_id 只确保 score 遵守 config 中提到的数据类型、允许值或数值范围。

Custom scores 可以从左侧导航的 Scores 标签页查看。

image.png

图 7.21:Langfuse 中的 Scores 页面

如果提交 score 时没有显式指定数据类型,Langfuse 会通过检查值本身来决定类型。对于 categorical 和 Boolean scores,在相关情况下,我们可以同时保留字符串标签和数值等价物,即使最初只提供了一种形式。

Custom scores 的自动数据类型推断示例如下:

Score TypeInput ValueInferred data typeValidation Rules
Numeric0.75, 42, -3.2NUMERIC,如果未设置 data type如果没有 config,会自动当作 numeric。若 data type 是 NUMERIC,但值非数字,则无效。
Numeric with Config0.5NUMERIC,基于 config 验证必须落在 Score Config 定义的允许范围或约束内。
Categorical"correct", "partial", "wrong"CATEGORICAL如果 value 是字符串,则推断为 categorical。如果设置了 Score Config,也可能接受 numeric codes。若 value 类型与 config 冲突,则无效。
Categorical with Config"high"CATEGORICAL只有 config 中允许的值有效;系统可能把字符串映射到数值表示用于报告。
BooleanTrue, False, 1, 0BOOLEAN如果没有 config,数字 1/0 或 True/False 会自动解释为 Boolean。如果字符串 “true”/“false” 未设置合适类型,则无效。
Boolean with ConfigTrueBOOLEAN必须匹配 config 中的类型和允许值,否则被拒绝。

表 7.2:Langfuse 中 Custom Score 的自动数据类型推断

本节只覆盖了在摄入过程中向 traces 添加 custom scores。不过,有时我们可能希望给过去已经生成的对象添加 custom scores。对于自动和人工评分,这很直接;但对于 custom scoring,需要通过编程方式检索 traces,然后使用 API 或 SDK 更新 scores。后续章节会看到如何对已有对象执行这一操作。

在 Langfuse 中运行 Experiments

到目前为止,本章已经为在 Langfuse 中运行 experiments 打下基础。Experiments 是 LLM 应用生命周期中非常关键的一步,也是每次修改应用时都需要反复回到的环节。

Langfuse 中的 Experiments 提供了一种结构化方式,用于针对 dataset 测试不同版本的 prompts、models 或 workflows,并在相同条件下比较输出。这使评估成为可重复、可衡量的过程。为应用运行 experiments 有很多好理由:

跨版本受控比较:当我们做出修改,例如修改 prompt、切换模型参数或更新业务逻辑时,需要确认变更确实带来了改进。但如果没有测试,就无法确定。运行 experiment 能让我们把同一个 dataset 分别输入旧版本和新版本,并以一致方式比较结果。这可以透明地看到变更带来的是更好、更差,还是只是很小差异。

早期回归检测和持续改进:随着系统演进,prompts 会改变,models 会更新,领域数据也会漂移,因此存在回归风险。Experiments 是防止回归的安全检查。每次修改时,我们都可以重新运行 benchmark dataset,观察质量是否下降。这有助于在开发新功能的同时确保新版本不降低性能。

做出数据驱动决策:与其假设新的 prompt 版本更好,不如用 experiments 基于定义好的 metrics 客观比较 prompt 版本。随着时间推移,这些定量结果会形成证据,指导 prompt 开发、模型选择和发布决策。这样我们可以有信心地说某个版本比另一个更好,而不是凭感觉判断。

Prompt Experiments

Langfuse 中第一个也是最常见的 experiment,是 Prompt Experiment。这类 experiments 直接从 UI 触发,主要目的是针对 dataset 比较 prompt 版本或 LLM 版本。

每个 experiment 都允许我们比较不同 runs 之间的基本指标,例如 token usage、cost 和 latency。为了让 experiments 更有意义,应该把 experimentation 与前面看到的一种或多种 scoring 技术结合起来,才能真正发挥 run 结果的价值。这个步骤在 Langfuse 中是可选的,但强烈推荐。

要运行 Prompt experiment,需要具备:

Prompt:这是我们要实验的 prompt。第 6 章详细介绍了 Prompt Management,我们学过如何在 Langfuse 中添加和维护 prompts。
Dataset:需要创建一个 dataset,prompt 会在这个 dataset 上运行。只有当 dataset items 是 JSON 对象,并且 keys 映射了 prompt 中所有变量时,dataset 才能用于 experiment。
LLM Connection:运行 prompt 时需要 LLM connection。LLM connections 也在第 6 章《Prompt Management in Langfuse》中介绍过。
LLM-as-a-Judge:Prompt experiments 最适合与这种 scoring 技术搭配使用。虽然 Human Annotations 和 Custom Scores 也可能使用,但默认情况下不适用于 Prompt Experiments。

一旦满足这些前置条件,就可以从 Prompts 导航菜单进入 prompt,并开始 experiment。

第一个例子中,我们会对 product_description_generator prompt 运行 experiment,用于比较 GPT 4.1 和 GPT 4.1-mini 模型之间的回答。比较使用三个指标:我们之前创建的 Friendly Tone,以及 Langfuse 内置 evaluators 中的 Helpfulness 和 Conciseness。

我们从选择 prompt 和版本开始。模型列表会根据我们设置过的 LLM connections 显示。在这个步骤中,也可以指定模型期望输出的 JSON 格式。

image.png

图 7.22:为 Experiment 选择 Prompt 和 Model

下一屏需要选择 experiment 使用的 dataset。选择 dataset 后,Langfuse 会验证 dataset 中每个 item 是否具备 prompt 所需变量。不正确的 dataset items 不会被包含在 experiment 中。

image.png

图 7.23:为 Experiment 选择 Dataset

Dataset 之后是 metrics/evaluators。这个步骤是可选的,但我们应该尽量为 run 配置一些 evaluators。在这个例子中,我们配置了三个 evaluators:Friendly Tone、Helpfulness 和 Conciseness。

image.png

图 7.24:为 Experiment 选择 Evaluators

下一屏可以为 experiment 起一个有意义的名称,并可选添加描述。

image.png

图 7.25:添加 Experiment 名称和描述

最后一页可以在开始 experiment 之前预览所有输入。Langfuse 也会自动为每个 experiment run 生成 run name。这个 run name 之后可以通过 API/SDK 获取 run。

我们也可以返回前面步骤,修改或更正信息。

image.png

图 7.26:开始前预览 Experiment 输入

点击 Run Experiment 后,Langfuse 会启动 experiment。对于每个 dataset item,prompt 会被填入变量,然后发送给我们选择的 LLM,在这里是 GPT 4.1。每个 item run 也会作为 trace 被 Langfuse 捕获。如果设置了 evaluators,它们也会作为单独 traces 运行,并相应捕获 scores。根据 dataset 大小和 evaluators 数量,experiment 可能需要几秒到几分钟。

Langfuse 在运行 items 或为它们评分方面非常高效。它的事件驱动架构结合后台处理,可以轻松处理数万次 item runs,而不会出现性能卡顿。

在我们的例子中,会用相同输入再运行一个 experiment,但模型改为 GPT 4.1-mini。

image.png

图 7.27:Experiment Comparisons

Experiments 的结果可以在 experiment 使用的 dataset 的 Datasets 区域下找到。

在这个简单例子中,可以看到 GPT 4.1-mini 比 GPT 4.1 快得多也便宜得多。如果只能在这两个选项之间选择,尽管 4.1-mini 在 Friendliness 上低了 0.2 分,但考虑成本和时间节省,它可能是理想选择。

我们也可以选择一个或多个 runs,然后选择 Actions -> Compare,获得更详细的 experiment run 对比。

image.png

图 7.28:Experiment Run 详细比较

运行这类 experiments 时,有几个潜在陷阱:

确保 prompt 中的每个变量,都匹配 dataset 中对应字段。不匹配会导致该 item 从 experiment 中被省略。

一次只改变一个主要因素,例如 prompt wording 或 model version。如果多个变量同时改变,就无法识别到底是哪一个导致改进或回归。

大型 experiments,尤其是跨多个模型版本或大规模 dataset 运行的实验,可能很快消耗 API tokens 和预算。即使使用 LLM-as-a-Judge 进行评估,也会消耗 API tokens,因此运行 experiment 时必须考虑这一点。

Prompt experiments 更适合开发的 Offline 阶段。当应用上线后,我们会希望在真实应用上运行 experiments,并基于 Human Annotation 或 Custom Scores 评分。如何做到这一点,是下一节的主题。

使用 SDK 运行 Experiments

通过 SDK 运行 experiments,相比直接在 UI 中运行 experiments,提供了更多控制、更高可靠性和更强扩展性。UI 很适合快速比较或简单 prompt 测试,但前面看到的许多限制,都可以通过以编程方式运行 experiments,并使用真实应用输出比较 prompt 或 model 版本来克服。使用 SDK 进行 experiments 的一些特性包括:

完全控制 experiment 逻辑:SDK 允许我们显式定义每个 dataset item 如何流经模型调用。这使我们可以使用来自 Langfuse 或任何其他来源的 datasets,而不需要 prompt 和 dataset items 之间严格变量匹配。

Custom Scoring:由于我们控制 runs,每个 item run 都可以使用自己的逻辑和 metrics 进行 custom scoring。

Parallel Runs:experiment runner SDK 允许多个 experiments 在同一个 dataset 上并行运行,并保持正确隔离。而 UI 中的 prompt experiments,需要我们手动触发每个 run。

与现有基础设施集成:由于 experiments 是用代码编写的,可以接入现有 CI pipelines、analytics dashboards 或结构化验证逻辑。

要运行类似上面看到的 experiment,可以使用以下代码:

from langfuse import get_client
from dotenv import load_dotenv
from langfuse.openai import openai

load_dotenv()
langfuse_client = get_client()

# 定义 dataset
dataset = [
    {
        "input": {
            "audience": "Environmentally conscious coffee lovers",
            "features": [
                "Made from sustainable stainless steel",
                "Fits most standard coffee makers",
                "Easy to clean and dishwasher safe",
                "Reduces single-use paper waste",
                "Improves coffee flavor by preserving oils",
            ],
            "product_name": "EcoBrew Reusable Coffee Filter",
        }
    },
    {
        "input": {
            "audience": "Casual and professional runners",
            "features": [
                "Breathable mesh upper",
                "Lightweight EVA sole",
                "Reflective accents for night safety",
                "Arch support with memory foam insole",
            ],
            "product_name": "AeroSwift Running Shoes",
        }
    },
]

# 从 Langfuse 获取 prompt
prompt = langfuse_client.get_prompt("product_description_generator")

# 定义运行每个 dataset item 的逻辑
def generate_product_description(*, item, **kwargs):
    input_variables = item["input"]
    response = openai.chat.completions.create(
        model="gpt-4.1-mini",
        messages=[
            {
                "role": "system",
                "content": prompt.compile(
                    product_name=input_variables["product_name"],
                    features=input_variables["features"],
                    audience=input_variables["audience"],
                ),
            }
        ],
    )
    return response.choices[0].message.content

# 在 dataset 上运行 experiment
result = langfuse_client.run_experiment(
    name="Prompt product_description_generator-v2 with GPT 4.1-mini",
    description="Experiment run of prompt product_description_generator-v2 with GPT 4.1-mini model",
    data=dataset,
    task=generate_product_description,
)

print(result.format())

要运行本地 experiment,需要:

Dataset:可以是本地 dataset,也可以是 Langfuse 中已有 dataset。
Task:这是对每个 item 执行的方法,应返回应用输出。

这次运行使用本地数据,因此不会创建 dataset item,只会生成单独 traces。若要生成 dataset items 并比较 runs,需要使用已有 datasets。可以通过下面方式替换手动创建 dataset 的部分:

dataset = langfuse.get_dataset("product_description_generator/happy_path")

result = dataset.run_experiment(
    name="Prompt product_description_generator-v2 with GPT 4.1-mini",
    description="Experiment run of prompt product_description_generator-v2 with GPT 4.1-mini model",
    task=generate_product_description,
)

Custom Evaluators

对于通过 SDK 运行的 experiments,Langfuse 提供选项,可以在 individual item 层面以及 run 层面添加自定义 evaluators 或 metrics。当计算 experiment metrics 需要应用级业务逻辑,或者需要访问外部系统或数据库时,这很有用。下面例子中,我们会针对 product_description_generator dataset 运行 experiment,并计算一个 item 层面的简单指标 word_count,以及 run 层面的 max_word_count

from langfuse import get_client
from dotenv import load_dotenv
from langfuse.openai import openai
from langfuse import Evaluation

load_dotenv()
langfuse_client = get_client()

# 定义 dataset
dataset = langfuse_client.get_dataset("product_description_generator/happy_path")

# 从 Langfuse 获取 prompt
prompt = langfuse_client.get_prompt("product_description_generator")

# 定义运行每个 dataset item 的逻辑
def generate_product_description(*, item, **kwargs):
    input_variables = item.input
    response = openai.chat.completions.create(
        model="gpt-4.1-mini",
        messages=[
            {
                "role": "system",
                "content": prompt.compile(
                    product_name=input_variables["product_name"],
                    features=input_variables["features"],
                    audience=input_variables["audience"],
                ),
            }
        ],
    )
    return response.choices[0].message.content

# 定义每个 item 的 evaluator
# 这个 evaluator 会输入 item 的 input 和 output
def word_count(*, input, output, **kwargs):
    """Calculates the word count for the output"""
    word_count = len(output.split())
    return Evaluation(
        name="word_count",
        value=word_count,
        comment=f"Response has {word_count} words",
    )

def max_word_count(*, item_results, **kwargs):
    """Finds the max word count across all items"""
    # item_results 包含 experiment 中每个 item run 的数据
    # 需要遍历每个 item 的每个 evaluation 来找到 word_count
    word_counts = [
        eval.value
        for result in item_results
        for eval in result.evaluations
        if eval.name == "word_count"
    ]
    return Evaluation(
        name="max_word_count",
        value=max(word_counts),
        comment=f"The longest output is {max(word_counts)} words long",
    )

# 在 dataset 上运行 experiment
result = dataset.run_experiment(
    name="Run level and item level evaluators",
    description="Experiment run to show the usage of run level and item level evaluators",
    task=generate_product_description,
    # Item level evaluators
    evaluators=[word_count],
    # Run level evaluators
    run_evaluators=[max_word_count],
)

我们可以使用 evaluatorsrun_evaluators 字段传入计算 custom metric 的方法引用。

Run level 和 item level scores 可以在 dataset 中看到。

image.png

图 7.29:SDK Experiments 的 Custom Metrics

在 Langfuse 中看过两类 experiments,也就是 Prompt 和 SDK 之后,可以用下面的小指南判断何时使用哪一种:

Prompt Experiments:

快速尝试几个新 prompts。
比较模型版本。
评估模型参数。

SDK Experiments:

运行多步骤 workflows。
使用真实 API 或业务逻辑进行评估。
并行运行大量 experiments。

结论

本章介绍了评估 LLM 应用的基础。我们探索了 Langfuse 中不同评估功能,从自动评分和 LLM-as-a-judge 方法,到人工标注和自定义评分逻辑,并看到每一种功能如何独特地帮助我们理解应用质量。我们进一步考察了 datasets 的作用,包括真实 datasets 和 synthetic datasets,并学习了 Langfuse 中的 experiments 如何帮助我们以受控且可重复的方式比较 prompts、models 和 configurations。贯穿本章,我们强调了把定量信号与人类判断结合的重要性,以创建一个全面评估流水线,能够捕捉回归、验证改进,并随着系统演进提供对系统行为的可见性。下一章中,我们会看到如何理解已经收集到的指标,并通过 Langfuse 的可视化能力把它们转化为洞察。

参考资料

Evaluation in Langfuse –
langfuse.com/docs/evalua…

Synthetic Datasets –
langfuse.com/guides/cook…

LLM as a judge in Langfuse –
langfuse.com/docs/evalua…

Scores Data Model in Langfuse –
langfuse.com/docs/evalua…

Datasets in Langfuse –
langfuse.com/docs/evalua…

Human Annotations in Langfuse –
langfuse.com/docs/evalua…

Custom Scores –
langfuse.com/docs/evalua…

Prompt Experiments –
langfuse.com/docs/evalua…

Experiments with the SDK –
langfuse.com/docs/evalua…