基于千帆(ERNIE-Functions-8K)Function Calling的简单使用

1.Function Calling是什么?

1.1 概念

Function calling是一种将LLM(大语言模型Large language model)连接外部工具的能力,LLM经过微调后,可以检测出何时需要调用函数以及需要调用函数的方法名及参数,并返回给调用方以便调用外部函数,然后把调用外部函数的结果再传给LLM,最后LLM给出用户问题的最终响应。

1.2 流程图

下面是一张Function Calling工作的流程图,重点有3个:

  1. LLM会告诉我们需要调用什么工具函数以及调用工具函数的具体参数
  2. 调用函数发生在我们的系统里(一般使用反射实现),并把函数的执行情况加入到与LLM交互的历史会话中
  3. 会有多次跟LLM的交互过程
    在这里插入图片描述

2. Function Calling的作用?

序号 作用 内容 应用场景
1 扩展能力 能允许LLM与外部函数或者服务交互,极大地扩展了LLM的能力 数据库查询、数学计算、第三方API
2 实时性与动态性 可以使LLM获取实时的数据,突破LLM的训练数据静态知识的限制 股票信息、天气预报
3 任务自动化 能实现任务的自动编排 智能家具系统中灯光、温度、湿度等设备的自动调节
4 自然语言到标准化的转化 能识别出需要用到的外部函数和参数,完成自然语言到函数和参数的转换 我想知道上海的天气? -> {‘name’: ‘get_city_weather’, ‘arguments’: ‘{“city”: “上海市”}’}
5 业务逻辑集成 能让LLM的智能处理能力与企业的业务逻辑紧密结合 一家客服公司基于现有的知识沉淀来做智能客服

基于Function Calling的上述作用,能极大地推进AI在产品形态上的落地,以往我们总希望AI是无所不能的,期望能给它很少的输入,能完成很复杂的事情,但结果往往事与愿违,这样很难得到我们想要的结果。其实应该把复杂的任务拆得足够简单,每次给到AI清晰的prompt,完成单次的任务,最后所有任务完成了再给出最终的响应,这其实就是Function Call的工作机制,而LLM在其中扮演了任务拆分的重要工作。

特别是在已有系统的基础上,利用已有系统的API作为外部函数,让LLM来实现任务拆分和编排,就能很快地让AI为我们业务赋能。

3. Function Calling怎么用?

很多大模型都在微调之后支持了Function Calling,下面是基于百度千帆的ERNIE-Functions-8K的具体实现

注意:ERNIE-Functions-8K的function calling的能力只有python版本的SDK,没有其他语言的。如果python基础好的,可以直接看官方文档,不过官方文档里没有给出调用函数的过程。

3.1 单函数调用

接下来,我们用Python代码看如何实现用户的问题:“请帮我查询一下数据库中用python撰写的代码文件数量”

3.1.1 首先定义函数get_file_num

如下所示,主要就是通过指定语言获取文件数量,这个函数主要是在LLM给予我们返回参数后调用的

def get_file_num(params: dict) -> str:
    """获取数据库中指定语言的代码文件数量"""
    language_low = params.get('language', '').lower()
    language_map = {
   
   
        "c/c++": 35,
        "java": 10,
        "javascript": 25,
        "python": 35,
        "go": 32,
    }
    return str(language_map.get(language_low, 0))

3.1.2 定义函数Schema

为了让LLM知道什么时候需要调用函数,以及调用函数名及参数,我们需要定义函数的schema传给LLM

# 定义函数schema
func_list = [{
   
   
    "name": "get_file_num",  # 函数名称
    "description": "获取内部数据库中以某一编程语言编写的文件数量",  # 函数描述
    "parameters": {
   
   
        "type": "object",
        "properties": {
   
     # 参数schema,如果参数为空,设为空字典即可
            "language": {
   
     # 参数名称
                "type": "string",  # 参数类型
                "description": "代码所运用的编程语言,例如:python、c/c++、go、java"  # 参数描述
            }
        },
        "required": ["language"]  # 必填参数(无默认值)
    }
}]

3.1.3 第一次调用LLM接口

可以看到,我们把用户的问题(msgs)和函数schema(funclist)传给了LLM

    chat_comp = qianfan.Function()
    msgs = qianfan.QfMessages()
    msgs.append(query, role='user')
    resp = chat_comp.do(
        messages=msgs,
        functions=func_list
    )

上述是千帆的SDK,实际调用LLM的Prompt如下:

        {
   
   
            "role": "user",
            "content": "接下来的所有对话中,你可以使用外部的工具来回答问题。\n你必须按照规定的格式来使用工具,当你使用工具时,我会在下一轮对话给你工具调用结果,然后你应该根据实际结果判断是否需要进一步使用工具,或给出你的回答。\n工具可能有多个,每个工具由名称、描述、参数组成,参数符合标准的json schema。\n\n下面是工具列表:\n名称:get_file_num\n描述:获取内部数据库中以某一编程语言编写的文件数量\n参数:{\"type\": \"object\", \"properties\": {\"language\": {\"type\": \"string\", \"description\": \"代码所运用的编程语言,例如:python、c/c++、go、java\"}}, \"required\": [\"language\"]}\n\n\n如果你需要使用外部工具,那么你的输出必须按照如下格式,只包含2行,不需要输出任何解释或其他无关内容:\nAction: 使用的工具名称\nAction Input: 使用工具的参数,json格式\n\n如果你不需要使用外部工具,不需要输出Action和Action Input,请输出你的回答。\n你的问题:请帮我查询一下数据库中用python撰写的代码文件数量"
        }

可以看出,整体是由:千帆system_prompt + 函数schema + 用户问题

3.1.4 解析第一次调用结果

第一次调用LLM,LLM发现要解决用户的问题,需要调用函数,则会返回如下的格式:

 {
   
   
    "id": "as-09dj7y7hzz",
    "object": "chat.completion",
    "created": 1736684991,
    "result": "",
    "is_truncated": false,
    "need_clear_history": false,
    "finish_reason": "normal",
    "usage": {
   
   
        "prompt_tokens": 226,
        "completion_tokens": 17,
        "total_tokens": 243
    },
    "function_call": {
   
   
        "name": "get_file_num",
        "arguments": "{\"language\": \"python\"}"
    }
}

千帆会把需要调用的函数和参数放到function_call中返回,所以我们用如下代码获得函数及调用的参数:

func_call_result = resp['body'].get('function_call')

3.1.5 调用函数获得结果

如下所示,我们获取需要调用的函数和参数,然后使用globals()反射的方式使用"{“language”: “python”}"作为参数调用函数get_file_num函数得到结果func_resp

    if func_call_result:
        # 获取函数名称、入参
        func_name = func_call_result["name"]
        func_param = json.loads(func_call_result["arguments"])
        func_resp = None
        # 使用 globals() 动态调用函数
        if func_name in globals() and callable(globals()[func_name]):
            func_resp = globals()[func_name](func_param)
            print("函数" + func_name + "执行结果:"+
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值