AI大模型03:Function Calling

news2024/12/23 17:56:50

接口Interface
(1)人际交互接口 UI (User Interface)
(2)应用程序编程接口 API (Application Programming Interface)

接口能通的关键:是两方都要遵守约定。
(1)人要按照UI的设计来操作。UI的设计要符合人的习惯。
(2)程序要按照API的设计来操作。API的设计要符合程序的惯例。

调接口的坑:
文档坑、大小写坑、参数顺序坑、参数类型坑。。。

接口的进化:
UI:
越来越适应人的习惯,越来越自然。
命令行–>图形界面–>语言界面–>脑机接口
API:
(1)从本地到远程,从同步到异步,,,本质都是程序猿约定好。
(2)进化到自然语言接口 NLI (Natural Language Interface)

-----------------------
OpenAI是如何用自然语言连接一切的?
大模型连接外部世界的意义

大模型的缺陷:
1.训练数据不可能什么都有。eg:非公开的数据
2.不知道最新信息。大模型的训练周期很长,且更新一次耗资巨大,还有越训练越傻的风险。所以不可能实时训练。
3.没有真正的逻辑。它表现出的逻辑、推理,是训练文本的统计规律,不是真正的逻辑,所以有幻觉。

所以,大模型需要连接真实世界,并对接真逻辑系统。

ChatGPT用Actions连接外部世界
Actions内置在GPTs中,解决了落地场景问题。

Points:
1.通过Actions的schema,GPT能读懂各个API能做什么、如何调用。
即,相当于AI就是程序员,我们把API的接口文档给到AI,那么它就知道应该如何去调用API。
2.拿到prompt,GPT分析出是否要调用API才能解决问题。
即,相当于人读需求。
3.如果要调用API,生成调用参数。
即,相当于人编写调用代码。
4.ChatGPT对话窗口(*不是GPT大模型)调用API。
即,相当于人运用程序。
5.API返回结果,GPT读懂结果,整合到回答中。
即,相当于人整理结果输出结论。

Actions对接
官方文档:https://platform.openai.com/docs/actions

Q:为什么不干脆整个描述文件都用自然语言写?非要结构化的JSON或YAML?
A:为了少歧义,减少二义性。至少外部API 、外部Actions少了很多很多的歧义。

Points:
1.GPTs 是一种让使用者能够量身打造自己的 AI 助理的工具 。你可以根据自己的需求和偏好,创建一个完全定制的 ChatGPT。

2.OpenAI GPTs、字节跳动Coze、Dify(可以本地部署,支持几乎所有大模型;eg:搞一个公司内部的知识库,首选DIfy去搭建)

------------------------
那么它可以调用外部API,这个能力怎么通过编程获得呢?这时就需要Function Calling。
Function Calling的机制
官方文档:https://platform.openai.com/docs/guides/function-calling

在这里插入图片描述

1.调用本地函数

# 初始化
from openai import OpenAI
from dotenv import load_dotenv,find_dotenv
import josn

_ = load_dotenv(find_dotenv())

client = OpenAI()

def print_json(data):
   """
   打印参数,如果参数是有结构的(如字典或列表),则以格式化的json形式打印;
   否则,直接打印该值。
   """
   if hasattr(data,'model_dump_json'):
       data = json.loads(data.model_dump_json())
   if (isinstance(data,(list))):
       for item in data:
           print_json(item)
   elif (isinstance(data,(dict))):
       print(json.dumps(
           data,
           indent = 4,
           ensure_ascii = false
           ))   
    else:
        print(data)
# ---------------------
def get_completion(messages,model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model = model,
        messages = messages,
        temperature = 0.7,
        tools = [{ # 用json描述函数。可以定义多个。由大模型决定调用谁。tools里面的是prompt。
            "type": "function",
            "function": {
                "name": "sum",
                "description": "加法器,计算一组数的和",
                "parameters": {
                    "type": "object",
                    "properties":{
                        "numbers": {
                            "type": "array",
                            "items": {
                                "type": "number"
                                }
                            }
                        }
                    }
                }
            }
        ]),
     )
     return response.choices[0].message
# ---------------------
from math import *

prompt = "Tell me the sum of 1,2,3,4,5,6,7,8,9,10."
# prompt = "桌上有2个苹果,四个桃和3本书,一共有几个水果?"
# prompt = "1+2+3...+99+100"
# prompt = "1024乘以1024是多少?" # Tools里没有定义惩罚,会怎么样?
# prompt = "太阳从那边升起?"  # 不需要算加法,会怎么样?

