赞
踩
LangChain(一)构建本地数据检索问答Agent,新手向-CSDN博客
LangChain(二)基础问答大模型,纯新手向-CSDN博客
LangChain(三)基础问答大模型,从LLMchain开始了解chain!纯新手向-CSDN博客
LangChain(四)工具调用的底层原理!给大模型按上双手吧!(新手向)-CSDN博客
经过前面三篇的内容,我想大家对于大模型的构建、Langchain的优势、Chain的构建有了相当程度的理解(虽然只是最简单的示例,但是足够有代表性)。
后续Chain的使用将会更加丰富多彩,您会了解Langchain开发的大模型会有多么逆天的可扩展性。但今天我们先暂缓此部分,我们来讲讲Langchain里面最最最重要的功能:工具调用!
我们会先从Langchain规定的官方API开始构建,带着大家跑通一系列工具之后,从底层原理出发,为大家讲解大模型是怎么调用工具的(不必担心,非常浅显易懂,本栏目始终是新手向的)。
在Langchain眼中,所谓的工具都只是函数而已,我们要做的就是把函数写好,并交给大模型去自主的调用。
Langchain给大模型调用的函数专门设定了一个函数装饰器: @tool
有关于函数装饰器,作为新手其实没必要理解太深刻,只需要理解装饰器给函数添加了一些功能和变量即可。而这个tool装饰器仅仅是给函数增加了几个变量而已,如下:
- 工具名称:(tool.name)
- 该工具是什么的描述(tool.description)
- 输入内容的 JSON 格式 (tool.args)
- 工具的结果是否应直接返回给用户(tool.return_direct)
@tool def func(input:int): ''' 没用的函数 ''' return input print(func.name) # 输出:func print(func.description) # 输出:没用的函数 print(func.args) # 输出: {'input': {'title': 'Input', 'type': 'int'}} print(func.return_direct) # 输出:false
在这个装饰器中最主要的必须知晓的就是tool.name和tool. description。这个是后续工具调用的基础。
若无特殊设定,默认为函数名。作为新手向,该变量就不要去有额外的操作。只需要知道 tool.name == 函数名 即可。(操作更多也不会有额外的效果,还增加理解难度)
若无特殊设定,默认为函数的文档字符串(即函数下方的函数说明),有关文档字符串的内容可以参考下面的博客,简单清晰。该部分作为小白直接利用该部分特性即可。有更高要求的看客可以查阅有关BaseTool类的相关知识。
Python 文档字符串(DocStrings)是个啥??-CSDN博客
在之前的项目中我们编写了有关基础大模型的相关内容,开发了第一个问答大模型以及尝试了LLMchain的相关内容,我们将在此基础上继续往前!
LangChain(二)基础问答大模型,纯新手向-CSDN博客
LangChain(三)基础问答大模型,从LLMchain开始了解chain!纯新手向-CSDN博客
其实不看也可以啦,看了理解起来会更快而已……
该部分我们先定义需要的工具,代码如下。@tool装饰器说明这是一个工具。工具名称为“multiply”,工具描述为“Multiply two integers together.”。
- from langchain_core.tools import tool
-
- @tool
- def multiply(first_int: int, second_int: int) -> int:
- """Multiply two integers together."""
- return first_int * second_int
这部分没啥难度,其实就是你自己设定一个函数,然后前面加上@tool,函数内部首行用""" """ 定义一下函数功能描述即可。
该部分我们定义大模型,详细内容可以参考我之前的博客内容哦。使用百度的千帆大模型
-
- import os
- from langchain_community.chat_models import QianfanChatEndpoint
-
- # 设定百度千帆大模型的AK和SK-去百度千帆官网的控制台新建一个应用即可
- os.environ["QIANFAN_AK"] = "your AK“"
- os.environ["QIANFAN_SK"] = "your SK"
-
- #创建千帆LLM模型
- qianfan_chat = QianfanChatEndpoint(
- model="ERNIE-3.5-8K",
- temperature=0.2,
- timeout=30,
- )
这部分依旧没啥难度,按部就班走即可。
该部分我们进行工具的设定和与大模型进行绑定!
- tools = [multiply]
- llm_with_tools = qianfan_chat.bind_tools(tools)
- tool_map = {tool.name: tool for tool in tools}
该部分必须要好好解释一下。不然大家初看之下可能会一头雾水。
由于在实际开发过程中,不可能只有一个工具,我们常常会调用多个工具,那么和大模型进行绑定难道要每个工具函数都绑定一次吗?咋可能对不对。这部分就是把所有需要调用的函数打造成一个列表,列表内保存的是各个函数(不是函数名!函数名是string,函数就是函数,本质上是个对象,这里理解不了跳过即可,我还记得这是个新手向的博客~~~)。
这一行是把大模型和工具进行一个绑定,构建一个工具选择模块(一个 agent)。大模型就是通过该模块进行的工具选择,具体的原理在下一篇博客会详细讲解,此部分我们先暂缓跳过~。
这一行是把函数名称(string)和函数(对象)作为一个字典保存。
key:函数名称,value:函数
这个变量大家先留意一下,现在可能看不出用途,后面就有用了。
接下来我们把后面的代码一次性和盘托出!
- def call_tools(msg: AIMessage) -> Runnable:
- """Simple sequential tool calling helper."""
- tool_calls = msg.tool_calls.copy()
- for tool_call in tool_calls:
- tool_call["output"] = tool_map[tool_call["name"]].invoke(tool_call["args"])
- return tool_calls
-
-
- chain = llm_with_tools | call_tools
-
- chain.invoke(
- "What's 25 times 11?"
- )
I know,I know,突然信息量就上来了对不对。没事,我们一个一个来! 我们先跳过call_tools的函数定义,我们先看下面:
对于chain还不理解的同学可以先看我之前的博客,链接在上面!看了我之前博客的同学想必依旧有疑惑,我们只是使用过LLMchain,怎么就变成这样了?
实际上Langchain确实有很多已经定义好的chain,只需要调用即可,但是在实际开发中,最实用的依旧是自己定义的chain,个性化的定义才能满足个性化的需求嘛。
Langchain官方自然有可以让我们自己个性化定义chain的方式。该处就是一个典型。
该处的chain是如何工作的呢?作为小白我们不需要去理解源码。从高维去俯瞰它。步骤如下:
我们运行下面的代码:
- query = "25 * 11 = ?"
-
- messages = [HumanMessage(query)]
-
- print("messages1 = ", messages)
-
- ai_msg = llm_with_tools.invoke(messages)
-
- print("ai_msg = ", ai_msg)
可得输出如下(手动标准格式了下):
- messages1 = [HumanMessage(content='25 * 11 = ?')]
- ai_msg =
- content=''
- additional_kwargs={
- 'finish_reason': 'function_call',
- 'request_id': 'as-y3xqr3j5b5',
- 'object': 'chat.completion',
- 'search_info': [],
- 'function_call': {
- 'name': 'Multiply',
- 'arguments': '{"a":25,"b":11}'},
- 'tool_calls': [
- {'type': 'function',
- 'function': {'name': 'Multiply', 'arguments': '{"a":25,"b":11}'},
- 'id': '07eeb8f7-56b0-42f0-b828-4c7d4b17c850'}]}
- response_metadata={
- 'token_usage': {'prompt_tokens': 51, 'completion_tokens': 24, 'total_tokens': 75},
- 'model_name': 'ERNIE-3.5-8K',
- 'finish_reason': 'function_call',
- 'id': 'as-y3xqr3j5b5',
- 'object': 'chat.completion',
- 'created': 1720421110,
- 'result': '',
- 'is_truncated': False,
- 'need_clear_history': False,
- 'function_call': {
- 'name': 'Multiply',
- 'thoughts': '用户需要进行乘法运算,我可以使用工具Multiply来完成这个任务。',
- 'arguments': '{"a":25,"b":11}'},
- 'usage': {'prompt_tokens': 51, 'completion_tokens': 24, 'total_tokens': 75}}
- id='run-082b9676-4902-4bf6-af1b-545f4a095001-0'
- tool_calls=[{
- 'name': 'Multiply',
- 'args': {'a': 25, 'b': 11},
- 'id': '07eeb8f7-56b0-42f0-b828-4c7d4b17c850'}]
大家先别慌!别着急!重点其实很少~。听我细细说来。
第一行的 messages1中的HumanMessage,仅仅只是告诉大模型这是用户发出的信息而已,至少在现在这个阶段不是重点,不用管他!
最主要的是下面的ai_msg,有三个重要模块
总而言之,在本篇工具调用栏目看来,最重要的就是tool_calls字段,其他直接忽略。函数选择器的详细原理将会放置下一篇博客详细讲解,本文仅说Langchain的API调用的步骤和思路。毕竟是新手向嘛~
综上 函数选择器 的功能输出正式讲解完毕,其实大家只需要知道函数选择器就是用来选择函数的,最重要的功能就是输出的tool_calls字段,其中保存大模型想要调用的函数名称(string)和对应的参数。
以防大家往上翻太烦,再粘贴一次。这个函数的主要作用就是获取AI想要调用的工具,并帮AI调用该工具。
- def call_tools(msg: AIMessage) -> Runnable:
- """Simple sequential tool calling helper."""
- tool_calls = msg.tool_calls.copy()
- for tool_call in tool_calls:
- tool_call["output"] = tool_map[tool_call["name"]].invoke(tool_call["args"])
- return tool_calls
该部分的输入参数就是上一步函数选择器的输出:AImessage(不知道大家有没有注意到,这和HumanMessage正好是对应关系,其实就是一个是用户的信息,一个是AI的信息而已,仅仅是对信息做一个标识,其实没啥用)
后面的 --> Runnable 请忽略,新手直接跳过即可,想了解可以自行了解。 接下来让我们分行说明!
养成好习惯,直接copy,解耦互不影响,尤其在流式场景下。
因为AI可能需要调用多个函数,所以对每一个AI想要调用的函数都需要处理。我不知道为什么我要解释这个……
最重要的是这一行,不知道大家还记不记得 tool_map 这个变量,在上文提过,截图如下。这一行我们慢慢来,对于当前需要调用的tool_call,有一个字段“name”保存着需要调用函数的函数名称。用tool_map访问该名称,返回该函数名称(string)对应的函数(对象)!这下大家终于理解为什么我需要强调这多次了吧~。即:
这一行 == multiply.invoke({'a': 25, 'b': 11}) == multiply('a': 25, 'b': 11) == 275,此时tool_call多了一个字段“output”,value = 275
到此就结束了,我们终于实现了大模型调用工具的基础操作。大家安心,上面代码好像很多,好像很复杂,我们最后复习一下,看一下全部的代码,你会发现没什么难的其实。
- import os
- from langchain_community.chat_models import QianfanChatEndpoint
- from langchain_core.tools import tool
- from langchain_core.messages import AIMessage
- from langchain_core.runnables import Runnable
-
- # 设定百度千帆大模型的AK和SK
- os.environ["QIANFAN_AK"] = " your AK"
- os.environ["QIANFAN_SK"] = " your SK"
-
- # 定义千帆大模型
- qianfan_chat = QianfanChatEndpoint(
- model="ERNIE-3.5-8K",
- temperature=0.2,
- timeout=30,
- )
-
- # 设定两个函数,记得描述函数功能,这很重要
- @tool
- def func1():
- ''' useless function '''
- return 0
-
- @tool
- def Multiply(a: int, b: int) -> int:
- """Multiplies a and b."""
- return a * b
-
- # 工具集合
- tools = [Multiply, func1]
- # 工具与大模型绑定,构建函数选择模块
- llm_with_tools = qianfan_chat.bind_tools(tools)
- # 构建一一对应的map
- tool_map = {tool.name: tool for tool in tools}
-
- # 工具函数执行
- def call_tools(msg: AIMessage) -> Runnable:
- """Simple sequential tool calling helper."""
- tool_calls = msg.tool_calls.copy()
- for tool_call in tool_calls:
- tool_call["output"] = tool_map[tool_call["name"]].invoke(tool_call["args"])
- return tool_calls
-
- # 构建链
- chain = llm_with_tools | call_tools
-
- print(chain.invoke("What's 25 times 11?")[0]["output"])
输出:275
有一说一,工具调用会了,世界上还有什么功能实现不了?
但是本篇博客是从API的角度出发为大家构建一个工具调用的操作,下一篇博客我们将从原理出发,直接手撸工具调用!放宽心,依旧是新手向~
由于小博主依旧是个卑微的打工人,只能上班摸鱼的时候写写博客,后续的博客将保持一周一篇的频率~ 敬请期待~
Copyright © 2003-2013 www.wpsshop.cn 版权所有,并保留所有权利。