一、前言
在大模型从对话交互走向实际落地的过程中,Function Call作为连接语言模型与外部能力的关键机制,早已成为行业标配。我们曾深入拆解过它的底层逻辑、执行流程与工程实现,清楚其核心价值在于让大模型输出结构化调用指令,完成计算、查询、接口交互等原本无法可靠完成的任务。但在很长一段时间里,我们广泛讨论 Function Call 时,很少提及Skill,甚至将二者混为一谈。
随着大模型应用复杂度快速提升,单一函数调用已无法满足多场景、多任务、可管理的智能体需求,Skill 这一概念才正式走到台前。相信很多人都会疑惑:同样是扩展大模型能力,Skill 与早已熟悉的 Function Call 究竟是什么关系?二者在定位、设计、使用场景上又存在哪些本质差异?带着这个疑问,我们从核心定义、工程形态、应用演进三个维度,清晰梳理 Skill 与 Function Call 的关联与区别,并解释为何早期只强调 Function Call,而如今必须将二者结合理解,为真正掌握大模型的能力扩展体系打下基础。

Function Calling往期文章参考:
《构建AI智能体:Function Calling - 解锁大语言模型的实际行动力+案例解析;》
二、核心基础
1. 什么是 Skill(技能)
Skill(技能)是为大模型定义的可执行任务模板,是对大模型能力的具象化扩展。我们可以把它理解为给大模型"安装的插件"或"学会的新本领,比如"计算数学题"、"查询天气"、"生成 Excel 报表"、"调用地图 API查路线"等,每个 Skill 对应一类特定任务,包含任务描述、输入参数、执行逻辑、输出格式四个核心要素。
关键特征:
- 针对性:一个 Skill 只解决一类问题,比如"文本翻译 Skill"仅处理翻译任务,避免功能混杂;
- 标准化:Skill 需定义清晰的输入输出规范,让大模型能精准理解"该传什么参数"、"能得到什么结果";
- 可组合性:多个 Skill 可以组合完成复杂任务,如"查天气 Skill"+"生成出行建议 Skill"="智能出行规划";
- 可复用性:定义好的 Skill 可以在不同场景重复调用,无需重复开发。
举个通俗例子:大模型本身像一个只会聊天的大脑,而 Skill 就是给它配备的工具手册,手册里写清楚"计算器工具怎么用"、"查快递工具怎么用",大脑只需按手册操作就能完成原本不会的任务。
2. 什么是 Function Call(函数调用)
Function Call(函数调用)是大模型执行Skill的桥梁机制,指大模型根据用户指令,识别需要调用的 Skill 对应的函数,按照预设格式生成函数调用指令,交由外部系统执行后,再将结果返回给用户的全过程。
核心区别:
- Skill 是"能力定义",比如"定义一个计算两数之和的技能";
- Function Call 是"能力执行",比如"调用这个求和技能,计算 10+20 的结果"。
Function Call 的核心要素:
-
- 函数元数据:函数名称、参数列表(参数名、类型、是否必填)、返回值类型;
-
- 调用触发器:大模型识别用户指令中包含"需要执行具体操作"的意图,比如用户说"帮我算一下 3.14×50 的结果",触发 "计算器函数"调用;
-
- 参数提取:大模型从用户指令中提取函数所需参数,比如从"3.14×50"中提取参数 a=3.14,b=50,操作符 =×;
-
- 执行器:执行函数调用的外部程序,可以是 Python 函数、API 接口、本地脚本等;
-
- 结果解析与返回:将函数执行结果转换为自然语言,返回给用户。
3. Skill 和 Function Call的特点
大模型原生能力存在三大痛点,而 Skill+Function Call 正是解决方案:
-
- 能力边界限制:大模型无法执行实时计算(比如复杂数学公式)、访问实时数据(比如最新股票价格)、操作外部系统(比如调用企业 ERP 接口);
-
- 准确性问题:大模型对数字计算、逻辑推理的准确率远低于程序,比如计算"12345×6789"时容易出错,而调用计算器函数可100%准确;
-
- 安全性与可控性:直接让大模型生成代码执行存在安全风险,而通过标准化 Skill 和 Function Call 可限定调用范围,降低风险。
简单来说:Skill 定义"大模型能做什么",Function Call 实现"大模型怎么做",二者结合让大模型从语言模型升级为智能执行体。