message = [
    {"role": "system","content": "你是一个数学家"},
    {"role": "user", "content": prompt}
]
response = get_completion(messages)

# 把大模型的恢复加入到历史对话中。必须有!!
messages.append(response)

print("====GPT 第一次回复====")
print_json(response)

# 如果返回的是函数调用结果,则打印出来
if (response.tool_calls is not None):  # 大模型判断出要调用大模型
    # 是否要调用 sum
    tool_call = response.tool_calls[0]
    if (tool_call.function.name == "sum"):
        # 通用 sum
        args = json.loads(tool_call.function.arguments)
        result = sum(args["number"])
        print("====函数返回结果====")
        print(result)
    
        # 把函数调用结果加入到对话历史中
        messages.append(
            {
                "tool_call_id": tool_call.id, # 用于标识函数调用的id
                "role": "tool",
                "name": "sum",
                "content": str(result) # 数值 result 必须转成字符串 ;所有的大模型处理的只能是纯文本,所以调用API发送的数据也必须转成纯文本才行。
            }
        )

        # 再次调用大模型
        print("====最终 GPT回复====")
        print(get_completion(messages).content)

# 第一组:运行结果展示
====GPT 第一次回复====
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_SEPcEaxpjyVwBUD4CSJoupj8",
            "function":{
                "arguments": "{\"numbers\": [1,2,3,4,5,6,7,8,9,10]}",
                "name": "sum"
            },
            "type": "function"
        }
    ]
}
====函数返回结果====
55
====最终 GPT回复====
The sum of 1,2,3,4,5,6,7,8,9 and 10 is 55.

# 第二组:运行结果展示
====GPT 第一次回复====
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_yNBR*PdqCJNEhKrUsKVsYMqc",
            "function":{
                "arguments": "{\"numbers\": [2,4]}",
                "name": "sum"
            },
            "type": "function"
        }
    ]
}
====函数返回结果====
6
====最终 GPT回复====
桌上有6个水果。

# 第三组:运行结果展示
====GPT 第一次回复====
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_xpxbUN5rKzr7LSeRVSobW2Cx",
            "function":{
                "arguments": "{\"numbers\": [1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21,22,23,24,25,26,27,28,29,30,31,32,33,34,35,36,37,38,39,40,41,42,43,44,45,46,47,48,49,50,51,52,53,54,55,56,57,58,59,60,61,62,63,64,65,66,67,68,69,70,71,72,73,74,75,76,77,78,79,80,81,82,83,84,85,86,87,88,89,90,91,92,93,94,95,96,97,98,99,100,]}",
                "name": "sum"
            },
            "type": "function"
        }
    ]
}
====函数返回结果====
5050
====最终 GPT回复====
The sum of numbers 1 to 100 is 5050.

# 第四组:运行结果展示(出幻觉了)
====GPT 第一次回复====
{
    "content": null,
    "role": "assistant",
    "function_call": null,
    "tool_calls": [
        {
            "id": "call_WfHDzfvvnWaaccgyDAmukwv3",
            "function":{
                "arguments": "{\"numbers\": [1024,1024]}",
                "name": "sum"
            },
            "type": "function"
        }
    ]
}
====函数返回结果====
2048
====最终 GPT回复====
1024乘以1024 等于 2048# 第五组:运行结果展示
====GPT 第一次回复====
{
    "content": "太阳从东方升起。",
    "role": "assistant",
    "function_call": null,
    "tool_calls": null
}

Points:
1.Function Calling 中的函数与参数的描述也是一种Prompt
2.这种Prompt也需要调优,否则会影响函数的召回、参数的准确性,甚至让GPT产生幻觉。

2.多Function调用


# 只写一下区别点,可以在tools里写多个"type": "function"
 tools = [{ # 用json描述函数。可以定义多个。由大模型决定调用谁。tools里面的是prompt。
            "type": "function",
            "function": {
                "name": "sum",
                "description": "加法器,计算一组数的和",
                "parameters": {
                    "type": "object",
                    "properties":{
                        "numbers": {
                            "type": "array",
                            "items": {
                                "type": "number"
                                }}}}}}"type": "function",
            "function": {
                "name": "sum",
                "description": "加法器,计算一组数的差",
                "parameters": {
                    "type": "object",
                    "properties":{
                        "numbers": {
                            "type": "array",
                            "items": {
                                "type": "number"
                                }}}}}}]

3.通过Function Calling 查询数据库/多表查询

官网链接:https://github.com/openai/openai-cookbook/blob/main/examples/How_to_call_functions_with_chat_models.ipynb
这部分内容:Specifying a function to execute SQL queries


