当前位置: 首页 > news >正文

AI - 如何构建一个大模型中的Tool

AI - 如何构建一个大模型中的Tool

大家好!今天我们聊聊一个有趣的技术问题:什么是工具(Tool),如何使用聊天模型调用工具,以及如何将工具的输出传递给聊天模型。我们还是基于LangChain来进行讨论,希望大家能更好地理解这个概念,并能够在自己的项目中应用它们。
ai-langchain

什么是LangChain?

再温习一下,LangChain是一个框架,旨在帮助开发者用大语言模型(例如GPT-4)构建智能应用。通过LangChain,你可以将不同的数据源、模型和工具整合到一起,从而构建出复杂、功能强大的应用程序。

什么是工具?

在自然语言处理和大语言模型的领域,"工具"实际上指的就是一些可以执行特定任务的外部函数、API或者服务。比如说,你可以有一个工具来查询天气,一个工具来翻译文本,或者一个工具来进行复杂的数学计算。

简单来说,工具就是那些帮助我们扩展大语言模型功能的小插件。

举个例子,我们可以有一个计算两个数字之和的工具:

def add_numbers(a, b):return a + b

这个add_numbers函数就是一个简单的工具。

如何使用聊天模型调用工具?

在现代的自然语言处理系统中,我们希望大语言模型(例如GPT-3)能够智能地调用这些工具来获取所需的信息。那么我们如何实现这一点呢?我们可以使用LangChain,一个强大的框架,它可以帮助我们整合这些工具。

举一个例子,现在我们想要让LLM计算一个算术表达式的值,用户输入的问题是"What is 3 * 12? Also, what is 11 + 49?",我们该怎么定义工具来实现呢?

示例代码

啥也不说了,直接上代码,以下是完整的代码实现,其中用到了ChatGroq这个LLM。

备注:对于本文中的代码片段,主体来源于LangChain官网,有兴趣的读者可以去官网查看。

import os
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_groq import ChatGroq
from langchain_core.output_parsers import PydanticToolsParser# 设置环境变量以配置不同的API密钥和LangChain相关配置
os.environ["GROQ_API_KEY"] = '************'# 定义一个工具函数,用于将两个整数相加。函数名、类型提示和文档字符串
# 都构成了工具的schema,这部分信息会被传递给模型,帮助其更好地理解工具功能。
def add(a: int, b: int) -> int:"""Add two integers.Args:a: First integerb: Second integer"""return a + b# 定义第二个工具函数,用于将两个整数相乘
def multiply(a: int, b: int) -> int:"""Multiply two integers.Args:a: First integerb: Second integer"""return a * b# 将上述两个函数添加到工具列表中
tools = [add, multiply]# 定义查询,这里包含了两个计算任务,一个乘法和一个加法
query = "What is 3 * 12? Also, what is 11 + 49?"# 初始化ChatGroq模型,这里使用的模型是"llama3-8b-8192"
llm = ChatGroq(model="llama3-8b-8192")# 绑定工具到语言模型中,使其能够调用这些工具
llm_with_tools = llm.bind_tools(tools)# 调用绑定了工具的模型,执行查询
output = llm_with_tools.invoke(query)
print(output)  # 打印语言模型返回的初始输出# 创建一个链式调用,将语言模型输出通过Pydantic工具解析器进行进一步处理
chain = llm_with_tools | PydanticToolsParser(tools=[add, multiply])# 执行链式调用,得到最终结果
result = chain.invoke(query)
print(result)  # 打印最终处理后的结果

最后的输出结果为:

[36, 60]

代码详细说明

  1. 导入必要的模块和库:首先,我们导入了一些用于配置环境变量、消息处理、调用模型和解析输出的库。
  2. 设置环境变量:通过os.environ设置了一些配置和 API 密钥,确保我们可以正确调用所需的服务。
  3. 定义工具函数:我们定义了两个工具函数addmultiply,分别用于加法和乘法运算。每个工具函数都有详细的类型提示和文档字符串,帮助模型更好地理解它们的功能。
  4. 创建工具列表:将定义好的工具函数添加到工具列表tools中,可以方便地进行批量操作和调用。
  5. 定义查询:这里的查询包含了两个计算任务,一个是3 * 12,另一个是11 + 49
  6. 初始化语言模型:使用ChatGroq初始化语言模型,并指定模型类型为llama3-8b-8192
  7. 绑定工具到模型:通过bind_tools方法将工具绑定到语言模型中,使模型能够在回答问题时调用这些工具来辅助完成任务。
  8. 调用绑定了工具的模型:使用invoke方法执行查询,获取模型的初始输出并打印。
  9. 创建链式调用:通过PydanticToolsParser进一步解析模型输出,使得工具的输出能够被正确处理和呈现。
  10. 执行链式调用:使用invoke方法,执行链式调用得到最终结果,并打印出经过解析器处理后的结果。

