LangChain 快速接入及聊天模型定义与工具调用
1. LangChain 生态分包介绍
LangChain 生态系统包含不同的包,用来准确选择要安装的功能。如下图所示:

- 底层公共依赖:
langchain-core langchain依赖langchain-corelangchain-community依赖langchainlanggraph直接依赖langchain-core- integrations 集成包(langchain-openai / langchain-postgres / langchain-anthropic 等)全部直接依赖
langchain-core)
主 langchain 包
这个包是使用 LangChain 的起点,安装方式:
pip install langchain
langchain-core 包
除了 langsmith SDK 之外,LangChain 生态系统中的所有包都依赖于 langchain-core,包含其它包使用的基类和抽象,以及 LangChain LCEL(表达式语言)。
它由 langchain 包自动安装,不需要显式安装该包。但是,如果使用的功能仅在该依赖项的特定版本本中可用,则可以选择这样做。如果这样做,则应确保已安装或固定的版本与使用的任何其他集成包兼容。
pip install langchain-core
Integrations 集成包
LangChain 的大部分价值来自于将各种能力进行集成,如各类模型集成(如 OpenAI 和 Anthropic)、各类组件集成(如数据存储、工具等)等。LangChain 中集成好的包见这里。
对于所需依赖项,需要单独安装。例如要使用 OpenAI,可以运行:
pip install langchain-openai
langchain-community 包
简单来说,任何尚未拆分到自己的包中的集成,都存在于 langchain-community 包中。安装方式:
pip install langchain-community
langgraph 包
langgraph 是一个库,用于使用 LLM 构建有状态的应用程序。它与 LangChain 顺利集成。安装方式:
pip install langgraph
LangSmith SDK
LangSmith SDK 由 LangChain 自动安装。但它不依赖于 langchain-core,如果需要,可以独立安装和使用,安装方式:
pip install langsmith
2. 快速接入OpenAI实操
2.1 申请API key并配置环境变量
1. 申请API key
以OpenAI为例,官网地址
如果没有账号先注册账号,登录成功后,出现settings图标,点击settings,选择API keys配置页面:
- 左侧菜单栏点击
API keys - 右上角齿轮
settings图标 - 点击
+ Create new secret key
点击“Create new secret key”按钮,新增API key:
- 输入key名称(自定义)
- Project选择默认项目
- Permissions权限默认All,点击
Create secret key - 创建完成后页面展示生成的密钥
将API Key在自己本地保存好,后续接入ChatGPT时需要使用。
注意:密钥只展示一次,丢失无法找回,务必复制保存。
对于不方便进行充值的友友,作者这里推荐一个还不错的中转站,PackyCode可以参考文档,进行apikey的配置;
2. 配置环境变量
Windows系统环境变量配置步骤:
- 系统变量区域点击【新建】
- 变量名输入:
OPENAI_API_KEY - 变量值粘贴申请好的完整API Key
- 点击【确定】保存编辑窗口
- 再次点击【确定】关闭环境变量总窗口
配置完成后需要重启终端/IDE才能读取到新环境变量
2.2 定义大模型
1. 安装OpenAI包
pip install -U langchain-openai
2. 定义大模型
代码:
# 定义大模型
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 1.定义OpenAI模型(使用中转站)
model = ChatOpenAI(
model="gpt-5.4",
temperature=0,
api_key="sk-XXX", # 替换成你的实际 key
base_url = "https://www.packyapi.com/v1", # 替换成你的中转站地址,或者不需要
)
2.3 定义消息列表
代码:
# 2.定义消息列表
messages = [
SystemMessage(content="你是一个有帮助的助手。"), # 这是系统消息,告诉模型它的角色和行为
HumanMessage(content="你是什么模型?请用一句话回答。"), # 这是人类消息,向模型提出问题
]
参数说明:
SystemMessage:表示系统角色消息,系统消息通常作为输入消息序列中的第一条传入,是用来启动AI行为的消息。HumanMessage:表示用户角色消息,是来自用户、从用户传递到模型的消息。
2.4 调用大模型
model 是LangChain Runnable(可运行)接口的实例,这意味着model提供了一个标准接口供我们与之交互。要简单地调用模型,我们可以将消息列表传递给 .invoke 方法。
使用 .invoke 方法进行大模型调用,代码:
result = model.invoke(messages)
print(result)
输出结果(调试可以看见 result 类型为 AIMessage):
content='我是由 OpenAI 提供的 AI 助手。' additional_kwargs={'refusal': None} response_metadata={'token_usage': {'completion_tokens': 32, 'prompt_tokens': 56, 'total_tokens': 88, 'completion_tokens_details': None, 'prompt_tokens_details': None}, 'model_name': 'gpt-5.4', 'system_fingerprint': None, 'id': 'resp_0e57bb03572b0219016a4c898b64188198b8557c0ffa53364d', 'service_tier': None, 'finish_reason': 'stop', 'logprobs': None} id='run--019f3af9-43fb-7c21-8ea0-21b13b4f39a0-0' usage_metadata={'input_tokens': 56, 'output_tokens': 32, 'total_tokens': 88, 'input_token_details': {}, 'output_token_details': {}}
我是由 OpenAI 提供的 AI 助手。