import sqlite3
# 连接数据库
conn = sqlite3.connect("data/Chinook.db")
print("Opened database successfully")
#
def get_table_names(conn):
    """Return a list of table names."""
    table_names = []
    tables = conn.execute("SELECT name FROM sqlite_master WHERE type='table';")
    for table in tables.fetchall():
        table_names.append(table[0])
    return table_names


def get_column_names(conn, table_name):
    """Return a list of column names."""
    column_names = []
    columns = conn.execute(f"PRAGMA table_info('{table_name}');").fetchall()
    for col in columns:
        column_names.append(col[1])
    return column_names


def get_database_info(conn):
    """Return a list of dicts containing the table name and columns for each table in the database."""
    table_dicts = []
    for table_name in get_table_names(conn):
        columns_names = get_column_names(conn, table_name)
        table_dicts.append({"table_name": table_name, "column_names": columns_names})
    return table_dicts

# 
database_schema_dict = get_database_info(conn)
database_schema_string = "\n".join(
    [
        f"Table: {table['table_name']}\nColumns: {', '.join(table['column_names'])}"
        for table in database_schema_dict
    ]
)

# 
tools = [
    {
        "type": "function",
        "function": {
            "name": "ask_database",
            "description": "Use this function to answer user questions about music. Input should be a fully formed SQL query.",
            "parameters": {
                "type": "object",
                "properties": {
                    "query": {
                        "type": "string",
                        "description": f"""
                                SQL query extracting info to answer the user's question.
                                SQL should be written using this database schema:
                                {database_schema_string}
                                The query should be returned in plain text, not in JSON.
                                """,
                    }
                },
                "required": ["query"],
            },
        }
    }
]

# 
def ask_database(conn, query):
    """Function to query SQLite database with a provided SQL query."""
    try:
        results = str(conn.execute(query).fetchall())
    except Exception as e:
        results = f"query failed with error: {e}"
    return results

messages = [{
    "role":"user", 
    "content": "What is the name of the album with the most tracks?"
}]

response = client.chat.completions.create(
    model='gpt-4o', 
    messages=messages, 
    tools= tools, 
    tool_choice="auto"
)

# 将消息附加到消息列表
response_message = response.choices[0].message 
messages.append(response_message)

print(response_message)

# ----------返回结果 join表-------
ChatCompletionMessage(
    content=None, 
    role='assistant', 
    function_call=None, 
    tool_calls=[
        ChatCompletionMessageToolCall(
           id='call_wDN8uLjq2ofuU6rVx1k8Gw0e', 
           function=Function(
               arguments='{
                  "query":"SELECT Album.Title, COUNT(Track.TrackId) AS TrackCount FROM Album INNER JOIN Track ON Album.AlbumId = Track.AlbumId GROUP BY Album.Title ORDER BY TrackCount DESC LIMIT 1;"
                          }', 
               name='ask_database'),
               type='function')
             ]
          )

4.Stream 模式

流式(stream)输出不会一次返回完整json结构,所以需要拼接后在使用。
就是指输出最终的结果时,不是一下展示出所有的文字,是一个字一个字蹦出来的。