如何将工具的输出传递给聊天模型?

当工具执行完任务并返回结果时,上面的例子只是简单的输出了答案,并不是一个完整的回答,这并不是我们期望的。我们需要将这些结果传递回聊天模型,以便生成最终的回复。让我们继续看另外一份示例代码。

示例代码

import os
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_groq import ChatGroq
from langchain_core.output_parsers import PydanticToolsParser
from langchain_core.tools import tool# 设置环境变量以配置不同的API密钥和LangChain相关配置
os.environ["GROQ_API_KEY"] = '********'# 使用@tool装饰器定义一个工具函数,用于将两个整数相加
@tool
def add(a: int, b: int) -> int:"""Adds a and b."""return a + b# 使用@tool装饰器定义另外一个工具函数,用于将两个整数相乘
@tool
def multiply(a: int, b: int) -> int:"""Multiplies a and b."""return a * b# 将所有定义好的工具函数添加到工具列表中
tools = [add, multiply]# 定义一个查询,包含了两个计算任务
query = "What is 3 * 12? Also, what is 11 + 49?"
# 创建一个消息列表,其中包含了人的消息
messages = [HumanMessage(query)]# 初始化ChatGroq模型,这里使用的模型是"llama3-8b-8192"
llm = ChatGroq(model="llama3-8b-8192")# 绑定工具到语言模型中,使其能够调用这些工具
llm_with_tools = llm.bind_tools(tools)# 使用绑定了工具的模型处理消息,生成AI回复
ai_msg = llm_with_tools.invoke(messages)# 将AI生成的回复消息添加到消息列表中
messages.append(ai_msg)# 遍历AI回复中的工具调用部分
for tool_call in ai_msg.tool_calls:# 根据工具调用的名称选择相应的工具selected_tool = {"add": add, "multiply": multiply}[tool_call["name"].lower()]# 调用选定的工具并获取工具的回复消息tool_msg = selected_tool.invoke(tool_call)# 将工具的回复消息添加到消息列表中messages.append(tool_msg)# 再次调用绑定了工具的模型,处理新的消息列表,生成最终输出
output = llm_with_tools.invoke(messages)# 打印出最终的内容输出
print(output.content)

最后的输出结果如下,符合期望。

36 * 12 = 432. 11 + 49 = 60. The answer to both questions is: 432 and 60.

代码详细说明

  1. 导入必要的模块和库:首先,我们导入了一些用于环境配置、消息处理、调用模型和工具定义的库。

  2. 设置环境变量:通过os.environ设置了一些配置和 API 密钥,确保我们可以正确调用所需的服务。

  3. 定义工具函数

    • 使用@tool装饰器定义两个工具函数:add用于加法运算,multiply用于乘法运算。
    • 这些函数的目标是提供可复用的逻辑,供语言模型在处理查询时调用。
  4. 创建工具列表:将定义好的工具函数添加到工具列表tools中,以方便进行批量操作和调用。

  5. 定义查询和消息

    • 定义一个查询,包含了加法和乘法两个计算任务。
    • 使用HumanMessage创建一个初始消息,并将其添加到消息列表messages中。
  6. 初始化语言模型:使用ChatGroq初始化语言模型,并指定模型类型为llama3-8b-8192

  7. 绑定工具到模型:通过bind_tools方法,将工具绑定到语言模型中,使模型能够在回答问题时调用这些工具来辅助完成任务。

  8. 处理初始消息:使用绑定了工具的模型调用invoke方法处理初始消息,生成AI回复并添加到消息列表中。

  9. 处理工具调用

    • 遍历AI回复中的工具调用部分,识别需要调用的工具名称。
    • 根据工具调用的名称选择相应的工具,并调用该工具。
    • 将工具的回复消息添加到消息列表中。
  10. 生成最终输出:第二次调用绑定了工具的模型,处理更新后的消息列表,生成最终的内容输出并打印。

其他案例

如果只是计算一个加减乘除,那么对于工具来说,现实意义不大,现在我们来看一个查询实时天气的案例,你可以询问某一个城市的天气。

