【零基础入门 LLM 开发 · Day 8】:Function Calling——让 AI 会"用"工具
先纠正一个常见误解
Function Calling不是AI 自己去执行函数。AI 是个活在服务器里的大脑,它碰不到你的电脑。
它真正能做的是:告诉你"我想调用 get_weather,参数是 location=北京",然后——由你的代码替它执行,再把结果喂回给它。
一句话总结:AI 出主意,你动手,结果再汇报给 AI。
完整流程图(先看懂再写代码)
用户提问 ──► 第一次请求(带上工具说明书 tools) │ ▼ AI 返回 tool_calls(想调用什么函数+参数) │ ▼ 你的代码本地执行真正的 Python 函数 │ ▼ 把执行结果以 role:"tool" 塞回 messages │ ▼ 第二次请求 ──► AI 结合结果生成最终回答完整代码
importosimportjsonfromdotenvimportload_dotenvfromopenaiimportOpenAI load_dotenv()client=OpenAI(api_key=os.getenv("YUMC_MODEL_API_KEY"),base_url=os.getenv("YUMC_MODEL_BASE_URL"),)# 1. 定义一个普通的 Python 函数(就是本地工具)defget_weather(location:str)->str:if"北京"inlocation:return"晴天,25度"elif"上海"inlocation:return"雷阵雨,28度"return"未知天气"# 2. 告诉大模型有这个工具可用(JSON Schema 说明书)tools=[{"type":"function","function":{"name":"get_weather","description":"获取指定城市的当前天气","parameters":{"type":"object","properties":{"location":{"type":"string","description":"城市名称:如:北京"}},"required":["location"],},},}]# 3. 用户提问messages=[{"role":"user","content":"今天北京和上海的天气怎么样?"}]# 4. 第一次请求:AI 决定要不要调用工具response=client.chat.completions.create(model="yumcode-pro",messages=messages,tools=tools,tool_choice="auto",# 让 AI 自己决定用不用工具)ai_message=response.choices[0].message messages.append(ai_message)# 把 AI 决定调用工具的记录存入历史# 5. 检查 AI 是否要调用工具ifai_message.tool_calls:fortool_callinai_message.tool_calls:func_name=tool_call.function.name func_args=json.loads(tool_call.function.arguments)# 参数是 JSON 字符串print(f"AI 想调用:{func_name}({func_args})")# 6. 本地真正执行函数iffunc_name=="get_weather":result=get_weather(**func_args)print(f"本地函数执行结果:{result}")# 7. 把执行结果塞回 messages,汇报给 AImessages.append({"role":"tool","tool_call_id":tool_call.id,"content":result})# 8. 第二次请求:AI 根据工具结果,生成最终人类可读的回答final_response=client.chat.completions.create(model="yumcode-pro",messages=messages)print("\nAI 最终回复:",final_response.choices[0].message.content)逐段拆解:最容易懵的 4 个点
① tools 里的 JSON Schema 是什么?
是写给 AI 看的工具说明书:函数叫什么、干什么用、需要哪些参数、哪些必填。AI 靠读这份说明决定"要不要用、怎么用"。(说明书怎么写才好用?明天专门讲。)
②tool_choice="auto"
让 AI 自己判断:需要工具就用,闲聊就直接答。不要看到"查天气"四个字就无脑调用。
③tool_call.function.arguments是字符串!
AI 返回的参数是 JSON字符串'{"location": "北京"}',必须json.loads()转成字典才能get_weather(**func_args)这样展开传参。
④ 回传结果时的三个讲究
role必须是"tool";tool_call_id必须对应上(AI 才知道这份结果是谁要的);- 有几个
tool_calls就回传几份结果(用户问了两个城市,AI 会一次性发起两次调用)。
运行效果
AI 想调用:get_weather ({'location': '北京'}) 本地函数执行结果:晴天,25度 AI 想调用:get_weather ({'location': '上海'}) 本地函数执行结果:雷阵雨,28度 AI 最终回复: 北京今天是晴天,25度;上海有雷阵雨,28度。出行记得带伞!这就是 Agent 的原子操作。Day 14-15 的多 Agent 系统,不过是把这个流程套了好几层。
今日踩坑点
- 忘 append AI 的 tool_calls 消息:第二次请求会报错——messages 必须完整连贯。
- arguments 没
json.loads:拿到的是字符串,**展开会炸。 result必须是字符串:函数返回了字典/对象,先str()或json.dumps()再塞回去。- 安全提醒:AI 只是"建议"调用,执行权和参数校验永远在你手里,绝不要把 AI 的输出直接拼进 SQL 或 shell 命令。
今日小练习
- 必做:新增一个
get_current_time()工具(返回当前时间),跑通"现在几点了?"。 - 进阶:让 AI 同时查三个城市,观察
tool_calls列表的长度。
明日预告
Day 9:同一个工具,说明书换个写法,AI 的行为天差地别——description 是门手艺。
附录:完整练习代码
openAITools.py
importosimportjsonfromdotenvimportload_dotenvfromopenaiimportOpenAI load_dotenv()client=OpenAI(api_key=os.getenv("YUMC_MODEL_API_KEY"),base_url=os.getenv("YUMC_MODEL_BASE_URL"),)# 1.定义一个普通的python 函数defget_weather(location:str)->str:"""模拟获取天气的函数"""if"北京"inlocation:return"晴天,25度"elif"上海"inlocation:return"雷阵雨,28度"return"未知天气"# 2. 告诉大模型有这个工具可用tools=[{"type":"function","function":{"name":"get_weather","description":"获取指定城市的当前天气","parameters":{"type":"object","properties":{"location":{"type":"string","description":"城市名称:如:北京"}},"required":["location"],},},}]# 3.用户提问messages=[{"role":"user","content":"今天北京和上海的天气怎么样?"}]# 4. 第一次请求:AI 决定调用工具response=client.chat.completions.create(model="yumcode-pro",messages=messages,tools=tools,tool_choice="auto",# 让AI 自己决定用不用工具)ai_message=response.choices[0].message messages.append(ai_message)# 把AI 决定调用工具的记录存入历史# 5.检查AI 是否要调用工具ifai_message.tool_calls:fortool_callinai_message.tool_calls:func_name=tool_call.function.name func_args=json.loads(tool_call.function.arguments)print(f"AI 想调用:{func_name}({func_args})")# 实际执行我们的python 函数iffunc_name=="get_weather":result=get_weather(**func_args)print(f"本地函数执行结果:{result}")# 6.把函数执行的结果塞回messages 里,告诉AImessages.append({"role":"tool","tool_call_id":tool_call.id,"content":result})# 7.第二次请求:AI 根据工具结果,生成最终人类可读的回答final_response=client.chat.completions.create(model="yumcode-pro",messages=messages)print("\nAI 最终回复:",final_response.choices[0].message.content)