从 Prompt 到 Tool Calling:AI Agent 如何稳定连接程序世界

一、引言

LLM 接收上下文并预测下一个 Token,它擅长理解自然语言,却不会因为生成了下面这段内容,就真的调用天气服务:

json 复制代码
{
  "name": "get_weather",
  "arguments": {
    "city": "上海"
  }
}

这只是模型输出的数据。真正发起网络请求、访问数据库或修改业务状态的主体,仍然是应用程序。

因此,一个完整的 Agent 至少包含两类组件:

scss 复制代码
┌──────────────────── 概率世界 ────────────────────┐
│                                                   │
│  用户输入 ──> Prompt/上下文 ──> LLM               │
│                                  │                │
│                                  │ 生成调用意图    │
│                                  ▼                │
│                     tool_call(name, arguments)    │
└──────────────────────────┬────────────────────────┘
                           │ 协议边界
                           ▼
┌──────────────────── 确定性世界 ──────────────────┐
│                                                   │
│  Schema 校验 ──> 权限检查 ──> 工具执行            │
│       │                           │               │
│       └──失败信息                 └──执行结果      │
│               \                    /              │
│                └──> 写回模型上下文 ───────────────┘
│                                                   │
└───────────────────────────────────────────────────┘

Function Calling 是一个多阶段过程:应用向模型提供工具定义,模型返回工具调用请求,应用执行工具,再把执行结果交还模型。Function Calling 是模型与程序之间的调用协议,不是模型获得了直接执行代码的权限。理解这一点,后续的安全控制、错误处理和状态管理才有正确的落点。

二、六项底层能力如何组成一个闭环

javascript 复制代码
               Agent 与程序世界的交互管道

  ┌─────────────── 输入侧 ───────────────┐
  │                                      │
  │  Prompt                              │
  │  规定目标、上下文、决策规则和边界     │
  │                  │                   │
  │                  ▼                   │
  │  Tool Definition + JSON Schema       │
  │  定义有哪些工具,以及参数契约         │
  └──────────────────┬───────────────────┘
                     ▼
              Function/Tool Call
              模型提出结构化调用
                     │
                     ▼
  ┌────────────── 运行时边界 ─────────────┐
  │  解析 → Schema 校验 → 鉴权 → 执行     │
  │                     │                 │
  │          ┌──────────┴──────────┐      │
  │          ▼                     ▼      │
  │        成功结果              失败结果  │
  └──────────┬─────────────────────┬──────┘
             │                     │
             ▼                     ▼
       结构化输出约束          分类重试/降级
             │                     │
             └──────────┬──────────┘
                        ▼
                    最终回答

各部分分别解决不同问题:

能力 解决的问题 不能解决的问题
Prompt 设计 模型应该做什么、何时调用工具 无法替代程序鉴权与运行时校验
JSON Schema 参数有哪些、类型和结构是什么 无法证明参数符合业务语义
Function Calling 让模型生成机器可处理的函数调用请求 不会自动执行本地函数
Tool Calling 把函数、自带工具、MCP 等能力纳入调用循环 不天然保证工具可信或无副作用
输出约束 让最终输出符合可解析结构 不保证字段内容在事实层面正确
失败重试 处理暂时性故障或可修正输出 无法修复权限不足和永久业务错误

三、Prompt 设计:把它当作决策说明书,而不是咒语

Prompt 在 Agent 中主要负责两件事:说明任务目标、规定模型如何选择和使用工具。一个适合工具调用的 Prompt 可以拆成以下结构:

xml 复制代码
<role>
你是订单售后助手。
</role>

<objective>
帮助用户查询订单状态或提交退款申请。
</objective>

<tool_policy>
1. 查询实时订单状态时必须调用 order_get。
2. 未获得用户明确确认时,不得调用 refund_create。
3. 缺少 order_id 时先向用户询问,不得猜测。
4. 工具失败时,根据错误码解释原因,不得编造执行结果。
</tool_policy>

