AI 应用开发入门 05|工具调用,怎么让模型干活
导语
很多开发者第一次接触大模型时,都会有一个错觉:模型已经很聪明了,只要把需求说清楚,它就能像程序一样替你完成任务。真正做下来才会发现,事情并不是这样。
我的判断是:只会生成文本的模型,最多算“会说”;接上工具之后,它才开始具备“干活”的能力。
模型默认只会生成下一个最可能的词。它擅长解释、总结、改写、推理,但并不会天然拥有你系统里的订单数据、库存状态,也不会真的帮你发请求、改数据库、创建日程。它会说“我可以帮你查一下”,不代表它真的查了。
这就是函数调用(Function Calling)和工具调用(Tool Use)的意义:让模型不只是“会说”,而是能在受控边界内“真做事”。
这一篇想讲清楚的,就是文本生成和工具调用到底差在哪,以及程序员把这层能力接进系统时最该注意什么。
文本生成,和工具调用,到底差在哪
先把概念分清。
纯文本生成模式下,模型做的事情是:根据上下文预测输出内容。比如你问“用户下单后库存应该怎么扣减”,它可以给你一段流程建议;你问“这个 SQL 为什么慢”,它可以分析可能原因。这类场景里,模型主要承担的是“认知层”的工作。
但如果你问它:
-
• “帮我查一下订单 A20260411001现在是什么状态” -
• “看一下 SKU-10086 还有多少库存” -
• “帮我约一个明天下午 3 点的评审会”
这时候就不只是生成文字了,而是需要访问外部系统。订单状态可能在 OMS,库存数据在 WMS 或数据库,日程要写入企业日历或 Google Calendar。模型本身没有这些权限和连接能力,必须通过你定义好的函数或工具,才能完成真实操作。
一句话概括:
-
• 文本生成:模型负责“想”和“说” -
• 工具调用:模型负责“判断何时调用”,系统负责“真的执行”
这也是为什么函数调用不是“模型变强了”,而是“模型接入了可执行能力”。
一个接地气的例子:查订单、看库存、排日程
设想你在做一个电商客服 Copilot,用户在聊天框里输入:
“我昨天买的耳机怎么还没发货?顺便帮我看看同款白色还有没有货,有的话下周一提醒我补单。”
如果没有工具调用,模型最多只能回复一段像模像样的话:
“您可以先检查订单状态,如果库存不足可能导致延迟发货……”
听起来挺像回事,但其实什么都没干。
如果接上工具,流程就完全不一样了:
-
1. 模型先识别用户意图:查订单、查库存、创建提醒。 -
2. 它决定调用哪些函数,比如 getOrderStatus、queryInventory、createReminder。 -
3. 你的服务端真正执行这些函数,拿到结果。 -
4. 模型再基于真实结果组织自然语言回复。
最后用户看到的可能是:
“订单 A20260411001 当前状态是‘待出库’,仓库预计今晚完成拣货。白色同款当前还有 12 件库存。我已经为你创建了下周一上午 10 点的补单提醒。”
这里最关键的变化不是“回复更像人”,而是“回复建立在真实执行结果之上”。
函数调用的核心设计:让模型会选,但别让模型乱来
很多文章会把函数调用讲得很玄,实际上工程上就三件事:
-
1. 给模型一组可用工具及其描述 -
2. 让模型输出“我想调用哪个工具、参数是什么” -
3. 由你的后端执行,并把结果再喂回模型
一个极简示意如下:
tools = [
{
"name": "get_order_status",
"description": "查询订单当前状态",
"parameters": {
"type": "object",
"properties": {
"order_id": {"type": "string", "description": "订单号"}
},
"required": ["order_id"]
}
}
]
user_input = "帮我查一下订单 A20260411001 的状态"
# 1. 把 tools 和 user_input 发给模型
tool_call = llm.chat(user_input, tools=tools)
# 2. 如果模型决定调用工具
if tool_call["name"] == "get_order_status":
result = get_order_status(order_id=tool_call["arguments"]["order_id"])
# 3. 把执行结果回传给模型,生成最终回复
final_answer = llm.chat(
messages=[
{"role": "user", "content": user_input},
{"role": "tool", "name": "get_order_status", "content": str(result)}
]
)
真正落地时,函数描述比代码本身还重要。因为模型不是靠类型系统理解你的意图,而是靠描述文本和参数 schema 做决策。
一个好工具定义,至少要做到两点:
-
• 名字和描述足够清晰,让模型知道“什么时候该用” -
• 参数边界足够明确,让模型知道“该传什么、不该传什么”
比如 query_data 这种万能函数,对模型很不友好;而 get_order_status、query_inventory_by_sku、schedule_meeting 这类语义清晰的函数,更容易被正确调用。
真正的工程重点,不在“调起来”,而在“控得住”
很多 Demo 都能跑通,但一到生产环境就出问题。原因通常不是模型不会调用,而是调用链路没有工程化。
第一层是权限控制。模型不应该因为“用户说了”就能执行敏感操作。比如“取消订单”“批量退款”“删除日程”这类动作,必须由业务系统再次鉴权。模型只能提出调用意图,真正的授权判断必须在你的服务端。
第二层是结果约束。工具返回什么,决定了模型后续能说什么。不要把内部异常、数据库原始报错、敏感字段直接回传给模型。应该返回结构化、最小必要的信息,比如状态码、业务描述、可展示字段。
第三层是调用编排。实际业务里,一个请求往往不是一次函数调用就结束。比如“查订单并解释为什么没发货”,可能要串行调用订单服务、物流服务、库存服务。这里更像一个受控 agent:模型负责决策,业务代码负责 orchestrate。
第四层是可观测性。你至少要记下这些日志:
-
• 用户原始请求 -
• 模型选择了哪个工具 -
• 工具参数是什么 -
• 工具执行结果是什么 -
• 最终返回给用户的内容是什么
没有这些日志,线上问题几乎没法排查。你会分不清是模型理解错了,还是工具描述写坏了,还是后端接口本身异常。
常见坑:权限、校验、幂等、超时
这是最容易踩坑的一组问题,也是 AI 应用和普通聊天机器人拉开差距的地方。
1. 权限
不要让模型越权操作。用户说“帮我把这个订单直接退款”,不代表当前账号真的有退款权限。工具层必须做 RBAC、租户隔离、资源归属校验。模型只能“建议执行”,不能替代权限系统。
2. 参数校验
模型会犯错,哪怕 schema 写了,也可能传出缺字段、错类型、模糊值。比如把“下周一下午”解析成错误时区,把商品名当 SKU。服务端必须做严格校验,必要时返回可恢复错误,再让模型向用户追问。
3. 幂等性
凡是可能修改状态的操作,都要考虑重复执行。网络抖动、用户重复点击、模型重试,都可能导致“重复创建提醒”“重复下单”“重复发消息”。创建类接口最好带幂等键,更新类接口要明确状态机约束。
4. 超时
工具调用不是无限快的。库存服务 300ms,订单服务 800ms,第三方日历 API 2s,加起来用户体验很容易崩。要给每个工具设置超时、降级和失败策略。查不到就明确告诉用户“系统暂时未返回结果”,不要让模型编一个答案补上。
什么时候该上函数调用,什么时候不该上
并不是所有场景都要上工具。
如果你的需求只是:
-
• 改写文案 -
• 总结日志 -
• 生成 SQL 草稿 -
• 解释报错原因
那纯文本生成通常就够了,系统简单、成本更低。
但只要涉及以下任意一种,就应该认真考虑函数调用:
-
• 需要读取真实业务数据 -
• 需要触发外部动作 -
• 需要跨多个系统编排流程 -
• 需要可审计、可回放、可控制的执行链路
判断标准很简单:用户要的是“答案”,还是“结果”。
如果要的是结果,就不能只靠模型说。
小结
如果只记住一个结论:函数调用解决的不是“模型会不会聊天”,而是“模型能不能安全、可靠地接入你的系统能力”。
你可以把它理解为一种新的交互层:
-
• 模型负责理解意图和选择工具 -
• 你的后端负责鉴权、校验、执行和审计 -
• 最终由模型把机器结果翻译成人能理解的话
对程序员来说,这一步很关键。因为当你把这层搭起来,AI 应用才真正从“会回答问题”,走向“能完成任务”。
再往后走,你就会自然碰到更复杂的话题:多步任务什么时候需要,Agent 什么时候有价值,什么时候其实只是把简单问题复杂化。
如果你也在关注 AI、Agent 和最新开源趋势,欢迎关注我的微信公众号:碳基生物观察局。
我会持续分享值得跟踪的 AI 项目、产品观察和实战解读。
夜雨聆风