ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

从 MCP 工具定义自动生成 Agent 评测集:把可靠性验证接入 CI

2026/9/9 17:29:31 拓冰建站 浏览量
从 MCP 工具定义自动生成 Agent 评测集:把可靠性验证接入 CI 一、为什么评测集应该从工具契约开始Agent 评测最容易失真的地方是用例和真实能力脱节。MCP 工具定义同时描述名称、参数和返回约束天然是一份机器可读契约。把它作为生成种子评测就能随接口变化同步更新。每次提交都能留下可追踪差异。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。很多团队仍靠人工维护 JSON 用例工具增加到 15 个后遗漏率会明显上升。定义驱动的方法先读取输入 Schema再把必填项、枚举值和边界值转成测试维度。这样测试覆盖的是能力边界而不是作者记忆。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。工具描述里的 description 不能只写营销话术。它应说明业务前置条件、权限要求和失败含义评测生成器才能构造有效任务。一个字段缺少范围说明往往会让验证器无法区分拒绝与异常。契约质量决定数据质量。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。生成评测集时必须保留来源指针包括工具名、Schema 路径和规则版本。报告发现失败时工程师可以直接回到定义定位原因。没有来源绑定的分数只能展示趋势不能支撑回归决策。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。MCP 的 Tool、Resource 与 Prompt 应分别建模。Tool 适合动作测试Resource 适合读取一致性Prompt 适合流程组合。把三类对象混成一张表会导致期望结果和实际副作用难以对应。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。工程上优先保留可回放证据而不是口头同步。第一轮生成不应追求大量样本。针对每个工具生成 2 类正常输入、3 类边界输入和 1 类拒绝输入通常已经能暴露 Schema 误差。小而稳定的种子比随机扩张更利于审查。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。评测样本需要区分静态断言和语义断言。状态码、字段类型、数组长度属于静态断言答案是否包含授权范围则属于语义断言。两者混在同一函数里会让失败原因失去可读性。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。版本变化是评测集的核心输入。工具从 v2 升到 v3 时生成器应输出新增、删除和收紧约束的清单。只有明确变化影响CI 才能判断是预期升级还是兼容性回退。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。二、把 JSON Schema 编译成可执行用例Schema 到用例不是简单遍历字段而是把类型、必填关系和条件约束转换成任务意图。字符串字段可以生成空值、超长值和合法值枚举字段则覆盖每个分支。组合规则必须进入同一个样本。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。边界数据必须来自 Schema而不是拍脑袋。整数范围若规定为 1 到 100生成器至少应覆盖 1、100 和越界值。浮点字段还要测试精度截断否则生产中的金额误差不会被发现。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。对象嵌套会放大组合数量。一个包含 4 个字段的对象如果每个字段取 3 种状态理论组合达到 81 种。工程上应使用正交采样和风险权重先保留高影响路径。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。条件 Schema 需要显式记录触发条件。字段 mode 为 batch 时才允许 batch_size评测器就应生成成对样本验证条件开启和关闭。只测默认路径会把分支错误隐藏到上线之后。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。名称和描述也参与生成质量。工具名表达动作描述表达约束二者共同决定用户任务的自然语言。生成器应拒绝空描述并把低质量定义标记为契约警告。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。参数顺序不应决定测试结果。调用层必须按键名构造对象再由 Schema 校验器统一排序。这样客户端换语言或 SDK 后评测集仍能复用不会出现位置参数漂移。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。错误用例要有期望分类。缺字段属于 validation_error权限不足属于 authorization_error后端故障属于 execution_error。分类越稳定CI 越能按责任边界分派告警。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。工程上优先保留可回放证据而不是口头同步。评测数据要避免真实隐私。生成器可以用固定租户、虚拟订单和合成邮箱保持字段形状但不触碰生产数据。脱敏不是附加步骤而是评测样本进入 Git 前的强制门。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。三、受控 Runner 让评测结果可重复评测 Runner 不应该直接连接生产工具。它应运行在隔离环境使用 Mock Server、临时数据库和可回滚文件系统。这样一次失败不会改变真实状态重复执行也能得到相同前置条件。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。Runner 的输入是用户任务和工具清单输出应包含调用序列、参数快照、返回结果和耗时。四类证据缺一不可。只保存最终答案无法判断模型选错工具还是工具返回错误。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。时间、随机数和网络响应会破坏重复性。测试环境需要固定时钟、种子和响应夹具并把外部访问默认设为拒绝。任何未声明的网络调用都应被记录为隔离违规。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。工具副作用必须采用事务边界。写入操作先落到临时仓库验证通过后再提交快照失败则回滚。这个设计既能测试写操作也能避免测试数据污染后续样本。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。多工具任务要记录因果链。一次查询可能先调用身份工具再读取资源最后执行动作。Runner 应为每步分配序号和父步骤报告才能解释哪一步改变了决策。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。超时策略要分层设置。单工具可以限制为 10 秒整项任务可以限制为 30 秒流水线总时长则应有独立上限。没有分层超时慢请求会拖住全部并发任务。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。并发执行要隔离租户和文件目录。即使两个样本调用同一个工具也不能共享可变缓存。隔离键至少包含任务编号和数据版本避免顺序差异造成偶发失败。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。Runner 的日志要面向审计而非调试堆栈。日志应隐藏令牌和个人数据只保留哈希、类型、时间与决策结果。安全日志可读评测结果才适合长期保存。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。四、验证器如何判断 Agent 真正完成任务验证器首先检查工具调用是否符合预期但不能停在工具被调用。用户目标可能要求结果过滤、权限收敛和状态更新必须用业务断言检查最终状态。调用成功不等于任务成功。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。字段级验证适合确定性接口。对返回对象逐项检查类型、必填键和数值范围再把差异路径写进报告。路径化错误比一段长字符串更容易定位也便于在 CI 中聚合。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。顺序验证用于有依赖的流程。身份确认必须发生在数据读取之前写操作必须发生在预览之后。验证器可以把调用序列转换成图再检查前置节点是否存在。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。语义验证需要控制尺度。可以检查答案是否提到租户、时间范围和风险提示但不应把自由表达锁成唯一文案。过严的文本匹配会把合理答案误报成失败。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。拒绝能力同样是质量指标。面对缺少权限或危险参数Agent 应拒绝调用并给出可行动解释。评测集要统计安全拒绝率而不是只统计完成率。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。验证器应返回结构化结果。每个断言包含 id、状态、证据和修复建议多个断言失败时仍要继续收集。结构化结果可直接转换成 Markdown、JSON 和评论机器人消息。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。分数必须按风险加权。只读查询通过并不能抵消越权写入安全断言应拥有更高权重。权重表放进版本控制改动时要求评审避免人为调分。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。失败样本要支持最小复现。报告保留脱敏任务、工具版本和验证器版本工程师可以用同一输入重跑。没有最小复现修复只能依赖猜测回归价值会迅速下降。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。五、接入 CI 的门禁与回归策略评测集进入 CI 后触发条件应覆盖工具 Schema、提示模板和 Agent 策略变化。只在模型代码变更时运行会漏掉契约更新带来的兼容性风险。变更检测应比较规范化后的 JSON。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。快速门和完整门需要分开。快速门运行高风险的 6 个样本控制反馈时间完整门在合并队列执行全部数据保证覆盖率。两层门禁能兼顾开发速度和发布可信度。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。CI 输出不能只有一个总分。应展示通过率、拒绝率、工具选择准确率、平均耗时和 P95 耗时。多个指标共同变化时团队才能判断是质量下降还是性能退化。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。阈值应采用基线加容差。若历史通过率为 90%可以设置 2 个百分点的回退警戒而不是要求每次完全相同。随机模型仍有波动门禁要阻止趋势性恶化。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。模型升级必须保留对照组。新模型和当前模型使用同一份种子、夹具和验证器差异报告才有意义。若只跑新模型任何变化都无法归因。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。失败重试不能掩盖不稳定。每个样本最多重试 2 次并同时记录首轮结果和最终结果。通过率高但重试率上升说明系统存在潜在抖动。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。CI 缓存只缓存不可变输入。工具定义哈希、夹具版本和模型标识应构成缓存键运行结果不能跨版本复用。错误缓存会让红灯变绿是比慢更危险的问题。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。评测报告要作为构建产物保存 30 天。保留周期足够覆盖一次发布窗口和一次事故复盘同时避免无限堆积。报告链接写入合并请求审查者无需进入运行节点查日志。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。import json import hashlib import sys from dataclasses import dataclass from typing import Any, Callable dataclass class Case: name: str tool: str arguments: dict[str, Any] expected: dict[str, Any] dataclass class CheckResult: case: str passed: bool details: list[str] def stable_hash(value: Any) - str: raw json.dumps(value, ensure_asciiFalse, sort_keysTrue, separators(,, :)) return hashlib.sha256(raw.encode(utf-8)).hexdigest()[:16] def make_cases(tool_def: dict[str, Any]) - list[Case]: schema tool_def.get(inputSchema, {}) props schema.get(properties, {}) required set(schema.get(required, [])) normal {} for name, spec in props.items(): if name in required: kind spec.get(type) if kind string: normal[name] demo elif kind integer: normal[name] spec.get(minimum, 1) elif kind boolean: normal[name] True else: normal[name] {} cases [Case(normal, tool_def[name], normal, {kind: success})] for name, spec in props.items(): if name in required: missing dict(normal); missing.pop(name, None) cases.append(Case(missing_ name, tool_def[name], missing, {kind: validation_error})) if spec.get(type) integer and minimum in spec: bad dict(normal); bad[name] spec[minimum] - 1 cases.append(Case(boundary_ name, tool_def[name], bad, {kind: validation_error})) return cases def run_case(case: Case, executor: Callable[[str, dict[str, Any]], dict[str, Any]]) - CheckResult: details [input stable_hash(case.arguments)] try: result executor(case.tool, case.arguments) except Exception as exc: result {kind: execution_error, error: type(exc).__name__} actual result.get(kind) expected case.expected.get(kind) passed actual expected details.append(expected str(expected)) details.append(actual str(actual)) return CheckResult(case.name, passed, details) def fake_executor(tool: str, args: dict[str, Any]) - dict[str, Any]: if tool ! search_orders: return {kind: validation_error} if user_id not in args or limit not in args: return {kind: validation_error} if not isinstance(args[limit], int) or args[limit] 1: return {kind: validation_error} return {kind: success, items: []} def main() - int: definition {name: search_orders, inputSchema: {type: object, required: [user_id, limit], properties: {user_id: {type: string}, limit: {type: integer, minimum: 1}}}} cases make_cases(definition) results [run_case(case, fake_executor) for case in cases] report {tool: definition[name], case_count: len(results), passed: sum(r.passed for r in results), results: [r.__dict__ for r in results]} print(json.dumps(report, ensure_asciiFalse, indent2)) return 0 if all(r.passed for r in results) else 1 if __name__ __main__: sys.exit(main())六、生产安全与持续演进安全评测从身份开始。每个工具调用都应绑定租户、主体和授权范围验证器检查实际参数是否越过边界。模型输出的 user_id 不能覆盖认证上下文中的真实主体。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。敏感工具要采用最小权限。数据库查询优先使用只读账号写操作拆成预览和提交两步删除动作要求人工确认。评测集必须覆盖越权、重复提交和重放请求。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。提示注入应作为攻击样本进入数据集。资源内容里放置诱导指令观察 Agent 是否把数据当命令执行。验证器重点检查工具选择、权限边界和最终解释而非只看文字。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。MCP Server 的描述内容也是供应链输入。接入前应校验来源、版本和哈希变更自动触发评测。未经审查的工具定义进入上下文可能扩大攻击面和数据外泄范围。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。密钥不能写入样本和日志。Runner 使用短期凭证报告保存脱敏标识失败堆栈经过字段过滤后才上传。评测平台本身拥有高权限更需要默认拒绝和分层审计。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。长期维护要关注样本老化。业务规则变化、工具弃用和模型更新都会让旧样本失去代表性。每个样本应标注创建版本、最近执行时间和失效原因便于周期清理。把该点写进变更说明复盘时按轨迹编号核对。把该点写进变更说明复盘时按轨迹编号核对。公开材料中的数字与边界已足够支撑判断。from jsonschema import validate schema{type:object,required:[user_id],properties:{user_id:{type:string}}} validate({user_id:demo}, schema) print(schema ok)