function-calling初体验

32 篇文章 5 订阅

课程地址:https://learn.deeplearning.ai/courses/function-calling-and-data-extraction-with-llms/lesson/1/introduction
github notebook地址:https://github.com/kingglory/LLMs-function-calling/tree/main


Function-Calling 介绍

函数调用(Function-Calling)允许使用自定义功能扩展LLM,使它们能够根据自然语言指令形成对外部函数的调用。结构化数据提取使LLM能够从非结构化文本中提取可用信息。
在这里插入图片描述
Function-calling is the capability of an LLM to take in a natural language query, along with a description of the function,and output a string that can be used to call that function.
Function-calling是LLM接收自然语言查询以及函数描述,并输出可用于调用该函数的字符串的能力。
在这里插入图片描述
在这里插入图片描述

先使用NexusRavenV2-13B 大模型,一个针对函数调用(function-calling)和数据提取进行了微调的开源模型。该模型可在Hugging Face上使用,在一些函数调用任务中表现优于GPT-4,并且具有130亿个参数,因此可以在本地托管。

写一个python tool

from matplotlib import pyplot as plt

def plot_some_points(x : list, y : list):
  """
  Plots some points!
  """
  plt.plot(x, y)
  plt.show()
USER_QUERY = "Hey can you plot y=10x where x=1, 2, 3 for me?"

plot_some_points(x=[1, 2, 3], y=[10, 20, 30])

在这里插入图片描述

想让大模型帮我们做上面的事,首先要给出一个tool 描述(description)和user query 给LLM。

  • 你首先要提供前面使用的函数的原型。
  • 你将要使用的LLM使用python格式的函数。
  • 你还将添加一些关于工具功能的描述。LLM会根据描述来判断是否应该使用该工具来回答用户查询的推理。
  • 你还需要提供LLM 用户的输入(user query)
prompt = \
f'''
Function:
def plot_some_points(x : list, y : list):
  """
  Plots some points!
  """
  plt.plot(x, y)
  plt.show()

User Query: {USER_QUERY}<human_end>
'''
# 这个大模型是训练过的,专门针对function-calling ,这里是接口调用,返回function-calling字符串
# def raven_post(payload):
#     """
#     Sends a payload to a TGI endpoint.
#     """
#     # Now, let's prompt Raven!
#     API_URL = "http://nexusraven.nexusflow.ai"
#     headers = {
#             "Content-Type": "application/json"
#     }
#     import requests
#     response = requests.post(API_URL, headers=headers, json=payload)
#     return response.json()

# def query_raven(prompt):
# 	"""
# 	This function sends a request to the TGI endpoint to get Raven's function call.
# 	This will not generate Raven's justification and reasoning for the call, to save on latency.
# 	"""
# 	import requests
# 	output = raven_post({
# 		"inputs": prompt,
# 		"parameters" : {"temperature" : 0.001, "stop" : ["<bot_end>"], "return_full_text" : False, "do_sample" : False, "max_new_tokens" : 2048}})
# 	call = output[0]["generated_text"].replace("Call:", "").strip()
# 	return call
from utils import query_raven
function_call = query_raven(prompt)

LLM function-calling 的结果是一个字符串: 函数名被调用的状态,并包含输入参数,然后exec 执行一下就会执行这个函数(tool)

print (function_call)
plot_some_points(x=[1, 2, 3], y=[10, 20, 30])

exec 执行储存在字符串或文件中的 Python 语句,相比于 eval,exec 可以执行更复杂的 Python 代码。https://www.runoob.com/python3/python3-func-exec.html

exec 的语法: exec(object[, globals[, locals]])

  • object:必选参数,表示需要被指定的 Python 代码。它必须是字符串或 code 对象。如果 object 是一个字符串,该字符串会先被解析为一组 Python 语句,然后再执行(除非发生语法错误)。如果 object 是一个 code 对象,那么它只是被简单的执行。
  • globals:可选参数,表示全局命名空间(存放全局变量),如果被提供,则必须是一个字典对象。
  • locals:可选参数,表示当前局部命名空间(存放局部变量),如果被提供,可以是任何映射对象。如果该参数被忽略,那么它将会取与 globals 相同的值。

exec 返回值永远为 None。

>>>exec('print("Hello World")')
Hello World
# 单行语句字符串
>>> exec("print ('runoob.com')")
runoob.com
 