4. Skill与Function Call的深度关系
不知道大家有没有一个疑问,为什么以前讲 Function Call 很少提 Skill?为什么现在又必须绑定 Skill 一起讲?
4.1 早期为什么只讲 Function Call,不提 Skill
早期,2023年初 OpenAI刚推出Function Call,大家的场景都超级简单:
- 只调用1个函数
- 函数是 写死的
- 用途是 计算器、查天气、查时间 这种小工具
- 没有"技能管理"、"技能选择"、"技能编排"
那时大家关心的只有三件事:
-
- 模型能不能输出结构化JSON
-
- 能不能 正确填参数
-
- 本地能不能 执行函数
所以大家只讲:Prompt → 模型输出函数调用 → 本地执行 → 返回结果
这就是 Function Call,Skill 这个概念根本没必要出现。
4.2 什么时候 Skill 被迫出现了
当应用从调用1个函数变成同时管理10/50/100个函数时,问题炸了:
- 函数太多,模型不知道该用哪个
- 函数描述混乱,模型经常乱调用
- 函数重复、冲突、难维护
- 不同场景要切换不同能力集
- 要做 权限控制、版本管理、热更新
这时大家突然发现:单纯的 Function Call 根本撑不起复杂应用。
于是行业统一做了一件事:给"一堆函数 + 描述 + 参数 + 逻辑"起个名字:Skill(技能)
4.3 本质区别
- Function Call:是调用方式、是协议、是格式、是技术机制,模型如何调用外部能力,包含机制、协议、格式
- Function Call 是执行层,核心是:模型 → 输出 JSON → 调用外部代码
- Skill:是被调用的能力单元、是封装好的功能模块、是可管理、可组合、可上架的技能,被模型调用的能力单元,包含功能、封装、业务
- Skill 是能力层,包含计算器 Skill、天气 Skill、订单查询 Skill、知识库 Skill...
总的来说,Skill 是 Function Call 的内容,Function Call 是 Skill 的入口。
三、原理与流程
1. Skill 的设计原理
一个完整的 Skill 本质是 "结构化的任务描述 + 可执行的逻辑代码",其设计遵循以下核心原理:
1.1 Skill 的结构化定义规范
无论使用哪种大模型平台,Skill 的定义都包含以下标准化结构(以 JSON 格式为例):
javascript
{
"skill_name": "calculator", // 技能名称(唯一标识)
"skill_description": "用于执行基本数学运算,支持加减乘除、平方、开方", // 技能描述(让大模型理解用途)
"parameters": [ // 参数列表
{
"name": "num1", // 参数名
"type": "float", // 参数类型
"required": true, // 是否必填
"description": "第一个运算数" // 参数描述
},
{
"name": "num2",
"type": "float",
"required": false, // 非必填(比如平方只需要一个数)
"description": "第二个运算数"
},
{
"name": "operation",
"type": "string",
"required": true,
"description": "运算类型,可选值:add, sub, mul, div, square, sqrt",
"enum": ["add", "sub", "mul", "div", "square", "sqrt"] // 枚举值(限定输入范围)
}
],
"return_value": { // 返回值定义
"type": "float",
"description": "数学运算的结果"
},
"error_handling": { // 异常处理规则
"div_by_zero": "除数不能为0,返回错误提示",
"invalid_param": "参数类型错误,返回参数要求说明"
}
}
这个结构化定义的核心目的是让大模型能精准理解 Skill 的使用规则,比如大模型看到这个定义后,就知道 "calculator" 技能需要哪些参数、参数类型是什么、支持哪些运算,从而在用户提问时准确提取参数并调用。

1.2 Skill 的分类
根据执行方式和应用场景,Skill 可分为三类:
-
- 本地函数型 Skill:对应的执行逻辑是本地 Python/Java 函数,比如本地计算器函数,优点是响应快、无网络依赖;
-
- API 调用型 Skill:对应的执行逻辑是调用外部 API,比如天气 API、地图 API,优点是能获取实时数据、访问远程服务;
-
- 组合型 Skill:由多个基础 Skill 组合而成,比"查天气 Skill"+"查交通 Skill"+"生成建议 Skill"="出行规划 Skill",优点是能解决复杂任务。

2. Function Call执行流程
Function Call 的执行是一个"大模型意图识别→参数提取→函数执行→结果返回" 的闭环流程,我们以"用户让大模型计算 3.14×50 的结果"为例,拆解完整执行流程:

流程说明与技术细节分析:
步骤 1:意图识别
大模型通过"指令理解 + Skill 匹配"判断是否需要调用函数。实现方式有两种:
- 基于提示词(Prompt)的意图识别:在 Prompt 中明确告诉大模型"当用户的问题涉及数学计算时,调用 calculator 函数";
- 基于微调的意图识别:对大模型进行微调,让它能更精准识别需要调用函数的场景,更适合专业场景。
**技术细节:**Prompt 设计需包含"Skill 列表 + 调用规则",比如:
你是一个能调用函数的智能助手,可用函数列表:
- calculator:用于数学计算,参数包括num1(浮点数)、num2(浮点数,可选)、operation(运算类型:add/sub/mul/div/square/sqrt)
当用户的问题涉及数学计算时,你需要生成JSON格式的函数调用指令,格式如下:
{"function_name": "calculator", "parameters": {"num1": 值, "num2": 值, "operation": 运算类型}}
如果不需要调用函数,直接回复用户即可。
步骤 2:参数提取
大模型从用户指令中提取函数所需参数,核心挑战是"处理非标准化输入",比如用户说"帮我算一下圆周率乘以 50",需要先将"圆周率" 转换为 3.14。
**技术细节:**可通过"实体识别 + 同义词映射 + 默认值填充"提升参数提取准确率:
- 实体识别:识别指令中的数字、运算类型等关键实体;
- 同义词映射:将"乘以"映射为"mul","加上"映射为"add";
- 默认值填充:比如用户只说"计算10的平方",自动填充num2为null,operation为square。
步骤 3:生成函数调用指令
大模型需生成标准化的调用指令,通常为JSON格式,确保外部执行器能解析。比如针对"3.14×50",生成的调用指令为:
javascript
{"function_name": "calculator", "parameters": {"num1": 3.14, "num2": 50, "operation": "mul"}}
**技术细节:**为避免大模型生成格式错误的 JSON,可在 Prompt 中加入格式约束,比如"必须严格按照JSON格式生成,不要添加额外文字,确保JSON可被Python的json.loads()解析"。
步骤 4:函数执行
外部执行器接收调用指令,解析参数后执行对应函数。执行器的核心逻辑是"函数注册表 + 参数校验 + 执行":
- 函数注册表:维护"函数名→函数对象"的映射关系;
- 参数校验:检查参数类型、必填项是否符合Skill定义;
- 执行:调用函数并捕获异常,比如除零错误。
步骤 5:结果返回与转换
- 执行器将函数结果返回给大模型,大模型将机器结果转换为自然语言结果。
- 比如函数返回157.0,大模型转换为"3.14 乘以 50 的计算结果是 157.0"。

