Function Calling 解决什么问题
大模型本身只能输出文字,但 Agent 需要它能「做事」——查天气、读数据库、发邮件。Function Calling 就是让大模型在合适的时候,输出一个结构化的函数调用请求,由你的程序去执行,再把结果喂回给模型继续回答。整个过程模型不直接碰外部系统,它只负责「决定调什么、传什么参数」。
和早期让模型自由生成代码再解析执行的做法相比,Function Calling 的优势是结构化:模型输出严格符合你定义的 JSON Schema,不需要正则去猜。这让工具调用变得可靠,也让调试变得容易——模型说要调哪个函数、传什么参数,一目了然。
Function Calling 的本质是给大模型装了一个「结构化输出开关」:不是让它自由发挥,而是让它按你定义的格式填写调用单。
- Function Calling让模型按 JSON Schema 输出工具调用请求,由外部代码执行。
- 工具描述工具的名称、参数说明和描述文字直接影响模型选对工具的概率。
- MCP 协议把工具接入标准化,一次接入多处可用,不用每个客户端重复对接。
- 参数校验模型生成的参数不一定对,必须在执行前做校验和纠错。
工具描述怎么写才有效
Function Calling 的效果很大程度上取决于你给工具写的描述。模型看不到你的代码,它只根据工具名称和描述来判断「这个工具能不能解决当前问题」。描述写得模糊,模型就会选错工具或者乱传参数。
# 一个工具的 JSON Schema 定义示例
tools = [{
"type": "function",
"function": {
"name": "search_flights",
"description": "查询指定城市间的航班信息,返回航班号、时间和价格。用于用户询问机票或出行方案时调用。",
"parameters": {
"type": "object",
"properties": {
"from": {"type": "string", "description": "出发城市,如北京"},
"to": {"type": "string", "description": "到达城市,如上海"},
"date": {"type": "string", "description": "出发日期,格式 YYYY-MM-DD"}
},
"required": ["from", "to", "date"]
}
}
}]
写描述的几个要点:说清楚工具做什么、什么时候该用、参数什么格式。特别是「什么时候该用」这一句,直接决定了模型在面对模糊问题时会不会乱调工具。
MCP 协议:工具接入的标准化
有了 Function Calling 之后,每个 AI 客户端都要自己对接一套工具。你做一个聊天应用要对接天气、日历、数据库;换一个客户端又要重新对接一遍。MCP(模型上下文协议)就是来解决这个重复劳动的:它定义了一套标准协议,工具方实现一次 MCP Server,任何支持 MCP 的客户端都能直接用。
从开发者角度看,MCP 的价值是「写一次工具,到处接入」。你不用为每个 AI 应用单独写对接代码,只要把工具封装成 MCP Server,Claude、IDE 插件、自定义 Agent 框架都能自动发现和调用它。这和 USB-C 的思路一样:统一接口,免去各做各的线。
- Server工具提供方,暴露一组可调用的工具和资源。
- Client大模型应用,通过协议发现和调用 Server 提供的工具。
- Resources除了工具,还可以暴露文件、数据库记录等可读资源。
# MCP Server 的最简结构(伪代码)
from mcp import Server
server = Server("my-tools")
@server.tool()
def search_flights(from_city: str, to_city: str, date: str):
"""查询航班信息"""
return flight_api.search(from_city, to_city, date)
server.run()
参数校验与错误处理
模型生成的工具调用参数不一定对。日期格式可能写错、城市名可能拼成拼音、必填参数可能漏了。在真正执行工具之前,必须加一层校验:按 JSON Schema 检查参数类型和必填项,发现错误就把错误信息返回给模型,让它修正后再试一次。
# 参数校验与纠错循环
while True:
call = model.next_tool_call()
errors = validate(call.parameters, tool_schema)
if not errors:
result = execute_tool(call)
break
# 把错误反馈给模型,让它修正
messages.append({"role": "tool", "content": f"参数错误: {errors}, 请修正后重试"})
这种「试错-反馈-修正」的循环是 Agent 可靠性的关键。不要假设模型一次就能调对,要把它当成一个会犯错但能看提示改正的实习生,给它纠错的机会。
多工具编排与选择策略
当你给 Agent 挂了十几个工具时,问题就变成了:模型怎么知道该用哪个?工具太多会让模型困惑,也会占用大量上下文。常见的做法有三种:让模型自己选(工具不多时)、先用一个小模型做工具分类再路由(工具多时)、或者按工作流预先固定每一步用哪个工具(流程明确时)。
| 策略 | 做法 | 适用场景 |
|---|---|---|
| 模型自选 | 所有工具描述塞进 prompt,让模型选 | 工具少于 10 个 |
| 路由分类 | 先用小模型判断意图,再路由到对应工具组 | 工具几十个 |
| 工作流固定 | 按流程定义好每一步用什么工具 | 业务流程明确 |
常见坑与调试技巧
做工具调用时最常见的几个问题:模型反复调同一个失败的工具——要加最大重试次数;模型选错工具——优化工具描述和名称;参数格式不对——加严格的校验和纠错反馈;工具太多导致上下文爆炸——用路由或者工作流拆分。
调试工具调用最有效的方法是记录每一次调用的完整日志:模型输出了什么调用请求、校验过没过、工具返回了什么、模型下一步怎么反应。有了这些日志,问题一眼就能定位。
落地路径与生态现状
如果你要在自己的产品里接工具调用,建议按这个顺序:先手动定义几个核心工具跑通 Function Calling 流程,把参数校验和纠错做扎实;工具变多之后再考虑接入 MCP 生态,复用社区已经写好的工具 Server;最后再根据业务复杂度决定要不要上工作流编排。
工具链是 Agent 的手脚。Function Calling 让模型会调用工具,MCP 让工具随处可用,参数校验让调用变得可靠。三者凑齐,Agent 才真的能干活。