跳转到内容

输入关键词开始搜索文档。

评估

最后更新 查看 Markdown 源文件

评估是一种自动化测试,它让智能体运行在实时模型上,并对其可观察行为进行断言:它产生的回复、它调用的工具、它发出的数据。本指南介绍评估与普通测试的区别、使用 Vitest 设置评估套件、在进程内和通过 HTTP 运行被测智能体、使用断言和评判器对结果评分,以及在本地和 CI 中运行评估。

什么是评估

智能体的行为由其指令、模型和工具共同产生。你编写的部分是带有普通测试的普通代码——工具的run 函数是可以直接进行单元测试的普通函数,不涉及模型。单元测试无法覆盖的是模型的贡献:智能体是否调用了正确的工具、遵循了其指令并产生了正确的答案。评估通过运行完整循环——指令、模型、工具——并对结果进行断言来覆盖这一点。

两个属性决定了评估的编写方式:

  • 评估是非确定性的。 相同的输入可能产生不同的措辞、不同的工具调用顺序,偶尔会产生不同的结果。对行为契约进行断言——必需的工具调用、回复中的关键事实、结构化数据的形状——而不是精确的输出字符串。
  • 评估消耗真实 token 和真实时间。 每个用例运行一个或多个实时模型轮次。因此评估位于自己的套件中,有自己的配置、凭据、超时和运行节奏,与单元测试分开。

Flue 没有专门的评估框架。评估是一个 Vitest 测试,通过与其他调用者相同的公共接口驱动智能体——进程内 init() 句柄 或 HTTP 对话接口——并对结果进行断言。vitest-evals 集成在其之上提供了评估工具、评判器和 CI 报告;参见下文。

设置评估套件

将评估放在专用的 Vitest 配置中。实时模型测试需要与单元测试不同的文件发现、更长的超时和不同的凭据,单独的配置让每个可以独立运行:

vitest.evals.config.ts
import { defineConfig } from 'vitest/config';

export default defineConfig({
  test: {
    include: ['src/evals/**/*.eval.ts'],
    testTimeout: 60_000,
  },
});

60_000 超时替代了 Vitest 的 5 秒默认值,单个实时模型轮次可能超过这个时间。添加一个脚本以便一键运行套件:

package.json
{
  "scripts": {
    "evals": "vitest run --config vitest.evals.config.ts"
  }
}

评估文件位于 src/evals/ 下,以它们评估的能力或场景命名——service-health.eval.ts、refund-policy.eval.ts——而不是每个智能体一个文件。

在进程中编写评估

在测试中运行智能体最直接的方式是使用 @flue/runtime/node 的 start(),它在测试进程内启动 Flue 运行时——无需服务器,无需构建。然后 init() 句柄发送消息并等待结算的回复:

src/evals/service-health.eval.ts
import { init } from '@flue/runtime';
import { start } from '@flue/runtime/node';
import { afterAll, expect, it } from 'vitest';
import { ServiceStatus } from '../agents/service-status.ts';

const flue = await start({ agents: [ServiceStatus] });
afterAll(() => flue.stop());

it('checks live service status before answering', async () => {
  const toolsCalled: string[] = [];

  // No id: init() mints a fresh conversation for this case.
  const agent = init(ServiceStatus);
  const receipt = await agent.dispatch('Is the checkout service currently operational?');
  const reply = await agent.read(receipt, {
    onEvent: (chunk) => {
      if (chunk.type === 'tool-input') toolsCalled.push(chunk.toolName);
    },
  });

  expect(reply.text).toContain('operational');
  expect(toolsCalled).toContain('get_service_status');
});

这里的一切都是普通的编程接口:

  • 每个用例全新的对话。 不带 id 的 init(agent) 指向一个新的、唯一命名的对话,因此用例保持独立——保存的对话历史不会影响其他用例。评估对话记忆的用例复用一个句柄并通过它发送多个 dispatch(...)/read(...) 对。
  • 回复是断言目标。 reply.text 是最终的助手文本,reply.data 携带命名的 useDataWriter 部分——这里可以对结构化结果进行断言。失败或中止的运行以 AgentRunError 拒绝 read(),这会使测试失败。
  • 工具调用作为事件到达。 read() 的 onEvent 回调在每个对话块被记录时接收它;tool-input 块携带模型发出的每个工具调用的 toolName 和 input。
  • 完整的运行时行为。 钩子、持久性和沙箱的工作方式与在服务器中完全相同——start() 是没有 HTTP 接口的相同组装。

三个约束适用。一个进程持有一个 Flue 运行时,因此每个测试文件调用一次 start() 并在文件完成时停止它——Vitest 的默认隔离为每个测试文件提供自己的 worker,防止文件冲突。提供商凭据来自测试进程环境(参见模型——提供商凭据)。评估直接导入智能体模块,因此模块必须在普通 Vitest 下加载:依赖构建解析导入的智能体,如 SKILL.md 导入,需要 Flue 构建,应该通过 HTTP 进行评估。

通过 HTTP 评估

在 app.ts 中挂载的智能体可以通过其 HTTP 接口使用 Flue 智能体 SDK 进行评估——与部署应用服务的相同边界,包括你的路由中间件。新的对话是挂载 URL 后附加的新 id:

src/evals/service-health.eval.ts
import { createFlueClient } from '@flue/sdk';
import { expect, it } from 'vitest';

// The agent's mount URL from app.ts; point FLUE_AGENT_URL at a deployment.
const mountUrl = process.env.FLUE_AGENT_URL ?? 'http://127.0.0.1:5173/agents/service-status';