import os
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_groq import ChatGroq  # 导入聊天模型
from langchain_core.tools import tool  # 导入工具装饰器
import requests  # 导入requests库,用于进行HTTP请求# 设置环境变量以配置不同的API密钥和LangChain相关配置
os.environ["GROQ_API_KEY"] = '**************'
os.environ["WEATHER_API_KEY"] = '************'# 使用@tool装饰器定义一个工具函数,用于查询某个城市的天气
@tool
def get_weather(city: str) -> str:"""Get the weather of a city.Args:city: the city to query weather"""api_key = os.environ["WEATHER_API_KEY"]  # 获取API密钥url = f"http://api.weatherapi.com/v1/current.json?key={api_key}&q={city}"  # 构建API请求URLresponse = requests.get(url)  # 发送HTTP GET请求return response.json()  # 返回API响应的JSON数据# 将定义好的工具函数添加到工具列表中
tools = [get_weather]# 定义一个查询,用于获取上海市的天气
query = "What is weather of Shanghai?"# 初始化ChatGroq模型,这里使用的模型是"llama3-8b-8192"
llm = ChatGroq(model="llama3-8b-8192")# 绑定工具到语言模型中,使其能够调用这些工具
llm_with_tools = llm.bind_tools(tools)# 创建一个消息列表,其中包含了人的消息
messages = [HumanMessage(query)]# 使用绑定了工具的模型处理消息,生成AI回复
ai_msg = llm_with_tools.invoke(messages)# 将AI生成的回复消息添加到消息列表中
messages.append(ai_msg)# 调用get_weather工具,并将查询结果添加到消息列表中
tool_msg = get_weather.invoke(ai_msg.tool_calls[0])
messages.append(tool_msg)# 再次调用绑定了工具的模型,处理更新后的消息列表,生成最终输出
output = llm_with_tools.invoke(messages)# 打印出最终的内容输出
print(output.content)

你可以问:

"What is the weather of Shanghai?"

回答是:

The weather in Shanghai is currently Clear with a temperature of 10.1°C (50.2°F) and a wind speed of 7.6 km/h (4.7 mph) from the Southeast. The humidity is at 62% and the atmospheric pressure is at 1018.0 millibars (30.06 inches).

与LLM的交互过程描述如下:
llm-tool-weather

详细描述:

  1. 用户发送查询: 用户向ChatGroq模型发送查询,内容为“上海的天气是什么?”
  2. ChatGroq模型处理查询: ChatGroq模型接收到用户的查询后,识别出该查询需要调用工具get_weather
  3. 调用工具get_weather: ChatGroq模型调用绑定的工具get_weather,传递参数“上海”。注意:这里其实是ChatGroq告知客户端,让客户端代码来调用工具。
  4. 工具get_weather请求Weather API: 工具get_weather使用HTTP GET请求Weather API,查询上海的天气信息。
  5. Weather API返回数据: Weather API返回包含上海天气信息的JSON数据。
  6. 工具get_weather返回天气信息: 工具get_weather解析JSON数据并将天气信息返回给ChatGroq模型。
  7. ChatGroq模型返回结果: ChatGroq模型将解析后的天气信息发送给用户。

代码详细说明

  1. 导入必要的模块和库
    • 导入处理环境变量的os模块。
    • 导入消息处理类HumanMessageSystemMessage
    • 导入ChatGroq用于初始化聊天模型。
    • 导入tool装饰器用于定义工具函数。
    • 导入requests库用于进行HTTP请求。
  2. 设置环境变量
    • 通过os.environ设置各类API密钥和LangChain相关配置,确保代码能够正确调用数据和服务。
  3. 定义工具函数
    • 使用@tool装饰器定义get_weather函数,该函数用于获取指定城市的天气。
    • 构建API请求URL并使用requests.get方法发送HTTP GET请求,从而获取数据并返回JSON格式的响应。
  4. 创建工具列表
    • get_weather函数添加到工具列表tools中,以便后续模型调用。
  5. 定义查询和消息
    • 定义查询字符串,表示用户想知道上海的天气。
    • 使用HumanMessage创建初始消息,并将其添加到消息列表messages中。
  6. 初始化语言模型
    • 使用ChatGroq初始化语言模型,并指定模型类型为llama3-8b-8192
  7. 绑定工具到模型
    • 通过bind_tools方法将工具绑定到语言模型中,使模型在处理消息时能够调用这些工具。
  8. 处理初始消息
    • 使用绑定了工具的模型调用invoke方法处理初始消息,生成AI回复,并将其添加到消息列表中。
  9. 处理工具调用
    • 调用get_weather工具函数处理AI回复中的工具调用部分,将得到的工具消息添加到消息列表中。
  10. 生成最终输出
    • 再次使用绑定了工具的模型调用invoke方法处理更新后的消息列表,生成最终内容输出,并打印结果。

