本指南将向您展示如何使用 LangSmith SDK 对 LLM 应用程序运行评估。
在本指南中,我们将介绍如何使用 evaluate() LangSmith SDK 中的方法评估应用程序。
定义应用程序
首先,我们需要一个要评估的应用程序。在这个示例中,让我们创建一个简单的毒性分类器。
from langsmith import traceable, wrappers
from openai import OpenAI
# Optionally wrap the OpenAI client to trace all model calls.
oai_client = wrappers.wrap_openai(OpenAI())
# Optionally add the 'traceable' decorator to trace the inputs/outputs of this function.
@traceable
def toxicity_classifier(inputs: dict) -> dict:
instructions = (
"Please review the user query below and determine if it contains any form of toxic behavior, "
"such as insults, threats, or highly negative comments. Respond with 'Toxic' if it does "
"and 'Not toxic' if it doesn't."
)
messages = [
{"role": "system", "content": instructions},
{"role": "user", "content": inputs["text"]},
]
result = oai_client.chat.completions.create(
messages=messages, model="gpt-5.4-mini", temperature=0
)
return {"class": result.choices[0].message.content}
// Optionally wrap the OpenAI client to trace all model calls.
const oaiClient = wrapOpenAI(new OpenAI());
// Optionally add the 'traceable' wrapper to trace the inputs/outputs of this function.
const toxicityClassifier = traceable(
async (text: string) => {
const result = await oaiClient.chat.completions.create({
messages: [
{
role: "system",
content: "Please review the user query below and determine if it contains any form of toxic behavior, such as insults, threats, or highly negative comments. Respond with 'Toxic' if it does, and 'Not toxic' if it doesn't.",
},
{ role: "user", content: text },
],
model: "gpt-5.4-mini",
temperature: 0,
});
return result.choices[0].message.content;
},
{ name: "toxicityClassifier" }
);
我们选择性地启用了追踪功能来捕获管道中每个步骤的输入和输出。要了解如何为追踪注释您的代码,请参阅 自定义插桩.
创建或选择数据集
我们需要 数据集 来评估我们的应用程序。我们的数据集将包含有毒和无毒文本的标记 样本。
需要 langsmith>=0.3.13
from langsmith import Client
ls_client = Client()
examples = [
{
"inputs": {"text": "Shut up, idiot"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "You're a wonderful person"},
"outputs": {"label": "Not toxic"},
},
{
"inputs": {"text": "This is the worst thing ever"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "I had a great day today"},
"outputs": {"label": "Not toxic"},
},
{
"inputs": {"text": "Nobody likes you"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "This is unacceptable. I want to speak to the manager."},
"outputs": {"label": "Not toxic"},
},
]
dataset = ls_client.create_dataset(dataset_name="Toxic Queries")
ls_client.create_examples(
dataset_id=dataset.id,
examples=examples,
)
const langsmith = new Client();
// create a dataset
const labeledTexts = [
["Shut up, idiot", "Toxic"],
["You're a wonderful person", "Not toxic"],
["This is the worst thing ever", "Toxic"],
["I had a great day today", "Not toxic"],
["Nobody likes you", "Toxic"],
["This is unacceptable. I want to speak to the manager.", "Not toxic"],
];
const [inputs, outputs] = labeledTexts.reduce<
[Array<{ input: string }>, Array<{ outputs: string }>]
>(
([inputs, outputs], item) => [
[...inputs, { input: item[0] }],
[...outputs, { outputs: item[1] }],
],
[[], []]
);
const datasetName = "Toxic Queries";
const toxicDataset = await langsmith.createDataset(datasetName);
await langsmith.createExamples({ inputs, outputs, datasetId: toxicDataset.id });
有关数据集的更多详细信息,请参阅 管理数据集 page.
定义评估器
定义评估器主要有两种方式。
在代码中本地定义
评估器 是用于对应用程序输出进行评分的函数。它们接收示例输入、实际输出,以及当存在时的参考输出。由于我们为这项任务提供了标签,我们的评估器可以直接检查实际输出是否与参考输出匹配。
- - Python: 需要
langsmith>=0.3.13 - - TypeScript: 需要
langsmith>=0.2.9
def correct(inputs: dict, outputs: dict, reference_outputs: dict) -> bool:
return outputs["class"] == reference_outputs["label"]
function correct({
outputs,
referenceOutputs,
}: {
outputs: Record<string, any>;
referenceOutputs?: Record<string, any>;
}): EvaluationResult {
const score = outputs.output === referenceOutputs?.outputs;
return { key: "correct", score };
}
在 LangSmith UI 中
您也可以在 LangSmith UI中定义评估器。您可以 在 UI 中创建评估器 ,位于 **评估器** 选项卡下。这些评估器将 在每次新实验时自动触发.
运行评估
我们将使用 evaluate() / aevaluate() 方法来运行评估。
关键参数如下:
- * 一个目标函数,它接受一个输入字典并返回一个输出字典。
example.inputs的 示例 字段是传递给目标函数的内容。在本例中,我们的toxicity_classifier已经设置好可以接受示例输入,因此我们可以直接使用它。 - *
data- 要评估的 LangSmith 数据集的名称或 UUID,或示例迭代器。 - *
evaluators- 用于对函数输出进行评分的评估器列表; Langsmith UI 中的数据集评估器也会自动触发。 - *
metadata- 一个可选对象,用于附加到实验。传递models,prompts和tools键以填充实验表视图中的相应列。
Python:需要 langsmith>=0.3.13
# optional metadata, used to populate model/prompt/tool columns in UI
EXPERIMENT_METADATA = {
"models": [
"openai:gpt-5.4-mini",
{
"id": ["langchain", "chat_models", "openai", "ChatOpenAI"],
"lc": 1,
"type": "constructor",
"kwargs": {"model_name": "gpt-5.5", "temperature": 0.2},
},
],
"prompts": ["my-org/my-eval-prompt:abc12345"],
"tools": [
{
"name": "web_search",
"description": "Search the web for information",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
],
}
# Can equivalently use the 'evaluate' function directly:
# from langsmith import evaluate; evaluate(...)
results = ls_client.evaluate(
toxicity_classifier,
data=dataset.name,
evaluators=[correct],
experiment_prefix="gpt-5.4-mini, baseline", # optional, experiment name prefix
description="Testing the baseline system.", # optional, experiment description
max_concurrency=4, # optional, add concurrency
metadata=EXPERIMENT_METADATA, # optional, used to populate model/prompt/tool columns in UI
)
// optional metadata, used to populate model/prompt/tool columns in UI
const EXPERIMENT_METADATA = {
models: [
"openai:gpt-5.4-mini",
{
id: ["langchain", "chat_models", "openai", "ChatOpenAI"],
lc: 1,
type: "constructor",
kwargs: { model_name: "gpt-5.5", temperature: 0.2 },
},
],
prompts: ["my-org/my-eval-prompt:abc12345"],
tools: [
{
name: "web_search",
description: "Search the web for information",
parameters: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
},
],
};
await evaluate((inputs) => toxicityClassifier(inputs["input"]), {
data: datasetName,
evaluators: [correct],
experimentPrefix: "gpt-5.4-mini, baseline", // optional, experiment name prefix
maxConcurrency: 4, // optional, add concurrency
metadata: EXPERIMENT_METADATA, // optional, used to populate model/prompt/tool columns in UI
});
向实验添加元数据
元数据是一组键值对,您可以附加到实验上,以便在实验表中对实验进行分组和筛选。您可以通过 metadata 参数在运行实验时传递元数据(参见 运行评估),或者之后直接在 LangSmith UI 中添加。
要打开 **编辑实验** 面板,请将鼠标悬停在实验表的实验行上,然后点击行右侧出现的 **编辑** 铅笔图标。
这个 **编辑实验** 面板允许您更新实验名称和描述,以及管理元数据键值对。点击 **+ 添加元数据** 添加新的键值对,然后点击右上角的 **提交** 保存您的更改。
一旦实验被标记了元数据,请使用 **按分组** 实验表顶部的控件可按任何元数据字段对实验进行分组。表上方的汇总图表会按组更新,显示每个配置的平均反馈分数、延迟和令牌使用量。这样可以轻松比较同一数据集上不同提示版本、模型或其他变更的表现。
保留的 models, prompts和 tools 键会自动填充实验表中的专用列。点击这些列中的某个值可以按该列进行筛选或分组。有关详细信息,请参阅 按模型、提示和工具进行筛选和分组.
探索结果
每次调用 evaluate() 都会创建一个 实验 您可以在 LangSmith UI 中查看或通过 SDK 查询。请参阅 分析实验 了解更多详情。
针对数据集运行的实验会列在实验表中。
对于从 Playground 或通过 SDK 运行的实验, **进度** 列会实时跟踪完成进度。进度反映了运行和评估状态。将鼠标悬停在进度条上可以查看已完成的运行数和已评估的运行数。
点击某个实验行可以查看每个示例的分数。按分数进行筛选和排序,以识别应用程序表现良好或不佳的模式。
点击某个示例可打开其详情面板,其中包含输入、输出、参考输出以及任何相关的追踪记录(如果您已在代码中添加了追踪注释)。
参考代码
Click to see a consolidated code snippet
from langsmith import Client, traceable, wrappers
from openai import OpenAI
# Step 1. Define an application
oai_client = wrappers.wrap_openai(OpenAI())
@traceable
def toxicity_classifier(inputs: dict) -> str:
system = (
"Please review the user query below and determine if it contains any form of toxic behavior, "
"such as insults, threats, or highly negative comments. Respond with 'Toxic' if it does "
"and 'Not toxic' if it doesn't."
)
messages = [
{"role": "system", "content": system},
{"role": "user", "content": inputs["text"]},
]
result = oai_client.chat.completions.create(
messages=messages, model="gpt-5.4-mini", temperature=0
)
return result.choices[0].message.content
# Step 2. Create a dataset
ls_client = Client()
dataset = ls_client.create_dataset(dataset_name="Toxic Queries")
examples = [
{
"inputs": {"text": "Shut up, idiot"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "You're a wonderful person"},
"outputs": {"label": "Not toxic"},
},
{
"inputs": {"text": "This is the worst thing ever"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "I had a great day today"},
"outputs": {"label": "Not toxic"},
},
{
"inputs": {"text": "Nobody likes you"},
"outputs": {"label": "Toxic"},
},
{
"inputs": {"text": "This is unacceptable. I want to speak to the manager."},
"outputs": {"label": "Not toxic"},
},
]
ls_client.create_examples(
dataset_id=dataset.id,
examples=examples,
)
# Step 3. Define an evaluator
def correct(inputs: dict, outputs: dict, reference_outputs: dict) -> bool:
return outputs["output"] == reference_outputs["label"]
# Step 4. Run the evaluation
# optional metadata, used to populate model/prompt/tool columns in UI
EXPERIMENT_METADATA = {
"models": [
"openai:gpt-5.4-mini",
{
"id": ["langchain", "chat_models", "openai", "ChatOpenAI"],
"lc": 1,
"type": "constructor",
"kwargs": {"model_name": "gpt-5.5", "temperature": 0.2},
},
],
"prompts": ["my-org/my-eval-prompt:abc12345"],
"tools": [
{
"name": "web_search",
"description": "Search the web for information",
"parameters": {
"type": "object",
"properties": {"query": {"type": "string"}},
"required": ["query"],
},
},
],
}
# Client.evaluate() and evaluate() behave the same.
results = ls_client.evaluate(
toxicity_classifier,
data=dataset.name,
evaluators=[correct],
experiment_prefix="gpt-5.4-mini, simple", # optional, experiment name prefix
description="Testing the baseline system.", # optional, experiment description
max_concurrency=4, # optional, add concurrency
metadata=EXPERIMENT_METADATA, # optional, used to populate model/prompt/tool columns in UI
)
const oaiClient = wrapOpenAI(new OpenAI());
const toxicityClassifier = traceable(
async (text: string) => {
const result = await oaiClient.chat.completions.create({
messages: [
{
role: "system",
content: "Please review the user query below and determine if it contains any form of toxic behavior, such as insults, threats, or highly negative comments. Respond with 'Toxic' if it does, and 'Not toxic' if it doesn't.",
},
{ role: "user", content: text },
],
model: "gpt-5.4-mini",
temperature: 0,
});
return result.choices[0].message.content;
},
{ name: "toxicityClassifier" }
);
const langsmith = new Client();
// create a dataset
const labeledTexts = [
["Shut up, idiot", "Toxic"],
["You're a wonderful person", "Not toxic"],
["This is the worst thing ever", "Toxic"],
["I had a great day today", "Not toxic"],
["Nobody likes you", "Toxic"],
["This is unacceptable. I want to speak to the manager.", "Not toxic"],
];
const [inputs, outputs] = labeledTexts.reduce<
[Array<{ input: string }>, Array<{ outputs: string }>]
>(
([inputs, outputs], item) => [
[...inputs, { input: item[0] }],
[...outputs, { outputs: item[1] }],
],
[[], []]
);
const datasetName = "Toxic Queries";
const toxicDataset = await langsmith.createDataset(datasetName);
await langsmith.createExamples({ inputs, outputs, datasetId: toxicDataset.id });
// Row-level evaluator
function correct({
outputs,
referenceOutputs,
}: {
outputs: Record<string, any>;
referenceOutputs?: Record<string, any>;
}): EvaluationResult {
const score = outputs.output === referenceOutputs?.outputs;
return { key: "correct", score };
}
// optional metadata, used to populate model/prompt/tool columns in UI
const EXPERIMENT_METADATA = {
models: [
"openai:gpt-5.4-mini",
{
id: ["langchain", "chat_models", "openai", "ChatOpenAI"],
lc: 1,
type: "constructor",
kwargs: { model_name: "gpt-5.5", temperature: 0.2 },
},
],
prompts: ["my-org/my-eval-prompt:abc12345"],
tools: [
{
name: "web_search",
description: "Search the web for information",
parameters: {
type: "object",
properties: { query: { type: "string" } },
required: ["query"],
},
},
],
};
await evaluate((inputs) => toxicityClassifier(inputs["input"]), {
data: datasetName,
evaluators: [correct],
experimentPrefix: "gpt-5.4-mini, simple", // optional, experiment name prefix
maxConcurrency: 4, // optional, add concurrency
metadata: EXPERIMENT_METADATA, // optional, used to populate model/prompt/tool columns in UI
});