测试 (Test)

智能体应用让 LLM 自主决定下一步操作来解决问题。这种灵活性非常强大,但模型的黑盒特性使得很难预测智能体某部分的调整会如何影响其他部分。要构建生产级智能体,全面的测试至关重要。

测试智能体有以下几种方法:

智能体应用往往更依赖集成测试,因为它们将多个组件链接在一起,并且必须处理由于 LLM 非确定性特性导致的不稳定性。

集成测试

许多智能体行为只有在使用真实 LLM 时才会出现,例如智能体决定调用哪个工具、如何格式化响应,或者提示修改是否会影响整个执行轨迹。LangChain 的 agentevals 包提供了专门用于测试智能体轨迹的评估器。

AgentEvals 让你可以通过执行轨迹匹配或使用 LLM 裁判来轻松评估智能体的轨迹(精确的消息序列,包括工具调用):

轨迹匹配

为给定输入硬编码参考轨迹,并通过逐步比较验证运行。

适用于测试预期行为明确的工作流。当你对应该调用哪些工具以及调用顺序有具体期望时使用。这种方法是确定性的、快速的且成本效益高,因为不需要额外的 LLM 调用。

LLM 裁判

使用 LLM 定性验证智能体的执行轨迹。"裁判" LLM 根据提示评分标准(可包含参考轨迹)审查智能体的决策。

更灵活,可以评估效率和适当性等细微方面,但需要 LLM 调用且确定性较低。当你想评估智能体轨迹的整体质量和合理性,而不需要严格的工具调用或顺序要求时使用。

安装 AgentEvals

npm install agentevals @langchain/core

或者,直接克隆 AgentEvals 仓库

轨迹匹配评估器

AgentEvals 提供 createTrajectoryMatchEvaluator 函数来将你的智能体轨迹与参考轨迹进行匹配。有四种模式可选:

模式 描述 使用场景
strict 消息和工具调用按相同顺序精确匹配 测试特定序列(如授权前先查询策略)
unordered 相同的工具调用允许任意顺序 验证信息检索,顺序不重要时
subset 智能体只调用参考中的工具(无额外调用) 确保智能体不超出预期范围
superset 智能体至少调用参考中的工具(允许额外调用) 验证执行了最低要求的操作

严格匹配示例

strict 模式确保轨迹包含相同顺序的相同消息和相同工具调用,但允许消息内容有差异。当你需要强制执行特定操作序列时很有用。

import { createAgent, tool, HumanMessage, AIMessage, ToolMessage } from "langchain"
import { createTrajectoryMatchEvaluator } from "agentevals";
import * as z from "zod";

const getWeather = tool(
  async ({ city }: { city: string }) => {
    return `${city}的天气是75度,晴天。`;
  },
  {
    name: "get_weather",
    description: "获取城市的天气信息。",
    schema: z.object({
      city: z.string(),
    }),
  }
);

const agent = createAgent({
  model: "gpt-4o",
  tools: [getWeather]
});

const evaluator = createTrajectoryMatchEvaluator({
  trajectoryMatchMode: "strict",
});

async function testWeatherToolCalledStrict() {
  const result = await agent.invoke({
    messages: [new HumanMessage("旧金山的天气怎么样?")]
  });

  const referenceTrajectory = [
    new HumanMessage("旧金山的天气怎么样?"),
    new AIMessage({
      content: "",
      tool_calls: [
        { id: "call_1", name: "get_weather", args: { city: "San Francisco" } }
      ]
    }),
    new ToolMessage({
      content: "San Francisco的天气是75度,晴天。",
      tool_call_id: "call_1"
    }),
    new AIMessage("旧金山的天气是75度,晴天。"),
  ];

  const evaluation = await evaluator({
    outputs: result.messages,
    referenceOutputs: referenceTrajectory
  });
  // {
  //     'key': 'trajectory_strict_match',
  //     'score': true,
  //     'comment': null,
  // }
  expect(evaluation.score).toBe(true);
}

无序匹配示例

unordered 模式允许相同的工具调用以任意顺序出现,当你想验证获取了特定信息但不关心顺序时很有用。

const evaluator = createTrajectoryMatchEvaluator({
  trajectoryMatchMode: "unordered",
});

// 参考显示工具调用顺序与实际执行不同
const referenceTrajectory = [
  new HumanMessage("今天SF有什么活动?"),
  new AIMessage({
    content: "",
    tool_calls: [
      { id: "call_1", name: "get_events", args: { city: "SF" } },
      { id: "call_2", name: "get_weather", args: { city: "SF" } },
    ]
  }),
  // ... 工具消息和最终响应
];

const evaluation = await evaluator({
  outputs: result.messages,
  referenceOutputs: referenceTrajectory,
});
// { 'key': 'trajectory_unordered_match', 'score': true }

LLM 裁判评估器

AgentEvals 提供 createTrajectoryLLMAsJudge 函数,让裁判 LLM 查看智能体的执行轨迹并评估整体质量。

import { createTrajectoryLLMAsJudge } from "agentevals";

const evaluator = createTrajectoryLLMAsJudge({
  model: "gpt-4o",
  rubric: `评估智能体是否:
    1. 使用了正确的工具
    2. 以合理的顺序执行
    3. 产生了准确的最终答案`,
});

const evaluation = await evaluator({
  inputs: [new HumanMessage("旧金山的天气怎么样?")],
  outputs: result.messages,
});
// {
//     'key': 'trajectory_llm_as_judge',
//     'score': true,
//     'comment': '智能体正确调用了 get_weather 工具...',
// }

带参考轨迹的 LLM 裁判

你也可以将参考轨迹传递给 LLM 裁判,帮助其理解预期行为:

const evaluation = await evaluator({
  inputs: [new HumanMessage("旧金山的天气怎么样?")],
  outputs: result.messages,
  referenceOutputs: referenceTrajectory,  // 可选的参考轨迹
});

LangSmith 集成

要大规模运行评估并跟踪结果,请使用 LangSmith。LangSmith 提供了一个平台来:

import { evaluate } from "langsmith/evaluation";

const dataset = await client.createDataset("weather-queries");

await evaluate(
  async (inputs) => {
    const result = await agent.invoke(inputs);
    return result.messages;
  },
  {
    data: dataset,
    evaluators: [
      createTrajectoryMatchEvaluator({ trajectoryMatchMode: "strict" }),
      createTrajectoryLLMAsJudge({ model: "gpt-4o", rubric: "..." }),
    ],
  }
);

最佳实践

下一步