消息抓取

以上整个过程中,我们都是在调用LangChain API与LLM在进行交互,至于底层发送的请求细节,一无所知。在某些场景下面,我们还是需要去探究一下这些具体的细节,这样可以有一个全面的了解。下面我们看一下具体的发送内容。

LLM请求1
{"messages": [[{"lc": 1,"type": "constructor","id": ["langchain","schema","messages","HumanMessage"],"kwargs": {"content": "What is weather of Shanghai?","type": "human"}}]]
}
LLM回答1
{"generations": [[{"text": "","generation_info": {"finish_reason": "tool_calls","logprobs": null},"type": "ChatGeneration","message": {"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessage"],"kwargs": {"content": "","additional_kwargs": {"tool_calls": [{"id": "call_2fdg","function": {"arguments": "{\"city\":\"Shanghai\"}","name": "get_weather"},"type": "function"}]},"response_metadata": {"token_usage": {"completion_tokens": 74,"prompt_tokens": 919,"total_tokens": 993,"completion_time": 0.061666667,"prompt_time": 0.105363553,"queue_time": 0.0005093089999999995,"total_time": 0.16703022},"model_name": "llama3-8b-8192","system_fingerprint": "fp_a97cfe35ae","finish_reason": "tool_calls","logprobs": null},"type": "ai","id": "run-1f49da98-4cc5-42ad-aa68-1962e447dbfa-0","tool_calls": [{"name": "get_weather","args": {"city": "Shanghai"},"id": "call_2fdg","type": "tool_call"}],"usage_metadata": {"input_tokens": 919,"output_tokens": 74,"total_tokens": 993},"invalid_tool_calls": []}}}]],"llm_output": {"token_usage": {"completion_tokens": 74,"prompt_tokens": 919,"total_tokens": 993,"completion_time": 0.061666667,"prompt_time": 0.105363553,"queue_time": 0.0005093089999999995,"total_time": 0.16703022},"model_name": "llama3-8b-8192","system_fingerprint": "fp_a97cfe35ae"},"run": null,"type": "LLMResult"
}
LLM请求2
{"messages": [[{"lc": 1,"type": "constructor","id": ["langchain","schema","messages","HumanMessage"],"kwargs": {"content": "What is weather of Shanghai?","type": "human"}},{"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessage"],"kwargs": {"content": "","additional_kwargs": {"tool_calls": [{"id": "call_2fdg","function": {"arguments": "{\"city\":\"Shanghai\"}","name": "get_weather"},"type": "function"}]},"response_metadata": {"token_usage": {"completion_tokens": 74,"prompt_tokens": 919,"total_tokens": 993,"completion_time": 0.061666667,"prompt_time": 0.105363553,"queue_time": 0.0005093089999999995,"total_time": 0.16703022},"model_name": "llama3-8b-8192","system_fingerprint": "fp_a97cfe35ae","finish_reason": "tool_calls","logprobs": null},"type": "ai","id": "run-1f49da98-4cc5-42ad-aa68-1962e447dbfa-0","tool_calls": [{"name": "get_weather","args": {"city": "Shanghai"},"id": "call_2fdg","type": "tool_call"}],"usage_metadata": {"input_tokens": 919,"output_tokens": 74,"total_tokens": 993},"invalid_tool_calls": []}},{"lc": 1,"type": "constructor","id": ["langchain","schema","messages","ToolMessage"],"kwargs": {"content": "{\"location\": {\"name\": \"Shanghai\", \"region\": \"Shanghai\", \"country\": \"China\", \"lat\": 31.005, \"lon\": 121.4086, \"tz_id\": \"Asia/Shanghai\", \"localtime_epoch\": 1733010903, \"localtime\": \"2024-12-01 07:55\"}, \"current\": {\"last_updated_epoch\": 1733010300, \"last_updated\": \"2024-12-01 07:45\", \"temp_c\": 9.2, \"temp_f\": 48.6, \"is_day\": 1, \"condition\": {\"text\": \"Sunny\", \"icon\": \"//cdn.weatherapi.com/weather/64x64/day/113.png\", \"code\": 1000}, \"wind_mph\": 4.7, \"wind_kph\": 7.6, \"wind_degree\": 229, \"wind_dir\": \"SW\", \"pressure_mb\": 1016.0, \"pressure_in\": 30.0, \"precip_mm\": 0.0, \"precip_in\": 0.0, \"humidity\": 76, \"cloud\": 0, \"feelslike_c\": 8.2, \"feelslike_f\": 46.7, \"windchill_c\": 9.6, \"windchill_f\": 49.3, \"heatindex_c\": 10.4, \"heatindex_f\": 50.8, \"dewpoint_c\": 4.4, \"dewpoint_f\": 40.0, \"vis_km\": 10.0, \"vis_miles\": 6.0, \"uv\": 0.0, \"gust_mph\": 8.6, \"gust_kph\": 13.8}}","type": "tool","name": "get_weather","tool_call_id": "call_2fdg","status": "success"}}]]
}
LLM回答2
{"generations": [[{"text": "According to the tool, the weather in Shanghai is currently Sunny with a temperature of 9.2°C (48.6°F) and a wind speed of 7.6 km/h (4.7 mph).","generation_info": {"finish_reason": "stop","logprobs": null},"type": "ChatGeneration","message": {"lc": 1,"type": "constructor","id": ["langchain","schema","messages","AIMessage"],"kwargs": {"content": "According to the tool, the weather in Shanghai is currently Sunny with a temperature of 9.2°C (48.6°F) and a wind speed of 7.6 km/h (4.7 mph).","response_metadata": {"token_usage": {"completion_tokens": 45,"prompt_tokens": 1370,"total_tokens": 1415,"completion_time": 0.0375,"prompt_time": 0.062532845,"queue_time": 0.0015657239999999906,"total_time": 0.100032845},"model_name": "llama3-8b-8192","system_fingerprint": "fp_179b0f92c9","finish_reason": "stop","logprobs": null},"type": "ai","id": "run-1b04ed85-1696-4574-be5a-dd7a37989fdf-0","usage_metadata": {"input_tokens": 1370,"output_tokens": 45,"total_tokens": 1415},"tool_calls": [],"invalid_tool_calls": []}}}]],"llm_output": {"token_usage": {"completion_tokens": 45,"prompt_tokens": 1370,"total_tokens": 1415,"completion_time": 0.0375,"prompt_time": 0.062532845,"queue_time": 0.0015657239999999906,"total_time": 0.100032845},"model_name": "llama3-8b-8192","system_fingerprint": "fp_179b0f92c9"},"run": null,"type": "LLMResult"
}

