如何让 LLM 使用外部函数 or 工具?Llama-3-Groq-8B-Tool-Use 模型使用详解

server/2025/1/14 18:30:27/

2024年7月份,Groq 团队在huggingface上发布了基于Meta llama3两个大小(8b和70b)的开源模型进行微调(官网介绍)的模型(Groq/Llama-3-Groq-8B-Tool-Use 和 Groq/Llama-3-Groq-70B-Tool-Use),以极大提升开源LLM在函数调用和工具使用方面的表现。在发布之时,Llama-3-Groq-70B-Tool-Use 是伯克利函数调用排行榜(BFCL)上表现最好的模型,超越了所有其他开源和专有模型。

在本篇博客中,我将较为详细地介绍通过 Ollama python 库的方式对量化 Llama-3-Groq-8B-Tool-Use 模型进行调用并发挥其基于用户提问进行意图识别并选择合适函数/工具进行调用的能力,即让大语言模型具备工具选择和使用的能力。

如果要运行后续示例代码,请确保安装了 Ollama(若OS是Ubuntu 22.04,可以参考 在Ubuntu 22.04上安装Ollama的两种方式)。

一、拉取模型

ollama pull llama3-groq-tool-use

拉取完成后,可以通过 ollama list 查看该模型,8B Q4_0 量化后大小是 4.7 GB。

llama_python_14">二、下载 Ollama python库

在指定python环境中下载 ollama python 库

pip install ollama

三、预先定义好可供LLM使用的python函数

定义两个非常基础的有关于数学运算的函数(这里我让 ChatGPT 生成了),一个是两个数的加法函数,另一个是计算数字阶乘的函数,都是数学里面比较基础的运算。

# 加法函数:计算两个数的和
def add_numbers(a, b):return a + b# 阶乘函数:计算给定数字的阶乘
def factorial(n):if n == 0 or n == 1:return 1else:result = 1for i in range(2, n + 1):result *= ireturn result

四、将定义的函数用某种特定JSON格式进行描述

类似于 OpenAI 在 llm进行函数调用时定义的规范,我们按照Groq 在 huggingface model card中的prompt示例中对tool的json描述将定义好的两个python函数进行同样规范下的描述。

Text Prompt Example:

<|start_header_id|>system<|end_header_id|>You are a function calling AI model. You are provided with function signatures within <tools></tools> XML tags. You may call one or more functions to assist with the user query. Don't make assumptions about what values to plug into functions. For each function call return a json object with function name and arguments within <tool_call></tool_call> XML tags as follows:
<tool_call>
{"name": <function-name>,"arguments": <args-dict>}
</tool_call>Here are the available tools:
<tools> {"name": "get_current_weather","description": "Get the current weather in a given location","parameters": {"properties": {"location": {"description": "The city and state, e.g. San Francisco, CA","type": "string"},"unit": {"enum": ["celsius","fahrenheit"],"type": "string"}},"required": ["location"],"type": "object"}
} </tools><|eot_id|><|start_header_id|>user<|end_header_id|>What is the weather like in San Francisco?<|eot_id|><|start_header_id|>assistant<|end_header_id|><tool_call>
{"id":"call_deok","name":"get_current_weather","arguments":{"location":"San Francisco","unit":"celsius"}}
</tool_call><|eot_id|><|start_header_id|>tool<|end_header_id|><tool_response>
{"id":"call_deok","result":{"temperature":"72","unit":"celsius"}}
</tool_response><|eot_id|><|start_header_id|>assistant<|end_header_id|>

加法函数

通过 one-shot 让 ChatGPT 生成

{"name": "add_numbers","description": "Calculate the sum of two numbers","parameters": {"properties": {"a": {"description": "The first number to add","type": "number"},"b": {"description": "The second number to add","type": "number"}},"required": ["a","b"],"type": "object"}
}

阶乘函数

通过 one-shot 让 ChatGPT 生成

{"name": "factorial","description": "Calculate the factorial of a given number","parameters": {"properties": {"n": {"description": "The number for which the factorial is calculated","type": "integer"}},"required": ["n"],"type": "object"}
}

llama_143">五、Ollama调用模型

将上一步骤准备的json格式的函数描述放入输入提示词的…标签中。

from ollama import Clientollama_client = Client(host="http://localhost:11434")
model_name = "llama3-groq-tool-use"user_query = "请问数字12的阶乘是多少?"prompt = """
<tools> 
{"name": "add_numbers","description": "Calculate the sum of two numbers","parameters": {"properties": {"a": {"description": "The first number to add","type": "number"},"b": {"description": "The second number to add","type": "number"}},"required": ["a","b"],"type": "object"}
}{"name": "factorial","description": "Calculate the factorial of a given number","parameters": {"properties": {"n": {"description": "The number for which the factorial is calculated","type": "integer"}},"required": ["n"],"type": "object"}
}
</tools>
""" + "\n" + user_query