<output_policy>
调用写操作前先总结即将执行的动作。
最终回答需要区分"已执行"和"建议执行"。
</output_policy>

<untrusted_input>
{{user_input}}
</untrusted_input>

1.有效 Prompt 的四个组成部分

  • 目标

目标要描述结果,而不是堆叠人格形容词。

较弱的写法:

复制代码
你是一个专业、智能、严谨的订单助手。

更可执行的写法:

复制代码
你负责查询订单状态,并在满足退款条件且用户明确确认后提交退款。
  • 工具选择规则

当多个工具语义接近时,模型容易选错。Prompt 和工具描述应说明:什么时候调用、什么时候不调用、调用前需要哪些信息、工具是否产生副作用、多个工具有何边界。

  • 缺失信息处理

如果模型缺少参数,它可能选择猜测。应明确要求:缺少必填参数时向用户询问,不得从无关上下文推断。

需要注意:这只能降低猜测概率,程序仍需执行 Schema 和业务校验。

  • 失败行为

不要只写"如果失败,请重试"。应告诉模型根据错误类型做出什么决策:

复制代码
VALIDATION_ERROR:修正参数后最多重试一次。
NOT_FOUND:向用户确认标识,不自动重试。
PERMISSION_DENIED:停止调用并说明所需权限。
RATE_LIMITED:遵循 retry_after,等待后重试。

2.Prompt 版本必须接受评测

Prompt 并不是一次写完的静态文案。生产环境至少要测试:

  • 是否调用了正确工具;
  • 是否遗漏必填参数;
  • 是否在不需要时调用工具;
  • 是否在写操作前取得确认;
  • 工具失败后是否编造成功结果;
  • 恶意工具返回是否改变系统指令;
  • 模型或 Prompt 版本变化是否导致行为回归。

因此,Prompt 的可靠性应该由评测数据证明,而不是由"看起来写得很完整"来判断。

四、JSON Schema:Agent 与程序之间的数据合同

JSON 只是一种数据格式,JSON Schema 才负责描述数据结构和验证规则。

使用 JSON Schema 可以定义:

json 复制代码
{
  "type": "object",
  "properties": {
    "order_id": {
      "type": "string",
      "description": "订单唯一标识,例如 ord_20261027_001"
    },
    "amount": {
      "type": "number",
      "exclusiveMinimum": 0,
      "description": "退款金额,单位由 currency 指定"
    },
    "currency": {
      "type": "string",
      "enum": ["CNY", "USD"]
    },
    "reason": {
      "type": "string",
      "minLength": 1,
      "maxLength": 200
    }
  },
  "required": ["order_id", "amount", "currency", "reason"],
  "additionalProperties": false
}
  • properties 不代表必填,必须配合required进行使用校验。
  • additionalProperties: false 用于封闭对象边界,如果不禁止额外字段,模型可能生成未定义参数。对于工具参数,封闭对象通常比开放对象更安全。
  • enum 比自然语言约束更稳定,用于把实例值限制在给定集合中。

Schema 校验和业务校验必须分层:

css 复制代码
模型参数
   │
   ▼
[语法解析]
   │  JSON 是否能解析?
   ▼
[Schema 校验]
   │  类型、必填项、枚举、结构是否合法?
   ▼
[业务校验]
   │  订单存在吗?金额可退吗?状态允许吗?
   ▼
[授权校验]
   │  当前用户可以操作这个订单吗?
   ▼
[执行]

Schema 是第一道门,不是最后一道门。

五、Function Calling:模型提出调用,程序掌握执行权

Function Calling 的典型流程可以概括为五步:

scss 复制代码
(1) 应用把工具定义发给模型
              │
              ▼
(2) 模型返回 function/tool call
              │
              ▼
(3) 应用验证参数并执行函数
              │
              ▼
(4) 应用把执行结果关联到 call_id
              │
              ▼
