智能体工具调用可靠性:参数校验与人工确认

把 MCP 工具接给模型之后,可靠性问题立刻从"能不能调通"升级为"敢不敢让它真做事"。模型会传错参数、会在错误时机调用工具、会尝试执行破坏性操作——服务端必须有独立的防线。这篇基于 Spring AI 的 MCP 实现拆解三层防线:参数校验、权限校验、人工确认。

架构分层:两个服务各司其职

参考实现拆成两个 Spring Boot 服务:mcp-server 只负责暴露工具(设备状态查询、重启方案创建),agent-api 作为 MCP 客户端组合模型与工具。依赖上,服务端引 spring-ai-starter-mcp-server-webmvc,客户端引 spring-ai-starter-mcp-client。API Key 放环境变量,不进 Git——这句老生常谈在 Agent 场景更重要,因为工具调用日志会放大泄露面。

第一层防线:在工具定义里声明边界

@McpTool 注解不只是登记工具名,它的字段本身就是防线:

@McpTool(
    name = "get_device_status",
    description = "只读查询设备状态。参数为设备编号,不会修改设备。",
    annotations = @McpTool.McpAnnotations(
        readOnlyHint = true,
        destructiveHint = false
    )
)
public DeviceStatus getDeviceStatus(
        @McpToolParam(description = "设备编号,例如 pump-017", required = true)
        String deviceId) {
    validateDeviceId(deviceId);
    return deviceService.getStatus(deviceId);
}

三个值得照抄的细节:readOnlyHint/destructiveHint 让模型在决策时能区分读写工具;参数描述里给出格式示例("例如 pump-017"),能显著降低模型传错格式的概率;description 写清副作用边界("不会执行重启"),既是给模型的说明也是给审计者的证据。

服务端内部再做一层硬校验:格式正则、白名单、长度上限。模型传来的参数永远当不可信输入处理,和 Web 层防注入是同一个道理。

第二层防线:权限内嵌在工具里

权限校验不要指望模型自觉,直接写进工具实现:会话上下文里带用户身份,工具内部判断该用户是否有权操作目标设备。这样即使模型被诱导调用不该调的工具,服务端也会拒绝。更彻底的做法是按用户身份注册不同的工具子集——权限小的人根本看不到高危工具,攻击面从源头缩小。

第三层防线:写操作走人工确认

破坏性操作的关键设计是"两段式":模型只能创建待审批方案,真正执行由人在管理端点确认按钮触发。重启方案创建(prepare_device_restart)和实际重启是两个独立工具,前者幂等可重复调用,后者只认审批单号。

审批记录要包含四要素:操作人、模型给出的原因、目标对象、时间戳。这条审计链在事后追责和流程优化时都是刚需——没有记录的 Agent 操作等于黑箱。

验收:四条判定标准

上线前用真实场景跑四步:

  1. 传非法参数(空串、超长、错误格式)→ 服务端拒绝且返回可读错误,模型能根据错误信息自我修正。
  2. 越权调用 → 用低权限会话调高危工具 → 被拒,且拒绝信息不泄露内部结构。
  3. 正常写操作 → 全程有人工确认卡点,无一步"自动执行"。
  4. 并发调用同一工具 → 结果一致,无脏写。

四条全过才有资格接真实设备。Agent 可靠性的本质是"假设模型一定会犯错,然后设计让错误可拦截、可恢复、可审计的系统"——三层防线缺一层,都是在等一次事故。

0

评论0

请先
显示验证码
没有账号?注册  忘记密码?