☰
基于MCP规范自动合成智能体评测的完整方案与代码实现
2026/10/1 0:59:06 网站建设 项目流程

最近在折腾智能体(Agent)项目时,最让我头疼的并不是模型怎么调用、工具怎么接,而是评测怎么做。代码写完了,功能跑通了,但一问“你的智能体到底靠不靠谱”,往往只能用“我试了几个例子,感觉还行”来回答。这种状态在开发阶段可以凑合,一旦要上生产环境或交给业务方验收,就完全站不住脚。

后来我接触到一个思路:与其人工设计评测用例,不如从 MCP(Model Context Protocol)规范中自动合成评测任务。顺着这个方向,我整理了一套基于“MCP 规范自动合成智能体评测”的完整方案,并用代码落地了核心流程。本文就把这套方法拆开讲清楚,内容包括核心概念、工作流程、代码示例、常见坑点以及工程建议,适合正在做智能体开发、AI 应用集成或评测体系建设的开发者阅读。

1. 背景与核心概念

先聊一个基础问题:为什么智能体评测这么难?

传统软件测试有明确的输入、输出和断言,但智能体的行为链路很长。它要理解用户指令,规划任务,调用工具,处理工具返回结果,再决定下一步动作。任何一个环节出错,最终表现都可能是失败的。更麻烦的是,智能体的“正确回答”往往不唯一,你很难用一条 SQL 或者一个 assertEquals 来判定它是否成功。

这时候,MCP 规范的出现给了评测一个很好的抓手。

1.1 MCP 到底是什么

MCP 是 Model Context Protocol 的缩写,中文常翻译为“模型上下文协议”。它定义了一套标准化接口,让 AI 模型(或智能体)能够与外部工具、数据源、服务进行交互。你可以把它理解成“AI 世界的 USB 接口”:只要工具方实现了 MCP 协议,任何支持 MCP 的智能体客户端就能直接使用这个工具,不用再为每家工具单独写适配代码。

MCP 协议中有几个核心概念:

  • MCP Server:提供工具能力的服务端,比如一个能查询天气、操作数据库、调用浏览器的服务。
  • Tool:MCP Server 暴露给模型的具体能力,每个 Tool 都有名称、描述、输入参数 schema。
  • MCP Client:智能体或应用侧的角色,负责连接 Server,发现 Tools,并在需要时调用 Tools。
  • Resource:可读取的数据资源,比如文件内容、API 返回结果。
  • Prompt:可复用的提示词模板,方便客户端规范化调用。

对于一个智能体来说,MCP 规范定义了“它能操作什么”的边界;而智能体评测的核心,恰恰就是验证“它是否正确地操作了这些能力”。

1.2 Agent Seer 是什么

Agent Seer 这个名字,我理解为一套“面向智能体的评测方法论和工具链”,核心动作是“从 MCP 规范中自动合成评测集并执行评测”。

它解决的痛点很直接:

  • 人工写评测用例,慢且覆盖不全。
  • 智能体接入了大量 MCP 工具,每个工具都要测,人力跟不上。
  • 手工用例很难跟上 Agent 行为的多变性,容易漏掉边界场景。
  • 评测结果主观性强,缺少可量化的指标。

而如果 MCP 规范已经写清楚了每个工具的名称、描述和参数 schema,理论上我们就可以基于这份规范自动生成评测任务:让智能体去完成“调用该工具完成某件事”的目标,再检查它的工具选择、参数填充和结果处理是否正确。

1.3 Agent Skill 与 MCP 的区别

在很多文章里会看到“Agent Skill”和“MCP”两个词。My understanding:

  • MCP 偏向“工具连接层”,负责定义并打通模型与外部能力的通道。
  • Agent Skill 偏向“能力封装层”,通常是一个包含提示词、工具调用逻辑、执行流程的完整技能单元。

你可以这样理解:MCP Server 提供了“能力接口”,Agent Skill 则是“如何用好这些接口的方法论”。两者有交集,但不能直接画等号。在评测时,MCP 更贴近协议层,容易做自动化断言;Skill 更贴近行为层,需要结合具体场景设计评测。

1.4 为什么“从规范自动合成评测”是可行的

因为 MCP 工具定义里已经包含了大量可结构化解析的信息:

{ "name": "get_weather", "description": "查询指定城市的实时天气信息", "inputSchema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如 北京、上海" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" } }, "required": ["city"] } }

这份 schema 本身就提供了:

  • 工具调用意图:用于生成用户自然语言请求。
  • 参数约束:用于构造合法参数、边界参数、缺失参数等测试场景。
  • 工具依赖关系:如果多个工具协同工作,可以合成多步任务。
  • 预期输出结构:为结果断言提供参考。

所以说,MCP 规范不只是给智能体看的接口文档,它同时是一份“高密度的评测需求说明书”。

2. 环境准备与版本说明

在开始写代码之前,先明确一下本文的环境。

由于 Agent Seer 目前还没有一个统一官方的标准发行版,不同团队落地方式也不同。本文以我习惯的 Python 技术栈为例,演示如何实现“MCP 规范解析 + 评测任务合成 + 评测执行与打分”的闭环。

版本方面,需要说明的是:MCP 协议本身还在快速演进,不同语言的 SDK 版本差异较大,本文示例不绑定特定 SDK 版本,重点演示协议层面的思路。你可以根据自己的项目实际情况调整,如果遇到接口变化,优先查阅你当前安装 SDK 的官方文档。

我本次实验使用的环境如下:

组件说明
操作系统Windows 11 / Ubuntu 22.04 均可
Python3.10 及以上
MCP SDK示例以 mcp Python SDK 为例,不写死版本
大模型 API以 OpenAI 兼容接口为例,实际可替换
IDEVS Code 或 PyCharm

为便于复现,我创建了一个项目目录结构:

agent-seer-demo/ ├── specs/ # 存放 MCP 工具定义文件 │ └── weather_server.json ├── generator/ # 评测任务生成器 │ ├── __init__.py │ └── task_synthesizer.py ├── executor/ # 评测执行器 │ ├── __init__.py │ └── evaluator.py ├── reports/ # 评测报告输出目录 └── main.py # 主流程入口

这只是我的个人组织方式,你可以按照团队规范调整。关键在于把“规范读取”“任务生成”“评测执行”“结果输出”四个环节解耦。

3. 核心原理与整体工作流

这套评测方案的全流程,可以拆成五个阶段:

  1. 解析阶段:读取 MCP Server 暴露的工具定义,提取工具名、描述、参数 schema。
  2. 合成阶段:根据工具定义,自动生成用户请求、预期工具调用序列、预期参数约束。
  3. 执行阶段:将生成的自然语言请求发送给被测智能体,记录智能体的工具调用轨迹和最终回答。
  4. 判定阶段:将智能体实际行为与预期行为对比,计算准确率、工具调用正确率、参数合规率等指标。
  5. 报告阶段:汇总评测结果,定位失败场景,输出结构化报告。

3.1 评测任务合成的基本策略

这是整套流程中最关键的环节。大致有三类策略:

第一类:单工具直接调用

根据单个工具的输入 schema,生成一条自然语言指令,期望智能体调用且只调用该工具,并正确填写参数。

例如:

  • 工具:get_weather
  • 参数:city必填,unit可填。
  • 生成请求:“北京现在多少度?请帮我查一下。”
  • 预期行为:调用get_weather,参数city="北京"。

这种策略适合验证基础工具调用能力,是评测集里的地基。

第二类:多工具协同任务

从工具集中挑选多个有关联的工具,组合成一条复杂任务。例如一个工具负责查询地址,另一个工具负责查询天气,评测任务就是“先查到杭州市西湖区的地址,再查一下那里的天气”。

这种策略能验证智能体的任务拆解和多步规划能力。

第三类:边界与异常场景

基于参数 schema 的约束,生成异常输入:

  • 缺少必填参数。
  • 参数类型错误。
  • 枚举值超出范围。
  • 语义模糊,需要澄清。
  • 多个参数组合冲突。

这类用例的目的是测试智能体的容错能力和兜底策略。

3.2 评测指标设计

评测不能只看“最终回答是否成功”,还需要关注过程指标。我常用的指标如下:

指标名称计算方式说明
工具选择正确率正确工具调用数 / 总评测任务数智能体是否选对了工具
参数填充完整率必填参数填完整的任务数 / 总任务数是否漏填参数
参数值合规率参数值符合 schema 的任务数 / 总任务数参数类型、枚举是否合法
任务完成率最终结果正确的任务数 / 总任务数是否真正完成任务
平均工具调用轮数工具调用次数总和 / 总任务数反映执行效率

在实现时,最关键的是“判定逻辑”要分两层:

  • 硬判定:程序自动比对,比如工具名称是否匹配、必填参数是否存在。
  • 软判定:语义相似度判断,比如最终回答是否准确,这部分建议用 LLM 作为 Judge,或者借助相似度计算。

4. 完整实战:实现一个最小可用的 Agent Seer

下面进入实操环节。我会从零实现一个简化版但逻辑完整的流程,让你可以复制到本地运行。

4.1 准备 MCP 工具定义

我们在specs/weather_server.json中准备一份简化的 MCP 工具定义。注意,这里为了演示而手动构造了一个 JSON 文件,实际项目中,你可以通过 MCP Client 的list_tools接口动态获取。

{ "tools": [ { "name": "get_weather", "description": "查询指定城市的实时天气信息", "inputSchema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市名称,例如 北京、上海" }, "unit": { "type": "string", "enum": ["celsius", "fahrenheit"], "default": "celsius" } }, "required": ["city"] } }, { "name": "get_city_code", "description": "根据城市名称查询城市代码", "inputSchema": { "type": "object", "properties": { "city": { "type": "string", "description": "城市中文名称" } }, "required": ["city"] } } ] }

