工具/函数调用详解。
语言模型只能输出文本。工具调用(又称函数调用)是把"输出文本"变成"在世界中做事"的协议:模型请求一个函数,你的代码运行它,结果再回到上下文。本文讲解确切的请求/响应形状、它隐含的循环、为何模型从不自己执行任何东西,以及让用工具的智能体保持安全的设计规则。
模型提议;你的代码裁决。
最重要的一个事实:模型不会调用你的函数。它输出一个结构化请求,要求你去调用它。模型内部没有代码执行。工具调用是一个有纪律的文本协议,分三步:
- 你在请求里描述可用工具(名称、用途、参数 schema)。
- 模型不再用散文作答,而可能输出一条工具使用消息:"用
{"city": "Paris"}调用get_weather。" - 你的应用执行真实函数,然后把结果作为一条工具结果消息发回。模型继续,此刻已能使用该结果。
模型所做的一切仍是下一令牌预测。它被后训练成在工具有帮助时输出一个特定 JSON 形状的结构;"调用"是你的代码对那个结构的反应。
线上格式。
# 1. You declare tools in the request tools = [{ "name": "get_weather", "description": "Current weather for a city. Use when asked about weather.", "input_schema": { "type": "object", "properties": {"city": {"type": "string"}}, "required": ["city"] } }]
# 2. Model replies with a tool-use request (not prose)
{ "type": "tool_use", "id": "t_01", "name": "get_weather",
"input": { "city": "Paris" } }
# 3. YOUR code runs get_weather("Paris") and returns a tool_result
{ "type": "tool_result", "tool_use_id": "t_01",
"content": "12C, light rain" }
# 4. Model now answers in prose, grounded in the result
"It's 12C with light rain in Paris right now."
工具描述和参数 schema 是提示词工程,不是样板。模型是否调用、如何调用某个工具,几乎完全基于那段文本来决定。含糊的描述("对用户做点事")产生不可靠的调用;精确的("按 ID 获取用户。仅当你有数字 user_id 时使用;不要瞎猜 ID")产生可靠的调用。
它隐含的循环。
一次工具调用很少就是终点。结果可能促使再一次调用,然后再一次,直到模型有足够信息作答。这个循环——模型 → 工具请求 → 执行 → 结果 → 模型——反复进行,直到模型输出最终答案而非工具请求,就是智能体循环(在"智能体 AI"一节详述)。工具调用是原语;循环是建于其上的模式。对初学者有两个后果:
- 循环归你管。API 不会自动执行。是你写代码检测工具使用消息、运行函数、追加结果、再次调用模型。
- 你需要停止条件。一个总是报错的有 bug 工具能让模型永远重试。务必给迭代次数设上限。
为何这个原语强大。
工具调用消除了裸 LLM 的三个结构性弱点:
- 知识陈旧 → 实时数据。模型训练有截止日期。一个
search或get_account工具给它当前事实,而非凭记忆的猜测。 - 不擅精确计算 → 真实计算。模型在算术和精确逻辑上不可靠。一个
calculator或run_sql工具把精确性卸载给真正精确的代码。 - 无法行动 → 能够行动。
send_email、create_ticket、deploy——工具是 LLM 影响世界而非只描述它的方式。
这也是本节的主线:RAG 本质上是一个接入上下文的检索工具;结构化输出是同一套 JSON 塑形机制,只是对准最终答案而非函数调用。
不可选的安全规则。
模型产出的工具参数是不可信输入。模型可能出错,或被检索/工具内容里的提示词注入操纵。把每次工具调用都当作互联网上的陌生人向你的函数发来的请求。
- 执行前校验。在代码里对照 schema 和你自己的业务规则重新检查参数。绝不把模型输出直接传进 shell、SQL 字符串或文件系统路径。
- 最小权限。暴露能完成工作的最窄工具。是带服务端限额的
refund_order(order_id, amount),而非run_arbitrary_sql。 - 为不可逆动作设闸。删除、付款、发送——要求显式确认或人工审批。即便提示词被颠覆,能力上限依然成立;一句叫模型"小心点"的话则不然。
- 把错误作为数据返回。工具失败时,把结构化错误作为工具结果发回,让模型能据此反应("该用户不存在"),而不是让循环崩溃。
- 尽量让工具幂等。循环可能重试;被重试的
charge_card绝不能重复扣款。
交付物
你把工具调用理解为一个文本协议:你用精确的描述和 schema 声明工具,模型输出一个结构化的工具使用请求,你的代码执行真实函数并返回工具结果,模型据此继续作答。环绕的循环及其停止条件归你管。你知道它的存在是为修复陈旧知识、薄弱计算和无法行动。而且你把每个模型产出的参数视为不可信:校验它,按最小权限限定工具,用能力上限而非指令为不可逆动作设闸,并让执行幂等。