it('checks live service status before answering', async () => {
  const conversation = createFlueClient({
    url: `${mountUrl}/eval-${crypto.randomUUID()}`,
  });

  const admission = await conversation.send({
    message: { kind: 'user', body: 'Is the checkout service currently operational?' },
  });
  await conversation.wait(admission);

  const { messages } = await conversation.history();
  const reply = messages.findLast((message) => message.role === 'assistant');
  const text =
    reply?.parts
      .filter((part) => part.type === 'text')
      .map((part) => part.text)
      .join('') ?? '';

  expect(text).toContain('operational');
});

通过 HTTP 的提示是即发即忘的:send() 接纳消息,wait() 等待其完成,history() 返回完成的对话——包括助手回复及其工具调用部分。评估进程不启动应用;在另一个终端运行 vite dev(或构建的服务器),或将 URL 设置为部署环境。当路由受保护时,向 createFlueClient(...) 传递 token 或 headers——参见保护你的智能体。

根据评估应测试的内容选择接口:

  • 进程内(start()) 测试智能体本身——指令、模型、钩子、工具——需要测试环境中的提供商凭据。
  • HTTP(@flue/sdk) 测试智能体加上 app.ts 路由和中间件,需要运行中的开发服务器或部署。

两个接口都是公共 API,因此也可以作为其他评估库和托管平台(如 Braintrust)的集成点——以相同方式驱动智能体并将结果交给你的评分流水线。

vitest-evals

vitest-evals 通过评估工具、LLM 评判器、规范化报告和 CI 报告扩展了 Vitest。通过蓝图添加 Flue 的集成:

flue add tooling vitest-evals

蓝图从上文创建评估配置和脚本,并生成 src/evals/harness.ts——一个通过 @flue/sdk 驱动每个用例的对话并将回复、工具调用和使用量转换为规范化 vitest-evals 结果的工具。设置、生成的文件和报告命令记录在 vitest-evals 生态系统页面;完整的可运行项目在 examples/vitest-evals。

用例使用 describeEval 编写,它将工具绑定到套件并为每个测试提供一个 run(...) 函数:

src/evals/service-health.eval.ts
import { expect } from 'vitest';
import { describeEval, toolCalls } from 'vitest-evals';
import { createFlueAgentHarness } from './harness.ts';

const harness = createFlueAgentHarness({
  agentUrl: process.env.FLUE_AGENT_URL ?? 'http://127.0.0.1:5173/agents/service-status',
});

describeEval('service status agent', { harness }, (it) => {
  it('checks live service status before answering', async ({ run }) => {
    const result = await run('Is the checkout service currently operational?');

    expect(result.output).toContain('operational');
    expect(toolCalls(result).map((call) => call.name)).toContain('get_service_status');
  });
});

评判器

确定性断言覆盖精确契约:必需的工具、禁止的工具、结构化输出、稳定内容。对于语义行为——事实一致性、语气、策略遵守——vitest-evals 提供评判器,对结果评分并在低于阈值时使用例失败。LLM 支持的评判器运行在_评判工具_上:评判器自己的模型连接,与被评估的智能体分开配置:

import { expect } from 'vitest';
import { describeEval, FactualityJudge } from 'vitest-evals';

describeEval('service status agent', { harness, judgeHarness }, (it) => {
  it('reports status consistent with the reference answer', async ({ run }) => {
    const result = await run('Is the checkout service currently operational?');

    await expect(result).toSatisfyJudge(FactualityJudge(), {
      expected: 'The checkout service is currently operational.',
      threshold: 0.6,
    });
  });
});

createJudge(...) 定义自定义评判器,确定性或 LLM 支持的;内置的 FactualityJudge、ToolCallJudge 和 StructuredOutputJudge 覆盖常见的评分标准。评判器构造和评判器工具适配器记录在 vitest-evals 文档。优先使用确定性断言,仅在行为无法精确检查时添加评判器。

在本地和 CI 中运行评估

本地运行时,一旦提供商凭据在环境中,进程内套件一键运行:

pnpm run evals

HTTP 套件还需要一个可达的目标:先在另一个终端启动应用,或将套件的 URL 变量设置为部署环境:

FLUE_AGENT_URL=https://preview.example.com/agents/service-status pnpm run evals

在 CI 中,评估套件是普通的 Vitest 运行——用例失败时以非零退出,因此它像其他测试作业一样拦截流水线。将其与单元测试分开作为单独作业:实时模型运行更慢、消耗 token,且可能在没有代码更改时失败,因此值得有自己的节奏——在合并时、按计划或按需。提供商凭据来自 CI 密钥;对于 HTTP 套件,要么在作业内构建并启动应用,要么指向预览部署。

对于报告,vitest-evals 蓝图添加了一个 evals:json 脚本,写入 vitest-results.json 产物。本地用 vitest-evals serve vitest-results.json 检查,或从 CI 用 getsentry/vitest-evals GitHub Action 发布。报告可能包含提示、输出、工具参数和结果以及错误——在上传前审查保留和访问要求。

后续步骤

  • Vitest Evals——蓝图、生成的工具和报告命令。
  • 智能体——start() 和独立脚本,评估构建的相同接口。
  • 智能体 API——完整的 init() 句柄契约、AgentReply 和 AgentRunError。
  • 智能体 SDK——一个对话 URL 上的 send()、wait() 和 history()。
  • 可观测性——使用 Braintrust 和其他提供商追踪智能体运行;submissionId 和对话 id 等标识符将失败用例与产生它的执行关联起来。