#  多行语句字符串
>>> exec ("""for i in range(5):
...     print ("iter time: %d" % i)
... """)
iter time: 0
iter time: 1
iter time: 2
iter time: 3
iter time: 4
exec(function_call)

在这里插入图片描述

再试一个
USER_QUERY = "帮我画个y=x**3, 其中 x = 1,2,3, 4, 5"
prompt = \
f'''
Function:
def plot_some_points(x : list, y : list):
  """
  Plots some points!
  """
  plt.plot(x, y)
  plt.show()

User Query: {USER_QUERY}<human_end>
'''
from utils import query_raven
function_call = query_raven(prompt)
exec(function_call)

在这里插入图片描述
在这里插入图片描述

试一个复杂的例子

定义一个函数
#  提供 脸的颜色, 眼睛的颜色, 鼻子的颜色 画一个笑脸
import matplotlib.pyplot as plt
import matplotlib.patches as patches

def draw_clown_face(face_color='yellow', eye_color='black', 
                    nose_color='red'):
    """
    Draws a customizable, simplified clown face using matplotlib.

    Parameters:
    - face_color (str): Color of the clown's face. Default is 'yellow'.
    - eye_color (str): Color of the clown's eyes. Default is 'black'.
    - nose_color (str): Color of the clown's nose. Default is 'red'.

    This function creates a plot displaying a simplified clown face, where essential facial features' size, position, and color can be customized. 
    """
    # Constants
    face_radius = 0.4
    nose_radius = 0.1
    nose_x, nose_y = 0.5, 0.5
    mouth_x, mouth_y = 0.5, 0.3
    mouth_color = 'black'
    eye_size = 0.05
    mouth_size = (0.3, 0.1)
    eye_offset=(0.15, 0.15)
    mouth_theta = (200, 340)

    fig, ax = plt.subplots()
    # Face
    face = patches.Circle((0.5, 0.5), face_radius, color=face_color, fill=True)
    ax.add_patch(face)
    # Eyes
    eye_left = patches.Circle((0.5-eye_offset[0], 0.5+eye_offset[1]), eye_size, color=eye_color, fill=True)
    eye_right = patches.Circle((0.5+eye_offset[0], 0.5+eye_offset[1]), eye_size, color=eye_color, fill=True)
    ax.add_patch(eye_left)
    ax.add_patch(eye_right)
    # Nose
    nose = patches.Circle((nose_x, nose_y), nose_radius, color=nose_color, fill=True)
    ax.add_patch(nose)
    # Mouth
    mouth = patches.Arc((mouth_x, mouth_y), mouth_size[0], mouth_size[1], angle=0, 
                        theta1=mouth_theta[0], theta2=mouth_theta[1], color=mouth_color, linewidth=2)
    ax.add_patch(mouth)
    # Setting aspect ratio to 'equal' to ensure the face is circular
    ax.set_aspect('equal')
    # Remove axes
    ax.axis('off')
    plt.show()
定义一个Prompt
USER_QUERY = \
"画一个白色的小丑脸和红色的鼻子吗" 

raven_prompt = \
'''
Function:
def draw_clown_face(face_color='yellow', 
                    eye_color='black',
                    nose_color='red'):
    """
    Draws a customizable, simplified clown face using matplotlib.

    Parameters:
    - face_color (str): Color of the clown's face.
    - eye_color (str): Color of the clown's eyes.
    - nose_color (str): Color of the clown's nose.
    """

User Query: {query}<human_end>
'''
raven_prompt_with_query = raven_prompt.format(query=USER_QUERY)

print (raven_prompt_with_query)
Function:
def draw_clown_face(face_color='yellow', 
                    eye_color='black',
                    nose_color='red'):
    """
    Draws a customizable, simplified clown face using matplotlib.

    Parameters:
    - face_color (str): Color of the clown's face.
    - eye_color (str): Color of the clown's eyes.
    - nose_color (str): Color of the clown's nose.
    """

User Query: 画一个白色的小丑脸和红色的鼻子吗<human_end>
from utils import query_raven
raven_call = query_raven(raven_prompt_with_query)
print (raven_call)
draw_clown_face(face_color='white', nose_color='red')
Run The Call
exec(raven_call)

在这里插入图片描述

再画一个笑脸
USER_QUERY = "画一个小丑的笑脸,眼睛是白色的,脸是黑色的,嘴巴是粉色的,鼻子是大红色,耳朵就跟米老鼠一样的"
raven_prompt_with_query = raven_prompt.format(query=USER_QUERY)