在这次的回答中,我们可以看到这样的天气状况。

According to the tool, the weather in Shanghai is currently Sunny with a temperature of 9.2°C (48.6°F) and a wind speed of 7.6 km/h (4.7 mph)."

结论

ai-quotes-1

今天,我们探讨了什么是工具,如何使用聊天模型调用工具,以及如何将工具的输出传递回聊天模型。通过LangChain,我们可以轻松地将这些工具集成到大语言模型中,使其功能更为强大。

希望大家通过这篇文章能够掌握这些基本概念和操作,并应用到自己的项目中。如果有任何问题或建议,欢迎在评论区分享!谢谢大家的关注!


http://www.mrgr.cn/news/78660.html

相关文章:

  • EXCEL截取某一列从第一个字符开始到特定字符结束的字符串到新的一列
  • 如何使用Edu邮箱获取Adobe免费福利
  • 重生之我在异世界学编程之C语言:一维数组篇
  • android-studio 下载并安装
  • PGSQL物化视图(Materialized View)
  • unity工程转为安卓使用的aar文件
  • ⽂件操作详解
  • Day1 生信新手笔记
  • 如何估算自然对流传热系数
  • [GKCTF 2021]签到
  • 四:工具、环境准备-compute node
  • 七:仪表盘安装-controller node
  • OpenMP出现Stack Overflow及其疑问
  • Kubernetes——part11 云原生中间件上云部署 Rocketmqkafkazookeeper
  • GPT的自回归语言建模(Autoregressive Language Modeling)
  • 科研小白成长记41——享受大起大落
  • 【排序用法】.NET开源 ORM 框架 SqlSugar 系列
  • 对拍详细使用方法
  • kubernetes——part3-2 集群声明式文件YAML
  • SQL优化与性能——数据库设计优化
  • Python中的六种“国际数字格式”实施方式
  • 刷LeetCode hot100--1.哈希表
  • Linux系统 异常控制流
  • 【CSS in Depth 2 精译_064】10.3 CSS 中的容器查询相对单位 + 10.4 CSS 容器样式查询 + 10.5 本章小结
  • 【连接池】.NET开源 ORM 框架 SqlSugar 系列
  • C_字符串其实就是字符数组