进行模型推理(zero-shot)

response = ollama_client.chat(model=model_name, messages=[{'role': 'user','content': prompt,}], options = {"temperature": 0.1})

将推理结果打印出来

output = response['message']['content']print(output)

打印出来的output内容如下,可以注意到其实生成的并不完全符合我们预期。首先</tool_call>单边标签缺失,而且llm还对该问题进行了回答。

<tool_call>
{"name": "factorial","parameters": {"n": 12}
}
<p>12! = 479001600</p>

六、调整推理方式为one-shot

为了让模型生成符合我们期望的<tool_call>…</tool_call>内容,我们对 ollama_client.chat() 函数中的 messages 列表参数做稍许调整,通过提供一个输出示例的方式(one-shot)来进行模型推理。

user_query_example = """
<tools> 
{"name": "add_numbers","description": "Calculate the sum of two numbers","parameters": {"properties": {"a": {"description": "The first number to add","type": "number"},"b": {"description": "The second number to add","type": "number"}},"required": ["a","b"],"type": "object"}
}{"name": "factorial","description": "Calculate the factorial of a given number","parameters": {"properties": {"n": {"description": "The number for which the factorial is calculated","type": "integer"}},"required": ["n"],"type": "object"}
}
</tools>What is the result of 3.121321 plus 9.7832198391321?
"""response_example = """<tool_call>
{"name":"add_numbers","parameters":{"a":3.121321, "b": 9.7832198391321}}
</tool_call>"""user_query = "请问23和78的和是多少?"response = ollama_client.chat(model=model_name, messages=[{'role': 'user','content': user_query_example,}, {'role': 'assistant','content': response_example}, {'role': 'user','content': user_query}], options = {"temperature": 0.1})

推理完成后打印出模型生成的内容

output = response['message']['content']
print(output)

可以看到通过提供一轮问答输入输出的one-shot方式,就可以得到完全符合我们预期的输出结果。

<tool_call>
{"name":"add_numbers","parameters":{"a":23, "b":78}}
</tool_call>

七、调用被选择的函数/工具

这里主要包含两个具体步骤,第一是将<tool_call> 标签内的JSON内容提取出来;第二是通过提取 JSON 数据中的 name 字段来判断应该调用哪个函数,并将 parameters 字段中的参数传递给对应的函数。

实现代码如下

import json
import rejson_str = re.search(r'<tool_call>\n(.*?)\n</tool_call>', output).group(1)# 解析 JSON 字符串为字典
data = json.loads(json_str)function_name = data['name']  # 'add_numbers'
parameters = data['parameters']  # {'a': 23, 'b': 78}# 动态调用函数
if function_name in globals():result = globals()[function_name](**parameters)

result 变量存储的即为调用函数之后得到的返回结果。

八、将调用函数/工具得到的结果传入模型,进行第二次推理

如果用户提问只涉及到数学运算,我们确实可以直接把上一个步骤得到的 result 值作为回答提供给用户,但往往我们还是更希望大语言模型将调用函数/工具得到的结果作为知识来源,以一种更为自然友好的对话方式将结果提供给用户。这样的话我们就需要进行二次推理,以生成最终用于回复用户的内容。