from utils import query_raven
raven_call = query_raven(raven_prompt_with_query)
print (raven_call)
exec(raven_call)
draw_clown_face(face_color='black', eye_color='white', nose_color='red')

在这里插入图片描述

试一下 chatgpt的function-calling

import json
from openai import OpenAI
from dotenv import load_dotenv
import os

_ = load_dotenv()

def query_openai(msg, functions=None):
  load_dotenv()
  GPT_MODEL = "gpt-3.5-turbo"

  openai_client = OpenAI(api_key=os.environ["OPENAI_API_KEY"])
  openai_response = openai_client.chat.completions.create(
    model = GPT_MODEL,
    messages = [{'role': 'user', 'content': msg}],
    tools = functions)
  return openai_response

需要以json格式提供tool的描述(description)和参数(arguments)

openai_function = {
  "type": "function",
  "function": {
    "name": "draw_clown_face",
    "description": "Draws a customizable, simplified clown face using matplotlib.",
    "parameters": {
      "type": "object",
      "properties": {
        "face_color": {
          "type": "string",
          "description": "Color of the clown's face."
        },
        "eye_color": {
          "type": "string",
          "description": "Color of the clown's eyes."
        },
        "nose_color": {
          "type": "string",
          "description": "Color of the clown's nose."
        }
        }
      }
    }
  }

openai_msg = \
"Hey can you draw a pink clown face with a red nose"
result = query_openai(openai_msg, functions=[openai_function])

print (result.choices[0].message.tool_calls[0].function)
Function(arguments='{"face_color": "pink", "eye_color": "black", "nose_color": "red"}', name='draw_clown_face')

注意!返回的结果跟之前不一样,这样的字符串是不能直接用exec执行的,所以需要一个归一化处理

tool_name = result.choices[0].message.tool_calls[0].function.name
tool_args = result.choices[0].message.tool_calls[0].function.arguments

function_call = f"{tool_name}(**{tool_args})"

print (function_call)
draw_clown_face(**{"face_color": "pink", "eye_color": "black", "nose_color": "red"})
exec(function_call)

在这里插入图片描述

使用训练过的LLM在非结构化数据和高度结构化的code之间搭建了桥梁!!

在这里插入图片描述


辅助函数:utils.py

import inspect
def raven_post(payload):
    """
    Sends a payload to a TGI endpoint.
    """
    # Now, let's prompt Raven!
    API_URL = "http://nexusraven.nexusflow.ai"
    headers = {
            "Content-Type": "application/json"
    }
    import requests
    response = requests.post(API_URL, headers=headers, json=payload)
    return response.json()

def call_functioncalling_llm(prompt, api_to_call):
    """
    This function sends a request to the TGI endpoint to get Raven's function call.
    This will not generate Raven's justification and reasoning for the call, to save on latency.
    """
    signature = inspect.signature(api_to_call)
    docstring = api_to_call.__doc__
    prompt = f'''Function:\n{api_to_call.__name__}{signature}\n"""{clean_docstring(docstring)}"""\n\n\nUser Query:{prompt}<human_end>'''
    import requests
    output = raven_post({
        "inputs": prompt,
        "parameters" : {"temperature" : 0.001, "stop" : ["<bot_end>"], "do_sample" : False, "max_new_tokens" : 2048, "return_full_text": False}})
    call = output[0]["generated_text"].replace("Call:", "").strip()
    return call

def query_raven(prompt):
	"""
	This function sends a request to the TGI endpoint to get Raven's function call.
	This will not generate Raven's justification and reasoning for the call, to save on latency.
	"""
	import requests
	output = raven_post({
		"inputs": prompt,
		"parameters" : {"temperature" : 0.001, "stop" : ["<bot_end>"], "return_full_text" : False, "do_sample" : False, "max_new_tokens" : 2048}})
	call = output[0]["generated_text"].replace("Call:", "").strip()
	return call

def clean_docstring(docstring):
    if docstring is not None:
        # Remove leading and trailing whitespace
        docstring = docstring.strip()
    return docstring

def build_raven_prompt(function_list, user_query):
    import inspect
    raven_prompt = ""
    for function in function_list:
        signature = inspect.signature(function)
        docstring = function.__doc__
        prompt = \
f'''
Function:
def {function.__name__}{signature}
    """
    {clean_docstring(docstring)}
    """
    
'''
        raven_prompt += prompt
        
    raven_prompt += f"User Query: {user_query}<human_end>"
    return raven_prompt

  • 24
    点赞
  • 16
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值