这里我故意放了一个“先查询城市代码,再查询天气”的潜在协同场景,方便后面演示多工具任务。

4.2 编写评测任务合成器

创建generator/task_synthesizer.py。这个模块的核心目标是:根据工具 schema,自动生成评测用例。

# 文件路径:generator/task_synthesizer.py import json from typing import Any, Dict, List class TaskSynthesizer: """根据 MCP 工具定义自动合成评测任务。""" def __init__(self, spec_path: str): with open(spec_path, "r", encoding="utf-8") as f: self.spec = json.load(f) self.tools = self.spec["tools"] def _generate_single_tool_tasks(self) -> List[Dict[str, Any]]: """基于单工具生成基础调用任务。""" tasks = [] for tool in self.tools: name = tool["name"] desc = tool["description"] schema = tool["inputSchema"] required = schema.get("required", []) # 从 properties 里取第一个必填参数作为示例值来源 if not required: continue first_prop = required[0] prop_schema = schema["properties"][first_prop] # 这里用简单的规则构造示例值,实际可以接 LLM 生成更丰富的描述 sample_value = "北京" if prop_schema.get("type") == "string" else 1 user_request = f"{desc},请查询 {sample_value} 的信息" task = { "task_id": f"single_{name}", "user_request": user_request, "expected_tool_calls": [ { "tool_name": name, "arguments": {first_prop: sample_value}, } ], "scenario": "single_tool", } tasks.append(task) return tasks def _generate_multi_tool_tasks(self) -> List[Dict[str, Any]]: """基于工具组合生成多步协同任务。""" tasks = [] # 示例:将 get_city_code 和 get_weather 串起来 city_tool = None weather_tool = None for tool in self.tools: if tool["name"] == "get_city_code": city_tool = tool if tool["name"] == "get_weather": weather_tool = tool if city_tool and weather_tool: tasks.append( { "task_id": "multi_city_weather", "user_request": "我想知道上海的天气,但调用天气接口前需要先根据城市名称查到城市代码,请帮我完成整个流程。", "expected_tool_calls": [ {"tool_name": "get_city_code", "arguments": {"city": "上海"}}, {"tool_name": "get_weather", "arguments": {"city": "上海"}}, ], "scenario": "multi_tool", } ) return tasks def synthesize(self) -> List[Dict[str, Any]]: """合成所有评测任务。""" tasks = [] tasks.extend(self._generate_single_tool_tasks()) tasks.extend(self._generate_multi_tool_tasks()) return tasks

这段代码的思路是:

  • 先读取工具定义。
  • 对每个工具生成一个基础调用任务。
  • 再根据工具之间的潜在依赖生成一个多工具协同任务。