3. Skill 与 Function Call原理总结
- Skill 的核心原理:标准化定义能力边界,让大模型知道"能调用什么、怎么调用";
- Function Call 的核心原理:意图驱动的闭环执行,将大模型的语言理解能力与外部程序的执行能 结合;
- 二者的本质:突破大模型的原生能力边界,实现"语言理解→逻辑执行→结果反馈"的完整智能闭环。
四、应用实践
我们常用的混元大模型(Hunyuan)已经提供了 OpenAI 兼容接口,支持 Function Call(函数调用)功能。特别是 hunyuan-turbo、hunyuan-pro 以及专门的 hunyuan-functioncall 模型都支持此功能。但由于在 OpenAI 兼容接口中,参数名已从旧版的 functions/function_call 更新为更通用的 tools/tool_choice,基于此我们实现一个计算的示例:
python
import json
import os
from dotenv import load_dotenv
from openai import OpenAI
# 加载环境变量 (如果需要)
load_dotenv()
# ====================== 配置腾讯混元客户端 ======================
# 注意:请确保您的 API Key 有权限访问混元大模型
client = OpenAI(
api_key=os.getenv("TENCENT_API_KEY"),
base_url="https://api.hunyuan.cloud.tencent.com/v1", # 腾讯混元 OpenAI 兼容接口地址
)
# ====================== 第一步:定义 Tool(计算器技能) ======================
# 混元兼容接口推荐使用 tools 格式 (OpenAI 新版标准)
calculator_tool = {
"type": "function",
"function": {
"name": "calculator",
"description": "用于执行基本数学运算,支持加减乘除、平方、开方",
"parameters": {
"type": "object",
"properties": {
"num1": {
"type": "number",
"description": "第一个运算数"
},
"num2": {
"type": "number",
"description": "第二个运算数(平方、开方时可选)"
},
"operation": {
"type": "string",
"description": "运算类型",
"enum": ["add", "sub", "mul", "div", "square", "sqrt"]
}
},
"required": ["num1", "operation"]
}
}
}
# ====================== 第二步:Skill 对应的执行函数 ======================
def calculator_function(num1: float, operation: str, num2: float = None) -> float:
"""
计算器函数,执行基本数学运算
"""
try:
if operation == "add":
return num1 + num2
elif operation == "sub":
return num1 - num2
elif operation == "mul":
return num1 * num2
elif operation == "div":
if num2 == 0:
raise ValueError("除数不能为0")
return num1 / num2
elif operation == "square":
return num1 ** 2
elif operation == "sqrt":
if num1 < 0:
raise ValueError("负数不能开平方")
return num1 ** 0.5
else:
raise ValueError(f"不支持的运算类型:{operation}")
except Exception as e:
raise Exception(f"计算失败:{str(e)}")
# ====================== 第三步:核心逻辑 ======================
def get_completion_with_tools(messages, tools=None, tool_choice="auto"):
"""
调用腾讯混元 API,处理工具调用逻辑
使用 tools 参数替代旧的 functions 参数
"""
try:
response = client.chat.completions.create(
model="hunyuan-turbo", # 推荐使用 turbo 或 functioncall 专用模型,lite 可能不支持
messages=messages,
tools=tools, # 使用 tools 列表
tool_choice=tool_choice # 自动选择或指定
)
return response
except Exception as e:
print(f"API 调用错误:{e}")
raise e
def execute_function_call(function_name, function_args):
"""
执行函数调用
"""
function_registry = {
"calculator": calculator_function
}
if function_name not in function_registry:
return f"错误:未找到函数 {function_name}"
try:
result = function_registry[function_name](**function_args)
return result
except Exception as e:
return f"函数执行失败:{str(e)}"
# ====================== 第四步:完整流程测试 ======================
def main():
# 1. 用户输入 (包含两个计算请求)
user_input = "帮我计算一下 3.14 乘以 50 的结果,再算一下 100 的平方根"
# 2. 构造对话消息
messages = [
{"role": "system", "content": "你是一个快乐的小助手,擅长使用工具进行精确计算。"},
{"role": "user", "content": user_input}
]
print(f"用户输入:{user_input}\n")
# 3. 第一次调用 API,获取工具调用指令
# 注意:混元模型可能需要明确的 tool_choice="auto" 来触发
response = get_completion_with_tools(
messages=messages,
tools=[calculator_tool],
tool_choice="auto"
)
response_message = response.choices[0].message
messages.append(response_message)
# 4. 判断是否需要调用工具
# 新版 API 返回的是 tool_calls 列表,而不是单一的 function_call
if hasattr(response_message, 'tool_calls') and response_message.tool_calls:
for tool_call in response_message.tool_calls:
function_name = tool_call.function.name
function_args = json.loads(tool_call.function.arguments)
print(f"模型决定调用函数:{function_name}")
print(f"函数参数:{function_args}")
# 执行函数
function_result = execute_function_call(function_name, function_args)
print(f"函数执行结果:{function_result}")
# 将结果返回给模型
# 注意:tool_call_id 必须与请求中的 id 一致,以便模型对应
messages.append({
"role": "tool",
"tool_call_id": tool_call.id,
"name": function_name,
"content": str(function_result)
})
# 5. 再次调用 API,让模型根据工具返回结果生成自然语言回复
# 此时不再需要传递 tools,或者传递但不设置 tool_choice,让模型直接回答
final_response = get_completion_with_tools(
messages=messages,
tools=[calculator_tool],
tool_choice="none" # 强制模型不再调用工具,直接生成回复
)
print("\n===== 最终回复 =====")
print(final_response.choices[0].message.content)
else:
# 不需要调用工具,直接输出响应
print("\n===== 模型直接回复 =====")
print(response_message.content)
if __name__ == "__main__":
main()
代码说明:
- 模型选择:
- 代码中使用了 hunyuan-turbo。hunyuan-lite 主要是轻量级模型,可能对 Function Call 支持不完善或不支持。如果遇到报错,请尝试切换到 hunyuan-pro 或确认您的账号是否开通了 hunyuan-functioncall 模型的使用权限。
- 参数名称变化:
- OpenAI 旧接口/部分兼容functions, function_call, 返回 message.function_call。
- OpenAI 新接口/混元推荐:tools, tool_choice, 返回 message.tool_calls。
- Role 的变化:
- 在返回工具执行结果时,新版规范要求 role 为 "tool" ,而不是 "function",并且必须携带 tool_call_id 字段,该 ID 来自模型第一次请求返回的 tool_call.id。这对于多工具并行调用尤其重要。
输出结果:
用户输入:帮我计算一下 3.14 乘以 50 的结果,再算一下 100 的平方根
模型决定调用函数:calculator
函数参数:{'num1': 3.14, 'num2': 50, 'operation': 'mul'}
函数执行结果:157.0
模型决定调用函数:calculator
函数参数:{'num1': 100, 'operation': 'sqrt'}
函数执行结果:10.0
===== 最终回复 =====
3.14 乘以 50 的结果是 157.0,100 的平方根是 10。
五、对大模型的意义
1. 突破大模型的能力边界
大模型的原生能力受限于训练数据和模型架构:
- 训练数据是静态的,无法获取实时信息,如最新的股票价格、最新天气;
- 模型架构是基于语言建模的,不擅长精确计算、逻辑推理、外部系统交互。
Skill+Function Call 让大模型具备了"实时数据访问"、"精确计算"、"外部系统操作"的能力,比如:
- 调用股票 API 获取实时股价,让大模型能做股票分析;
- 调用企业 ERP API 查询库存,让大模型能做库存管理建议;
- 调用代码执行引擎,让大模型能生成并执行代码解决复杂问题。
2. 提升大模型的准确性和可靠性
大模型在处理数字、逻辑、专业数据时容易出现幻觉,生成错误信息,比如:
- 计算"123456×789012"时,大模型可能生成错误结果;
- 回答"2024 年杭州GDP"时,可能生成过时或错误的数据。
而通过 Skill+Function Call 调用专业工具:
- 计算类问题调用计算器函数,准确率 100%;
- 数据类问题调用权威 API,确保数据实时且准确;
- 逻辑类问题调用代码执行引擎,确保推理过程正确。
3. 增强大模型的可控性和安全性
直接让大模型生成代码、执行操作存在安全风险,比如生成恶意代码、调用未授权API,而 Skill+Function Call 通过以下方式提升可控性:
- 白名单机制:只允许调用预定义的 Skill,禁止调用未授权的函数;
- 参数校验:严格校验函数参数的类型、范围,防止注入攻击;
- 权限控制:为不同 Skill 分配不同权限,比如普通用户只能调用天气 Skill,管理员才能调用数据库 Skill;
- 审计日志:记录所有 Function Call 的执行记录,便于追溯和排查问题。
4. 推动大模型从对话工具向智能体进化
智能体(Agent)是具备"感知→决策→执行→反馈"能力的智能系统,而 Skill+Function Call 是大模型成为智能体的核心基础:
- 感知:通过自然语言理解获取用户需求;
- 决策:通过意图识别选择需要调用的 Skill;
- 执行:通过 Function Call 执行具体操作;
- 反馈:将执行结果转换为自然语言返回给用户。
比如一个"智能出行助手"智能体:
-
- 感知:用户说"明天去上海出差,帮我规划行程";
-
- 决策:需要调用"天气 Skill"(查上海明天天气)、"机票 Skill"(查北京到上海机票)、"酒店 Skill"(查上海酒店)、"路线 Skill"(查机场到酒店路线);
-
- 执行:依次调用上述 Skill,获取各部分数据;
-
- 反馈:整合所有数据,生成完整的出行规划。
六、总结
总的来说,Skill 和 Function Call,一个是能做什么,一个是怎么去做,两者绑定在一起,才能真正让大模型突破自身局限,从只会聊天变成能干活的工具。
Function Call 就是一套调用机制,就是大模型识别到有具体任务(比如算个账、查天气),就按规矩生成指令,让外部程序去执行。而 Skill 呢,就是被调用的那个本事,是咱们提前定义好的、标准化的功能模块,比如计算器 Skill、天气 Skill,里面写清楚了能做啥、要啥参数、怎么返回结果。
以前我们聊 Function Call,很少提 Skill,是那时候场景太简单,就调用一两个函数,直接叫函数、工具就够了。但现在不一样了,大模型要做复杂任务,要管理十几个、几十个功能,还得组合使用,这时候就必须把这些功能标准化、模块化,这就是 Skill 的作用。
简单说,两者结合起来,大模型才能精准干活、高效执行,这也是咱们现在做大模型应用、搞智能体,必须吃透的核心逻辑。