输出说明:
AIMessage:来自AI的消息。从聊天模型返回,作为对提示(输入)的响应。content:消息的内容。additional_kwargs:与消息关联的其他有效负载数据。对于来自AI的消息,可能包括模型提供程序编码的工具调用。response_metadata:响应元数据。例如:响应标头、logprobs、令牌计数、模型名称。- 侧重于“响应”本身的信息,比如这次请求的ID、使用的模型版本、以及服务商返回的所有原始元数据。它主要用于调试、日志记录和获取请求的上下文信息。
usage_metadata:消耗的量化使用元数据,例如令牌消耗计数。- 侧重于“资源消耗”的量化信息,即这次请求消耗了多少Token。它主要用于成本计算、监控和预算控制。
2.5 输出解析
若只想输出聊天模型返回的字符串,可以使用 StrOutputParser 输出解析器组件,将大模型输出结果解析为最纯粹的字符串。代码:
# from langchain字符串输出解析器
from langchain_core.output_parsers import StrOutputParser
parser = StrOutputParser()
print(parser.invoke(result))
输出结果:
你好!
2.6 链式执行
通过上述步骤,无论是调用大模型,还是输出解析,每次都调用了一个 invoke() 方法,最终才会得到我们想要的结果。
对于LangChain,它给我们提供了链式执行的能力,即我们只需要定义各个“组件”,将它们“链起来”,一次性执行即可得到最终效果。
核心代码如下:
from langchain_core.messages import HumanMessage, SystemMessage
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
# 1.定义OpenAI模型(使用中转站)
model = ChatOpenAI(
model="gpt-5.4",
temperature=0,
api_key="sk-Wxxx", # 替换成你的实际 key
base_url = "https://www.packyapi.com/v1", # 替换成你的中转站地址
)
# 2.定义消息列表
messages = [
SystemMessage(content="你是一个有帮助的助手。"), # 这是系统消息,告诉模型它的角色和行为
HumanMessage(content="你是什么模型?请用一句话回答。"), # 这是人类消息,向模型提出问题
]
# 3.调用大模型
result = model.invoke(messages) # 调用模型,传入消息列表
# 返回的其实是一个包含模型输出的对象,通常包括 content、usage 等信息,其中AIMessage表示ai返回的消息
print(result) # 输出模型的回答
# 4.定义输出解析器
parser=StrOutputParser() # 创建一个字符串输出解析器实例
print(parser.invoke(result)) # 解析模型的输出内容,并打印结果
# 5.定义链
chain=model | parser
print(chain.invoke(messages)) # 直接调用链,传入消息列表,返回解析后的结果
输出结果:
你好!
3. 引出LangChain相关概念
3.1 Runnable 接口
Runnable 接口是使用LangChain Components(组件)的基础。
概念:
- Components(组件):用来帮助我们构建应用程序时,提供了一系列的核心构建块,例如语言模型、输出解析器、检索器、编译的LangGraph图等。
- Runnable 定义了一个标准接口,允许 Runnable 组件:
invoke(调用):单个输入转换为输出。batch(批处理):多个输入被有效地转换为输出。stream(流式传输):输出在生成时进行流式传输。inspect(检查):可以访问有关 Runnable 的输入、输出和配置的原理图信息。composed(组合):可以组合多个 Runnable,以使用 LCEL 协同工作以创建复杂的管道。
因此,刚才定义的语言模型(model)、输出解析器(StrOutputParser)都是 Runnable 接口的实例。都使用了 invoke(调用)的能力,
也就是说不管是什么组件,执行逻辑统一都使用相同的接口,也就是invoke等,消除了组件之间的差异:
# 语言模型(model)
result = model.invoke(messages)
# 输出解析器(StrOutputParser)
parser = StrOutputParser()
parser.invoke(result)
3.2 LangChain Expression Language(LCEL)
LangChain Expression Language:采用声明性方法,从现有Runnable对象构建新的LangChain对象。
通过LCEL构建出的新的Runnable对象,被称为 RunnableSequence,表示可运行序列。RunnableSequence 就是一种链,通过调试步骤就能发现,chain的类型就是 RunnableSequence。RunnableSequence 也是 Runnable 接口的实例,它实现了完整的Runnable接口,因此它可以与任何其他Runnable以相同的方法使用。
chain = model | parser
# chain 是 Runnable 接口实例,允许invoke调用
LCEL其实是一种编排解决方案,它使LangChain能够以优化的方式处理链的运行时执行。任何两个Runnable实例都可以“链”在一起成序列。上一个可Runnable对象的 .invoke() 调用的输出作为输入递给下一个可运行对象。方法就是使用 |(管道运算符):
chain = model | parser
它通过两个Runnable对象去创建一个RunnableSequence。在本质上LangChain重载了|运算符,使用|运算符就相当于:
from langchain_core.runnables import RunnableSequence
chain = RunnableSequence(first=model, last=parser)
除此之外,可以使用 .pipe 方法代替。这也相当于 | 运算符:
chain = model.pipe(parser)
本人认为pipe或者RunnableSequence等方法没有直接使用|符号直观,推荐在工程项目中使用|
4. 聊天模型核心能力
4.1 定义聊天模型
大语言模型(LLM)在各种与语言相关的任务(例如文本生成、翻译、摘要、问答等)中表现出色。现代LLM通常通过聊天模型接口访问,该接口将消息列表作为输入,并返回消息作为输出,而不是使用纯文本。
这里需要注意 LLM 与 LangChain 中聊天模型 的关系:
- 在 LangChain 的官方文档中,认为 LLM 大多数是纯文本补全模型。这些纯文本模型封装的API接受一个字符串提示作为输入,并输出一个字符串补全结果(实际上LLM还包括多模态输入)。
- LangChain 中的聊天模型通常由 LLM 提供支持,但经过专门调整以用于对话。关键在于不是接受单个字符串作为输入,而是接受聊天消息列表,并返回一条AI消息作为输出。
流程图说明:
- LLM:输入单字符串 → 输出单字符串
- 聊天模型:输入多条Message列表 → 输出AIMessage
4.1.1 通过 API 定义聊天模型
方式1: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 常用初始化参数说明
| 参数名 | 参数描述 |
|---|---|
| model | 要使用的OpenAI模型的名称 |
| temperature | 采样温度,温度值越高,AI回答越离谱;温度越低,回答越靠谱。 |
| max_tokens | 要生成的最大令牌数 |
| timeout | 请求超时时间 |
| max_retries | 最大重试次数 |
| openai_api_key / api_key | OpenAI API密钥。如果未传入,将从环境变量中读取 OPENAI_API_KEY。 |
| base_url | API请求的基本URL。一般用来指定中转站 |
| organization | OpenAI组织ID。如果未传入,将从 env var OPENAI_ORG_ID 中读取。 |
| …… | 其他扩展参数 |
from langchain_openai import ChatOpenAI
model = ChatOpenAI(
model="gpt-5-mini",
temperature=0,
max_tokens=None,
timeout=None,
max_retries=2,
# api_key="...",
# base_url="...",
# organization="...",
# other params...
)
若使用其它与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,然后重新进行环境变量配置。DeepSeek API Key 申请地址
invoke() 调用
Runnable 接口中的 .invoke() 调用:该方法是将单个输入转换为对应的输出。例如对于聊天模型来说,就是根据用户的问题输入,输出相应的答案。
invoke() 方法定义
abstractmethod invoke(
input,
config: RunnableConfig | None = None,
**kwargs: Any,
) -> Any
请求参数:
input:输入一个 Runnable 实例config(默认空):用于 Runnable 的配置。
返回值:
返回一个 Runnable 实例
from langchain.chat_models import init_chat_model
from langchain_core.messages import HumanMessage
# 1. 创建可配置聊天模型
configurable_llm = init_chat_model(
temperature=0,
# 允许运行时修改这些字段
configurable_fields=["model", "model_provider", "temperature"],
config_prefix="llm" #指定修改的前缀
)
# 2. invoke 时传入 config
result = configurable_llm.invoke(
input=[HumanMessage(content="你好,介绍下自己")], #传入的是一个消息列表
config={
"run_name": "动态DeepSeek对话", # 日志追踪名称
"metadata": {"biz": "demo", "user_id": "1001"}, # 自定义业务元数据
"configurable": {
"llm_model": "deepseek-chat",
"llm_model_provider": "deepseek",
"llm_temperature": 0.7
}
}
)
class langchain_core.runnables.config.RunnableConfig 常用参数说明
| 参数名 | 参数描述 |
|---|---|
| configurable | 通过 configurable_fields() 在此 Runnable 或子 Runnable 上配置的属性的运行时值。 |
| run_id | 针对调用运行的跟踪器的唯一标识符。如果未提供,将生成新的UUID。 |
| run_name | 此调用的跟踪器运行的名称。默认为类的名称。 |
| metadata | 此次调用和任何子调用的元数据。键是字符串,值是JSON。类型:dict[str, Any] |
| …… | 其他扩展参数 |
方式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 = None,
config_prefix: str | None = None,
**kwargs: Any,
) -> BaseChatModel
init_chat_model() 常用参数说明
| 参数名 | 参数描述 |
|---|---|
| model | 要使用的模型的名称 |
| model_provider | 模型提供方。支持的 model_provider 值和相应的集成包有: • openai -> langchain-openai• anthropic -> langchain-anthropic• google_genai -> langchain-google-genai• ollama -> langchain-ollama• deepseek -> langchain-deepseek• …… 如果未指定,将尝试从模型推断 model provider。 |
| configurable_fields | 设置哪些模型参数是可配置的。若配置为: • None: 没有可配置的字段。• 'any': 所有字段都是可配置的,类似 api_key、base_url 等可以在运行时更改。• Union[List[str],Tuple[str,...]]: 指定的字段是可配置的。 |
| config_prefix | 配置为非空字符串,则模型将在运行时通过查找 config["configurable"][f"{config_prefix}_{param}"] 字段设置配置项。配置为 '' 字符串,那么配置项可以通过 config["configurable"][param] |
| temperature | 采样温度,温度值越高,AI回答天马行空;温度越低,回答越保守靠谱。 |
| max_tokens | 要生成的最大令牌数 |
| timeout | 请求超时时间 |
| max_retries | 最大重试次数 |
| openai_api_key / api_key | OpenAI API密钥。如果未传入,将从环境变量中读取 |
| base_url | API请求的基本URL。 |
| …… | 其他扩展参数 |
init_chat_model() 返回值说明
函数返回一个与指定的 model_name 和 model_provider 相对应的 BaseChatModel(如 ChatOpenAI、ChatAnthropic 等)。注意要是模型可配置,则返回一个聊天模型模拟器,该模拟器在传入配置后,于运行时才会初始化底层模型。
示例1:基本用法
使用不同的模型提供方,需要安装为其各自包,与设置各自的API Key环境变量!例如:
- OpenAI 环境变量配置为:
OPENAI_API_KEY= "your openai api key"- 安装命令:
pip install -U langchain-openai
- 安装命令:
- Anthropic 环境变量配置为:
ANTHROPIC_API_KEY= "your anthropic api key"- 安装命令:
pip install -U langchain-anthropic
- 安装命令:
- DeepSeek 环境变量配置为:
DEEPSEEK_API_KEY= "your deepseek api key"- 安装命令:
pip install -U langchain-deepseek
- 安装命令:
- Google VertexAI 环境变量配置为:
GOOGLE_API_KEY= "your google api key"- 安装命令:
pip install -U langchain-google-vertexai
- 安装命令:
- 更多见官方文档
from langchain.chat_models import init_chat_model
# 返回 langchain_openai.ChatOpenAI 实例
gpt_model = init_chat_model("gpt-5-mini", model_provider="openai", temperature=0)
# 返回 langchain_deepseek.ChatDeepSeek 实例
deepseek_model = init_chat_model("deepseek-chat", model_provider="deepseek", temperature=0)
# 由于所有模型集成都实现了ChatModel接口,因此可以以相同的方式使用它们。
print("gpt-5-mini: " + gpt_model.invoke("what's your name?").content + "\n")
print("deepseek-chat: " + deepseek_model.invoke("what's your name?").content + "\n")
输出:
gpt-5-mini: I'm called ChatGPT. How can I assist you today?
deepseek-chat: I'm DeepSeek Chat! 😊 You can call me DeepSeek or just Chat if you'd like. I'm here to help with anything you need-ask me anything!
示例2:创建可配置模型
# 可配置模型模拟器
configurable_model_1 = init_chat_model(
temperature=0
)
# 动态修改配置,初始化模型并调用
configurable_result_1 = configurable_model_1.invoke(
"what's your name?",
config={
"configurable": {"model": "gpt-5-mini"}
}
)
print("configurable1: " + configurable_result_1.content + "\n")
输出:
configurable1: I'm called ChatGPT. How can I assist you today?
示例3:具有默认值的可配置模型
# 可配置模型模拟器2
configurable_model_2 = init_chat_model(
model="gpt-5-mini",
temperature=0,
configurable_fields=("model", "model_provider", "temperature", "max_tokens"),
config_prefix="first",
)
# 动态修改配置,初始化模型并调用
configurable_result_2 = configurable_model_2.invoke(
"what's your name?",
config={
"configurable": {
"first_model": "deepseek-chat",
"first_temperature": 0.5,
"first_max_tokens": 100,
}
},
)
print("configurable2: " + configurable_result_2.content + "\n")
输出:
configurable2: My name is DeepSeek Chat! 😊 I'm here to help you with any questions or topics you're curious about. How can I assist you today?
4.1.2 通过本地部署的 LLM 定义聊天模型
ChatOllama
若想使用 ChatOllama,需要先安装 Ollama 包:
pip install -U langchain_ollama
class langchain_ollama.chat_models.base.ChatOllama 是LangChain为通过Ollama部署的聊天模型提供的具体实现类。ChatOllama 同样也实现了标准的 Runnable 接口。
ChatOllama 常用初始化参数说明
| 参数名 | 参数描述 |
|---|---|
| model | 要使用的Ollama模型的名称 |
| temperature | 采样温度,温度值越高,AI回答天马行空;温度越低,回答越保守靠谱。 |
| timeout | 请求超时时间 |
| base_url | API请求的基本URL。 |
| num_ctx | 设置用于生成下一个令牌的上下文窗口的大小。(默认值:2048) |
| num_gpu | 要使用的GPU数量。在macOS上,默认为1表示启用金属支持,默认为0表示禁用。 |
| …… | 其他扩展参数 |
示例代码
from langchain_ollama import ChatOllama
ollama_model = ChatOllama(model="deepseek-r1:70b", base_url='http://127.0.0.1:11434')
result = ollama_model.invoke("what's your name?")
print(result)
输出:
content="\n\n\n\nGreetings! I'm DeepSeek-R1, an artificial intelligence assistant created by DeepSeek. I'm at your service and would be delighted to assist you with any inquiries or tasks you may have." additional_kwargs={} response_metadata={'model': 'deepseek-r1:70b', 'created_at': '2025-08-20T06:10:54.974263Z', 'done': True, 'done_reason': 'stop', 'total_duration': 9158375400, 'load_duration': 1032569000, 'prompt_eval_count': 8, 'prompt_eval_duration': 1303997800, 'eval_count': 44, 'eval_duration': 7739543700, 'model_name': 'deepseek-r1:70b'} id='run--c62716da-3c5c-4ddf-8e76-da27dc9291a7-0' usage_metadata={'input_tokens': 8, 'output_tokens': 44, 'total_tokens': 52}
4.2 聊天模型调用工具
工具调用根本作用是让大语言模型(LLM)具备与外部世界交互的能力。
LLM本身是一个封闭的知识系统,其能力受限于其训练数据(存在滞后性)和内在的文本生成逻辑。它无法执行直接计算、查询实时信息、操作数据库或调用任何外部API。工具调用打破了这层壁垒,其作用具体体现在:
- 扩展能力边界:模型可以借助工具完成它自身无法完成的任务,如执行数学计算、搜索网络、查询数据库等。
- 保证信息实时性:通过调用搜索工具或数据库查询工具,LLM可以获取最新的、训练数据中不存在的信息,避免回答过时或“一本正经地胡说八道”。
- 处理复杂任务:将一个复杂的用户请求(如“分析我上个月的消费趋势”)分解成多个步骤,并依次调用不同的工具(如“从数据库获取数据” -> “用Python进行数据分析” -> “生成图表”)来协同完成。协调这件事更体现在Agent智能体上。
- 连接现有系统:可以将企业内部已有的系统、API和数据库封装成工具,让LLM成为一个用自然语言驱动的统一接口,极大地提升了自动化和集成能力。
4.2.1 创建工具
使用 @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 integers. ...省略...
print(multiply.args)
# 输出: {'a': {'title': 'A', 'type': 'integer'}, 'b': {'title': 'B', 'type': 'integer'}}
可以看出,工具通过@tool加Python函数实现,其中:
- 该装饰器默认使用函数名称作为工具名称。
- 该装饰器将使用函数的文档字符串作为工具的描述。
因此,函数名、类型提示和文档字符串都是传递给工具 Schema 的一部分,不可缺失。定义好的描述是使模型良好运行的重要部分。
工具 Schema的具体定义
示例1
{
"name": "张小红",
"birthDay": "1972年2月22日",
"address": "陕西省西安市雁塔区"
}
示例2
{
"surname": "王",
"givenName": "刚",
"birthDate": "1972-02-22",
"address": {
"district": "萧山区",
"city": "杭州市",
"province": "浙江省",
"country": "中国"
}
}
这两种表述同样有效,尽管示例2显然比示例1更正式。记录的设计在很大程度上取决于其在应用程序中的预期用途,因此这里没有正确或错误的答案。重要的是要确切地知道该如何组织记录。例如,当应用程序需要“一个人的JSON记录”时,这就是要确切地知道该如何组织记录。你可能见过这种可视化配置方式,实际上是在构造JSON Schema。
转换成编码方式,则为以下内容(此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,它将从函数名、类型提示和文档字符串中获取相关属性,以此来声明一个工具,包括其名称、描述、输入参数、输出类型等等,这些属性可以帮助AI更好地知道如何去调用模型以及具体调用哪一个模型。这里需要说明的是,若是简单定义工具,如上述示例,工具schema需要解析 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响应。
"""
# ... 函数实现 ...
除了 @tool 装饰器+文档字符串这种方式,还有其他方式可以让工具 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
点击运行,报错:ValueError: Function must have a docstring if description not provided.
此时,在 LangChain 中,可以使用 Pydantic 类,提供运行时数据验证和类型检查。通过 Field(description="...") 添加字段描述,LangChain 会自动提取。
注意,除非提供默认值,否则所有字段都是 required。如下所示:
from pydantic import BaseModel, Field
class AddInput(BaseModel):
"""Add two integers."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
class MultiplyInput(BaseModel):
"""Multiply two integers."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
完整代码如下所示:
# pydantic 数据验证
from pydantic import BaseModel, Field
class AddInput(BaseModel):
"""Add two integers."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
class MultiplyInput(BaseModel):
"""Multiply two integers."""
a: int = Field(..., description="First integer")
b: int = Field(..., description="Second integer")
# 定义工具
from langchain_core.tools import tool
@tool(args_schema=AddInput) # 这代表输入参数的具体含义去addinput类中找
def add(a: int, b: int) -> int:
# 未提供描述
return a + b
@tool(args_schema=MultiplyInput)
def multiply(a: int, b: int) -> int:
# 未提供描述
return a * b
注意代码中 @tool 的 args_schema 参数,它表示工具函数在未提供描述、文档字符串等需要传递给工具 Schema 的内容时,依赖 Pydantic 类使用 args_schema 参数,定义并提供工具输入参数的schema。默认为None,且将来运行时会进行数据验证。
模式2:依赖 Annotated
在 LangChain 中,可以依赖 Annotated 和文档字符串传递给工具 Schema。如下所示:
from langchain_core.tools import tool
from typing_extensions import Annotated
@tool
def add(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"],
) -> int:
"""Add two integers."""
return a + b
@tool
def multiply(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"],
) -> int:
"""Multiply two integers."""
return a * b
使用 StructuredTool 类提供的函数创建工具
class langchain_core.tools.structured.StructuredTool 类用来初始化工具,其中 from_function 类方法通过给定的函数来创建并返回一个工具。from_function 类方法定义如下:
@classmethod
def from_function(
func: Callable[..., Any] | 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属性。 - 如果配置为
"content_and_artifact",则输出应是与ToolMessage的content属性与artifact属性相对应的二元组。
- 如果配置为
示例1:常规用法
对于用该类方法创建的工具,同样函数名、类型提示和文档字符串也都是传递给工具 Schema 的一部分,不可缺失。
from langchain_core.tools import StructuredTool
def multiply(a: int, b: int) -> int:
"""Multiply two numbers."""
return a * b
calculator_tool = StructuredTool.from_function(func=multiply)
print(calculator_tool.invoke({"a": 2, "b": 3})) # 输出:6
示例2:加入配置,依赖 Pydantic 类
同样的,让工具函数不提供描述、文档字符串等需要传递给工具 Schema 的内容,如下所示:
此时可以:
- 使用
args_schema参数,依赖 Pydantic 类定义并提供工具输入参数的 Schema 属性。 - 使用
description参数,替代文档字符串中对于工具描述的 schema 属性。
from langchain_core.tools import StructuredTool
from pydantic import BaseModel, Field
class CalculatorInput(BaseModel):
a: int = Field(description="first number")
b: int = Field(description="second number")
def multiply(a: int, b: int) -> int:
return a * b
calculator_tool = StructuredTool.from_function(
func=multiply,
name="Calculator",
description="两数相乘",
args_schema=CalculatorInput,
)
print(calculator_tool.invoke({"a": 2, "b": 3})) # 输出:6
print(calculator_tool.description) # 输出:两数相乘
print(calculator_tool.args)
# 输出:{'description': 'Multiply two numbers.', 'title': 'CalculatorInput', 'type': 'object', 'properties': {'a': {'description': 'first number', 'title': 'A', 'type': 'integer'}, 'b': {'description': 'second number', 'title': 'B', 'type': 'integer'}}}
示例3:加入 response_format 配置
如果希望工具区分消息内容(content)和其他工件(artifact),让大模型读取 content,而一些用来构造 content 的原始数据保存下来,若后续有一些记录、分析的步骤,就可以派上用场。需要在定义工具时指定 response_format="content_and_artifact" 参数,并确保返回一个元组 (content, artifact):
from langchain_core.tools import StructuredTool
def multiply(a: int, b: int) -> tuple[int, dict]:
"""Multiply two numbers."""
result = a * b
artifact = {"input_a": a, "input_b": b, "calc_time": "2026-07-07"}
return result, artifact
calc_tool = StructuredTool.from_function(
func=multiply,
response_format="content_and_artifact"
)
res, art = calc_tool.invoke({"a": 4, "b": 5})
print("计算结果content:", res)
print("原始数据artifact:", art)
输出:
计算结果content: 20
原始数据artifact: {'input_a': 4, 'input_b': 5, 'calc_time': '2026-07-07'}
4.2.2 绑定工具
为了实际将这些工具绑定到聊天模型,可以使用聊天模型的 .bind_tools() 方法。如下所示:
from langchain_openai import ChatOpenAI
# 定义大模型
model = ChatOpenAI(model="gpt-4o-mini")
# ... 省略工具定义代码 ...
# 绑定工具,返回一个 Runnable 实例
tools = [add, multiply]
model_with_tools = model.bind_tools(tools)
bind_tools() 方法定义
def bind_tools(
tools: Sequence[dict[str, Any] | type | Callable | BaseTool],
*,
tool_choice: dict[str, 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(默认auto):要求模型调用哪个工具。可以设置为:- 形式为
{"tool": "<tool_name>"}的str:调用指定工具。 "auto":自动选择工具(包括无工具)。"none":不调用工具。"any"/"required"/True:强制调用至少一个工具。False:无效果,默认OpenAI的行为。
- 形式为
strict(默认None):- 如果为
True,则保证模型输出与工具定义中提供的JSON Schema完全匹配。输入也将根据Schema进行验证。 - 如果为
False,则不会验证输入,也不会验证模型输出。 - 如果为
None,则不会将strict参数传递给模型。
- 如果为
parallel_tool_calls:默认为None,允许并行工具使用。设置为False以禁用并行工具。**kwargs(Any):任何附加参数都直接传递给bind()。
返回一个 Runnable 实例。
该实例支持多种格式输入:
- 原始提示 PromptValue
- 字符串:
"上海天气如何?" - 消息或消息列表:
[HumanMessage(content="...")]
例子:
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
from typing_extensions import Annotated
@tool
def add(
a: Annotated[int, ..., "first int"],
b: Annotated[int, ..., "second int"]
)->int:
"""两数相加"""
return a+b
@tool
def multiply(
a: Annotated[int, ..., "first int"],
b: Annotated[int, ..., "second int"]
)->int:
"""两数相乘"""
return a*b
model = ChatOpenAI(
model="deepseek-v4-flash",
temperature=0,
api_key="sk-871e1db0ec8a4c31b7a7d2b3bd8e5b3c",
base_url = "https://api.deepseek.com/v1",
)
# 绑定工具
tools=[add,multiply]
model_with_tools=model.bind_tools(tools=tools)
# 调用工具
print(model_with_tools.invoke(input="请帮我计算一下 3+5 和 4*6 的结果,并返回结果。"))
该实例的输出:包含工具调用信息的 AIMessage
{
"content": "好的,我同时计算这两个表达式:",
"tool_calls": [
{
"id": "call_00_KrKtxxxrgp2DXr7l4Xsz7033",
"name": "add",
"args": {"a": 3, "b": 5},
"type": "tool_call"
},
{
"id": "call_01_txXeKOez97Cs8MLxVga26364",
"name": "multiply",
"args": {"a": 4, "b": 6},
"type": "tool_call"
}
],
"response_metadata": {
"model_name": "deepseek-v4-flash",
"finish_reason": "tool_calls",
"token_usage": {
"completion_tokens": 134,
"prompt_tokens": 385,
"total_tokens": 519,
"completion_tokens_details": {
"reasoning_tokens": 22
},
"prompt_tokens_details": {
"cached_tokens": 0
}
}
}
}
完整的业务流程应该是:定义工具->绑定工具->调用工具(先选择后调用),对于一个问题,LLM不一定选择传入的工具
输出说明
AIMessage:来自AI的消息。从聊天模型返回,作为对提示(输入)的响应。content:消息的内容。additional_kwargs:与消息关联的其他有效负载数据。对于来自AI的消息,可能包括模型提供程序编码的工具调用。response_metadata:响应元数据。例如:响应标头、logprobs、令牌计数、模型名称。
从输出结果看来,AI给出的响应是进行工具的调用,工具调用的一个关键原则是:模型根据输入的相关性决定何时使用工具。模型并不总是需要调用工具。例如,给定一个不相关的输入,模型不会调用该工具:
result = model_with_tools.invoke("hello world!")
print(result)
输出结果(AIMessage):
AIMessage(
content='Hello there! How can I assist you today?',
additional_kwargs={'refusal': None},
response_metadata={
'token_usage': {
'completion_tokens': 10,
'prompt_tokens': 82,
'total_tokens': 92,
'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-C6AoYPeJN27mQjbIsML6pIspv',
'service_tier': 'default',
'finish_reason': 'stop',
'logprobs': None
},
id='run--072c35e6-036b-421a-9817-4417005914a2-0',
usage_metadata={
'input_tokens': 82,
'output_tokens': 10,
'total_tokens': 92,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
}
)
强制模型调用工具
强制调用至少一个工具。示例如下,那就需要在绑定工具时,设置 tool_choice="any",表示强制调用至少一个工具。
result = model_with_tools.bind_tools(tools, tool_choice="any").invoke("hello world!")
print(result)
输出结果(AIMessage):
AIMessage(
content='',
additional_kwargs={
'tool_calls': [
{
'id': 'call_mBNxNY7vfAoEXEETcdEC6G1',
'function': {'arguments': '{"a":1,"b":2}', 'name': 'add'},
'type': 'function'
}
]
},
response_metadata={
'token_usage': {
'completion_tokens': 14,
'prompt_tokens': 84,
'total_tokens': 98,
'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-C6AoYPeJN27mQjbIsML6pIspv',
'service_tier': 'default',
'finish_reason': 'tool_calls',
'logprobs': None
},
id='run--072c35e6-036b-421a-9817-4417005914a2-0',
usage_metadata={
'input_tokens': 84,
'output_tokens': 14,
'total_tokens': 98,
'input_token_details': {'audio': 0, 'cache_read': 0},
'output_token_details': {'audio': 0, 'reasoning': 0}
}
)
4.2.3 工具属性
tool_calls 属性是一个列表,此属性包含执行该工具所需的一切,包括工具名称和输入参数。示例如下:
result = model_with_tools.bind_tools(tools, tool_choice="any").invoke("9乘6等于多少?")
print(result.tool_calls)
输出结果:
[{'name': 'multiply', 'args': {'a': 9, 'b': 6}, 'id': 'call_mBNxNY7vfAoEXEETcdEC6G1', 'type': 'tool_call'}]
4.2.4 将工具输出传递给聊天模型
仅仅只是成功调用了工具,聊天模型并没有返回我们真正需要的答案。此时需要:
- 解析模型输出的工具调用;
- 调用工具获取消息输入,将最终结果 AIMessage 返回。
聊天模型通常不是接受单个字符串作为输入,而是接受消息(XxxMessage)列表。因此需要将工具的返回,构造成 ToolMessage,再传输给聊天模型,如果使用 @tool 装饰器创建的工具,使用 tool.invoke(tool_calls),将自动构建一个 ToolMessage。完整示例如下:
from langchain_openai import ChatOpenAI
from langchain_core.tools import tool
from langchain_core.messages import HumanMessage
# 定义大模型
model = ChatOpenAI(model="gpt-4o-mini")
# @tool工具
@tool
def add(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"],
) -> int:
"""Add two integers."""
return a + b
@tool
def multiply(
a: Annotated[int, ..., "First integer"],
b: Annotated[int, ..., "Second integer"],
) -> int:
"""Multiply two integers."""
return a * b
tools = [add, multiply]
model_with_tools = model.bind_tools(tools)
# 添加消息
messages = [HumanMessage(content="5乘3等于多少?")]
ai_msg = model_with_tools.invoke(messages) # 这里只是在选择模型
# 遍历工具调用
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)
result = model.invoke(messages)
print(result.content)
打印结果如下:
[HumanMessage(content='5乘3等于多少?'), AIMessage(content='', additional_kwargs={'tool_calls': [{'id': 'call_mBNxNY7vfAoEXEETcdEC6G1', 'function': {'arguments': '{"a": 5, "b": 3}', 'name': 'multiply'}, 'type': 'function'}]}, 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-G6DSvhskItWl6TKPILuaDDUEN', 'service_tier': 'default', 'finish_reason': 'tool_calls', 'logprobs': None}, id='run--777ef0be-55c2-411a-a4ab-104a45c22f20-0', 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}}), ToolMessage(content='15', name='multiply', tool_call_id='call_mBNxNY7vfAoEXEETcdEC6G1')]
15
从流程与代码中可以看到,实际上调用了两次聊天模型:
- 第一次:仅将
[HumanMessage]发送给聊天模型进行处理,结果返回了【包含工具调用的 AIMessage】,并没有返回结果。然后执行工具,得到 ToolMessage。 - 第二次:将
[HumanMessage + AIMessage + ToolMessage]消息发送给聊天模型进行处理,结果返回了【包含结果的 AIMessage】。
4.3 LangChain 提供的工具
LangChain 官方也已经给我们提供了很多现成的工具(Tool)和工具包(Toolkit)。写好的工具一般都是为了使用 LangChain 中集成的三方组件或工具而创造的,有搜索、数据库、网页浏览器等相关的工具。LangChain 中的工具实际上是继承了 BaseTool 与 BaseToolkit。下面来简单使用其中一个工具类:
TavilySearch
TavilySearch 类可以支持我们进行搜索,Tavily 是一个专门为 AI 设计的搜索引擎,专为智能体检索与推理需求量身打造的工具。
Tavily 不仅提供了高度可编程的 API 接口,还具备显著优于传统搜索引擎的上下文相关性理解能力。能够以结构化、可解析的形式返回搜索结果,便于将检索到的信息直接用于后续的推理、生成或任务执行流程。
- Tavily官网:https://www.tavily.com/,需魔法使用。登录完成后,新建 API Keys。
- 点击左侧 API Playground,可以使用刚申请的 API Keys,进行搜索测试。这会返回根据查询内容得到的多条搜索结果。
- 点击左侧 Use Cases,可以试用提供好的案例,如聊天中可以支持搜索(Chat)。
在 LangChain 中接入该搜索工具,步骤如下:
- 安装 langchain-tavily 包
pip install -U langchain-tavily
-
配置环境变量
TAVILY_API_KEY,值为申请的 API Key。 -
代码接入
TavilySearch类,实现搜索功能
from langchain_openai import ChatOpenAI
from langchain_core.messages import HumanMessage
from langchain_tavily import TavilySearch
# 定义大模型
model = ChatOpenAI(model="gpt-4o-mini")
# 绑定工具
tool = TavilySearch(max_results=4) # max_results 返回的最大搜索结果
model_with_tools = model.bind_tools([tool])
# 添加HumanMessage到消息中去
messages = [
HumanMessage("中国西安今天的天气怎么样?")
]
ai_msg = model_with_tools.invoke(messages)
# 工具调用
for tool_call in ai_msg.tool_calls:
tool_msg = tool.invoke(tool_call)
# 将 ToolMessage 加入消息
messages.append(tool_msg)
result = model_with_tools.invoke(messages)
print(result.content)
结果打印示例:
根据最新气象信息,**今天(7月7日)北京的天气情况**如下:
- **天气状况**:雷阵雨 ⛈️
- **气温范围**:23°C ~ 33°C
- **风力**:南风2级
- **空气质量**:优(AQI指数约40)
**温馨提示**:
- 北京今天有雷阵雨,出门建议**携带雨具**。
- 早晚温差较大,白天最高可达33°C,体感较热,建议穿着**凉爽透气的衣物**。
- 近期北京处于多雨季节,雷雨天气可能伴有短时大风,出行请注意安全。
如果想了解未来几天的详细预报,随时可以问我哦!
更多推荐
所有评论(0)