实际工程中,这里应该接入 LLM,根据工具描述生成更自然、更多样的用户请求,而不仅是“请查询 XX”。但作为最小示例,这套规则已经能跑通流程。

4.3 编写评测执行器

创建executor/evaluator.py。这里的核心职责是:输入一个评测任务,模拟智能体执行过程,并输出判定结果。

为了让示例不依赖真实大模型和 MCP 网络服务,我们先实现一个“模拟智能体”。这个模拟器中,我们写死一个工具调用逻辑:如果用户请求里包含“北京”或“上海”,就调用get_weather。这样做的目的是快速验证评测框架本身;真实环境中,你可以换成对大模型 Agent 的调用。

# 文件路径:executor/evaluator.py import json from typing import Any, Dict, List class MockAgent: """一个用于演示的模拟智能体,负责模拟工具调用行为。""" def __init__(self, tool_specs: Dict[str, Any]): self.tool_specs = tool_specs def run(self, user_request: str) -> List[Dict[str, Any]]: """根据用户请求模拟返回工具调用轨迹。""" tool_calls = [] if "城市代码" in user_request or "查城市代码" in user_request: tool_calls.append( { "tool_name": "get_city_code", "arguments": {"city": "上海"}, } ) if "天气" in user_request or "温度" in user_request: tool_calls.append( { "tool_name": "get_weather", "arguments": {"city": "北京"}, } ) return tool_calls class Evaluator: """评测执行器:比较智能体实际行为与预期行为。""" def __init__(self, agent: MockAgent): self.agent = agent def _check_tool_call( self, actual_calls: List[Dict[str, Any]], expected_calls: List[Dict[str, Any]] ) -> Dict[str, Any]: """对比工具调用轨迹。""" actual_names = [call["tool_name"] for call in actual_calls] expected_names = [call["tool_name"] for call in expected_calls] tool_correct = actual_names == expected_names # 检查参数 param_all_ok = True for expected in expected_calls: matched = [ call for call in actual_calls if call["tool_name"] == expected["tool_name"] ] if not matched: param_all_ok = False break for key, value in expected["arguments"].items(): if matched[0]["arguments"].get(key) != value: param_all_ok = False break return { "tool_correct": tool_correct, "param_correct": param_all_ok, } def evaluate(self, task: Dict[str, Any]) -> Dict[str, Any]: """执行单个评测任务。""" user_request = task["user_request"] expected_calls = task["expected_tool_calls"] actual_calls = self.agent.run(user_request) check_result = self._check_tool_call(actual_calls, expected_calls) return { "task_id": task["task_id"], "scenario": task.get("scenario", ""), "user_request": user_request, "expected_calls": expected_calls, "actual_calls": actual_calls, **check_result, } def generate_report(results: List[Dict[str, Any]]) -> Dict[str, Any]: """汇总评测结果并生成报告。""" total = len(results) tool_correct_num = 0 param_correct_num = 0 for result in results: if result["tool_correct"]: tool_correct_num += 1 if result["param_correct"]: param_correct_num += 1 return { "total": total, "tool_correct_rate": round(tool_correct_num / total, 4) if total else 0, "param_correct_rate": round(param_correct_num / total, 4) if total else 0, "details": results, }

这里需要注意:我的MockAgent存在一个明显问题,当请求“上海的天气”时,它会同时调用get_city_code和get_weather,但get_weather的参数会被错误地写死为“北京”。这正是评测框架的价值:它能自动发现模拟智能体的工具参数错误。

4.4 编写主流程入口

创建main.py,把合成器和执行器串联起来。

# 文件路径:main.py import json from generator.task_synthesizer import TaskSynthesizer from executor.evaluator import Evaluator, MockAgent, generate_report SPEC_PATH = "specs/weather_server.json" def main(): # 1. 根据 MCP 规范合成评测任务 synthesizer = TaskSynthesizer(SPEC_PATH) tasks = synthesizer.synthesize() print(f"共合成评测任务 {len(tasks)} 个:") for task in tasks: print(f" - {task['task_id']}: {task['user_request']}") # 2. 构造模拟智能体 with open(SPEC_PATH, "r", encoding="utf-8") as f: spec = json.load(f) agent = MockAgent(spec["tools"]) # 3. 执行评测 evaluator = Evaluator(agent) results = [evaluator.evaluate(task) for task in tasks] # 4. 生成报告 report = generate_report(results) print("\n==== 评测报告 ====") print(f"工具选择正确率: {report['tool_correct_rate']:.2%}") print(f"参数填充正确率: {report['param_correct_rate']:.2%}") # 输出详细结果 for detail in report["details"]: print("\n----------------------------") print(f"任务ID: {detail['task_id']}") print(f"用户请求: {detail['user_request']}") print(f"预期工具: {[c['tool_name'] for c in detail['expected_calls']]}") print(f"实际工具: {[c['tool_name'] for c in detail['actual_calls']]}") print(f"工具选择是否正确: {detail['tool_correct']}") print(f"参数是否正确: {detail['param_correct']}") if __name__ == "__main__": main()