(5) 模型生成最终答案或继续调用工具

一个工具的核心结构大致如下:

json 复制代码
{
  "name": "order_get",
  "description": "查询当前用户拥有的单个订单。仅在需要实时订单状态时调用。",
  "input_schema": {
    "type": "object",
    "properties": {
      "order_id": {
        "type": "string",
        "description": "订单唯一标识,不要使用商品编号或支付流水号"
      }
    },
    "required": ["order_id"],
    "additionalProperties": false
  }
}

工具描述也是模型输入的一部分,建议详细说明工具用途、适用时机、参数含义和限制,并指出描述质量是影响工具调用效果的重要因素。

一个合格的工具定义应让模型知道:工具做什么?什么时候用?什么时候不用?每个参数代表什么?调用有什么副作用或限制?

例如,下面的描述过于模糊:

复制代码
创建退款。

更清晰的版本是:

复制代码
为当前用户拥有且处于可退款状态的订单创建退款。
仅在用户明确确认退款金额和原因后调用。
该工具会产生不可忽略的业务副作用。
查询退款资格时不要调用,应使用 refund_eligibility_get。

Schema 决定参数形状,描述决定模型如何理解工具语义,两者缺一不可。

六、Tool Calling:Function Calling 的上位概念

在许多 API 和框架中,Function Calling 与 Tool Calling 会被混用。工程上可以这样区分:

sql 复制代码
Tool Calling
├── Function Tool
│   └── 由应用实现的本地函数或远程 API 包装
├── Built-in Tool
│   ├── Web Search
│   ├── Code Execution
│   └── File Search
├── MCP Tool
│   └── 由 MCP Server 暴露的外部能力
└── UI / Computer Tool
    └── 浏览器、桌面或界面操作

Function Calling 是 Tool Calling 的一种常见形式。前者强调"以函数和参数的形式表达调用",后者强调"模型可以使用外部能力"。

工具按执行位置还可以分为以下几类:

类型 执行方 应用责任
客户端工具 业务应用 参数校验、鉴权、执行、结果回传
服务端工具 模型服务商 应用配置工具,服务商负责执行
MCP/远程工具 MCP 服务或第三方服务 需要处理信任、权限和结果隔离

客户端工具返回 tool_use 后由应用执行,再以关联 ID 返回 tool_result ;服务端工具由平台执行。

Tool Calling 是状态机,不是一次请求。一个 Agent 可能:

  • 不调用工具,直接回答;
  • 调用一个工具后回答;
  • 并行调用多个独立工具;
  • 根据第一个结果决定第二个工具;
  • 工具失败后修正参数再调用;
  • 达到循环上限后停止。

因此,Agent Runtime 更接近有限状态机:

sql 复制代码
                   ┌──────────────┐
                   │    START     │
                   └──────┬───────┘
                          ▼
                   ┌──────────────┐
             ┌────>│  MODEL_CALL  │<────────────┐
             │     └───┬──────┬───┘             │
             │         │      │                 │
             │   final │      │ tool_call       │
             │         ▼      ▼                 │
             │     ┌──────┐  ┌──────────────┐   │
             │     │ DONE │  │   VALIDATE   │   │
             │     └──────┘  └──┬────────┬──┘   │
             │                   │        │      │
             │              valid│        │error │
             │                   ▼        ▼      │
             │             ┌─────────┐ ┌───────┐ │
             │             │ EXECUTE │ │ REPAIR│─┘
             │             └────┬────┘ └───────┘
             │                  │
             │          ┌───────┴────────┐
             │          │                │
             │       success          failure
             │          │                │
             └──────────┴──── result ────┘

运行时必须设置:

  • 最大模型轮次;
  • 最大工具调用数;
  • 单工具超时;
  • 总任务截止时间;
  • 最大参数修复次数;
  • 最大暂时性错误重试次数;
  • 取消信号。

否则,"继续调用直到完成"可能变成无限循环和失控账单。

