Damn AgentBeta

结构化输出与工具调用

JSON Mode、函数调用、工具 schema 与编排层校验:模型意图如何变成可执行接口。

Agent 系统里,模型最重要的输出不总是自然语言,而是可解析、可校验、可路由的结构化片段:JSON 对象、函数名、参数、工具调用 ID 和结束原因。

为什么需要结构化输出

自然语言适合解释,不适合直接驱动程序。编排层需要稳定字段来:

  • 判断是继续对话、调用工具还是结束任务。
  • 校验参数类型、必填项和枚举范围。
  • 记录审计日志,并在失败后重试或降级。

《智能体设计模式》把工具使用拆成六步:定义工具、让 LLM 判断是否调用、生成结构化参数、由编排层执行、返回观察结果、再让 LLM 处理结果。模型只负责第 2、3 步的“意图生成”,执行和验证必须在系统层完成。来源:《智能体设计模式》,pp. 49-50。

三类常见接口

接口形态模型做什么编排层做什么
JSON Mode / Response Schema按约定字段生成 JSON 文本解析 JSON、做 schema 校验
Function Calling / Tool Use返回工具名和参数对象查注册表、鉴权、执行、回填结果
文本协议(ReAct、XML 标签)用固定格式包裹动作正则或流式解析,容错和回退

优先使用 provider 原生支持的 function calling 或 JSON schema 能力;文本协议适合兼容旧系统,但解析成本和脆弱性更高。

工具 Schema 设计原则

一个可上线的工具至少要定义:

字段作用
name稳定标识,供模型和注册表匹配
description告诉模型何时使用,避免和功能相近工具冲突
input_schema / parameters用 JSON Schema 约束参数形状
required明确必填项,减少幻觉参数
权限与副作用标记只读、可写、是否需人工审批

Claude Code 的工具系统让所有工具都经过查找、输入验证、Hook、权限检查、执行、格式化,再返回给模型。来源:《Demystifying Claude Code v1.8》,pp. 83-87。

设计要点:

  1. 描述写给模型看,不是写给人类文档看:说明何时用、何时不用。
  2. 参数尽量小而稳:不要把整个业务流程塞进一个巨型参数对象。
  3. 输出也要结构化:工具返回统一 envelope,包含 okdataerror_coderetryable 等字段。
  4. 不要相信自然语言参数:即使模型返回了 JSON,也要做 schema 校验。

模型层与编排层的责任边界

正在渲染图表…

模型层失败示例:选错工具、漏填参数、编造不存在字段。 编排层失败示例:未校验参数、未做权限检查、执行超时却没有错误语义。

调试时要先定位是哪一层出了问题,不要笼统说“模型不行”。

JSON 与函数调用的常见坑

  • JSON 合法但语义错误:字段存在,值不符合业务规则。
  • 多工具冲突:描述重叠,模型随机选择。
  • 流式截断:tool arguments 还没完整到达就开始执行。
  • 循环调用:模型反复请求同一工具,缺少停止条件。
  • 大结果反灌:工具输出过长,挤爆上下文。

工具调用闭环

一个可维护的工具调用链路至少包含这些步骤:

  1. 注册工具:名称、描述、参数 schema、权限等级、是否有副作用。
  2. 装配上下文:只暴露本轮可用工具,避免工具列表过大。
  3. 模型选择工具:生成工具名和参数,而不是直接执行。
  4. 编排层校验:检查 schema、权限、速率限制、人工确认要求。
  5. 执行工具:记录输入、输出、耗时、错误码和副作用。
  6. 结果回填:把结构化 observation 放回上下文,让模型继续决策。
  7. 停止判断:达到目标、失败不可恢复或需要人工接管时结束。

模型只负责第 3 步的意图生成;其余步骤都应由系统代码负责。

工具 schema 示例

{
  "name": "read_project_file",
  "description": "读取当前项目内的文本文件。只在需要查看源码、配置或文档原文时使用。",
  "input_schema": {
    "type": "object",
    "properties": {
      "path": {
        "type": "string",
        "description": "相对项目根目录的文件路径,不允许绝对路径。"
      },
      "max_lines": {
        "type": "integer",
        "minimum": 1,
        "maximum": 400
      }
    },
    "required": ["path"],
    "additionalProperties": false
  },
  "side_effect": "read-only"
}

这个 schema 的重点不是字段多,而是边界清楚:什么时候用、参数怎么约束、是否只读、超出范围怎么办。

错误语义

工具返回错误时,不要只返回一段自然语言。建议使用统一 envelope:

{
  "ok": false,
  "error_code": "FILE_NOT_FOUND",
  "message": "文件不存在或不在允许目录内。",
  "retryable": false,
  "data": null
}

模型看到 retryable: false 时就不应继续用同样参数重试;编排层也可以用错误码做熔断、告警或人工接管。

安全边界

结构化输出不是安全机制本身。即使模型返回了合法 JSON,也要继续检查:

  • 工具是否允许当前用户、当前任务和当前环境调用。
  • 参数是否越权,例如绝对路径、外部 URL、生产环境标识。
  • 副作用是否需要人工确认,例如删除文件、转账、发邮件、推送代码。
  • 重试是否可能放大损失,例如重复扣费、重复写库、重复通知。
  • 工具结果是否包含敏感信息,是否允许回灌给模型或展示给用户。

与 MCP 的关系

函数调用通常是应用和模型之间的集成;MCP 则是工具层的开放协议,让工具提供者实现一次 Server,多个 Agent 客户端复用。详见 智能体基础 中的 MCP 章节。

检查清单

  • 是否优先使用 provider 原生的 structured output 或 tool calling 能力。
  • 是否对模型返回做 JSON 解析、schema 校验和业务规则校验。
  • 是否把工具权限、副作用和人工确认写进系统层,而不是只写进 prompt。
  • 是否限制单轮最大工具调用次数,避免循环调用。
  • 是否对工具结果做截断、摘要和敏感信息过滤。

延伸阅读

On this page