今年圈子里聊得最多的一句话大概是:AI 写代码已经不是问题,问题是「怎么让 AI 真正去干活」。我这边把团队里几个重复的人工活(查天气、查库存、发通知)交给 Agent 跑了两个月,最大的体感是——Agent 能不能干活,不取决于模型多聪明,而取决于你能不能把「工具」干净地交给它。
这篇文章不堆概念,直接用一个能跑的例子讲清楚:Function Calling(函数调用 / 工具调用)到底怎么写,以及我实际踩过的几个坑。代码全程可复制,跑通你就能理解主流 Agent 框架(LangChain、AutoGen、OpenAI Agents SDK)底下那层最朴素的逻辑。
一句话:模型不直接执行你的函数,而是输出一段结构化的「调用请求」,由你的代码去真正执行。
很多人第一次用会误解成「模型帮我把函数跑了」。不是。真实流程是:
{"name": "get_weather", "arguments": {"city": "西安"}}get_weather("西安"),拿到结果模型只负责「决策调哪个、填什么参数」,执行永远在你手里。这一点理解了,后面所有 Agent 框架都是这个循环的包装。
只需要一个 OpenAI 兼容的 Python SDK:
初始化客户端时,关键是 base_url 这个参数。任何兼容 OpenAI 接口规范的端点都能接进来——本地网关、云上推理服务、或者你团队已有的统一接入层。把 base_url 换成你的地址即可:
小提示:把密钥写死在代码里只是演示。真实项目请用环境变量
os.getenv("OPENAI_API_KEY"),别把密钥提交进仓库。
工具用 JSON Schema 描述。模型靠 description 判断「什么时候该调这个工具」,所以描述写清楚比什么都重要。我这里定义一个查天气的工具:
description 写的是「什么时候用」,不是「这个函数干嘛的」。这两件事不一样——模型靠前者决策,靠后者理解边界。
下面这段是一个最小闭环:发消息 → 模型可能返回工具调用 → 你执行 → 把结果回传 → 模型总结。复制即可跑:
跑出来大概是这样:
你看,模型从来没真的「查」过天气,它只是正确地判断了「该调 get_weather('西安')」,真正查的是你的 get_weather 函数。
跑通上面的例子不难,难的是接到真实业务里。我踩过几个,列出来帮你省时间:
坑 1:工具描述写模糊,模型就瞎调用 早期我把 description 写成「获取天气信息」,结果用户问「明天去哪玩好」模型也去调天气工具。后来改成「当用户问到某地的天气、温度、是否下雨时使用」,误调率明显下降。描述就是模型的决策边界,值得多花十分钟打磨。
坑 2:忘了把 assistant 的 tool_calls 消息回传 这是新手最高频的报错——tool_call_id 对不上。模型第一轮返回的 message 里带着 tool_calls,你必须把它原样 append 进 messages,再追加 role: "tool" 的结果。漏掉任何一环,第二轮请求直接 400。
坑 3:参数没校验就直接执行 模型返回的是 JSON 字符串,解析后一定要校验字段类型和必填项。我线上出过一次事:模型把 city 填成了数组 ["西安", "上海"],我的函数只接字符串,直接崩了。现在所有工具入口都先做一层 pydantic / 手写校验。
坑 4:工具执行要有超时和重试 真实工具背后是外部 API,会挂、会慢。我没加超时那次,一个天气接口阻塞了 40 秒,整个 Agent 卡死。现在每个工具调用都包 timeout + 最多两次重试,失败就返回「工具暂不可用」,让模型降级回答,而不是整个链路崩溃。
坑 5:多轮工具调用要循环,不是 if 上面例子只调了一个工具。真实场景模型可能要连调好几个(先查天气、再查交通、再查门票)。正确做法是把「模型决策 → 执行 → 回传」包成一个 while 循环,直到模型不再返回 tool_calls 才输出最终答案。
接真实业务时,你会有十几个工具。两个经验:
while 循环的封装。自己跑通一遍,再看框架文档,理解速度完全不一样。这篇文章的代码就是最朴素的版本。把它吃透,你去读任何一个 Agent 框架的源码,都会觉得「哦,原来就这么回事」。
原创声明:本文系作者授权腾讯云开发者社区发表,未经许可,不得转载。
如有侵权,请联系 cloudcommunity@tencent.com 删除。