强烈推荐收藏Function Calling 内部原理LLM 怎么「学会」用工具——从Token注入到并行调用的完整链路你把工具定义发给 LLM它就能「自动选择」——这个魔法背后是什么Function Calling 不是 API 的一个简单参数而是一整套 Token 注入、结构化输出和路由决策机制。本文从 Special Token 讲到并行调用每个环节带真实代码。一、起源从 GPT-3 的「猜」到 GPT-4 的「结构化调用」1.1 GPT-3 时代2020-2022靠 Prompt 硬来# 没有 Function Calling只能这样「骗」LLMprompt 你只能回复 JSON不要回复其他内容。 工具列表 - get_weather(city): 查询天气 用户问北京天气怎么样 请回复{tool: get_weather, city: 北京} responsellm.chat(prompt)# → {tool: get_weather, city: 北京}# 然后手动 json.loads()再手动调函数问题LLM 可能输出{tool: get_wether, city: Beijing}——拼写错误JSON 解析失败整个流程断裂。1.2 GPT-4 Function Calling2023.6范式转移# 现在LLM 直接返回结构化的 tool_calls 对象tools[{type:function,function:{name:get_weather,description:获取指定城市的天气,parameters:{type:object,properties:{city:{type:string,description:城市名如 北京、上海}},required:[city]}}}]responseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:北京天气怎么样}],toolstools)# response.choices[0].message.tool_calls 是一个结构化的列表# [{id: call_xxx, function: {name: get_weather, arguments: {city: 北京}}}]二、原理Token 层看 Function Calling2.1 Special Token 注入当你把tools参数传给 APIOpenAI 底层做了三件事1. 把 tools JSON Schema 序列化为 Token 序列 2. 在消息序列中插入特殊的 Special Token如 |tool_call| 3. 模型被微调为「当需要工具时输出 Special Token 工具名 参数 JSON」# API 背后的 Token 流简化示意[# 系统消息|im_start|system 你可以使用以下工具get_weather(city:str)-获取天气|im_end|# 用户消息|im_start|user 北京天气怎么样|im_end|# LLM 输出|im_start|assistant|tool_call|get_weather{city:北京}|im_end|]这就是为什么 Function Calling 比「手写 JSON Prompt」稳定得多——模型在训练时见过几百万次这种格式。2.2 JSON Schema 声明 给模型的「装箱单」# ❌ 参数不限制类型模型可能传空、传错{name:send_email,parameters:{to:{type:string}}}# ✅ 类型、枚举、约束全写上{name:send_email,description:发送邮件。只发送到内部邮箱。,parameters:{type:object,properties:{to:{type:string,format:email,description:收件人邮箱必须为 company.com},priority:{type:string,enum:[low,normal,high],default:normal}},required:[to]}}效果对比没有format: email→ LLM 可能传{to: 张三}加了format: email→ LLM 至少会传{to: zhangcompany.com}三、机制Tool Choice 的三种模式# 1. auto — 让 LLM 自己决定# 适用不确定是否需要工具让模型自己选responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choiceauto)# → 可能返回 text content不需要工具# → 也可能返回 tool_calls需要工具# 2. required — 强制调工具# 适用你已经知道这个请求一定需要工具responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choicerequired)# → 一定返回 tool_calls不会返回普通文本# 3. 指定工具名 — 强制调特定工具# 适用外层已做意图识别直接路由responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choice{type:function,function:{name:get_weather}})# → 一定返回 get_weather 的调用Tool Choice 决策树你的场景是 │ ├─ 不确定是否需要工具 → auto │ 例用户可能只是聊天 │ ├─ 确定需要工具但不确定哪个 → required │ 例用户一定在问需要实时数据的事 │ └─ 外层已识别具体意图 → 指定工具名 例NLP 分类器已判断为「天气查询」四、进阶并行调用 流式输出4.1 并行调用# 用户北京和上海今天天气怎么样# LLM 一次返回两个 tool_callresponseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:北京和上海天气怎么样}],tools[weather_tool])tool_callsresponse.choices[0].message.tool_calls# [# {id: call_1, function: {name: get_weather, arguments: {city: 北京}}},# {id: call_2, function: {name: get_weather, arguments: {city: 上海}}}# ]# 并行执行不需要等北京结果就可以查上海importasyncio resultsawaitasyncio.gather(get_weather(北京),get_weather(上海))4.2 流式输出 工具调用Agent 对话中不能让用户干等每一步都发状态更新asyncdefagent_stream(user_input:str):yield{type:status,text:正在分析...\n}responseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:user_input}],toolstools,tool_choiceauto)msgresponse.choices[0].messageifmsg.tool_calls:fortcinmsg.tool_calls:yield{type:status,text:f 调用{tc.function.name}...\n}resultexecute(tc.function.name,json.loads(tc.function.arguments))yield{type:status,text:f✓{result[:80]}...\n}# 最终回复流式输出final_responseclient.chat.completions.create(modelgpt-4o,messagesupdated_messages,streamTrue)asyncforchunkinfinal_response:ifchunk.choices[0].delta.content:yield{type:chunk,text:chunk.choices[0].delta.content}五、工具描述的工程实践5.1 描述要同时给「模型」和「维护者」看# ❌ 描述只写了用途没写不适用场景{name:search,description:搜索东西}# ✅ 描述写了四个 W{name:search_harmonyos_docs,description:(搜索华为 HarmonyOS 官方开发文档。适用场景用户问 API 用法、组件属性、开发指南。不适用场景CSDN 博客搜索或通用技术问题。返回匹配的文档片段和官方链接。),parameters:{query:{type:string,description:中文搜索关键词。例状态管理、页面路由}}}加上「不适用场景」可以让 LLM 减少 30% 以上的错误工具选择。5.2 工具数量少比多好工具数 决策准确率 1-3 90% 4-10 70-90% 10-20 40-70% 20 40%经验每个 Agent 不超过 8 个工具。多了就让 LLM 困惑——它会随机选一个。六、总结层级做什么关键Token 层注入 Special Token 工具 Schema这是 FC 为什么比手写 JSON 稳定的原因推理层LLM 内部决策是否调工具tool_choice控制行为执行层代码真正执行工具LLM 不知道工具实现体验层流式 状态更新不能干等Function Calling 的本质LLM 输出一个结构化意图你的代码去执行结果再喂回 LLM。调用二字有误导性——LLM 从不真正调用任何东西它只是说「应该调这个」。 下一篇《RAG 深度原理从向量数学到混合检索》——Embedding 的数学直觉 索引算法演进 评估指标。标签#FunctionCalling #ToolUse #Agent #LLM #并行调用 #程序员必读
Function Calling内部原理:从提示词工程到结构化输出
强烈推荐收藏Function Calling 内部原理LLM 怎么「学会」用工具——从Token注入到并行调用的完整链路你把工具定义发给 LLM它就能「自动选择」——这个魔法背后是什么Function Calling 不是 API 的一个简单参数而是一整套 Token 注入、结构化输出和路由决策机制。本文从 Special Token 讲到并行调用每个环节带真实代码。一、起源从 GPT-3 的「猜」到 GPT-4 的「结构化调用」1.1 GPT-3 时代2020-2022靠 Prompt 硬来# 没有 Function Calling只能这样「骗」LLMprompt 你只能回复 JSON不要回复其他内容。 工具列表 - get_weather(city): 查询天气 用户问北京天气怎么样 请回复{tool: get_weather, city: 北京} responsellm.chat(prompt)# → {tool: get_weather, city: 北京}# 然后手动 json.loads()再手动调函数问题LLM 可能输出{tool: get_wether, city: Beijing}——拼写错误JSON 解析失败整个流程断裂。1.2 GPT-4 Function Calling2023.6范式转移# 现在LLM 直接返回结构化的 tool_calls 对象tools[{type:function,function:{name:get_weather,description:获取指定城市的天气,parameters:{type:object,properties:{city:{type:string,description:城市名如 北京、上海}},required:[city]}}}]responseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:北京天气怎么样}],toolstools)# response.choices[0].message.tool_calls 是一个结构化的列表# [{id: call_xxx, function: {name: get_weather, arguments: {city: 北京}}}]二、原理Token 层看 Function Calling2.1 Special Token 注入当你把tools参数传给 APIOpenAI 底层做了三件事1. 把 tools JSON Schema 序列化为 Token 序列 2. 在消息序列中插入特殊的 Special Token如 |tool_call| 3. 模型被微调为「当需要工具时输出 Special Token 工具名 参数 JSON」# API 背后的 Token 流简化示意[# 系统消息|im_start|system 你可以使用以下工具get_weather(city:str)-获取天气|im_end|# 用户消息|im_start|user 北京天气怎么样|im_end|# LLM 输出|im_start|assistant|tool_call|get_weather{city:北京}|im_end|]这就是为什么 Function Calling 比「手写 JSON Prompt」稳定得多——模型在训练时见过几百万次这种格式。2.2 JSON Schema 声明 给模型的「装箱单」# ❌ 参数不限制类型模型可能传空、传错{name:send_email,parameters:{to:{type:string}}}# ✅ 类型、枚举、约束全写上{name:send_email,description:发送邮件。只发送到内部邮箱。,parameters:{type:object,properties:{to:{type:string,format:email,description:收件人邮箱必须为 company.com},priority:{type:string,enum:[low,normal,high],default:normal}},required:[to]}}效果对比没有format: email→ LLM 可能传{to: 张三}加了format: email→ LLM 至少会传{to: zhangcompany.com}三、机制Tool Choice 的三种模式# 1. auto — 让 LLM 自己决定# 适用不确定是否需要工具让模型自己选responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choiceauto)# → 可能返回 text content不需要工具# → 也可能返回 tool_calls需要工具# 2. required — 强制调工具# 适用你已经知道这个请求一定需要工具responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choicerequired)# → 一定返回 tool_calls不会返回普通文本# 3. 指定工具名 — 强制调特定工具# 适用外层已做意图识别直接路由responseclient.chat.completions.create(modelgpt-4o,messages[...],toolstools,tool_choice{type:function,function:{name:get_weather}})# → 一定返回 get_weather 的调用Tool Choice 决策树你的场景是 │ ├─ 不确定是否需要工具 → auto │ 例用户可能只是聊天 │ ├─ 确定需要工具但不确定哪个 → required │ 例用户一定在问需要实时数据的事 │ └─ 外层已识别具体意图 → 指定工具名 例NLP 分类器已判断为「天气查询」四、进阶并行调用 流式输出4.1 并行调用# 用户北京和上海今天天气怎么样# LLM 一次返回两个 tool_callresponseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:北京和上海天气怎么样}],tools[weather_tool])tool_callsresponse.choices[0].message.tool_calls# [# {id: call_1, function: {name: get_weather, arguments: {city: 北京}}},# {id: call_2, function: {name: get_weather, arguments: {city: 上海}}}# ]# 并行执行不需要等北京结果就可以查上海importasyncio resultsawaitasyncio.gather(get_weather(北京),get_weather(上海))4.2 流式输出 工具调用Agent 对话中不能让用户干等每一步都发状态更新asyncdefagent_stream(user_input:str):yield{type:status,text:正在分析...\n}responseclient.chat.completions.create(modelgpt-4o,messages[{role:user,content:user_input}],toolstools,tool_choiceauto)msgresponse.choices[0].messageifmsg.tool_calls:fortcinmsg.tool_calls:yield{type:status,text:f 调用{tc.function.name}...\n}resultexecute(tc.function.name,json.loads(tc.function.arguments))yield{type:status,text:f✓{result[:80]}...\n}# 最终回复流式输出final_responseclient.chat.completions.create(modelgpt-4o,messagesupdated_messages,streamTrue)asyncforchunkinfinal_response:ifchunk.choices[0].delta.content:yield{type:chunk,text:chunk.choices[0].delta.content}五、工具描述的工程实践5.1 描述要同时给「模型」和「维护者」看# ❌ 描述只写了用途没写不适用场景{name:search,description:搜索东西}# ✅ 描述写了四个 W{name:search_harmonyos_docs,description:(搜索华为 HarmonyOS 官方开发文档。适用场景用户问 API 用法、组件属性、开发指南。不适用场景CSDN 博客搜索或通用技术问题。返回匹配的文档片段和官方链接。),parameters:{query:{type:string,description:中文搜索关键词。例状态管理、页面路由}}}加上「不适用场景」可以让 LLM 减少 30% 以上的错误工具选择。5.2 工具数量少比多好工具数 决策准确率 1-3 90% 4-10 70-90% 10-20 40-70% 20 40%经验每个 Agent 不超过 8 个工具。多了就让 LLM 困惑——它会随机选一个。六、总结层级做什么关键Token 层注入 Special Token 工具 Schema这是 FC 为什么比手写 JSON 稳定的原因推理层LLM 内部决策是否调工具tool_choice控制行为执行层代码真正执行工具LLM 不知道工具实现体验层流式 状态更新不能干等Function Calling 的本质LLM 输出一个结构化意图你的代码去执行结果再喂回 LLM。调用二字有误导性——LLM 从不真正调用任何东西它只是说「应该调这个」。 下一篇《RAG 深度原理从向量数学到混合检索》——Embedding 的数学直觉 索引算法演进 评估指标。标签#FunctionCalling #ToolUse #Agent #LLM #并行调用 #程序员必读