Skip to main content
概念:成对评估
LangSmith 支持以比较方式评估现有实验。您可以同时对多个实验的输出进行评分,而不是一次评估一个输出。在本指南中,您将使用 evaluate() 与两个现有实验来定义成对评估器运行成对评估。最后,您将使用 LangSmith UI 来查看成对实验

前提条件

  • 如果您尚未创建要比较的实验,请查看快速入门操作指南以开始进行评估。
  • 本指南需要 langsmith Python 版本 >=0.2.0 或 JS 版本 >=0.2.9
您也可以使用 evaluate_comparative() 来比较两个以上的现有实验。

evaluate() 比较参数

最简单的 evaluate / aevaluate 函数接受以下参数:
参数描述
target要相互评估的两个现有实验的列表。可以是 UUID 或实验名称。
evaluators要附加到此评估的成对评估器列表。请参阅下文了解如何定义这些评估器。
除了这些,您还可以传入以下可选参数:
参数描述
randomize_order / randomizeOrder可选布尔值,指示是否应在每次评估中随机化输出的顺序。这是最小化提示中位置偏差的一种策略:通常,LLM 会根据顺序对其中一个回答产生偏差。这应主要通过提示工程来解决,但这是另一种可选的缓解措施。默认为 False。
experiment_prefix / experimentPrefix要附加到成对实验名称开头的前缀。默认为 None。
description成对实验的描述。默认为 None。
max_concurrency / maxConcurrency要运行的最大并发评估数。默认为 5。
client要使用的 LangSmith 客户端。默认为 None。
metadata要附加到成对实验的元数据。默认为 None。
load_nested / loadNested是否加载实验的所有子运行。当为 False 时,只有根跟踪会传递给您的评估器。默认为 False。

定义成对评估器

成对评估器只是具有预期签名的函数。

评估器参数

自定义评估器函数必须具有特定的参数名称。它们可以接受以下任意子集的参数:
  • inputs: dict:与数据集中单个示例对应的输入字典。
  • outputs: list[dict]:每个实验在给定输入上产生的两个字典输出的列表。
  • reference_outputs / referenceOutputs: dict:示例关联的参考输出字典(如果可用)。
  • runs: list[Run]:两个实验在给定示例上生成的完整 Run 对象的两个项目列表。如果您需要访问中间步骤或每个运行的元数据,请使用此参数。
  • example: Example:完整的数据集 Example,包括示例输入、输出(如果可用)和元数据(如果可用)。
对于大多数用例,您只需要 inputsoutputsreference_outputs / referenceOutputsrunsexample 仅在您需要应用程序实际输入和输出之外的一些额外跟踪或示例元数据时有用。

评估器输出

自定义评估器应返回以下类型之一: Python 和 JS/TS
  • dict:包含以下键的字典:
    • key,表示将记录的反馈键
    • scores,是从运行 ID 到该运行分数的映射。
    • comment,是一个字符串。最常用于模型推理。
目前仅限 Python
  • list[int | float | bool]:一个包含两个分数的列表。假定列表的顺序与 runs / outputs 评估器参数相同。评估器函数名称用作反馈键。
请注意,您应选择一个与运行上标准反馈不同的反馈键。我们建议在成对反馈键前加上 pairwise_ranked_

运行成对评估

以下示例使用一个提示,要求 LLM 在两个 AI 助手回答之间决定哪个更好。它使用结构化输出来解析 AI 的响应:0、1 或 2。
在下面的 Python 示例中,我们从 LangChain Hub 拉取这个结构化提示,并与 LangChain 聊天模型包装器一起使用。LangChain 的使用完全是可选的。 为了说明这一点,TypeScript 示例直接使用 OpenAI SDK。
  • Python:需要 langsmith>=0.2.0
  • TypeScript:需要 langsmith>=0.2.9
from langchain_classic import hub
from langchain.chat_models import init_chat_model
from langsmith import evaluate

# 查看提示:https://smith.langchain.com/hub/langchain-ai/pairwise-evaluation-2
prompt = hub.pull("langchain-ai/pairwise-evaluation-2")
model = init_chat_model("gpt-4.1")
chain = prompt | model