def get_completion (messages, model="gpt-3.5-turbo"):
    response = client.chat.completions.create(
        model=model,
        messages=messages,
        temperature=0,
        tools= [{
            "type": "function",
            "function": {
                "naile": "sum".
                "description":"计算一组数的加和",
                "parameters": [
                    "type": "object" ,
                    "properties": {
                        "numbers": {
                            "type": "array",
                            "items": {
                            "type" : "number"
                            }
                        }
                    }
                }
            }
        }],
        stream = True   # 启动流式输出return response

print("====Streaming===="# 需要把 stream 里的 token 拼起来,才能得到完整的 call
for msg in response:
    delta = msg. choices [0] .delta
    if delta.tool_calls:
        if not function_name:
            function_name = delta. tool_calls [0].function.name
            print (function_name)
        args_delta = delta.tool_calls [0].function.arguments
        print (args_delta) # 打印每次得到的数据
        args = args + args_delta
    elif delta.content:
        text_delta = delta. content
        print (text_delta)
        text = text + text_delta
print"ニニニニdone!=ニニニ"if function_name or args:
    print (function_name)
    print_json(args)
if text:
    print (text)


# 输出结果展示
====Streaming=== # 这是一个token一个token的结果
sum
{" nu
mbers
": [1,
2,
31}
====done!==== # 需要把token拼接起来
sum
{"numbers": [1, 2, 3]}

Points:
1.使用模型别名 gpt-3.5-turbo 和 gpt-4-turbo 会调用最新模型,但要防范模型升级带来的负面效果,做好充足测试
2. 函数声明是消耗token的。要在功能覆盖、省钱、节约上下文窗口之间找到最佳平衡
3. Function Calling 不仅可以调用读函数,也能调用写函数。写之前,一定要确认正确性

支持 Function Calling 的国产大模型
百度文心大模型、MiniMax、ChatGLM3-6B、讯飞星火3.0、通义千问…

Points:
1.详细拆解业务,不要幻想让大模型一下都解决所有事情。
2.不是所有任务都适用大模型来解决。
3.要评估大模型的准确率,要先测试,评估差的案例的影响。
4.大模型永远不是100%正确的,要允许有错误发生,要评估业务是否容错,容错率有多高。

------------------------
Q:未来OpenAI会不会直接把所有接口都连上,不再需要自己call了?
A:不会的,他不会自己主动调用接口,接口还是需要我们来调用的,我们只是告诉大模型有这个接口可以调用。

Q:function calling的工具集定义,一定要在每次请求中都提供吗?有集中预先定义,避免重复的说法吗?
A:是的,一定要每次都提供。没有先定义,再重复利用的说法。大模型的每次调用不会改变它本身。

Q:是否可以把所有函数先做embedding,插到向量数据库,然后在function calling时通过RAG来减少prompt中token使用,这样可行吗?
A:可行。

Q:传统AI大模型、AI多模态大模型到底有什么区别?
A:传统:只支持语言
多模态:支持图像、声音、视频等

Q:使用功能树对function进行组织,在使用结构化输入输出分段,能不能减少token的损耗?
A:能。

Q:function calling是OpenAI微调出来的功能吗?
A:是

Q:能不能定义一个外部的函数库,让大模型每次去函数库查找而不是每次发送所有函数
A:可以

Q:大模型对于中见位置的prompt的 关注力会下降,那么较多的函数声明场景下,大模型会不会出现一样的情况?
A:有可能,但未来趋势是会逐渐完善解决这个问题,强调无损压缩。

Q:能不能在微调过程中,把需要的function写到模型里,减少function calling?
A:可以

Q:可以让chatGPT帮忙写function calling的schema吗?
A:可以

学习笔记–
参考文献:知乎:AI大模型全栈工程师
如有不可公开的内容,请私聊联系,会尽快关闭~

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1991860.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

EI检索,2天录用,3天见刊!截稿在即,这本水刊你还不投吗?

点击关注:关注GZH【欧亚科睿学术】,GET完整版2023JCR分区列表! 🎉 🎉 🎉 🎉 恭喜!这本毕业水刊仅2天录用!3天见刊! 重要时间节点如下 2024-08-03 Sub…

360安全大模型为什么是“非卖品”?

大模型虽然不是万能的,但是没有大模型又是万万不能的。以AI大模型为动力引擎,AI正在重塑各行各业,并快速“飞入寻常百姓家”。 AI安全 以“模”制“模” 2024年全国两会,“人工智能”首次被写入政府工作报告。报告中提出&#xff…

Vue3 搭建前端工程,并使用idea配置项目启动

1 下载node.js 先下载 node.js LTS 并安装:node.js 的 npm,用于管理前端项目包依赖,这里以 14.17.3 这个版本为例。如果已经安装过 node.js,可以在设置中找到应用,点进去搜索 node.js 即可卸载 node.js 14.17.3 安装…

人生感悟|该如何最大化提升个人价值?

哈喽,你好啊,我是雷工! 你肯定也听到过这个观点:人际关系的本质是价值交换,社会的本质就是价值互换。 我们立足社会,无论是上班还是创业,本质上都是在互换价值。 那么我们该如何最大化提升我…

Linux IPC-管道

前言 前面我们已经对进程概念、进程控制做了介绍!本期开始将来介绍进程的最后一章进程间通信,即如何让两个进程进行通信!本博客主要介绍管道! 本期内容介绍 • 初识进程间通信 • 管道 一、初识进程间通信 1、进程间通信的概念…

基于Java+SpringBoot+Vue的教师工作量管理系统

基于JavaSpringBootVue的教师工作量管理系统 前言 ✌全网粉丝20W,csdn特邀作者、博客专家、CSDN[新星计划]导师、java领域优质创作者,博客之星、掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域和毕业项目实战✌ 🍅文末获取项目下载方式&#x1f345…

OpenAi not returning result and exited with code=0

题意:OpenAi 没有返回结果,程序以代码 0 退出 问题背景: Im trying to use OpenAI, but I cant get a result. Im accessing the API via Visual Studio Code. I have downloaded these extensions for Visual Studio Code: Code Runner and…

Bash考古以及 StackOverflow的2024年度技术统计报告

0.缘起 前段时间,有一次调试.sh时废了好大功夫,单独执行各行指令,可以,但是存储为.sh就不行了,最终发现,我漏加了文件头部的那个声明: #!/bin/bash https://wikimili.com/en/Stephen_R._Bourne…

代发考生战报:考试通过 H12-831科目

代发考生战报:考试通过 H12-831科目,同事2人分别2天考HCIP续认证,考试题基本都是题库里的,印象有1-2个题是新题,也许是自己没记准,题库更新很及时,题库看会了考试很简单,考试半个小时…

认识Modbus RTU与Modbus TCP

(选自成都纵横智控-Modbus RTU与Modbus TCP协议区别详解 ) Modbus RTU 和 Modbus TCP 是两种常用的工业通信协议,用于连接电子设备,但它们在多方面有所不同。以下是它们的详细比较: Modbus RTU 协议类型: …

【精通Redis】Redis持久化和复制

文章目录 前言一、Redis持久化1.1 RDB快照手动触发自动触发save和bgsave的区别 1.2 AOF持久化appendonly配置 二、Redis复制2.1 开启Redis主从复制2.2 Redis复制的启动过程 前言 本文主要讨论Redis的持久化方式和复制特性。Redis的持久化方式有两种,一种叫RDB&…

越秀大悦城·天悦海湾 | 繁华底色 北方头等地标

每一座骄傲的城市,都以奔涌向前的气魄,屹立时代潮头。每一处让城市仰望的居所,亦怀揣与城市共美好的磐心,以焕新的生活方式致敬理想生活家。 越秀大悦城天悦海湾,踞青岛北站旁创新创业活力区,以优越生态基…

望获实时Linux与EtherCAT的硬实时解决方案

在追求极致实时性与可靠性的工业自动化领域,望获实时Linux以其卓越的实时性能和广泛的兼容性,正逐步成为工业控制领域的核心力量。结合EtherCAT这一高效通信协议,我们共同打造了一套创新的硬实时工业控制方案,旨在满足现代工业对快…

怎样批量音频格式转换?5个方法帮你搞定

炎炎夏日的到来,大学生们也迎来了期盼已久的暑期生活。对于无论是计划外出旅行,还是宅在家中享受悠闲时光的朋友们来说,音乐总是不可或缺的伴侣。 然而,面对不同设备对音频格式的要求,如何轻松转换音频格式&#xff0…

数组下标越界异常(ArrayIndexOutOfBoundsException)以及解决方案

在Java学习的初期,我们往往可能会遇到一些程序的错误提示,告诉我们,程序出现了某些不正常的情况,在这种情况发生时,我们一般称之为出现了异常。 我们目前有两类常见的错误: 一个是编译时异常 &#xff0c…

PicGo + gitee 免费搭建个人图床

目录 1 图床概念2 使用gitee和PicGo搭建图床流程2.1 下载安装PicGo工具 3 图片上传错误处理3.1 PicGo客户端提示404错误信息图片上传失败3.2 PicGo客户端提示400错误信息图片上传失败 1 图床概念 ​ "图床"是一个网络术语,它指的是一种用于存储和托管图片…

springboot基于微信小程序的旅游攻略-计算机毕业设计源码96432

目录 摘要 1 绪论 1.1 选题背景与意义 1.2国内外研究现状 1.3论文结构与章节安排 2系统分析 2.1 可行性分析 2.1.1技术可行性 2.1.2经济可行性 2.1.3操作可行性 2.2 系统流程分析 2.2.1系统开发流程 2.2.2 用户登录流程 2.2.3 系统操作流程 2.2.4 添加信息流程 …

实用python代码之修改图片大小

前言 通过这段代码可以修改图片的像素尺寸大小 运行截图如下 代码如下: import tkinter as tk from tkinter import filedialog, messagebox, colorchooser from PIL import Image, ImageDrawclass ImageProcessorApp:def __init__(self, root):self.root rootse…

审稿速度奇慢的大佬期刊?到底值不值得投?

关注GZH【欧亚科睿学术】,第一时间了解期刊最新动态! 🔥 🔥 🔥 🔥 中科院1区SCI,各指标优秀! 今天小编给大家介绍的是一本计算机科学领域的大佬期刊《Swarm and Evolutionary…