
1. 项目概述当大模型学会“使用工具”最近在折腾大模型应用开发的朋友估计没少被两个词刷屏Skill和Function Call。乍一看它们好像都在说同一件事——让大模型去调用外部工具或函数。但当你真正上手想把一个想法落地成可用的AI应用时会发现这背后的水很深。为什么我的大模型有时候能精准调用API有时候又像个“人工智障”为什么用LangChain搭的工具链响应时快时慢为什么Function Call的结果处理不好对话就会当场“死机”这些问题本质上都指向了大模型能力拓展的底层逻辑。我们不是在简单地“教”模型一个新知识而是在为它构建一套可理解、可执行的“行动准则”。这个项目我们就来彻底拆解Skill与Function Call的异同、底层原理并基于腾讯的混元大模型进行一次从理论到实战的深度探索。无论你是想开发一个能查天气、订机票的智能助手还是构建一个能调用内部系统API的复杂业务Agent理解这些核心机制都是绕不开的第一步。2. 核心概念辨析Skill vs. Function Call在深入实战前我们必须先厘清这两个经常被混用的概念。它们目标相似但设计哲学和适用场景有显著区别。2.1 Function Call大模型的“标准外设接口”你可以把Function Call理解为大模型原生支持的、一种标准化的“插件”调用协议。它的核心流程是定义开发者预先定义好一系列函数Function包括函数名、描述、参数名称、类型、描述。描述将这些函数的“说明书”Schema以特定的格式通常是JSON Schema提交给大模型。决策在对话过程中大模型根据用户的问题判断是否需要以及调用哪个函数。响应如果决定调用大模型会停止生成常规回复转而输出一个结构化的调用请求其中包含要调用的函数名和具体的参数值。执行与反馈你的应用程序接收到这个请求后在本地或远程执行真正的函数逻辑获取结果再将结果以文本形式返回给大模型。总结大模型结合函数返回的结果生成最终面向用户的自然语言回答。它的底层逻辑是什么本质上Function Call是大模型理解任务后进行“规划”和“分解”的一种体现。模型并没有真正执行代码它只是根据对函数描述的理解输出了一个符合格式的“调用指令”。这个指令能被你的程序解析并执行。OpenAI的Chat Completions API是这一模式的典型代表它通过tools或functions参数来接收函数定义。注意一个关键陷阱在于“执行结果”的上下文管理。如果函数返回的结果没有被正确地塞回给大模型即放入下一轮对话的“消息”历史中那么大模型就会丢失这部分关键信息无法基于结果进行总结对话逻辑就会中断也就是热词里提到的“当场死机”。这要求开发者在架构上必须妥善管理对话状态。2.2 Skill更上层的抽象与封装Skill这个概念在不同框架和语境下含义略有浮动但通常指比Function Call更高级、更封装的一层。如果说Function Call是“芯片指令集”那么Skill更像是“软件应用”。在LangChain等框架中一个Skill或称为Tool通常将Function Call的能力封装成了一个更易用的对象。它除了包含函数的基本信息名称、描述、参数外还可能内置了验证逻辑、错误处理、甚至一些简单的预处理或后处理步骤。LangChain的Tool接口就是典型代表它统一了各种后端包括OpenAI Function Calling、自定义函数、API请求等的调用方式。在如“仓颉Skill”等特定平台中Skill可能指一个完整的、可复用的能力模块它可能由多个Function Call、条件判断、数据处理流程组合而成目标是为了完成一个更复杂的任务比如“生成周报并发送邮件”。在如Claude的Code Skill中这可能指的是让模型编写并执行代码片段的能力这实际上是一种更强大但也更危险的“函数调用”因为它动态生成代码。那么LangChain工具调用和LLM Function Call速度受什么影响这问到了点子上。LangChain作为中间层其工具调用的速度主要受以下因素影响网络延迟与LLM API如OpenAI的通信耗时是大头。工具本身执行时间如果你调用的工具是一个慢速查询数据库的API那么整体响应就会变慢。LangChain的抽象开销为了提供统一接口和复杂功能如Agent的循环决策LangChain会引入一些额外的逻辑判断和序列化/反序列化操作在极高并发或简单场景下可能成为微小的瓶颈。大模型生成Function Call参数的时间模型需要“思考”并生成结构化的参数这比生成普通文本略慢。2.3 核心差异与选用指南特性维度Function Call (原生)Skill/Tool (框架层)抽象层级底层协议标准化。高层封装更易用功能更丰富。灵活性高直接与模型交互控制精细。较高但受框架设计约束。开发便利性较低需自行处理序列化、状态管理。高框架提供了大量样板代码和集成。功能范围严格限于函数调用。可包含函数调用、数据流、条件逻辑等。适用场景需要极致控制、轻量级集成或框架不支持时。快速应用开发、复杂Agent构建、需要利用现有生态工具时。如何选择对于大多数应用开发我建议从LangChain这类框架的Tool/Skill入手它能快速帮你搭起架子处理很多边缘情况。当你遇到性能瓶颈或需要实现框架未覆盖的特殊逻辑时再深入研究原生的Function Call机制进行定制优化。3. 混元大模型与Function Calling实战腾讯混元大模型作为国内领先的模型之一提供了完善的Function Calling能力支持。下面我们以一个“智能旅行助手”的实战场景来演示如何一步步实现。场景用户说“我想下周五从北京飞往上海看看机票并告诉我上海的天气怎么样。”这个任务需要混合调用两个外部能力查询航班和查询天气。3.1 环境准备与依赖安装首先确保你有Python环境并安装必要的SDK。pip install tencentcloud-sdk-python-hunyuan # 混元官方SDK # 此外你可能还需要 requests 用于调用模拟的航班/天气API pip install requests接下来你需要前往腾讯云官网开通混元大模型服务并获取你的SecretId和SecretKey。这些是调用API的凭证务必妥善保管不要硬编码在代码中建议使用环境变量。export TENCENT_CLOUD_SECRET_ID你的SecretId export TENCENT_CLOUD_SECRET_KEY你的SecretKey3.2 定义我们的“工具”函数我们需要用混元API能理解的格式来定义两个函数。混元的Function Calling格式与OpenAI的tools参数兼容这降低了我们的学习成本。# tools_definition.py def get_flight_info(departure_city: str, arrival_city: str, date: str): 根据出发城市、到达城市和日期查询航班信息。 Args: departure_city (str): 出发城市如“北京”。 arrival_city (str): 到达城市如“上海”。 date (str): 出发日期格式为 YYYY-MM-DD如“2024-06-21”。 Returns: str: 返回航班信息的字符串例如“找到5个航班最早一班是CA150108:00起飞价格1200元。” # 这里是模拟实现真实场景应调用航班API如携程、航司接口 print(f[模拟调用] 查询航班{departure_city} - {arrival_city} on {date}) # 模拟API调用延迟和返回 import time time.sleep(0.5) return f已为您找到从{departure_city}飞往{arrival_city}在{date}的航班。共3个班次\n1. CA1501, 08:00-10:15, 经济舱 ¥1250\n2. MU5101, 10:30-12:45, 经济舱 ¥1380\n3. HO1255, 14:00-16:10, 经济舱 ¥1100 def get_weather(city: str): 查询指定城市的近期天气情况。 Args: city (str): 城市名称如“上海”。 Returns: str: 返回天气信息的字符串。 print(f[模拟调用] 查询天气{city}) import time time.sleep(0.3) # 模拟返回 return f{city}近期天气明天6月21日多云转晴25-32°C南风3级后天晴26-34°C。 # 定义给混元模型的工具描述 tools [ { type: function, function: { name: get_flight_info, description: 查询两个城市之间在指定日期的航班信息。, parameters: { type: object, properties: { departure_city: {type: string, description: 出发城市}, arrival_city: {type: string, description: 到达城市}, date: {type: string, description: 出发日期格式为YYYY-MM-DD} }, required: [departure_city, arrival_city, date] } } }, { type: function, function: { name: get_weather, description: 查询指定城市的天气情况。, parameters: { type: object, properties: { city: {type: string, description: 需要查询天气的城市名称} }, required: [city] } } } ]关键点解析描述description至关重要这是大模型理解工具用途的唯一依据。必须清晰、准确说明工具做什么、参数是什么。例如“查询航班信息”就比“获取交通数据”好得多。参数定义要严谨required字段指明了哪些参数是模型必须提供的。参数描述也要清晰比如date的格式要求能极大提高模型填充参数的准确率。函数实现是安全的模型永远看不到你的get_flight_info函数内部代码它只看到上面的JSON描述。实际执行完全在你的服务器控制之下。3.3 构建对话循环与函数调用逻辑这是最核心的部分我们需要处理用户输入、模型决策、函数执行和结果反馈的完整循环。# main.py import os import json from tencentcloud.hunyuan.v20230901 import hunyuan_client, models from tencentcloud.common.profile.client_profile import ClientProfile from tencentcloud.common.profile.http_profile import HttpProfile from tencentcloud.common import credential from tools_definition import tools, get_flight_info, get_weather # 1. 初始化混元客户端 def init_hunyuan_client(): secret_id os.environ.get(TENCENT_CLOUD_SECRET_ID) secret_key os.environ.get(TENCENT_CLOUD_SECRET_KEY) if not secret_id or not secret_key: raise ValueError(请设置 TENCENT_CLOUD_SECRET_ID 和 TENCENT_CLOUD_SECRET_KEY 环境变量) cred credential.Credential(secret_id, secret_key) http_profile HttpProfile() http_profile.endpoint hunyuan.tencentcloudapi.com # 终端节点 client_profile ClientProfile() client_profile.httpProfile http_profile return hunyuan_client.HunyuanClient(cred, ap-guangzhou, client_profile) # 以广州区域为例 client init_hunyuan_client() # 2. 工具执行器根据模型返回的函数调用请求执行本地函数 def execute_function_call(tool_call): 执行单个工具调用。 tool_call 结构示例: {name: get_weather, arguments: {city: 上海}} func_name tool_call.get(name) func_args json.loads(tool_call.get(arguments, {})) if func_name get_flight_info: return get_flight_info(**func_args) elif func_name get_weather: return get_weather(**func_args) else: return f错误未知的函数 {func_name} # 3. 主对话循环 def chat_with_assistant(user_input, conversation_history[]): 与助手进行一轮对话支持函数调用。 conversation_history: 列表包含之前的消息每条消息是一个dict如 {role: user, content: ...} 或 {role: assistant, content: ...} # 将用户输入加入历史 conversation_history.append({role: user, content: user_input}) # 构建请求 req models.ChatCompletionsRequest() # 设置模型例如混元-pro req.Model hunyuan-pro req.Messages conversation_history # 关键传入我们定义的工具列表 req.Tools tools # 设置工具调用策略。auto表示模型可以自行决定是否调用工具。 req.ToolChoice auto print(f\n[用户] {user_input}) # 发送请求到混元 resp client.ChatCompletions(req) # 解析响应 assistant_message resp.Choices[0].Message response_content assistant_message.Content tool_calls assistant_message.ToolCalls # 获取模型建议的工具调用列表 # 情况1模型直接回复没有调用工具 if not tool_calls: final_answer response_content conversation_history.append({role: assistant, content: final_answer}) print(f[助手] {final_answer}) return final_answer, conversation_history # 情况2模型决定调用工具 print(f[助手] 决定调用工具: {[tc.get(name) for tc in tool_calls]}) # 用于存储所有工具执行结果的消息 tool_messages_for_history [] # 遍历并执行每一个工具调用 for tool_call in tool_calls: # 执行函数 tool_result execute_function_call(tool_call[function]) print(f[系统] 执行 {tool_call[function][name]}结果: {tool_result[:50]}...) # 将工具执行结果构造为一条特殊的“工具”角色消息必须包含 tool_call_id tool_message { role: tool, content: tool_result, tool_call_id: tool_call.get(id) # 这个id由模型在tool_calls中提供用于关联调用和结果 } tool_messages_for_history.append(tool_message) # 关键步骤将工具调用的请求和结果都加入到历史记录中 # 先加入助手请求调用工具的消息 conversation_history.append({ role: assistant, content: response_content, # 这里可能是null或空具体看API返回 tool_calls: tool_calls }) # 再加入所有工具执行结果的消息 conversation_history.extend(tool_messages_for_history) # 现在我们有了新的历史记录包含用户问题、助手工具调用请求、工具结果 # 我们需要再次调用模型让它基于工具结果生成最终回答。 print([系统] 将工具结果反馈给模型请求最终回答...) final_req models.ChatCompletionsRequest() final_req.Model hunyuan-pro final_req.Messages conversation_history # 这次的历史包含了工具结果 # 这次调用通常不需要再传递Tools参数除非希望模型继续调用新工具 # final_req.Tools tools # final_req.ToolChoice none # 可以设置为none让模型只总结不触发新调用 final_resp client.ChatCompletions(final_req) final_answer final_resp.Choices[0].Message.Content # 将模型的最终回答也加入历史 conversation_history.append({role: assistant, content: final_answer}) print(f[助手] {final_answer}) return final_answer, conversation_history # 4. 运行示例 if __name__ __main__: history [] user_query 我想下周五从北京飞往上海看看机票并告诉我上海的天气怎么样。 # 假设今天是2024-06-14下周五是2024-06-21 # 在实际应用中你需要一个时间解析逻辑来处理“下周五”这种相对日期。 # 这里为了演示我们手动替换为具体日期。 user_query_parsed user_query.replace(下周五, 2024-06-21) answer, updated_history chat_with_assistant(user_query_parsed, history) # 你可以继续对话history中已经保存了完整的上下文包括工具调用和结果 print(\n--- 下一轮对话上下文已保留---) next_answer, _ chat_with_assistant(那航班CA1501的准点率怎么样, updated_history)实操心得与避坑指南日期等模糊信息的处理大模型在Function Call中能出色地提取结构化参数但像“下周五”这样的相对日期它通常只会原样提取为字符串“下周五”。最佳实践是在将参数传递给真实函数前增加一个“参数后处理”层使用专门的库如dateparser将自然语言日期转换为标准格式或者让模型在描述中明确要求YYYY-MM-DD格式并在函数内部做转换。tool_call_id的重要性当模型一次请求调用多个工具时每个工具调用都有一个唯一的id。返回工具结果时必须将结果与对应的tool_call_id精确绑定。如果弄混模型将无法正确关联结果和请求。上下文管理是生命线正如之前强调的必须把assistant的tool_calls消息和所有tool角色的结果消息按顺序、完整地加入到下一轮请求的Messages中。缺少任何一环模型都会“失忆”。这是新手最容易出错导致对话“死机”的地方。工具描述的精准性如果模型频繁调用错误的工具或参数提取不准首先检查工具描述。描述要像给新人写文档一样清晰、无歧义。可以多用例子比如在参数描述里写“例如上海北京市”。4. 进阶构建复杂Skill与性能优化掌握了基础调用后我们可以向更实用的Skill迈进。4.1 设计一个复合型Skill旅行规划助手上面的例子是模型自主决定调用多个独立工具。我们也可以主动定义一个更复杂的Skill内部编排多个步骤。# complex_skill.py import json from datetime import datetime, timedelta class TravelPlanningSkill: 一个复合Skill内部协调航班和天气查询并生成旅行建议。 def __init__(self, flight_tool, weather_tool): self.get_flight_info flight_tool self.get_weather weather_tool def run(self, departure: str, destination: str, travel_date_str: str): 执行旅行规划。 1. 查询航班。 2. 查询目的地天气。 3. 综合信息生成建议。 # 步骤1: 查询航班 print(f[旅行规划Skill] 正在查询航班信息...) flight_result self.get_flight_info(departure, destination, travel_date_str) # 步骤2: 查询天气 (假设查询旅行日期后三天的天气) print(f[旅行规划Skill] 正在查询目的地天气...) try: travel_date datetime.strptime(travel_date_str, %Y-%m-%d) weather_date_str (travel_date timedelta(days1)).strftime(%Y-%m-%d) # 这里简化处理实际天气API可能需要日期范围 weather_result self.get_weather(destination) except ValueError: weather_result 日期格式错误无法查询天气。 # 步骤3: 生成综合建议 (这里可以用一个简单的规则引擎甚至再调用一次LLM进行总结) suggestion self._generate_suggestion(flight_result, weather_result) final_output f ## 您的旅行规划报告 **行程**{departure} - {destination}日期{travel_date_str} **航班信息** {flight_result} **目的地天气** {weather_result} **综合建议** {suggestion} return final_output def _generate_suggestion(self, flight_info, weather_info): 一个简单的规则引擎生成建议。 suggestion [] if 经济舱 ¥1100 in flight_info: suggestion.append(- 发现HO1255航班价格最低¥1100如对时间不敏感可考虑。) if 晴 in weather_info and 34 in weather_info: suggestion.append(- 目的地气温较高34°C建议携带防晒用品和轻薄衣物。) if CA1501 in flight_info: suggestion.append(- CA1501为早班机请提前规划前往机场的交通。) if not suggestion: suggestion.append(- 请根据您的具体偏好选择航班并关注目的地天气变化。) return \n.join(suggestion) # 将这个复合Skill包装成一个可供大模型调用的Function travel_skill_tool { type: function, function: { name: travel_planning, description: 为一个从A城市到B城市的旅行提供综合规划包括航班查询、天气查询和简单建议。, parameters: { type: object, properties: { departure: {type: string, description: 出发城市}, destination: {type: string, description: 目的地城市}, travel_date: {type: string, description: 旅行日期格式为YYYY-MM-DD} }, required: [departure, destination, travel_date] } } }这样用户可以直接说“帮我规划一下下周五从北京到上海的旅行”模型只需调用一次travel_planning技能你后端的这个Skill类就会自动执行航班、天气查询和逻辑整合最后返回一个结构化的报告。这降低了模型的调用次数和复杂度。4.2 性能优化与缓存策略频繁调用大模型和外部API是性能瓶颈。以下是一些优化思路工具结果缓存对于短时间内不变的数据如天气可缓存1小时航班价格缓存时间较短在execute_function_call中引入缓存层如使用functools.lru_cache或Redis。相同的参数查询直接返回缓存结果大幅降低延迟和API开销。并行工具调用如果模型一次请求调用多个独立的工具如同时查航班和天气可以在execute_function_call环节使用线程池或asyncio并发执行而不是串行。但要注意混元模型返回的tool_calls是一个列表你需要确保并行执行后结果与tool_call_id的对应关系不乱。精简上下文长时间的对话历史会消耗大量Token增加成本和延迟。对于不再需要的早期对话可以进行摘要用模型将长对话总结成一段话或者只保留最近几轮交互。特别注意工具调用的请求和结果消息通常较长在问题解决后可以考虑从历史中移除只保留最终的总结性回答。设置合理的超时与重试调用外部API可能失败。在你的工具执行函数中必须添加超时和重试机制如使用tenacity库并为失败情况提供友好的降级响应如“暂时无法获取航班信息请稍后再试”避免整个流程因一个工具失败而崩溃。5. 常见问题排查与调试技巧在实际开发中你肯定会遇到各种问题。这里记录几个我踩过的坑和解决方法。5.1 问题模型不调用工具总是直接回答可能原因1工具描述不清晰或与问题不匹配。模型认为自己的知识足以回答或者无法从你的描述中理解工具的用途。检查仔细阅读工具的描述description和参数描述。确保它们清晰、具体、无歧义。例如“获取天气”不如“查询指定城市未来三天的天气预报”好。调试在请求中将ToolChoice参数暂时设置为{type: function, function: {name: your_tool_name}}强制调用某个工具测试工具定义本身是否能被正确触发和解析。可能原因2提示词System Prompt引导不足。你没有在系统指令中告诉模型“你是一个助手可以调用工具来回答问题”。解决在对话历史的开头加入一条role为system的消息内容明确指示模型使用工具。例如“你是一个智能旅行助手你可以通过调用工具来查询实时航班和天气信息。当用户的问题涉及这些信息时请务必调用相应的工具来获取准确数据后再回答。”可能原因3模型版本或参数问题。某些模型或特定参数可能对工具调用的支持有差异。解决确认你使用的混元模型版本如hunyuan-pro支持Function Calling。查阅官方文档确认API调用方式无误。5.2 问题模型调用了工具但参数提取错误可能原因1参数描述模糊。例如参数date的描述只是“日期”模型可能填“明天”、“next Friday”。解决在参数描述中严格规定格式如“日期必须为YYYY-MM-DD格式例如2024-06-21”。可能原因2用户问题过于复杂或隐含信息多。例如“帮我看看明天从这儿到那儿的机票”模型无法推断“这儿”和“那儿”具体指什么。解决这是当前技术的局限。需要在产品设计上引导用户提供明确信息或者在调用工具前让模型先与用户进行一轮澄清对话多轮对话能力。例如模型可以先回复“请问您的出发城市和目的地城市分别是哪里”5.3 问题对话在工具调用后“死机”根本原因上下文断裂。即模型发出的工具调用请求assistant消息含tool_calls和工具返回的结果tool消息没有完整、顺序地包含在下一轮请求的Messages中。排查清单检查你是否将上轮模型响应中的tool_calls列表完整地保存了下来。检查你是否为每个工具调用生成了正确的tool消息并且tool_call_id与请求中的id一一对应。检查你是否将assistant含tool_calls消息和所有tool消息都append到了历史记录列表里。检查你在发送最终总结请求时是否携带了这个包含了工具调用和结果的完整历史记录。5.4 调试技巧打印完整请求和响应在开发阶段将发送给混元API的req.Messages和接收到的resp对象完整地打印出来注意脱敏敏感信息。这是诊断问题最直接的方法你可以清晰地看到模型“想”做什么以及API返回了什么。使用模拟工具在开发初期将真实API调用替换为模拟函数像我们示例中的get_flight_info快速测试整个调用链路是否通畅避免受外部API不稳定性的干扰。分步测试先测试一个最简单的工具调用如一个“加法器”工具确保基础流程跑通。再逐步增加工具复杂度和对话轮次。6. 总结与展望通过这次对Skill和Function Call底层逻辑的拆解以及在混元大模型上的实战我们可以看到让大模型“使用工具”已经从一个概念变成了非常工程化的实践。其核心在于清晰的协议定义工具描述、严谨的上下文管理和鲁棒的错误处理。对于开发者而言从LangChain这类框架入手可以快速搭建原型理解其背后的抽象。但深入到底层掌握原生Function Call的机制能让你在性能调优、处理复杂场景时更有底气。尤其是在设计企业级应用时你需要考虑工具的执行权限、安全性防止恶意参数注入、熔断降级等一系列工程问题。混元大模型提供了稳定可靠的Function Calling能力结合腾讯云的生态为构建本土化的AI智能应用提供了强大基础。无论是简单的信息查询还是串联起企业内部多个系统的复杂业务流程自动化RPA这个技术范式都打开了巨大的想象空间。我个人在实际开发中的体会是设计工具描述是一门艺术它直接决定了模型的“工具使用体验”。而维护对话上下文的完整性则是整个系统稳定运行的基石丝毫马虎不得。下一步可以探索更复杂的Agent模式比如让模型具备“思考链”Chain-of-Thought能力在调用工具前先规划步骤或者处理工具调用失败后的备选方案这会让你的AI助手变得更加智能和可靠。