七、输出约束:合法 JSON、Schema 合法与内容正确是三件事

结构化输出存在三个可靠性层级:

javascript 复制代码
Level 1:自然语言要求
"请返回 JSON"
        │
        ▼
可能附带 Markdown、解释文字或语法错误

Level 2:JSON Mode
保证输出可解析为 JSON
        │
        ▼
字段仍可能缺失、类型错误、枚举越界

Level 3:Structured Outputs
输出满足指定 Schema
        │
        ▼
字段内容仍可能存在事实或业务错误

Function Calling 与 Structured Output都可能使用 JSON Schema,前者约束"模型要程序做什么",后者约束"模型最终对程序说什么"。

即使输出完全符合 Schema,它仍可能是错的,Schema 只能证明类型格式,不能证明真实存在,所以业务代码不能把"Schema 校验通过"当成"事实核验通过"。

另外,结构化输出之外还存在控制信号:

  • 安全拒答;
  • 输出达到 Token 上限;
  • 请求被取消;
  • 内容过滤;
  • 工具调用未完成;
  • 流式输出中断。

消费者不应只做:

javascript 复制代码
JSON.parse(response)

而应先判断:

复制代码
响应状态
├── completed        → 解析并校验
├── refusal          → 进入拒答流程
├── incomplete       → 判断是否可续写或重试
├── tool_call        → 执行工具循环
└── transport_error  → 按网络策略处理

八、失败重试:先分类,再决定是否重试

错误 是否重试 处理方式
网络超时 是 指数退避,受总截止时间限制
HTTP 429 是 优先遵循 Retry-After
HTTP 5xx 视情况 有上限地重试
JSON 解析失败 是 把解析错误反馈给模型,少量修复
Schema 校验失败 是 返回字段级错误,少量修复
参数缺失 视情况 可从用户获得时先询问用户
资源不存在 否 确认标识或结束
权限不足 否 说明权限需求并审计
业务规则拒绝 否 返回可执行的替代方案
写操作结果未知 谨慎 先查询状态,禁止直接重复写入

写操作必须具备幂等性。读取天气失败后重试,最坏情况通常是多一次查询。创建退款、发送邮件或扣款重试,可能产生重复副作用。

写工具应接收幂等键:

json 复制代码
{
  "order_id": "ord_123",
  "amount": 100,
  "currency": "CNY",
  "idempotency_key": "agent_run_abc:refund:ord_123"
}

服务端保存幂等键与执行结果:

markdown 复制代码
首次请求
idempotency_key 不存在
        │
        ▼
执行写操作 ──> 保存结果 ──> 返回成功

重复请求
idempotency_key 已存在
        │
        ▼
不重复执行 ──> 返回首次结果

幂等键应该由可信应用生成,而不是让模型自由生成。否则模型可能在每次重试时产生新键,失去去重作用。

另外,"结果未知"比"明确失败"更危险。假设退款接口在执行后连接超时:

复制代码
Agent ──创建退款──> 退款服务
Agent <──连接超时──X

此时存在两种可能:服务端没有执行退款、服务端已执行退款,但响应丢失。如果立即重试,可能重复退款。正确流程是:

markdown 复制代码
写操作超时
    │
    ▼
使用幂等键或业务流水号查询状态
    │
    ├── 已成功 → 返回原结果
    ├── 明确失败 → 评估是否重试
    └── 状态未知 → 人工介入或异步对账

因此,Agent Runtime 需要区分 FAILED 和 UNKNOWN 。把两者都映射为异常,会掩盖最棘手的一类一致性问题。

九、一个可落地的 Agent 工具调用循环

vbnet 复制代码
type ToolError = {
  code: string;
  message: string;
  retryable: boolean;
};