4.5 运行与预期结果

在项目根目录执行:

python main.py

预期输出大致如下:

共合成评测任务 3 个: - single_get_weather: 查询指定城市的实时天气信息,请查询 北京 的信息 - single_get_city_code: 根据城市名称查询城市代码,请查询 北京 的信息 - multi_city_weather: 我想知道上海的天气,但调用天气接口前需要先根据城市名称查到城市代码,请帮我完成整个流程。 ==== 评测报告 ==== 工具选择正确率: 66.67% 参数填充正确率: 33.33% ---------------------------- 任务ID: single_get_weather ...

从结果可以看到,模拟智能体在single_get_weather中参数填错了(把北京写成了默认),在multi_city_weather中工具顺序和参数也有问题。这个最小示例证明了评测框架的有效性:即使是一个很粗糙的 Agent,也能通过这套机制快速暴露问题。

5. 进阶改造:接入真实 MCP Server 与 LLM Agent

上面的示例是本地模拟,实际落地时你需要替换两个核心模块:

  • 真实 Agent:接入大模型并启用 MCP Client。
  • 真实工具执行:可调用 MCP Server 执行工具,或使用 Mock Server。

下面给出一个“接入 OpenAPI 兼容大模型作为 Agent 推理核心”的代码草图。

# 文件路径:executor/llm_agent.py from openai import OpenAI class LLMAgent: """通过大模型 API 驱动智能体,使用 OpenAI 兼容格式。""" def __init__(self, base_url: str, api_key: str, model: str, tools: list): self.client = OpenAI(base_url=base_url, api_key=api_key) self.model = model # tools 是 MCP 中定义的 tools,转换为 OpenAI function calling 格式 self.tools = tools def run(self, user_request: str) -> list: messages = [{"role": "user", "content": user_request}] response = self.client.chat.completions.create( model=self.model, messages=messages, tools=self.tools, ) tool_calls = response.choices[0].message.tool_calls or [] parsed_calls = [] for call in tool_calls: args_str = call.function.arguments parsed_calls.append( { "tool_name": call.function.name, "arguments": json.loads(args_str), } ) return parsed_calls

这段代码只是一个核心片段,需要放入你自己的项目文件中,并安装openaiPython SDK。注意,OpenAI 的function calling格式与 MCP 的 Tool 格式有差异,你需要做一次格式转换。转换并不复杂,主要是将 MCP 的inputSchema映射到 OpenAI 的parameters字段。

如果你的模型不支持 function calling,也可以通过“文本提示 + 结构化输出”的方式,让模型输出 JSON 格式的工具调用指令,然后解析 JSON。这种方式更通用,但对提示词要求较高。

6. 常见问题与排查思路

在实现这套评测方案时,我遇到了一些典型问题,这里以表格形式分享排查思路。

问题现象常见原因解决思路
MCP 工具列表获取为空MCP Server 未启动或鉴权失败检查 Server 地址、Token;先用 Postman 或 curl 验证工具接口可访问
工具注册不上工具 schema 格式与客户端期望不一致检查 MCP 规范版本,确认 client 与 server 的 SDK 版本兼容
生成的自然语言请求太僵硬规则模板过于简单接入 LLM,根据工具描述生成多样化的用户意图,并保留预期结果字段
智能体调用了多个工具但顺序不对评测任务拆解不明确在合成任务时,加入步骤序号和依赖关系字段,判定时要求顺序匹配
参数类型频繁出错模型对 schema 理解不足在工具描述中增加参数示例,并在 System Prompt 中强调参数格式
评测结果不稳定大模型输出有随机性设 temperature=0 或较低值;多次运行取统计结果,而不是依赖单次输出
多工具任务无法验证中间步骤只检查最终回答需要 Agent 支持记录工具调用日志,从日志中解析真实调用轨迹