def ranked_preference(inputs: dict, outputs: list[dict]) -> list:
    # 假设示例输入有 'question' 键,实验输出有 'answer' 键。
    response = chain.invoke({
        "question": inputs["question"],
        "answer_a": outputs[0].get("answer", "N/A"),
        "answer_b": outputs[1].get("answer", "N/A"),
    })
    if response["Preference"] == 1:
        scores = [1, 0]
    elif response["Preference"] == 2:
        scores = [0, 1]
    else:
        scores = [0, 0]
    return scores

evaluate(
    ("experiment-1", "experiment-2"),  # 替换为您的实验名称/ID
    evaluators=[ranked_preference],
    randomize_order=True,
    max_concurrency=4,
)
import { evaluate} from "langsmith/evaluation";
import { Run } from "langsmith/schemas";
import { wrapOpenAI } from "langsmith/wrappers";
import OpenAI from "openai";
import { z } from "zod";

const openai = wrapOpenAI(new OpenAI());

async function rankedPreference({
  inputs,
  runs,
}: {
  inputs: Record<string, any>;
  runs: Run[];
}) {
  const scores: Record<string, number> = {};
  const [runA, runB] = runs;
  if (!runA || !runB) throw new Error("Expected at least two runs");

  const payload = {
    question: inputs.question,
    answer_a: runA?.outputs?.output ?? "N/A",
    answer_b: runB?.outputs?.output ?? "N/A",
  };

  const output = await openai.chat.completions.create({
    model: "gpt-4-turbo",
    messages: [
      {
        role: "system",
        content: [
          "请扮演公正的裁判,评估两个 AI 助手对下方显示的用户问题提供的回答质量。",
          "您应选择遵循用户指令并更好地回答用户问题的助手。",
          "您的评估应考虑其回答的有用性、相关性、准确性、深度、创意性和详细程度等因素。",
          "通过比较两个回答开始您的评估,并提供简短解释。",
          "避免任何位置偏差,并确保回答的呈现顺序不会影响您的决定。",
          "不要让回答的长度影响您的评估。不要偏袒某些助手的名称。尽可能客观。",
        ].join(" "),
      },
      {
        role: "user",
        content: [
          `[用户问题] ${payload.question}`,
          `[助手 A 回答开始] ${payload.answer_a} [助手 A 回答结束]`,
          `[助手 B 回答开始] ${payload.answer_b} [助手 B 回答结束]`,
        ].join("\n\n"),
      },
    ],
    tool_choice: {
      type: "function",
      function: { name: "Score" },
    },
    tools: [
      {
        type: "function",
        function: {
          name: "Score",
          description: [
            `提供解释后,严格按照以下格式输出您的最终裁决:`,
            `如果助手 A 的回答基于上述因素更好,则输出 "1"。`,
            `如果助手 B 的回答基于上述因素更好,则输出 "2"。`,
            `如果是平局,则输出 "0"。`,
          ].join(" "),
          parameters: {
            type: "object",
            properties: {
              Preference: {
                type: "integer",
                description: "哪个助手的回答更受青睐?",
              },
            },
          },
        },
      },
    ],
  });

  const { Preference } = z
    .object({ Preference: z.number() })
    .parse(
      JSON.parse(output.choices[0].message.tool_calls[0].function.arguments)
    );

  if (Preference === 1) {
    scores[runA.id] = 1;
    scores[runB.id] = 0;
  } else if (Preference === 2) {
    scores[runA.id] = 0;
    scores[runB.id] = 1;
  } else {
    scores[runA.id] = 0;
    scores[runB.id] = 0;
  }

  return { key: "ranked_preference", scores };
}

await evaluate(["earnest-name-40", "reflecting-pump-91"], {
  evaluators: [rankedPreference],
});

查看成对实验

从数据集页面导航到“成对实验”标签页: 成对实验标签页 点击您要检查的成对实验,您将进入比较视图: 成对比较视图 您可以通过点击表头中的竖起大拇指/倒大拇指按钮来筛选第一个实验表现更好或反之的运行: 成对筛选