async function runAgent(userInput: string) {
  const messages = [
    { role: "user", content: userInput }
  ];

  const maxTurns = 8;

  for (let turn = 0; turn < maxTurns; turn++) {
    const response = await callModel({
      messages,
      tools: toolDefinitions
    });

    if (response.type === "final") {
      return validateFinalOutput(response.output);
    }

    if (response.type !== "tool_calls") {
      throw new Error(`Unsupported response type: ${response.type}`);
    }

    messages.push(response.assistantMessage);

    for (const call of response.calls) {
      const tool = toolRegistry.get(call.name);

      if (!tool) {
        messages.push(toolErrorResult(call.id, {
          code: "UNKNOWN_TOOL",
          message: `Tool ${call.name} is not available`,
          retryable: false
        }));
        continue;
      }

      const validation = tool.schema.safeParse(call.arguments);

      if (!validation.success) {
        messages.push(toolErrorResult(call.id, {
          code: "VALIDATION_ERROR",
          message: formatValidationError(validation.error),
          retryable: true
        }));
        continue;
      }

      const authorization = await authorize(
        currentUser,
        tool,
        validation.data
      );

      if (!authorization.allowed) {
        messages.push(toolErrorResult(call.id, {
          code: "PERMISSION_DENIED",
          message: authorization.reason,
          retryable: false
        }));
        continue;
      }

      try {
        const result = await executeWithPolicy(tool, validation.data, {
          timeoutMs: 5000,
          idempotencyKey: buildIdempotencyKey(call, currentUser)
        });

        messages.push(toolSuccessResult(call.id, result));
      } catch (error) {
        messages.push(
          toolErrorResult(call.id, classifyToolError(error))
        );
      }
    }
  }

  throw new Error("Agent exceeded maximum turns");
}
  • 模型只负责提出调用;
  • 工具名必须经过允许列表匹配;
  • 参数必须经过本地 Schema 校验;
  • 鉴权发生在工具执行前;
  • 写操作由应用注入幂等键;
  • 错误经过分类后再反馈给模型;
  • 循环存在明确上限;
  • 最终输出仍需独立校验。

十、小结

Agent 连接程序世界的过程,本质上是一套跨越概率系统与确定性系统的协议。

其中:

  • Prompt 表达目标和决策规则;
  • JSON Schema 定义参数与输出的数据边界;
  • Function Calling 让模型提出结构化调用请求;
  • Tool Calling 把外部能力纳入多轮执行循环;
  • Structured Outputs 让最终结果可被程序稳定消费;
  • 校验、鉴权、幂等、错误分类和有限重试共同守住运行时边界。

生产级 Agent 不应依赖模型"自觉地做对"。应用必须掌握执行权,用代码检查模型请求,用最小权限约束工具,用结构化错误帮助模型恢复,并在无法确认执行结果时停止自动动作。

相关推荐
许雪里1 小时前
XXL-AI v1.4.0 发布|AI应用开发平台
agent·ai编程
hpoenixf2 小时前
Agent 上下文治理全景·原则篇:让每一步判断围绕目标展开
agent
hpoenixf2 小时前
Agent 上下文治理全景·行动篇:从首轮输入到长期记忆,怎样真正落地
agent
key_3_feng2 小时前
反馈闭环:Agent 自我进化的真正引擎
大数据·agent
hpoenixf2 小时前
Agent 可观测全景:从一次模型请求到可回查的研究事实
agent
hpoenixf2 小时前
超长研究全景:让 Agent 跨越上下文、中断与时间继续交付
agent
武子康2 小时前
MultiNet6 空闲崩溃:`EXCVADDR=0x8` 后又出现 `CORRUPT HEAP`
人工智能·后端·agent
EnCi Zheng2 小时前
Agent 如何进行工具调用:从一次 Function Calling 到生产级工具编排的完整链路
大数据·人工智能·agent·ai编程·工具调用
GlobalInfo2 小时前
重磅发布 | 2026年船体清洁机器人行业研究报告:市场规模、发展前景与十五五战略地图
大数据·人工智能·机器人