另一个非常常见的坑是:在 Codex 或 VS Code Copilot 这类工具里使用 Figma MCP 时,工具注册不稳定。通常原因是 MCP Server 需要 WebSocket 连接,网络代理或权限配置不对。排查顺序是先确认 MCP Server 能在独立客户端中正常工作,再去排查 IDE 插件的连接配置。

7. 最佳实践与工程建议

到了这个环节,我结合自己的落地经验,给出一些工程层面的建议。

7.1 MCP 规范本身要提前规范化

既然评测依赖 MCP 规范,规范的完整性就直接影响评测质量。工具定义里下面几点必须写清楚:

  • 工具名称:语义明确,不要用func1这种无意义命名。
  • description:写清楚工具能力、适用场景、限制条件。
  • 参数说明:每个参数都要有 description、类型、枚举值、默认值。
  • 必填约束:务必准确,required列表不要漏项。
  • 错误返回:如果工具会返回错误码,在 schema 或描述中补充说明。

如果你的团队有 Git 提交规范或代码规范,建议同样将 MCP 规范文件的命名、格式、目录纳入版本管理,方便回溯评测集的变化。

7.2 评测集与规范版本强绑定

每次 MCP 服务端更新,评测集都应重新生成并回归执行。建议在 CI 流程中增加一个自动化任务:当specs/目录下的文件变更时,自动触发评测。这样可以第一时间发现工具升级对智能体的影响。

7.3 合成样例 + 人工审核结合

完全自动合成的评测用例,可能会出现“自然语言表达奇怪”或“预期结果与真实业务不符”的问题。我的建议是:

  • 自动合成一批候选用例。
  • 由测试工程师或业务方抽查并标注其中一部分。
  • 将人工修正后的用例加入回归集,逐步形成“自动生成 + 人工沉淀”的混合测试集。

这种方法既保证了覆盖率,也不会让评测集完全脱离人控。

7.4 安全与权限边界

在评测真实 MCP Server 时,要特别注意安全边界:

  • 尽量使用 Mock Server 或隔离的测试环境,不要直接评测生产环境工具。
  • 对会修改数据的工具(如写数据库、发送消息),要在用例设计阶段避免真实副作用。
  • 如果评测中涉及密钥或 Token,使用环境变量注入,不要写死在代码或报告里。
  • 最小权限原则:智能体评测的“工具执行账号”应只拥有测试环境的最小权限。

7.5 指标要区分“过程”和“结果”

只盯着“任务完成率”很容易掩盖过程问题。比如智能体最终答对了,但中间误调用了多个无关工具,这在实际生产中是高成本的。因此建议指标体系中同时保留:

  • 工具选择正确率。
  • 参数合规率。
  • 平均调用轮数。
  • 整体任务成功率。

这样既能发现“能不能完成”,也能发现“完成得好不好”。

8. 总结与下一步

从 MCP 规范自动合成智能体评测,并不是一个遥远的概念,而是可以落地到日常开发流程中的工程方法。通过解析工具定义,我们能够批量生成覆盖单工具调用、多工具协同、边界异常等场景的评测任务,再结合模拟或真实智能体,得到量化的评测结果。

本文用一套最小 Python 实现走通了核心链路,你可以在此基础上继续做三件事:

  1. 把 MockAgent 替换成真实 LLM Agent 和 MCP Client,连接实际的大模型产品。
  2. 把评测结果输出为 JSON/HTML 报告,并接入 CI。
  3. 引入 LLM-as-a-Judge 机制,对最终回答的质量做语义层面的评分。

智能体的评测体系,本质上决定了一个 Agent 项目能不能从“能跑”走向“可信”。如果你正准备做智能体平台或 MCP 工具链,建议尽早把“评测”当成一等公民纳入设计。

如果这篇文章对你有帮助,欢迎收藏备查。后续我还会继续分享 Agent 评测集构建、多智能体评测以及 MCP 工具链路压测方面的实战内容。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询