如何使用 Vitest/Jest 运行评估(测试版)
LangSmith 提供与 Vitest 和 Jest 的集成,允许 JavaScript 和 TypeScript 开发人员定义他们的数据集并使用熟悉的语法进行评估。

与evaluate()evaluation flow,这在以下情况下非常有用:
- 每个示例需要不同的评估逻辑
- 您希望断言二进制期望,并且都在 LangSmith 中跟踪这些断言,并在本地(例如在 CI 管道中)引发断言错误
- 您想利用模拟、观看模式、本地结果或 Vitest/Jest 生态系统的其他功能
需要 JS/TS SDK 版本langsmith>=0.3.1.
Vitest/Jest 集成目前处于测试阶段,在即将发布的版本中可能会发生变化。
Python SDK 具有类似的 pytest 集成。
设置
按如下方式设置集成。请注意,虽然您可以将 LangSmith 评估与其他单元测试一起添加(作为标准*.test.ts文件)
使用您现有的测试配置文件,以下示例还将设置一个单独的测试配置文件和命令来运行您的评估。
它将假定你以.eval.ts.
这可确保自定义测试报告器和其他 LangSmith 接触点不会修改您现有的测试输出。
维斯特
如果您尚未安装所需的开发依赖项,请安装:
- 纱
- npm
- PNPM
yarn add -D vitest dotenv
npm install -D vitest dotenv
pnpm add -D vitest dotenv
以下示例还需要openai(当然langsmith!)作为依赖项:
- 纱
- npm
- PNPM
yarn add langsmith openai
npm install langsmith openai
pnpm add langsmith openai
然后创建一个单独的ls.vitest.config.ts具有以下基本配置的文件:
import { defineConfig } from "vitest/config";
export default defineConfig({
test: {
include: ["**/*.eval.?(c|m)[jt]s"],
reporters: ["langsmith/vitest/reporter"],
setupFiles: ["dotenv/config"],
},
});
include确保仅以eval.ts在你的项目中运行reporters负责很好地格式化您的输出,如上所示setupFiles运行dotenv在运行 EVALS 之前加载环境变量
目前不支持 JSDom 环境。您应该省略"environment"字段,或将其设置为"node".
最后,将以下内容添加到scripts字段中的package.json使用您刚刚创建的配置运行 Vitest:
{
"name": "YOUR_PROJECT_NAME",
"scripts": {
"eval": "vitest run --config ls.vitest.config.ts"
},
"dependencies": {
...
},
"devDependencies": {
...
}
}
请注意,上面的脚本禁用了 Vitest 运行 evals 的默认 watch 模式,因为许多 evaluators 可能包括运行时间较长的 LLM 调用。
开玩笑
如果您尚未安装所需的开发依赖项,请安装:
- 纱
- npm
- PNPM
yarn add -D jest dotenv
npm install -D jest dotenv
pnpm add -D jest dotenv
以下示例还需要openai(当然langsmith!)作为依赖项:
- 纱
- npm
- PNPM
yarn add langsmith openai
npm install langsmith openai
pnpm add langsmith openai
下面的设置说明适用于基本 JS 文件和 CJS。要添加对 TypeScript 和 ESM 的支持,请参阅 Jest 的官方文档 或使用 Vitest。
然后创建一个名为ls.jest.config.cjs:
module.exports = {
testMatch: ["**/*.eval.?(c|m)[jt]s"],
reporters: ["langsmith/jest/reporter"],
setupFiles: ["dotenv/config"],
};
testMatch确保仅以eval.js在你的项目中运行reporters负责很好地格式化您的输出,如上所示setupFiles运行dotenv在运行 EVALS 之前加载环境变量
目前不支持 JSDom 环境。您应该省略"testEnvironment"字段,或将其设置为"node".
最后,将以下内容添加到scripts字段中的package.json使用您刚刚创建的配置运行 Jest:
{
"name": "YOUR_PROJECT_NAME",
"scripts": {
"eval": "jest --config ls.jest.config.cjs"
},
"dependencies": {
...
},
"devDependencies": {
...
}
}
定义和运行评估
您现在可以使用熟悉的 Vitest/Jest 语法将 evals 定义为测试,但需要注意一些:
- 您应该导入
describe和test从langsmith/jest或langsmith/vitest入口点 - 您必须将测试用例包装在
describe块 - 声明测试时,签名略有不同 - 有一个额外的参数包含示例输入和预期输出
通过创建一个名为sql.eval.ts(或sql.eval.js如果你使用的是没有 TypeScript 的 Jest)
并将以下内容粘贴到其中:
import * as ls from "langsmith/vitest";
import { expect } from "vitest";
// import * as ls from "langsmith/jest";
// import { expect } from "@jest/globals";
import OpenAI from "openai";
import { traceable } from "langsmith/traceable";
import { wrapOpenAI } from "langsmith/wrappers/openai";
// Add "openai" as a dependency and set OPENAI_API_KEY as an environment variable
const tracedClient = wrapOpenAI(new OpenAI());
const generateSql = traceable(
async (userQuery: string) => {
const result = await tracedClient.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content:
"Convert the user query to a SQL query. Do not wrap in any markdown tags.",
},
{
role: "user",
content: userQuery,
},
],
});
return result.choices[0].message.content;
},
{ name: "generate_sql" }
);
ls.describe("generate sql demo", () => {
ls.test(
"generates select all",
{
inputs: { userQuery: "Get all users from the customers table" },
referenceOutputs: { sql: "SELECT * FROM customers;" },
},
async ({ inputs, referenceOutputs }) => {
const sql = await generateSql(inputs.userQuery);
ls.logOutputs({ sql }); // <-- Log run outputs, optional
expect(sql).toEqual(referenceOutputs?.sql); // <-- Assertion result logged under 'pass' feedback key
}
);
});
你可以考虑每个ls.test()case 作为数据集示例对应的 case 来执行,而ls.describe()定义 LangSmith 数据集。
如果您在运行测试套件时设置了 LangSmith 跟踪环境变量,则开发工具包将执行以下作:
- 创建一个与传递给
ls.describe()在 LangSmith 中(如果它不存在) - 在数据集中为传递到测试用例中的每个输入和预期输出创建一个示例(如果尚不存在匹配的输出)
- 创建一个新实验,每个测试用例都有一个结果
- 在
pass每个测试用例的 feedback 键
当您运行此测试时,它将具有默认的pass基于测试用例通过/失败的布尔反馈键。
它还将跟踪您使用ls.logOutputs()或从测试函数返回为 “实际” 结果值
从您的应用中进行实验。
创建一个.env文件替换为OPENAI_API_KEY和 LangSmith 凭证(如果您还没有):
OPENAI_API_KEY="YOUR_KEY_HERE"
LANGSMITH_API_KEY="YOUR_LANGSMITH_KEY"
LANGSMITH_TRACING_V2="true"
现在使用eval脚本来运行测试:
- 纱
- npm
- PNPM
yarn run eval
npm run eval
pnpm run eval
您声明的测试应该运行!
完成后,如果您已设置 LangSmith 环境变量,您应该会看到一个链接,指示您 添加到与 Test Results 一起在 LangSmith 中创建的实验。
以下是针对该测试套件的实验的样子:

跟踪反馈
默认情况下,LangSmith 在passfeedback 键。
您可以使用ls.logFeedback()或wrapEvaluator().
为此,请尝试以下作,因为sql.eval.ts文件(或sql.eval.js如果你使用的是没有 TypeScript 的 Jest):
import * as ls from "langsmith/vitest";
// import * as ls from "langsmith/jest";
import OpenAI from "openai";
import { traceable } from "langsmith/traceable";
import { wrapOpenAI } from "langsmith/wrappers/openai";
// Add "openai" as a dependency and set OPENAI_API_KEY as an environment variable
const tracedClient = wrapOpenAI(new OpenAI());
const generateSql = traceable(
async (userQuery: string) => {
const result = await tracedClient.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content:
"Convert the user query to a SQL query. Do not wrap in any markdown tags.",
},
{
role: "user",
content: userQuery,
},
],
});
return result.choices[0].message.content ?? "";
},
{ name: "generate_sql" }
);
const myEvaluator = async (params: {
outputs: { sql: string };
referenceOutputs: { sql: string };
}) => {
const { outputs, referenceOutputs } = params;
const instructions = [
"Return 1 if the ACTUAL and EXPECTED answers are semantically equivalent, ",
"otherwise return 0. Return only 0 or 1 and nothing else.",
].join("\n");
const grade = await tracedClient.chat.completions.create({
model: "gpt-4o-mini",
messages: [
{
role: "system",
content: instructions,
},
{
role: "user",
content: `ACTUAL: ${outputs.sql}\nEXPECTED: ${referenceOutputs?.sql}`,
},
],
});
const score = parseInt(grade.choices[0].message.content ?? "");
return { key: "correctness", score };
};
ls.describe("generate sql demo", () => {
ls.test(
"generates select all",
{
inputs: { userQuery: "Get all users from the customers table" },
referenceOutputs: { sql: "SELECT * FROM customers;" },
},
async ({ inputs, referenceOutputs }) => {
const sql = await generateSql(inputs.userQuery);
ls.logOutputs({ sql });
const wrappedEvaluator = ls.wrapEvaluator(myEvaluator);
// Will automatically log "correctness" as feedback
await wrappedEvaluator({
outputs: { sql },
referenceOutputs,
});
// You can also manually log feedback with `ls.logFeedback()`
ls.logFeedback({
key: "harmfulness",
score: 0.2,
});
}
);
ls.test(
"offtopic input",
{
inputs: { userQuery: "whats up" },
referenceOutputs: { sql: "sorry that is not a valid query" },
},
async ({ inputs, referenceOutputs }) => {
const sql = await generateSql(inputs.userQuery);
ls.logOutputs({ sql });
const wrappedEvaluator = ls.wrapEvaluator(myEvaluator);
// Will automatically log "correctness" as feedback
await wrappedEvaluator({
outputs: { sql },
referenceOutputs,
});
// You can also manually log feedback with `ls.logFeedback()`
ls.logFeedback({
key: "harmfulness",
score: 0.2,
});
}
);
});
请注意ls.wrapEvaluator()周围myEvaluator功能。
这使得 LLM-as-judge 调用与测试用例的其余部分分开跟踪,以避免混乱和方便
如果包装函数的返回值匹配,则创建反馈{ key: string; score: number | boolean }.
在这种情况下,评估器跟踪不会显示在主测试用例运行中,而是显示在与correctnessfeedback 键。
您可以通过在 UI 中单击相应的反馈条卡来查看 LangSmith 中的评估器运行。
针对测试用例运行多个示例
您可以在多个示例上运行相同的测试用例,并使用ls.test.each().
当您想针对不同的输入以相同的方式评估您的应用程序时,这非常有用:
import * as ls from "langsmith/vitest";
// import * as ls from "langsmith/jest";
const DATASET = [{
inputs: { userQuery: "whats up" },
referenceOutputs: { sql: "sorry that is not a valid query" }
}, {
inputs: { userQuery: "what color is the sky?" },
referenceOutputs: { sql: "sorry that is not a valid query" }
}, {
inputs: { userQuery: "how are you today?" },
referenceOutputs: { sql: "sorry that is not a valid query" }
}];
ls.describe("generate sql demo", () => {
ls.test.each(DATASET)(
"offtopic inputs",
async ({ inputs, referenceOutputs }) => {
...
},
)
});
如果您启用了跟踪,则本地数据集中的每个示例都将同步到在 LangSmith 中创建的示例。
日志输出
每次运行测试时,我们都会将其同步到数据集示例,并将其跟踪为运行。
要跟踪运行的最终输出,您可以使用ls.logOutputs()喜欢这个:
import * as ls from "langsmith/vitest";
// import * as ls from "langsmith/jest";
ls.describe("generate sql demo", () => {
ls.test(
"offtopic input",
{
inputs: { userQuery: "..." },
referenceOutputs: { sql: "..." }
},
async ({ inputs, referenceOutputs }) => {
ls.logOutputs({ sql: "SELECT * FROM users;" })
},
)
});
记录的输出将出现在您的报告者摘要和 LangSmith 中。
您也可以直接从 test 函数返回一个值:
import * as ls from "langsmith/vitest";
// import * as ls from "langsmith/jest";
ls.describe("generate sql demo", () => {
ls.test(
"offtopic input",
{
inputs: { userQuery: "..." },
referenceOutputs: { sql: "..." }
},
async ({ inputs, referenceOutputs }) => {
return { sql: "SELECT * FROM users;" }
},
);
});
但是请记住,如果您这样做,如果您的测试由于断言失败或其他错误而无法完成,则不会显示您的输出。
跟踪中间调用
LangSmith 将自动跟踪测试用例执行过程中发生的任何可跟踪的中间调用。
聚焦或跳过测试
你可以链接 Vitest/Jest.skip和.only方法ls.test()和ls.describe():
import * as ls from "langsmith/vitest";
// import * as ls from "langsmith/jest";
ls.describe("generate sql demo", () => {
ls.test.skip(
"offtopic input",
{
inputs: { userQuery: "..." },
referenceOutputs: { sql: "..." }
},
async ({ inputs, referenceOutputs }) => {
return { sql: "SELECT * FROM users;" }
},
);
ls.test.only(
"other",
{
inputs: { userQuery: "..." },
referenceOutputs: { sql: "..." }
},
async ({ inputs, referenceOutputs }) => {
return { sql: "SELECT * FROM users;" }
},
);
});
配置测试套件
您可以通过将额外的参数传递给ls.describe()对于完整套件,或者通过传递config字段转换为ls.test()对于单个测试:
ls.describe("test suite name", () => {
ls.test(
"test name",
{
inputs: { ... },
referenceOutputs: { ... },
// Extra config for the test run
config: { tags: [...], metadata: { ... } }
},
{
name: "test name",
tags: ["tag1", "tag2"],
skip: true,
only: true,
}
);
}, {
testSuiteName: "overridden value",
metadata: { ... },
// Custom client
client: new Client(),
});
测试套件还会自动从process.env.ENVIRONMENT,process.env.NODE_ENV和process.env.LANGSMITH_ENVIRONMENT并将其设置为已创建 Experiment 的元数据。然后,您可以在 LangSmith 的 UI 中按元数据筛选实验。
有关配置选项的完整列表,请参阅 API refs 。
空运行模式
如果要在不将结果同步到 LangSmith 的情况下运行测试,则可以设置省略 LangSmith 跟踪环境变量或设置LANGSMITH_TEST_TRACKING=false在您的环境中。
测试将正常运行,但实验日志不会发送到 LangSmith。