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

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 integer
        b: Second integer
    """
    return a + b

# 定义第二个工具函数,用于将两个整数相乘
def multiply(a: int, b: int) -> int:
    """Multiply two integers.

    Args:
        a: First integer
        b: 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请求URL
    response = 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.kler.cn/a/421767.html

相关文章:

  • 开源C代码之路:一、Gitee
  • 如何使用 pprof 简单检测和修复 Go 中的内存泄漏
  • C++:std::fstream详细介绍
  • C#:时间与时间戳的转换
  • 【Golang】WaitGroup 实现原理
  • API 数据接口使用与安全指南
  • HTMLCSS 奇幻森林:小熊的甜蜜蛋糕派对大冒险
  • 「Python数据科学」数据科学的概念及Python常用数据科学库
  • 青海摇摇了3天,技术退步明显.......
  • 云服务器和物理服务器租用哪个好?
  • 在Neo4j中导入多个csv文件
  • 无人机主控芯片技术与算法详解!
  • 级联树结构TreeSelect和上级反查
  • spring boot mapper测试类优化
  • HTML 快速上手
  • 【网络安全】CSRF
  • `pnpm` 不是内部或外部命令,也不是可运行的程序或批处理文件(问题已解决,2024/12/3
  • 【Vue3】【Naive UI】<n-upload>标签
  • 【Delphi】modbus-TCP 协议库
  • 前端学习笔记-Vue篇-01
  • 前端用到的一些框架
  • python蓝桥杯刷题3
  • 游戏引擎学习第25天
  • 【XGlassTerminal.js】快速 构建 炫酷 终端 网页 以及 Linux 模拟器 在线!!
  • android视频播放器之DKVideoPlayer
  • C语言编程1.21波兰国旗问题