@
目录
- 前言
- 一、聊天模型的工具定义
- (一)创建工具
- [1、使用 @tool 装饰器创建工具](#1、使用 @tool 装饰器创建工具)
- [(1)依赖 Pydantic 类模式](#(1)依赖 Pydantic 类模式)
- [(2)依赖 Annotated](#(2)依赖 Annotated)
- [2、使用 StructuredTool 类提供的函数创建工具](#2、使用 StructuredTool 类提供的函数创建工具)
- (1)常规方法
- [(2)依赖 Pydantic 类](#(2)依赖 Pydantic 类)
- [(3)加入 response_format 配置](#(3)加入 response_format 配置)
- [1、使用 @tool 装饰器创建工具](#1、使用 @tool 装饰器创建工具)
- (二)绑定与调用工具
- (三)工具与聊天模型的交互
- [(四)LangChain 提供的工具](#(四)LangChain 提供的工具)
- (一)创建工具
- 结语
前言
在讲述了如何用Langchain的接口来进行聊天模型的定义后,我们本期来具体介绍聊天模型中的工具。
一、聊天模型的工具定义
工具调用根本作用是让大语言模型(LLM)具备与外部世界交互的能力。工具调用打破了LLM因受限于其训练数据(存在滞后性)和内在的文本生成逻辑,无法执行直接计算、查询实时信息、操作数据库或调用任何外部 API的壁垒,使其获得更多权限与能力。
(一)创建工具
1、使用 @tool 装饰器创建工具
这种方式需要注意以下几点:
- 该装饰器默认使用函数名称作为工具名称。
- 该装饰器将使用函数的文档字符串作为工具的描述。
而函数名、类型提示和文档字符串都是传递给工具 Schema 的⼀部分,不可缺失。Schema 是一个描述工具的Json串。
(1)依赖 Pydantic 类模式
python
from langchain_core.tools import tool
from pydantic import BaseModel,Field
class AddInput(BaseModel):
'''两数相加'''
x : int = Field(...,description='第一个整数')
y : int = Field(...,description='第二个整数')
@tool(args_schema=AddInput)
def add(x:int,y:int)->int:
return x+y
值得注意的是代码中 @tool 的 args_schema 参数,其表示工具函数在未提供描述、文档字符串等需要传递给工具 Schema 的内容时,依赖 Pydantic 类使用 args_schema 参数,来定义并提供工具输⼊参数的schema,默认为 None。
(2)依赖 Annotated
依赖 Annotated 和文档字符串传递给工具 Schema。
python
@tool
def add(
x:Annotated[int,...,'第一个整数'],
y:Annotated[int,...,'第二个整数'],
)->int:
"""两数相加
Args:
x: 第一个整数
y: 第二个整数
"""
return x + y
print(add.invoke({'x':1,'y':2}))
print(add.name)
print(add.description)
print(add.args)
2、使用 StructuredTool 类提供的函数创建工具
class langchain_core.tools.structured.StructuredTool 这个类使用来初始化工具的,可以用其中的 from_function 类方法通过给定的函数来创建并返回一个工具。from_function 的具体参数可查看此处。
(1)常规方法
使用from_function 类方法同样要将函数名、类型提示和文档字符串传递给工具 Schema 。
python
from langchain_core.tools import StructuredTool
def add(x:int,y:int)->int:
"""两数相加
Args:
x: 第一个整数
y: 第二个整数
"""
return x + y
add_tool = StructuredTool.from_function(func= add)
print(add_tool.invoke({'x':1,'y':2}))
(2)依赖 Pydantic 类
我们同样可以用Pydantic 类来传递所需信息传递给 Schema 。
python
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
from pydantic_core.core_schema import arguments_schema
class addInput(BaseModel):
x:int = Field(...,description="第一个整数")
y:int = Field(...,description="第二个整数")
def add(x:int,y:int)->int:
return x+y
add_tool = StructuredTool.from_function(
func = add,
description='两数相加',
arguments_schema=addInput
)
print(add_tool.invoke({'x':1,'y':2}))
(3)加入 response_format 配置
设想如果我们让大模型读取content的同时,将一些用来构造 content 的原始数据保存下来,当后续有⼀些记录、分析的步骤,就可以派上用了。而部分这就是 artifact 。而artifact 通常需要使用字典 Dict 或列表 List 保存。通过artifact里存放的原生数据,我们能更有效地做日志记录、分析,或自定义后续的处理。
因此我们可以在工具区中分消息内容(content)和其他工件(artifact)
需要在定义工具时指定 response_format="content_and_artifact" 参数,并确保我们返回一个元组 (content, artifact)。
python
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
class addInput(BaseModel):
x:int = Field(...,description="第一个整数")
y:int = Field(...,description="第二个整数")
def add(x:int,y:int)->tuple[str,list[int]]:
nums = [x,y]
content = f"{nums} 相加结果是{x+y}"
return content,nums
add_tool = StructuredTool.from_function(
func = add,
description='两数相加',
arguments_schema=addInput,
response_format='content_and_artifact',
)
print(add_tool.invoke(
{
"name": "add",
"args": {'x': 3, 'y': 6},
"type": "tool_call", # 必填
'id': "111", # 必填,用来将工具调用请求和结果关联起来
}
))
在链(Chain)中,⼯具调用之后的其他组件或函数,可能需要工具的原始且结构化数据(即artifact )来执行特定操作。而这些大量数据不适合直接交给LLM,所以artifact 里的数据其实是为了给链中后续的组件或函数使用的。
(二)绑定与调用工具
为了实际将这些⼯具绑定到聊天模型,可以使用聊天模型的 .bind_tools() 方法。
python
@tool
def add(
x: Annotated[int,...,'第一个整数'],
y: Annotated[int,...,'第二个整数'],
)->int:
"""两数相加"""
return x + y
@tool
def multiply(
x:Annotated[int,...,'第一个整数'],
y:Annotated[int,...,'第二个整数'],
)->int:
"""两数相乘"""
return x * y
model = ChatOpenAI(
model = "deepseek-v4-flash",
api_key=os.environ.get('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com",
)
# 绑定工具
tools = [add, multiply]
model_with_tools = model.bind_tools(tools=tools)
同时.bind_tools()方法所返回的是一个Runnable实例,所以我们可以用调用 .invoke() 方法,完成工具的调用。工具调用 的⼀个关键原则是,模型根据输入的相关性决定何时使用⼯具。模型并不总是需要调用工具。所以如果给定一个不相关的输入,模型不会调用该工具。
当然如果我们想让模型强制调用工具,那就需要在绑定工具时,设置 tool_choice="any" ,表示强制调用至少一个工具。
python
model_with_tools = model.bind_tools(tools=tools,tool_choice="any")
此时可通过两种不同的工具绑定方式来让模型来调用工具,看一下效果。
python
print(model_with_tools.invoke("2乘以3等于多少?"))
# print(model_with_tools.invoke("who are you?"))
# 模型在没有强制选择工具的情况下,会根据问题来决定是否使用工具
(三)工具与聊天模型的交互
在这之前,我们都是将工具与聊天模型进行绑定后,让聊天模型对工具进行调用,但大模型没有使用到工具的结果来进行问题的处理。此时就需要将工具输出传递给聊天模型。
1、工具的属性
在将工具输出传递给聊天模型之前,我们要了解工具的属性以及工具的具体输出是什么。
我们知道.invoke()的输出结果是一个 AIMessage,如果调用了工具,则 输出结果里将具有一个 tool_calls 属性。此属性包括执行该工具所需的一切,包括工具名称和输入参数。
2、将工具输出传递给聊天模型
要将工具输出传递给聊天模型,就要包括 HumanMessage、AIMessage (工具调用)、ToolMessage。而聊天模型会根据以上消息输入,将最终结果 AIMessage 返回。
python
import os
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from typing_extensions import Annotated
from langchain_core.tools import tool
@tool
def add(
x: Annotated[int,...,'第一个整数'],
y: Annotated[int,...,'第二个整数'],
)->int:
"""两数相加"""
return x + y
@tool
def multiply(
x:Annotated[int,...,'第一个整数'],
y:Annotated[int,...,'第二个整数'],
)->int:
"""两数相乘"""
return x * y
model = ChatOpenAI(
model = "deepseek-v4-flash",
api_key=os.environ.get('DEEPSEEK_API_KEY'),
base_url="https://api.deepseek.com",
)
# 绑定工具
tools = [add, multiply]
model_with_tools = model.bind_tools(tools=tools)
# 定义消息列表,添加要传递给聊天模型的消息
messages = [
HumanMessage("2乘以3等于多少?6加3等于多少?")
]
ai_msg = model_with_tools.invoke(messages)
# print(ai_msg)
messages.append(ai_msg)
# 构造ToolMessage,并添加到消息列表中去
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)
print(messages)
print(model_with_tools.invoke(messages).content)
通过运行上面的代码,我们发现实际上我们调用了两次聊天模型。第一次仅将【 HumanMessage 】发送给聊天模型进行处理,结果返回了【包含⼯具调用的AIMessage】,然后执行工具,得到 ToolMessage 。第二次将【HumanMessage + AIMessage + ToolMessage 】消息记录发送给聊天模型进行处理,结果返回了【包含结果的 AIMessage 】。
(四)LangChain 提供的工具
我们除了可以自己写的工具,LangChain 官⽅也已经给我们提供了很多现成的工具(Tool)和工具包(Toolkit),具体见官网此处。同时我们也可以去官网查找里面工具的具体说明与使用方法,具体见官网此处。
结语
以上就是在Langchain中如何具体给聊天模型绑定工具与调用工具的内容了。如果喜欢我的内容,请点赞、收藏加关注,多多支持我,以及欢迎各位在评论区讨论交流,并指出我的不足,谢谢大家。