response_2 = ollama_client.chat(model=model_name, messages=[{'role': 'user','content': user_query}, # role为tool的content(调用函数/工具拿到的返回值)提供要放在user_query之后{'role': 'tool','content': call_tool_result}], options = {"temperature": 0.1})final_answer = response_2['message']['content']

打印出最终回复

print(final_answer)
The sum of 23 and 79.898 is 102.898.

问答示例

示例一

user_query = "请问数字12的阶乘是多少?"
print(final_answer)
The factorial of 12 is 479001600.

示例二

user_query = "请问89.893289103加上231.321321等于多少?"
print(final_answer)
The sum of 89.893289103 and 231.321321 is approximately 321.214610103.

完整代码

from ollama import Client
import json
import reollama_client = Client(host="http://localhost:11434")
model_name = "llama3-groq-tool-use"# 加法函数:计算两个数的和
def add_numbers(a, b):return a + b# 阶乘函数:计算给定数字的阶乘
def factorial(n):if n == 0 or n == 1:return 1else:result = 1for i in range(2, n + 1):result *= ireturn resultdef call_tool(tool_call_str):json_str = re.search(r'<tool_call>\n(.*?)\n</tool_call>', output).group(1)# 解析 JSON 字符串为字典data = json.loads(json_str)function_name = data['name']  # 'add_numbers'parameters = data['parameters']  # {'a': 23, 'b': 78}# 动态调用函数if function_name in globals():result = globals()[function_name](**parameters)return str(result)user_query_example = """
<tools> 
{"name": "add_numbers","description": "Calculate the sum of two numbers","parameters": {"properties": {"a": {"description": "The first number to add","type": "number"},"b": {"description": "The second number to add","type": "number"}},"required": ["a","b"],"type": "object"}
}{"name": "factorial","description": "Calculate the factorial of a given number","parameters": {"properties": {"n": {"description": "The number for which the factorial is calculated","type": "integer"}},"required": ["n"],"type": "object"}
}
</tools>What is the result of 3.121321 plus 9.7832198391321?
"""response_example = """<tool_call>
{"name":"add_numbers","parameters":{"a":3.121321, "b": 9.7832198391321}}
</tool_call>"""user_query = "请问数字12的阶乘是多少?"response = ollama_client.chat(model=model_name, messages=[{'role': 'user','content': user_query_example,}, {'role': 'assistant','content': response_example}, {'role': 'user','content': user_query}], options = {"temperature": 0.1})output = response['message']['content']
call_tool_result = call_tool(output)response_2 = ollama_client.chat(model=model_name, messages=[{'role': 'user','content': user_query}, {'role': 'tool','content': call_tool_result}], options = {"temperature": 0.1})final_answer = response_2['message']['content']
print(final_answer)

最后

本篇博客的重点放在了让LLM选择、调用函数/工具并基于返回值进行回答的流程搭建上,所以选择了两个数学运算的函数作为例子(实际上很多较新的大语言模型已经具备了内部调用相关函数进行准确计算的能力),所以函数的选择在实际的使用场景中并不具备太多参考价值,大家可以参考搭建流程根据具体需要去尝试更多有趣实用的工具,让LLM的能力更进一步!


http://www.ppmy.cn/server/158350.html

相关文章

Chromium 132 编译指南 Windows 篇 - 配置核心环境变量 (三)

1. 引言 在之前的 Chromium 编译指南系列文章中&#xff0c;我们已经完成了编译前的准备工作以及 depot_tools 工具的安装与配置。本篇我们将聚焦于 Chromium 编译过程中至关重要的环境变量设置&#xff0c;这些配置是您顺利进行 Chromium 构建的基石。 2. 启用本地编译&…

Jupyter notebook入门教程

一、优点&#xff1a; 1、代码分成小块逐块运行&#xff0c;方便查看中间结果&#xff0c;调试和修改 2、文档和代码结合&#xff0c;比普通的注释好看&#xff0c;使代码的可读性大大提高 3、可以生成多种格式的报告&#xff0c;适合演示使用 二、如何打开 命令行下载jupy…

如何更轻松的对React refs 的理解?都有哪些应用场景?

React refs 的理解与应用 refs 是 React 提供的一种机制&#xff0c;用于直接访问 DOM 元素或 React 组件实例。在 React 中&#xff0c;refs 主要用于获取对 DOM 元素的引用&#xff0c;或访问类组件中的实例方法。在许多情况下&#xff0c;refs 是避免使用传统的 JavaScript…

后端:Spring(IOC、AOP)

文章目录 1. Spring2. IOC 控制反转2-1. 通过配置文件定义Bean2-1-1. 通过set方法来注入Bean2-1-2. 通过构造方法来注入Bean2-1-3. 自动装配2-1-4. 集合注入2-1-5. 数据源对象管理(第三方Bean)2-1-6. 在xml配置文件中加载properties文件的数据(context命名空间)2-1-7. 加载容器…

基于单片机的指纹密码锁

【摘要】 本设计是一款基于单片机的指纹识别电子密码锁系统。该系统以STC89C52单片机作为模块核心同时结合ZFM-60指纹模块实现录取指纹并存储指纹数据的功能&#xff0c;并且通过HS12864-15C液晶显示比对流程及比对结果&#xff0c;该指纹电子密码锁通过直流继电器与发光二极管…

小米vela系统(基于开源nuttx内核)——如何使用信号量进行PV操作

如何使用信号量进行PV操作 前言信号量1. 信号量简介2. NuttX中信号量的创建与使用2.1 Nuttx信号量的初始化和销毁2.2 信号量的等待和发布 3. 信号量的实际应用&#xff1a;下载任务示例3.1 实际代码3.2 代码说明3.3 执行说明 4. 信号量的优势与应用场景5. 常见应用场景&#xf…

PyTorch 张量的分块处理介绍

分块处理是将大型张量分解成较小的块&#xff0c;以便更高效地进行计算&#xff0c;减少内存占用&#xff0c;特别适用于处理超大张量的场景&#xff08;如深度学习中的大批量数据或大型模型训练&#xff09;。 PyTorch 提供了多种方法来分块张量&#xff0c;包括 chunk、spli…

c++ haru生成pdf输出文本实例

haru是一个开源的生成pdf的库&#xff0c;花时间终于编译成功&#xff0c;以下是一个特别简单的写文本的实例&#xff1a; #include "hpdf.h" void CDemoDlg::OnBnClickedOk() { HPDF_Error_Handler error_handler NULL; HPDF_Doc pdf; pdf HPDF_New(…