主题
如何使用轨迹评估来评估你的 agent
许多 agent 行为只有在使用真实 LLM 时才会显现,例如 agent 决定调用哪个工具、如何格式化响应,或者提示修改是否影响整个执行轨迹。LangChain 的 agentevals 包提供了专门用于使用实时模型测试 agent 轨迹的评估器。
NOTE
本指南涵盖开源 LangChain agentevals 包,它与 LangSmith 集成以进行轨迹评估。
AgentEvals 允许你通过执行轨迹匹配或使用 LLM 评判来评估 agent 的轨迹(包括工具调用在内的精确消息序列):
Trajectory match
为给定输入硬编码参考轨迹,并通过逐步比较来验证运行。
非常适合测试你已知预期行为的明确定义的工作流。当你对应该调用哪些工具以及调用顺序有具体期望时使用。此方法是确定性的、快速的且经济高效的,因为它不需要额外的 LLM 调用。
LLM-as-judge
使用 LLM 定性地验证 agent 的执行轨迹。“评判者”LLM 根据提示评分标准(其中可以包含参考轨迹)审查 agent 的决策。
更灵活,可以评估效率和适当性等细微方面,但需要 LLM 调用且确定性较低。当你想要评估 agent 轨迹的整体质量和合理性,而不需要严格的工具调用或顺序要求时使用。
安装 AgentEvals
bash
pip install agentevalsbash
npm install agentevals @langchain/core或者,直接克隆 AgentEvals 仓库。
轨迹匹配评估器
AgentEvals 在 Python 中提供 create_trajectory_match_evaluator 函数,在 TypeScript 中提供 createTrajectoryMatchEvaluator,用于将 agent 的轨迹与参考轨迹进行匹配。
你可以使用以下模式:
| 模式 | 描述 | 使用场景 |
|---|---|---|
strict | 消息和工具调用按相同顺序精确匹配 | 测试特定序列(例如,在授权前进行策略查询) |
unordered | 允许相同的工具调用以任意顺序出现 | 在顺序不重要时验证信息检索 |
subset | agent 仅调用参考中的工具(无额外工具) | 确保 agent 不超出预期范围 |
superset | agent 至少调用参考中的工具(允许额外工具) | 验证执行了最低要求的操作 |
严格匹配
strict 模式确保轨迹包含相同顺序的相同消息以及相同的工具调用,但允许消息内容存在差异。当你需要强制执行特定的操作序列时(例如要求在授权操作前进行策略查询),这很有用。
python
from langchain.agents import create_agent
from langchain.tools import tool
from langchain.messages import HumanMessage, AIMessage, ToolMessage
from agentevals.trajectory.match import create_trajectory_match_evaluator
@tool
def get_weather(city: str):
"""Get weather information for a city."""
return f"It's 75 degrees and sunny in {city}."
agent = create_agent("gpt-5.5", tools=[get_weather])
evaluator = create_trajectory_match_evaluator(
trajectory_match_mode="strict",
)
def test_weather_tool_called_strict():
result = agent.invoke({
"messages": [HumanMessage(content="What's the weather in San Francisco?")]
})
reference_trajectory = [
HumanMessage(content="What's the weather in San Francisco?"),
AIMessage(content="", tool_calls=[
{"id": "call_1", "name": "get_weather", "args": {"city": "San Francisco"}}
]),
ToolMessage(content="It's 75 degrees and sunny in San Francisco.", tool_call_id="call_1"),
AIMessage(content="The weather in San Francisco is 75 degrees and sunny."),
]
evaluation = evaluator(
outputs=result["messages"],
reference_outputs=reference_trajectory
)
# {
# 'key': 'trajectory_strict_match',
# 'score': True,
# 'comment': None,
# }
assert evaluation["score"] is Truets
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 `It's 75 degrees and sunny in ${city}.`;
},
{
name: "get_weather",
description: "Get weather information for a city.",
schema: z.object({
city: z.string(),
}),
}
);
const agent = createAgent({
model: "gpt-5.5",
tools: [getWeather]
});
const evaluator = createTrajectoryMatchEvaluator({
trajectoryMatchMode: "strict",
});
async function testWeatherToolCalledStrict() {
const result = await agent.invoke({
messages: [new HumanMessage("What's the weather in San Francisco?")]
});
const referenceTrajectory = [
new HumanMessage("What's the weather in San Francisco?"),
new AIMessage({
content: "",
tool_calls: [
{ id: "call_1", name: "get_weather", args: { city: "San Francisco" } }
]
}),
new ToolMessage({
content: "It's 75 degrees and sunny in San Francisco.",
tool_call_id: "call_1"
}),
new AIMessage("The weather in San Francisco is 75 degrees and sunny."),
];
const evaluation = await evaluator({
outputs: result.messages,
referenceOutputs: referenceTrajectory
});
// {
// 'key': 'trajectory_strict_match',
// 'score': true,
// 'comment': null,
// }
expect(evaluation.score).toBe(true);
}无序匹配
unordered 模式允许相同的工具调用以任意顺序出现,当你想要验证调用了正确的工具集但不关心顺序时,这很有帮助。例如,agent 可能需要同时查询某个城市的天气和活动,但顺序无关紧要。
python
from langchain.agents import create_agent
from langchain.tools import tool
from langchain.messages import HumanMessage, AIMessage, ToolMessage
from agentevals.trajectory.match import create_trajectory_match_evaluator
@tool
def get_weather(city: str):
"""Get weather information for a city."""
return f"It's 75 degrees and sunny in {city}."
@tool
def get_events(city: str):
"""Get events happening in a city."""
return f"Concert at the park in {city} tonight."
agent = create_agent("gpt-5.5", tools=[get_weather, get_events])
evaluator = create_trajectory_match_evaluator(
trajectory_match_mode="unordered",
)
def test_multiple_tools_any_order():
result = agent.invoke({
"messages": [HumanMessage(content="What's happening in SF today?")]
})
# Reference shows tools called in different order than actual execution
reference_trajectory = [
HumanMessage(content="What's happening in SF today?"),
AIMessage(content="", tool_calls=[
{"id": "call_1", "name": "get_events", "args": {"city": "SF"}},
{"id": "call_2", "name": "get_weather", "args": {"city": "SF"}},
]),
ToolMessage(content="Concert at the park in SF tonight.", tool_call_id="call_1"),
ToolMessage(content="It's 75 degrees and sunny in SF.", tool_call_id="call_2"),
AIMessage(content="Today in SF: 75 degrees and sunny with a concert at the park tonight."),
]
evaluation = evaluator(
outputs=result["messages"],
reference_outputs=reference_trajectory,
)
# {
# 'key': 'trajectory_unordered_match',
# 'score': True,
# }
assert evaluation["score"] is Truets
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 `It's 75 degrees and sunny in ${city}.`;
},
{
name: "get_weather",
description: "Get weather information for a city.",
schema: z.object({ city: z.string() }),
}
);
const getEvents = tool(
async ({ city }: { city: string }) => {
return `Concert at the park in ${city} tonight.`;
},
{
name: "get_events",
description: "Get events happening in a city.",
schema: z.object({ city: z.string() }),
}
);
const agent = createAgent({
model: "gpt-5.5",
tools: [getWeather, getEvents]
});
const evaluator = createTrajectoryMatchEvaluator({
trajectoryMatchMode: "unordered",
});
async function testMultipleToolsAnyOrder() {
const result = await agent.invoke({
messages: [new HumanMessage("What's happening in SF today?")]
});
// Reference shows tools called in different order than actual execution
const referenceTrajectory = [
new HumanMessage("What's happening in SF today?"),
new AIMessage({
content: "",
tool_calls: [
{ id: "call_1", name: "get_events", args: { city: "SF" } },
{ id: "call_2", name: "get_weather", args: { city: "SF" } },
]
}),
new ToolMessage({
content: "Concert at the park in SF tonight.",
tool_call_id: "call_1"
}),
new ToolMessage({
content: "It's 75 degrees and sunny in SF.",
tool_call_id: "call_2"
}),
new AIMessage("Today in SF: 75 degrees and sunny with a concert at the park tonight."),
];
const evaluation = await evaluator({
outputs: result.messages,
referenceOutputs: referenceTrajectory,
});
// {
// 'key': 'trajectory_unordered_match',
// 'score': true,
// }
expect(evaluation.score).toBe(true);
}子集和超集匹配
superset 和 subset 模式关注调用哪些工具,而不是工具调用的顺序,允许你控制 agent 的工具调用必须与参考对齐的严格程度。
- 当你想要验证执行中调用了几个关键工具,但可以接受 agent 调用额外工具时,使用
superset模式。agent 的轨迹必须至少包含参考轨迹中的所有工具调用,并且可以包含参考之外的额外工具调用。 - 使用
subset模式通过验证 agent 未调用参考之外的任何无关或不必要工具来确保 agent 效率。agent 的轨迹必须仅包含参考轨迹中出现的工具调用。
以下示例演示 superset 模式,其中参考轨迹只要求 get_weather 工具,但 agent 可以调用额外工具:
python
from langchain.agents import create_agent
from langchain.tools import tool
from langchain.messages import HumanMessage, AIMessage, ToolMessage
from agentevals.trajectory.match import create_trajectory_match_evaluator
@tool
def get_weather(city: str):
"""Get weather information for a city."""
return f"It's 75 degrees and sunny in {city}."
@tool
def get_detailed_forecast(city: str):
"""Get detailed weather forecast for a city."""
return f"Detailed forecast for {city}: sunny all week."
agent = create_agent("gpt-5.5", tools=[get_weather, get_detailed_forecast])
evaluator = create_trajectory_match_evaluator(
trajectory_match_mode="superset",
)
def test_agent_calls_required_tools_plus_extra():
result = agent.invoke({
"messages": [HumanMessage(content="What's the weather in Boston?")]
})
# Reference only requires get_weather, but agent may call additional tools
reference_trajectory = [
HumanMessage(content="What's the weather in Boston?"),
AIMessage(content="", tool_calls=[
{"id": "call_1", "name": "get_weather", "args": {"city": "Boston"}},
]),
ToolMessage(content="It's 75 degrees and sunny in Boston.", tool_call_id="call_1"),
AIMessage(content="The weather in Boston is 75 degrees and sunny."),
]
evaluation = evaluator(
outputs=result["messages"],
reference_outputs=reference_trajectory,
)
# {
# 'key': 'trajectory_superset_match',
# 'score': True,
# 'comment': None,
# }
assert evaluation["score"] is Truets
import { createAgent } from "langchain"
import { tool } from "@langchain/core/tools";
import { HumanMessage, AIMessage, ToolMessage } from "@langchain/core/messages";
import { createTrajectoryMatchEvaluator } from "agentevals";
import * as z from "zod";
const getWeather = tool(
async ({ city }: { city: string }) => {
return `It's 75 degrees and sunny in ${city}.`;
},
{
name: "get_weather",
description: "Get weather information for a city.",
schema: z.object({ city: z.string() }),
}
);
const getDetailedForecast = tool(
async ({ city }: { city: string }) => {
return `Detailed forecast for ${city}: sunny all week.`;
},
{
name: "get_detailed_forecast",
description: "Get detailed weather forecast for a city.",
schema: z.object({ city: z.string() }),
}
);
const agent = createAgent({
model: "gpt-5.5",
tools: [getWeather, getDetailedForecast]
});
const evaluator = createTrajectoryMatchEvaluator({
trajectoryMatchMode: "superset",
});
async function testAgentCallsRequiredToolsPlusExtra() {
const result = await agent.invoke({
messages: [new HumanMessage("What's the weather in Boston?")]
});
// Reference only requires getWeather, but agent may call additional tools
const referenceTrajectory = [
new HumanMessage("What's the weather in Boston?"),
new AIMessage({
content: "",
tool_calls: [
{ id: "call_1", name: "get_weather", args: { city: "Boston" } },
]
}),
new ToolMessage({
content: "It's 75 degrees and sunny in Boston.",
tool_call_id: "call_1"
}),
new AIMessage("The weather in Boston is 75 degrees and sunny."),
];
const evaluation = await evaluator({
outputs: result.messages,
referenceOutputs: referenceTrajectory,
});
// {
// 'key': 'trajectory_superset_match',
// 'score': true,
// 'comment': null,
// }
expect(evaluation.score).toBe(true);
}INFO
你还可以通过设置 tool_args_match_mode(Python)或 toolArgsMatchMode(TypeScript)属性,以及 tool_args_match_overrides(Python)或 toolArgsMatchOverrides(TypeScript)属性,自定义评估器如何考虑实际轨迹与参考中工具调用之间的相等性。默认情况下,只有对同一工具使用相同参数的工具调用才被视为相等。请访问仓库了解更多详情。
LLM 作为评判者的评估器
NOTE
本节介绍 agentevals 包中特定于轨迹的 LLM-as-a-judge 评估器。有关 LangSmith 中通用 LLM-as-a-judge 评估器,请参阅 LLM-as-a-judge 评估器。
你还可以使用 LLM 评估 agent 的执行路径。与轨迹匹配评估器不同,它不需要参考轨迹,但如果有可用的参考轨迹,也可以提供。
无参考轨迹
python
from langchain.agents import create_agent
from langchain.tools import tool
from langchain.messages import HumanMessage, AIMessage, ToolMessage
from agentevals.trajectory.llm import create_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT
@tool
def get_weather(city: str):
"""Get weather information for a city."""
return f"It's 75 degrees and sunny in {city}."
agent = create_agent("gpt-5.5", tools=[get_weather])
evaluator = create_trajectory_llm_as_judge(
model="openai:o3-mini",
prompt=TRAJECTORY_ACCURACY_PROMPT,
)
def test_trajectory_quality():
result = agent.invoke({
"messages": [HumanMessage(content="What's the weather in Seattle?")]
})
evaluation = evaluator(
outputs=result["messages"],
)
# {
# 'key': 'trajectory_accuracy',
# 'score': True,
# 'comment': 'The provided agent trajectory is reasonable...'
# }
assert evaluation["score"] is Truets
import { createAgent } from "langchain"
import { tool } from "@langchain/core/tools";
import { HumanMessage, AIMessage, ToolMessage } from "@langchain/core/messages";
import { createTrajectoryLLMAsJudge, TRAJECTORY_ACCURACY_PROMPT } from "agentevals";
import * as z from "zod";
const getWeather = tool(
async ({ city }: { city: string }) => {
return `It's 75 degrees and sunny in ${city}.`;
},
{
name: "get_weather",
description: "Get weather information for a city.",
schema: z.object({ city: z.string() }),
}
);
const agent = createAgent({
model: "gpt-5.5",
tools: [getWeather]
});
const evaluator = createTrajectoryLLMAsJudge({
model: "openai:o3-mini",
prompt: TRAJECTORY_ACCURACY_PROMPT,
});
async function testTrajectoryQuality() {
const result = await agent.invoke({
messages: [new HumanMessage("What's the weather in Seattle?")]
});
const evaluation = await evaluator({
outputs: result.messages,
});
// {
// 'key': 'trajectory_accuracy',
// 'score': true,
// 'comment': 'The provided agent trajectory is reasonable...'
// }
expect(evaluation.score).toBe(true);
}有参考轨迹
如果你有参考轨迹,可以在提示中添加一个额外变量并传入参考轨迹。下面,我们使用预构建的 TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE 提示,并配置 reference_outputs 变量:
python
evaluator = create_trajectory_llm_as_judge(
model="openai:o3-mini",
prompt=TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE,
)
evaluation = evaluator(
outputs=result["messages"],
reference_outputs=reference_trajectory,
)ts
import { TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE } from "agentevals";
const evaluator = createTrajectoryLLMAsJudge({
model: "openai:o3-mini",
prompt: TRAJECTORY_ACCURACY_PROMPT_WITH_REFERENCE,
});
const evaluation = await evaluator({
outputs: result.messages,
referenceOutputs: referenceTrajectory,
});INFO
如需更灵活地配置 LLM 评估轨迹的方式,请访问仓库。
异步支持 (Python)
所有 agentevals 评估器都支持 Python asyncio。对于使用工厂函数的评估器,可以通过在函数名中的 create_ 后添加 async 来使用异步版本。
以下示例使用异步评判器和评估器:
python
from agentevals.trajectory.llm import create_async_trajectory_llm_as_judge, TRAJECTORY_ACCURACY_PROMPT
from agentevals.trajectory.match import create_async_trajectory_match_evaluator
async_judge = create_async_trajectory_llm_as_judge(
model="openai:o3-mini",
prompt=TRAJECTORY_ACCURACY_PROMPT,
)
async_evaluator = create_async_trajectory_match_evaluator(
trajectory_match_mode="strict",
)
async def test_async_evaluation():
result = await agent.ainvoke({
"messages": [HumanMessage(content="What's the weather?")]
})
evaluation = await async_judge(outputs=result["messages"])
assert evaluation["score"] is True