大模型应用:解锁大模型能力边界:Skill 与 Function Call的底层逻辑与实战应用.117

一、前言

在大模型从对话交互走向实际落地的过程中,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 的核心要素:

    1. 函数元数据:函数名称、参数列表(参数名、类型、是否必填)、返回值类型;
    1. 调用触发器:大模型识别用户指令中包含"需要执行具体操作"的意图,比如用户说"帮我算一下 3.14×50 的结果",触发 "计算器函数"调用;
    1. 参数提取:大模型从用户指令中提取函数所需参数,比如从"3.14×50"中提取参数 a=3.14,b=50,操作符 =×;
    1. 执行器:执行函数调用的外部程序,可以是 Python 函数、API 接口、本地脚本等;
    1. 结果解析与返回:将函数执行结果转换为自然语言,返回给用户。

3. Skill 和 Function Call的特点

大模型原生能力存在三大痛点,而 Skill+Function Call 正是解决方案:

    1. 能力边界限制:大模型无法执行实时计算(比如复杂数学公式)、访问实时数据(比如最新股票价格)、操作外部系统(比如调用企业 ERP 接口);
    1. 准确性问题:大模型对数字计算、逻辑推理的准确率远低于程序,比如计算"12345×6789"时容易出错,而调用计算器函数可100%准确;
    1. 安全性与可控性:直接让大模型生成代码执行存在安全风险,而通过标准化 Skill 和 Function Call 可限定调用范围,降低风险。

简单来说:Skill 定义"大模型能做什么",Function Call 实现"大模型怎么做",二者结合让大模型从语言模型升级为智能执行体。

4. Skill与Function Call的深度关系

不知道大家有没有一个疑问,为什么以前讲 Function Call 很少提 Skill?为什么现在又必须绑定 Skill 一起讲?

4.1 早期为什么只讲 Function Call,不提 Skill

早期,2023年初 OpenAI刚推出Function Call,大家的场景都超级简单:

  • 只调用1个函数
  • 函数是 写死的
  • 用途是 计算器、查天气、查时间 这种小工具
  • 没有"技能管理"、"技能选择"、"技能编排"

那时大家关心的只有三件事:

    1. 模型能不能输出结构化JSON
    1. 能不能 正确填参数
    1. 本地能不能 执行函数

所以大家只讲: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 可分为三类:

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

2. Function Call执行流程

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

流程说明与技术细节分析:

步骤 1:意图识别

大模型通过"指令理解 + Skill 匹配"判断是否需要调用函数。实现方式有两种:

  • 基于提示词(Prompt)的意图识别:在 Prompt 中明确告诉大模型"当用户的问题涉及数学计算时,调用 calculator 函数";
  • 基于微调的意图识别:对大模型进行微调,让它能更精准识别需要调用函数的场景,更适合专业场景。

**技术细节:**Prompt 设计需包含"Skill 列表 + 调用规则",比如:

你是一个能调用函数的智能助手,可用函数列表:

  1. 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 执行具体操作;
  • 反馈:将执行结果转换为自然语言返回给用户。

比如一个"智能出行助手"智能体:

    1. 感知:用户说"明天去上海出差,帮我规划行程";
    1. 决策:需要调用"天气 Skill"(查上海明天天气)、"机票 Skill"(查北京到上海机票)、"酒店 Skill"(查上海酒店)、"路线 Skill"(查机场到酒店路线);
    1. 执行:依次调用上述 Skill,获取各部分数据;
    1. 反馈:整合所有数据,生成完整的出行规划。

六、总结

总的来说,Skill 和 Function Call,一个是能做什么,一个是怎么去做,两者绑定在一起,才能真正让大模型突破自身局限,从只会聊天变成能干活的工具。

Function Call 就是一套调用机制,就是大模型识别到有具体任务(比如算个账、查天气),就按规矩生成指令,让外部程序去执行。而 Skill 呢,就是被调用的那个本事,是咱们提前定义好的、标准化的功能模块,比如计算器 Skill、天气 Skill,里面写清楚了能做啥、要啥参数、怎么返回结果。

以前我们聊 Function Call,很少提 Skill,是那时候场景太简单,就调用一两个函数,直接叫函数、工具就够了。但现在不一样了,大模型要做复杂任务,要管理十几个、几十个功能,还得组合使用,这时候就必须把这些功能标准化、模块化,这就是 Skill 的作用。

简单说,两者结合起来,大模型才能精准干活、高效执行,这也是咱们现在做大模型应用、搞智能体,必须吃透的核心逻辑。

相关推荐
Shining05962 小时前
AI 编译器系列(四)《AI 编译器中的后端优化》
linux·服务器·人工智能·线性代数·算法·triton·ai编译器
郑同学zxc2 小时前
机器学习18-tensorflow4.1
人工智能·机器学习
晓时谷雨2 小时前
本地 AI Agent 平台实测:以 QClaw 为例,聊聊这类工具的优势与局限
人工智能·ai agent·qclaw
databook2 小时前
从直觉到算法:贝叶斯思维的技术底层与工程实现
人工智能·python·机器学习
信道者2 小时前
爱尔兰微电网破局:欧洲数据中心在AI时代如何“自给自足”?
人工智能·物联网·电网
生活予甜2 小时前
“安、迅、智、省”四维发力,高频科技解锁半导体超纯水运维新价值
大数据·人工智能
字节跳动开源2 小时前
OpenViking x OpenClaw:开箱即用 解决 Agent 的长期记忆困局
数据库·人工智能·开源
AI猫站长2 小时前
快讯|智谱GLM-5-Turbo实测:面向OpenClaw深度优化,响应提速60%,token消耗减少17.8%
大数据·人工智能·数据挖掘·具身智能·灵心巧手