【LangChain】定义模型,调用工具
🌈个人主页:秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_13137010.html
目录
前言
💬 hello! 各位铁子们大家好哇。
今日更新了LangChain相关内容
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
聊天模型核心能力
定义聊天模型
大语言模型 (LLM) 在各种与语言相关的任务(例如文本生成、翻译、摘要、问答等)中表现出色。 现代 LLM 通常通过聊天模型接口访问,该接口将消息列表作为输入,并返回消息作为输出,而不是使 用纯文本。
这里需要注意 LLM 与 LangChain 中 聊天模型 的关系:
- 在 LangChain 的官方文档中,认为 LLM 大多数是纯文本补全模型。这些纯文本模型封装的 API 接 受一个字符串提示作为输入,并输出一个字符串补全结果(实际上 LLM 还包括多模态输入)。 OpenAI 的 GPT-5 就是作为 LLM 来实现的。
- LangChain 中的 聊天模型 通常由 LLM 提供支持,但经过专门调整以用于对话。关键在于,它们 不是接受单个字符串作为输入,而是接受聊天消息列表,并返回一条 AI 消息作为输出。

通过 API 定义聊天模型
方式1:ChatOpenAI
ChatOpenAI 定义聊天模型在快速上手模块中已经涉及。
class langchain_openai.chat_models.base.ChatOpenAI 是 LangChain 为 OpenAI 的 聊天模型(如 gpt-5 , gpt-5-mini )提供的具体实现类。
其继承了 class langchain_openai.chat_models.base.BaseChatOpenAI ,且 BaseChatOpenAI 实现了标准的 Runnable 接口。
ChatOpenAI 常用初始化参数说明

若使用其它与 OpenAI 兼容的大模型,例如 DeepSeek,则可以使用以下定义方式:
import os
OPENAI_API_KEY = os.getenv('OPENAI_API_KEY')
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
base_url="https://api.deepseek.com/v1",
openai_api_key=OPENAI_API_KEY,
model="deepseek-chat",
# ...
)
参数说明:
- base_url :出于与 OpenAI 兼容考虑,要将 base_url 设置为 https://api.deepseek.com/v1 来使用,但注意,此处 v1 与模型版本无关。
- openai_api_key :需要单独申请 DeepSeek 的 API Key,然后重新进行环境变量配置。
invoke() 调用
介绍 方式2 之前,需要先来了解一下关于 Runnable 接口中的 .invoke() 调用。该方法是将单个输 入转换为对应的输出。例如对于聊天模型来说,就是根据用户的问题输入,输出相应的答案。
invoke() 方法定义:
abstractmethod invoke(
input: Input,
config: RunnableConfig | None = None,
**kwargs: Any,
) → Output
请求参数:
- input :输入一个 Runnable 实例
- config (默认空):用于 Runnable 的配置。
返回值:
- 返回一个 Runnable 实例
class langchain_core.runnables.config.RunnableConfig 常用参数说明

具体示例,下面的 方式2 会用到
方式2:init_chat_model
前面的 ChatOpenAI 用于明确创建 OpenAI 聊天模型的实例。而 init_chat_model() 是一个工 厂函数,它可以初始化多种支持的聊天模型(如 OpenAI、Anthropic、FireworksAI 等),不仅仅是 OpenAI 的聊天模型。
init_chat_model() 函数定义
langchain.chat_models.base.init_chat_model(
model: str,
*,
model_provider: str | None = None,
configurable_fields: Literal[None] = None,
config_prefix: str | None = None,
**kwargs: Any,
) → BaseChatModel
init_chat_model() 常用参数说明

init_chat_model() 返回值说明
函数返回一个与指定的 model_name 和 model_provider 相对应的 BaseChatModel (如 ChatOpenAI , ChatAnthropic 等)。注意要是模型可配置,则返回一个聊天模型模拟器,该模 拟器在传入配置后,于运行时才会初始化底层模型。
案例:
# 1. 基本用法
# LangChain 封装了更上层的方法,让我们初始化模型
# gpt_model = init_chat_model(model="gpt-4o-mini", model_provider="openai", temperature=0.3)
# deepseek_model = init_chat_model("deepseek-chat", model_provider="deepseek", temperature=0.3)
#
# print(f"gpt-4o-mini:{gpt_model.invoke("你是谁?").content}")
# print(f"deepseek-chat:{deepseek_model.invoke("你是谁?").content}")
# 2. 定义可配置的模型(模型模拟器)
# config_model = init_chat_model(temperature=0.3)
# messages = [
# SystemMessage(content="请补全一段故事,100个字以内:"),
# HumanMessage(content="一只猫正在__?")
# ]
# # .invoke() 的config参数才真正意义上定义了模型
# print(f"config_model:{config_model.invoke(input=messages, config={"configurable" : {"model" : "deepseek-chat"}}).content}")
# 3. 可配置的模型(默认参数)
# 原本输出
# 精简版本
model = init_chat_model(
model="gpt-4o-mini",
model_provider="openai",
temperature=0.3,
max_tokens=1024,
configurable_fields=("max_tokens", "model", "model_provider",),
config_prefix="first",
)
messages = [
SystemMessage(content="请补全一段故事,100个字以内:"),
HumanMessage(content="一只猫正在__?")
]
result = model.invoke(
input = messages,
config={
"configurable" : {
"first_max_tokens" : 10,
"first_model": "deepseek-chat",
"first_model_provider": "deepseek",
}
}
)
print(result)
通过本地部署的 LLM 定义聊天模型
ChatOllama
若想使用 ChatOllama,需要先安装 Ollama 包:
pip install -U langchain_ollama
class langchain_ollama.chat_models.base.ChatOllama 是 LangChain 为通过 Ollama 部署的聊天模型提供的具体实现类。 ChatOllama 同样也实现了标准的 Runnable 接口。
ChatOllama 常用初始化参数说明

示例:
from langchain_ollama import ChatOllama
ollama_model = ChatOllama(model="deepseek-r1:1.5b", base_url="http://127.0.0.1:11434")
print(ollama_model.invoke("你是谁?").content)
聊天模型--调用工具
工具调用根本作用是让大语言模型(LLM)具备与外部世界交互的能力。
LLM 本身是一个封闭的知识系统,其能力受限于其训练数据(存在滞后性)和内在的文本生成逻辑。 它无法执行直接计算、查询实时信息、操作数据库或调用任何外部 API。工具调用打破了这层壁垒,其 作用具体体现在:
- 扩展能力边界:模型可以借助工具完成它自身无法完成的任务,如执行数学计算、搜索网络、查询 数据库等。
- 保证信息实时性:通过调用搜索工具或数据库查询工具,LLM 可以获取最新的、训练数据中不存在 的信息,避免回答过时或“一本正经地胡说八道”。
- 处理复杂任务:将一个复杂的用户请求(如“分析我上个月的消费趋势”)分解成多个步骤,并依 次调用不同的工具(如“从数据库获取数据” -> “用 Python 进行数据分析” -> “生成图表”) 来协同完成。协调这件事这更体现在 Agent 智能体上。
- 连接现有系统:可以将企业内部已有的系统、API 和数据库封装成工具,让 LLM 成为一个用自然语 言驱动的统一接口,极大地提升了自动化和集成能力。
在 LangChain 中,聊天模型提供了额外的功能:工具调用。它能使 LLM 与外部服务、API 和数据库进 行交互。工具调用还可用于从非结构化数据中提取结构化信息并执行各种其他任务。
例如,当我们希望获取当前天气情况时,由于 LLM 无法获取实时信息,此时我们就可以借助工具,通 过外部服务进行搜索完成查询:

再例如,当我们希望获取数据库表中的数据时,由于 LLM 无法直接获取表数据,此时我们就可以借助 工具,通过与数据库交互完成查询:

创建工具
使用 @tool 装饰器创建工具
在 LangChain 中,实现了一个 @tool 装饰器来创建工具, @tool 装饰器是自定义工具的最简单方 法。如下所示:
from langchain_core.tools import tool
@tool
def multiply(a: int, b: int) -> int:
"""Multiply two integers.
Args:
a: First integer
b: Second integer
"""
return a * b
print(multiply.invoke({"a": 2, "b": 3})) # 输出:6
print(multiply.name) # 输出:multiply
print(multiply.description) # 输出:Multiply two ...省略...b:Second integer
print(multiply.args) # 输出:{'a': {'title': 'A', 'type':'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
可以看出,工具通过 @tool 加 Python函数 实现,其中:
- 该装饰器默认使用函数名称作为工具名称。
- 该装饰器将使用函数的文档字符串作为工具的描述。
因此,函数名、类型提示和文档字符串都是传递给工具 Schema 的一部分,不可缺失。定义好的描述 是使模型良好运行的重要部分。
什么是 Schema ?
想象以不同的方式,在 JSON 中表示有关一个人的信息:
{
"name": "张小红",
"birthday": "1732年2月22日",
"address": "陕西省西安市雁塔区"
}
{
"surname": "王",
"given_name": "刚",
"birthday": "1732-02-22",
"address": {
"district": "萧山区",
"city": "杭州市",
"province": "浙江省"
"country": "中国"
}
}
这两种表述同样有效,尽管示例2显然比示例1更正式。记录的设计在很大程度上取决于其在应用程 序中的预期用途,因此这里没有正确或错误的答案。
但是,当应用程序说“给我一个人的 JSON 记录”时,重要的是要确切地知道该如何组织记录。例 如,我们需要知道需要哪些字段,以及如何表示这些值。这就是 JSON Schema 的用武之地。
{
"type": "object",
"properties": {
"surname": { "type": "string" },
"given_name": { "type": "string" },
"birthday": { "type": "string", "format": "date" },
"address": {
"type": "object",
"properties": {
"district": { "type": "string" },
"city": { "type": "string" },
"province": { "type": "string" },
"country": { "type" : "string" }
}
}
}
}
若用此 JSON Schema “验证”示例1,那么示例1是不符合当前 JSON Schema 的,但是示例2可 以“验证”通过。
注意, JSON Schema 是数据本身,而不是计算机程序,它只是一种“描述其他数据结构”的声明 格式。简明扼要地描述数据的表面结构,并根据数据自动验证数据很容易。但是,由于 JSON Schema 不能包含任意代码,因此无法表达数据元素之间的关系存在某些约束。
因此,对于足够复杂的数据格式,任何“验证工具”都可能有两个验证阶段:一个在 schema (或 结构)级别,一个在语义级别。后一种检查可能需要使用更通用的编程语言来实现。
最后回答一下问题, Schema 就是描述其他数据结构的声明格式,用于自动验证数据而存在。
有了以上概念铺垫,对于工具schema,它将从函数名、类型提示和文档字符串中获取相关属性,以此 来声明一个工具,包括其名称、描述、输入参数、输出类型等等。这里需要说明的是,若是简单定义 工具,如上述示例,工具 schema 需要解析 Google 风格的文档字符串去获取【参数描述】。
什么是 Google 风格的文档字符串?Google 风格是 Python 文档字符串的一种写作规范。它并非 Python 语言官方强制要求,而是由 Google 为其内部 Python 项目制定的规范,后来因为其极高的可 读性和简洁性而在整个 Python 社区中变得非常流行。它使用 Args: , Returns: 等关键字,参数 描述简洁明了,如下所示:
def fetch_data(url, retries=3):
"""从给定的URL获取数据。
Args:
url (str): 要从中获取数据的URL。
retries (int, optional): 失败时重试的次数。默认为3。
Returns:
dict: 从URL解析的JSON响应。
"""
除了这种方式,还有其他方式可以让工具 schema,获取相关工具声明需要的内容。下面再展示其他常 用的工具定义模式:
模式1:依赖 Pydantic 类
若使用 @tool 定义工具时,没有提供文档字符串,则会报错:
from langchain_core.tools import tool
@tool
def add(a: int, b: int) -> int:
return a + b
@tool
def multiply(a: int, b: int) -> int:
return a * b
点击运行,报错
此时,在 LangChain 中,可以使用 Pydantic 类,提供运行时数据验证和类型检查。通过 Field(description="...") 添加字段描述,LangChain 会自动提取。
注意,除非提供默认值,否则所有字段都是 required 。如下所示:
class AddInput(BaseModel):
"""两数相加"""
a: int = Field(..., description="第一个整数")
b: int = Field(..., description="第二个整数")
@tool(args_schema=AddInput)
def add(a: int, b: int) -> int:
return a + b
注意是代码中 @tool 的 args_schema 参数,它表示工具函数在未提供描述、文档字符串等需要传 递给工具 Schema 的内容时,依赖 Pydantic 类使用 args_schema 参数,定义并提供工具输入参数 的schema。默认为 None。
点击运行,不会报错,且将来运行时会进行数据验证。因此,我们再次印证了函数名、类型提示和文 档字符串都是传递给工具 Schema 的一部分,不可缺失。
模式2:依赖 Annotated
在 LangChain 中,可以依赖 Annotated 和文档字符串传递给工具 Schema 。如下所示
@tool
def add(
a : Annotated[int, ..., "第一个整数"],
b : Annotated[int, ..., "第二个整数"],
) -> int:
"""两数相加
Args:
a: 第一个整数
b: 第二个整数
"""
return a + b
使用 StructuredTool 类提供的函数创建工具
class langchain_core.tools.structured.StructuredTool 类用来初始化工具,其中 from_function 类方法通过给定的函数来创建并返回一个工具。 from_function 类方法定义如 下:
classmethod from_function(
func: Callable | None = None,
coroutine: Callable[[...], Awaitable[Any]] | None = None,
name: str | None = None,
description: str | None = None,
return_direct: bool = False,
args_schema: type[BaseModel] | dict[str, Any] | None = None,
infer_schema: bool = True,
*,
response_format: Literal['content', 'content_and_artifact'] = 'content',
parse_docstring: bool = False,
error_on_invalid_docstring: bool = False,
**kwargs: Any,
) → StructuredTool
关键参数说明:
- func:要设置的工具函数
- coroutine:协程函数,要设置的异步工具函数
- name:工具名称。默认为函数名称。
- description:工具描述。默认为函数文档字符串。
- args_schema:工具输入参数的schema。默认为 None。
- response_format:工具响应格式。默认为“content”。
- 如果配置为 “content” ,则工具的输出为 ToolMessage 的 content 属性。
- 对于 HumanMessage 、 AIMessage 已经见过,分别表示 用户消息 和 AI消息响应 , 对于 ToolMessage ,它表示对应工具角色所发出的消息。
- 如果配置为 “content_and_artifact” ,则输出应是与 ToolMessage 的 content 属性 与 artifact 属性相对应的二元组。如果配置为 “content” ,则工具的输出为 ToolMessage 的 content 属性。
- 如果配置为 “content” ,则工具的输出为 ToolMessage 的 content 属性。
常规用法
对于用该类方法创建的工具,同样函数名、类型提示和文档字符串也都是传递给工具 Schema 的一部 分,不可缺失。
def add(a: int, b: int) -> int:
"""两数相加"""
return a + b
add_tool = StructuredTool.from_function(func=add)
加入配置,依赖 Pydantic 类
同样的,让工具函数不提供描述、文档字符串等需要传递给工具 Schema 的内容,如下所示:
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
class AddInput(BaseModel):
a:int = Field(description="第一个整数")
b:int = Field(description="第二个整数")
def add(a: int, b: int) -> int:
return a + b
add_tool = StructuredTool.from_function(
func=add,
name="ADD", # 工具名
description="两数相加", # 工具描述
args_schema=AddInput, # 工具参数
)
加入 response_format 配置
如果希望我们的工具区分消息内容(content)和其他工件(artifact),让大模型读取 content , 而一些用来构造 content 的原始数据保存下来,若后续有一些记录、分析的步骤,就可以派上用场 了,这就是 artifact 。 artifact 通常需要使用字典 Dict 或列表 List 保存。
接下来举个例子再来理解下。例如我们定义了一个搜索天气的 tool ,若使用搜索引擎工具询问 “今 天的天气如何?” 时:
- content 可能是: “根据最新搜索结果,今天北京晴,气温在25°C到32°C之间。建议穿短袖衣 物。”
- artifact 可能是某搜索引擎 API 返回的完整 JSON 响应,其中包含多个搜索结果条目、每个条 目的标题、链接、摘要、排名等元数据。
如何做到?
我们需要在定义工具时指定 response_format="content_and_artifact" 参数,并确保我 们返回一个元组 (content, artifact),代码如下:
class AddInput(BaseModel):
a:int = Field(description="第一个整数")
b:int = Field(description="第二个整数")
def add(a: int, b: int) -> Tuple[str, List[int]]:
nums = [a, b]
content = f"{nums}相加的结果是{a + b}"
return content, nums
add_tool = StructuredTool.from_function(
func=add,
name="ADD", # 工具名
description="两数相加", # 工具描述
args_schema=AddInput, # 工具参数
response_format="content_and_artifact"
)
# 模拟大模型调用姿势
print(add_tool.invoke(
{
"name": "ADD",
"args": {"a": 3, "b": 4},
"type": "tool_call", # 必填
"id": "111", # 必填, 用来将工具调用请求和结果关联起来。
}
))
由于 LLM 大多理解文本,所以工具的主要输出 content 必须是结构良好、简洁的文本, 以便模型能够轻松理解和基于它进行推理、生成下一步的指令。
在链(Chain)中,工具调用之后的其他组件或函数,可能需要工具的原始且结构化数据(即 artifact )来执行特定操作。这些数据可能是庞大的、且非文本的。这些数据不适合直接塞给模 型。因此, artifact 其实是为了给链中后续的组件或函数使用的,不被大模型所直接使用!!
绑定工具
为了实际将这些工具绑定到聊天模型,可以使用聊天模型的 .bind_tools() 方法。如下所示:
@tool
def add(
a : Annotated[int, ..., "第一个整数"],
b : Annotated[int, ..., "第二个整数"],
) -> int:
"""两数相加"""
return a + b
@tool
def multiply(
a : Annotated[int, ..., "第一个整数"],
b : Annotated[int, ..., "第二个整数"],
) -> int:
"""两数相乘"""
return a * b
model = ChatOpenAI(model="gpt-4o-mini")
# 绑定工具
tools = [add, multiply]
model_with_tools = model.bind_tools(tools=tools)
# 调用工具
print(model_with_tools.invoke("2乘3等于多少?"))
bind_tools() 方法定义
bind_tools(
tools: Sequence[dict[str, Any] | type | Callable | BaseTool],
*,
tool_choice: dict | str | Literal['auto', 'none', 'required', 'any'] | bool
| None = None,
strict: bool | None = None,
parallel_tool_calls: bool | None = None,
**kwargs: Any,
) → Runnable[PromptValue | str | Sequence[BaseMessage | list[str] |
tuple[str, str] | str | dict[str, Any]], BaseMessage]
请求参数:
- tools :绑定到此聊天模型的工具定义列表。支持的类型为:字典、pydantic.BaseModel 类、 Python 函数和 BaseTool(如 @tool 装饰器创建的类)。
- tool_choice (默认空):要求模型调用哪个工具。可以设置为:
- 形式为 '>' 的 str:调用 工具。
- 'auto' :自动选择工具(包括无工具)。
- 'none' :不调用工具。
- 'any' 或 'required' 或 True :强制调用至少一个工具。
- False 或 None :无效果,默认 OpenAI 的行为。
- strict (默认空):
- 如果为 True ,则保证模型输出与工具定义中提供的 JSON Schema 完全匹配。输入也将根 据提供的 Schema 进行验证。
- 如果为 False ,则不会验证输入,也不会验证模型输出。
- 如果为 None ,则不会将 strict 参数传递给模型。
- parallel_tool_calls :默认为 None ,允许并行工具使用。设置为 False 以禁用并行 工具。
- kwargs(Any) :任何附加参数都直接传递给 bind() 。
返回值:
- 返回一个 Runnable 实例。
- 该实例支持多种格式输入:
- 原始提示 PromptValue
- 字符串: "上海天气如何?"
- 消息或消息列表:[HumanMessage(content="...")]
- 该实例的输出:
- 包含工具调用信息的 AIMessage
工具调用
通过 .bind_tools() 方法我们可知,它返回了一个 Runnable 实例,因此我们可以使用该 Runnable 实例,调用 .invoke() 方法,完成工具调用。示例如下
# 调用工具
print(model_with_tools.invoke("2乘3等于多少?"))
输出结果(AIMessage):
content='' additional_kwargs={'tool_calls': [{'id':
'call_mBnxNMY7vfAOrExEETcdEC6l', 'function': {'arguments': '{"a":9,"b":6}',
'name': 'multiply'}, 'type': 'function'}], 'refusal': None} response_metadata=
{'token_usage': {'completion_tokens': 17, 'prompt_tokens': 86, 'total_tokens':
103, 'completion_tokens_details': {'accepted_prediction_tokens': 0,
'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}},
'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-C6DSvbskJtwUl6YTkPILUaiDdDuEN', 'service_tier': 'default',
'finish_reason': 'tool_calls', 'logprobs': None} id='run--1f43e942-d56b-4d6d8d3f-145b7e5d0036-0' tool_calls=[{'name': 'multiply', 'args': {'a': 9, 'b':
6}, 'id': 'call_mBnxNMY7vfAOrExEETcdEC6l', 'type': 'tool_call'}]
usage_metadata={'input_tokens': 86, 'output_tokens': 17, 'total_tokens': 103,'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details':
{'audio': 0, 'reasoning': 0}}
输出说明:
- AIMessage :来自 AI 的消息。从聊天模型返回,作为对提示(输入)的响应。
- content :消息的内容。
- additional_kwargs :与消息关联的其他有效负载数据。对于来自 AI 的消息,可能包括模 型提供程序编码的工具调用。
- response_metadata :响应元数据。例如:响应标头、logprobs、令牌计数、模型名称。
从输出结果看来,AI 给出的响应是进行工具的调用!!
工具调用的一个关键原则是,模型根据输入的相关性决定何时使用工具。模型并不总是需要调用工 具。例如,给定一个不相关的输入,模型不会调用该工具:
强制模型调用工具
我们也可以让模型强制调用工具,那就需要在绑定工具时,设置 tool_choice="any" ,表示 强制调用至少一个工具。示例如下:
model_with_tools = model.bind_tools(tools, tool_choice="any")
result = model_with_tools.invoke("hello world!")
工具属性
现在我们知道,输出结果是一个 AIMessage 。但是,如果调用了工具,则 result 将具有一个 tool_calls 属性。此属性包括执行该工具所需的一切,包括工具名称和输入参数,示例如下:
model_with_tools = model.bind_tools(tools, tool_choice="any")
result = model_with_tools.invoke("9乘6等于多少?")
print(result.tool_calls)
输出结果:
[{'name': 'multiply', 'args': {'a': 9, 'b': 6}, 'id':
'call_csFbMmYD4Dmz8yMja3ZWK1QW', 'type': 'tool_call'}]
将工具输出传递给聊天模型
到这里可以发现,我们仅仅只是成功调用了工具,但是聊天模型并没有给我们返回我们真正需要的答 案。此时就需要:
- 将工具输出传递给聊天模型,包括 HumanMessage 、 AIMessage (工具调用)、ToolMessage
- 聊天模型根据以上消息输入,将最终结果 AIMessage 返回。
为什么要发 ToolMessage 呢?
之前我们讲过,聊天模型通常不是接受单个字符串作为输入,而是接受 聊天消息(XxxMessage) 列表,因此在这里我们需要将工具的返回,构造成 ToolMessage ,再传输给聊天模型!!!
方便的是,如果我们使用 @tool 装饰器创建的工具,使用 tool.invoke(tool_calls) ,将自 动返回一个 ToolMessage 。完整示例如下:
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_core.tools import tool
from typing_extensions import Annotated
@tool
def add(
a : Annotated[int, ..., "第一个整数"],
b : Annotated[int, ..., "第二个整数"],
) -> int:
"""两数相加"""
return a + b
@tool
def multiply(
a : Annotated[int, ..., "第一个整数"],
b : Annotated[int, ..., "第二个整数"],
) -> int:
"""两数相乘"""
return a * b
model = ChatOpenAI(model="gpt-4o-mini")
# 绑定工具
tools = [add, multiply]
model_with_tools = model.bind_tools(tools=tools)
# 定义消息列表,添加要传递给聊天模型的消息
messages = [
HumanMessage("9乘6等于多少?5加3等于多少?")
]
ai_msg = model_with_tools.invoke(messages)
messages.append(ai_msg)
# 构造ToolMessage, 并添加到消息列表中去
for tool_call in ai_msg.tool_calls:
# 根据工具名选择对应工具函数(不区分大小写)
selected_tool = {"add": add, "multiply": multiply}
[tool_call["name"].lower()]
# 执行工具调用,返回 ToolMessage
tool_msg = selected_tool.invoke(tool_call)
# 将 ToolMessage 加入消息
messages.append(tool_msg)
print(messages)
result = model.invoke(messages)
print(result)
打印结果如下:
[HumanMessage(content='9乘6等于多少?5加3等于多少?', additional_kwargs={},
response_metadata={}), AIMessage(content='', additional_kwargs={'tool_calls':
[{'id': 'call_tZhIzjEQrLk0GquaKXb6JZMy', 'function': {'arguments': '{"a": 9,
"b": 6}', 'name': 'multiply'}, 'type': 'function'}, {'id':
'call_fesQTvJGD7AiuRi1hnBJ67el', 'function': {'arguments': '{"a": 5, "b": 3}',
'name': 'add'}, 'type': 'function'}], 'refusal': None}, response_metadata=
{'token_usage': {'completion_tokens': 50, 'prompt_tokens': 93, 'total_tokens':
143, 'completion_tokens_details': {'accepted_prediction_tokens': 0,
'audio_tokens': 0, 'reasoning_tokens': 0, 'rejected_prediction_tokens': 0},
'prompt_tokens_details': {'audio_tokens': 0, 'cached_tokens': 0}},
'model_name': 'gpt-4o-mini-2024-07-18', 'system_fingerprint': 'fp_560af6e559',
'id': 'chatcmpl-C6DVwdK6M9L2AbqB34ICzL9TY9ECE', 'service_tier': 'default',
'finish_reason': 'tool_calls', 'logprobs': None}, id='run--e8e2e70d-1a74-4023-
b0de-a7df05afe5fe-0', tool_calls=[{'name': 'multiply', 'args': {'a': 9, 'b':
6}, 'id': 'call_tZhIzjEQrLk0GquaKXb6JZMy', 'type': 'tool_call'}, {'name':
'add', 'args': {'a': 5, 'b': 3}, 'id': 'call_fesQTvJGD7AiuRi1hnBJ67el','type': 'tool_call'}], usage_metadata={'input_tokens': 93, 'output_tokens':
50, 'total_tokens': 143, 'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}}),
ToolMessage(content='54', name='multiply',
tool_call_id='call_tZhIzjEQrLk0GquaKXb6JZMy'), ToolMessage(content='8',
name='add', tool_call_id='call_fesQTvJGD7AiuRi1hnBJ67el')]
content='9乘6等于54,5加3等于8。' additional_kwargs={'refusal': None}
response_metadata={'token_usage': {'completion_tokens': 15, 'prompt_tokens':
159, 'total_tokens': 174, 'completion_tokens_details':
{'accepted_prediction_tokens': 0, 'audio_tokens': 0, 'reasoning_tokens': 0,
'rejected_prediction_tokens': 0}, 'prompt_tokens_details': {'audio_tokens': 0,
'cached_tokens': 0}}, 'model_name': 'gpt-4o-mini-2024-07-18',
'system_fingerprint': 'fp_560af6e559', 'id': 'chatcmplC6DVyKrbbGZqfDU8ykJMNy9GZVEdJ', 'service_tier': 'default', 'finish_reason':
'stop', 'logprobs': None} id='run--b6ef0591-1ab8-4ce2-abe2-7ec509204fd0-0'
usage_metadata={'input_tokens': 159, 'output_tokens': 15, 'total_tokens': 174,
'input_token_details': {'audio': 0, 'cache_read': 0}, 'output_token_details':
{'audio': 0, 'reasoning': 0}}
从流程与代码中可以看到,实际上我们调用了两次聊天模型:
- 第一次:仅将【 HumanMessage 】发送给聊天模型进行处理,结果返回了【包含工具调用的 AIMessage 】,并没有返回我们想要的结果。然后我们执行工具,得到 ToolMessage 。
- 第二次:将【 HumanMessage + AIMessage + ToolMessage 】消息记录发送给聊天模型进行 处理,结果返回了【包含结果的 AIMessage 】
LangChain 提供的工具
工具也不是全部都需要我们自己手搓,其实 LangChain 官方也已经给我们提供了很多现成的工具 (Tool)和工具包(Toolkit)。
LangChain 提供的工具见这里
https://docs.langchain.com/oss/python/integrations/tools
。写好的工具一般都是为了使用 LangChain 中集成的三方组件或工具而 创造的,有搜索、数据库、网页浏览器等相关的工具。

下面我们简单看一个搜索工具类。
TavilySearch
TavilySearch 类可以支持我们进行搜索,Tavily 是一个专门为 AI 设计的搜索引擎,专为智能体检索与 推理需求量身打造的工具。
Tavily 不仅提供了高度可编程的 API 接口,还具备显著优于传统搜索引擎的上下文相关性理解能力。 能够以结构化、可解析的形式返回搜索结果,便于将检索到的信息直接用于后续的推理、生成或任务 执行流程。
- Tavily官网:https://www.tavily.com/,需魔法使用。
下面我们在 LangChain 中接入该搜索工具,步骤如下:
1.安装 langchain-tavily 包:
pip install -U langchain-tavily
2.配置环境变量 TAVILY_API_KEY ,值为我们申请的 API Key
3.代码接入 TavilySearch 类,实现搜索功能:
from langchain_core.messages import HumanMessage
from langchain_openai import ChatOpenAI
from langchain_tavily import TavilySearch
# 定义模型
model = ChatOpenAI(model="gpt-4o-mini")
# 定义工具
tool = TavilySearch(max_results=4)
# 绑定工具
model_with_tools = model.bind_tools([tool])
# 定义消息列表
messages = [
HumanMessage("北京今天的天气怎么样?")
]
ai_message = model_with_tools.invoke(messages)
messages.append(ai_message)
for tool_call in ai_message.tool_calls:
tool_message = tool.invoke(tool_call)
messages.append(tool_message)
print(messages)
print(model.invoke(messages).content)
结果打印:
今天西安的天气情况如下:
- **天气**:晴天转多云
- **最高气温**:约31℃
- **最低气温**:约21℃
- 预计在晚上会有降雨。
有关更详细的信息,你可以查看[中国气象局的天气预报]
(https://weather.cma.cn/web/weather/V8870.html)或其他相关天气网站。
更多推荐


所有评论(0)