【LangChain】Python agent LangChain入门开发教程
本文介绍了LangChain开发教程和AI基础知识。首先讲解了神经网络结构的基本组成,包括输入层、隐藏层和输出层。然后详细阐述了大语言模型的核心技术——词向量和Transformer模型的工作原理,包括自注意力机制、SoftMax层等关键组件。文章还探讨了模型涌现现象和大模型应用架构,比较了GPT与ChatGPT的区别,并列举了国内外主要的大模型云服务平台。最后介绍了私有部署方案和使用DeepSe
介绍
AI通识
欢迎来到 LangChain 入门开发教程。在你已经掌握了大模型基础、Python 环境配置和 API 调用之后,LangChain 就是帮你把这些零散能力串联起来的关键工具。它不仅提供了统一的模型调用接口,更封装了对话记忆、提示词工程、工具调用、智能体编排等复杂逻辑,让你不用重复造轮子,就能快速构建出可扩展的大模型应用。
LangChain入门
在这篇教程里,我们会从「认识 LangChain 架构」开始,一步步带你完成模型初始化、消息处理、提示词工程,再到 Agent 与工具调用的完整流程。无论你是想做一个对话机器人,还是搭建更复杂的智能体系统,都能在这里找到清晰、可落地的入门路径。
AI通识
学习Agent之前,我们先要了解ai最基础的模型—神经网络结构
神经网络结构
-
AI的核心是Transformer,这是一种神经网络架构,本质就是在模仿人类的神经元,将千千万万个神经元连接起来
-
深度神经网络分为很多层(Layer),是神经网络基本的计算单元,分为:
-
输入层(Input Layer):入口,接收数据
-
隐藏层(Hidden Layer):信息处理和学习,可以有很多层
-
输出层(OutPut Layer):出口,产生结果

-
大语言模型
-
词向量(Word Embedding) 就是把词转为多维空间向量的一种技术
- 首先,将人类自然语言文字拆分为一个一个片段,成为Token(词)
- 每个Token都经过模型计算转为一个浮点数数组,作为向量坐标

- 目标:使词向量在多维空间中的不同方向表达不同的语义,就比如说一个 只有两个元素的向量组 可以表示为一个二维向量 三个元素 那么就是三维 每一个向量表达一个语义

-
经过模型的一些训练 某两组不同的向量相加或相减可以表示一种相同的语义 比如说下图

很明显 这两组向量相减表达的都是一种性别上的差异
那么 有什么用呢?
经过数学上的向量运算 是不是可以得到E(妈妈) = E(爸爸) + E(女人) - E(男人) 是不是很神奇!Transformer模型
2017 年Google发表的论文《Attention is All You Need》中提出了Transformer模型,其中提出的自注意力机制(self-attention)使模型能更高效的根据上下文信息处理token,理解token含义。
我们举个例子:
给你一个词:知名男艺人 是一个向量 擅长唱歌 又是一个向量 跳舞 又是一个向量 打篮球… 我们通过向量相加可以得到一个向量
Transformer模型 不单单只是词向量 他还有Attention层来进行微调整 MLP层进行逻辑推理…依次循环
SoftMax层
Transfromer还有一个SoftMax层 他会根据模型计算出的向量结果得出下一个token的概率分布 然后基于概率的随机采样方式给出一个结果 这个概率受模型的Temperature参数影响 值越大 概率分布越均匀 模型生成的结果随机性越强 反之 就越确定
简单来说 就是词向量经过Attention层 MLP层计算出的结果 SoftMax在那个向量附近进行搜索得出可能结果
得到一个可能的 推 字(不一定是荐 它只是可能性最大)我们把他与前文拼在一起 然后传入Transformer 可能得到下图的结果
依次循环 这样我们是不是就可以得到一个回答
模型涌现现象
读到这里 你们是不是有一个疑问 Transformer就是算概率? 不能吧?
不要急我们看下图的一组数据
这是OpenAi 的GPT模型的模型参数增长 可以看到 非常的迅猛 2021 到 2022 甚至翻了10倍不止
Transformer本来是作为翻译用的,但当模型规模和训练数据量增突破某个临界点时,模型像是突然拥有了智能,不仅可以理解人类语言,还能推理、分析。这种现象被称为“涌现”(Emergent Abilities)。这种超大规模的语言模型则被称为大语言模型(Large Language Model)
是不是感觉很神奇 为什么数据量大了就感觉 模型像 开智 了一样
人类就不管了 反正量变产生质变!
大模型应用
你们觉得GPT 跟 ChatGPT是同一个东西吗
其实不是 让我们看看两者区别在哪
GPT
GPT是OpenAI研发的一个基于Transformer架构的大语言模型
可以看到 三个字母分别代表的意思不就是我们上面学的大语言模型吗
大模型
大语言模型(Large Language Model, LLM)是指模型的参数规模、训练数据量、训练成本、智能程度都远超普通模型的一种神经网络模型。
ChatGPT是基于GPT模型的一种对话产品 是一个对话机器人
对话机器人
对话机器人(ChatBot)是指可以与用户聊天,答疑,且具有记忆的大模型应用 例如:Qwen ChatGPT
如何转换LLM与ChatBot?
我们怎么才能把一个生成式模型变成一个对话机器人呢?
比如说 我们先告诉模型 我们将要进行一个对话 是一个用户与一位可靠的,知识渊博的AI助手之间的
然后 我们将用户的问题拼接在上面的基础之上 再传入一个AI助手的空回答 传入Transformer
这样 我们是不是可以得到大模型的回答 这是第一步 但是不仅仅于此 豆包之类的聊天机器人 是不是还有联网搜索等等的功能 大模型显然不具备这种能力
所以我们就需要将传统编程与大模型结合 实现ChatBot 就是大模型应用
大模型应用
大模型应用是基于大模型的推理、分析、生成能力,结合传统编程精确计算控制能力,开发出的程序应用。
大模型应用结构
大模型应用通常是由传统 web 应用作为入口,再由传统应用调用大模型服务提供的基于 HTTP 协议的 API 接口,实现用户与大模型的交互。
大模型服务
大模型云服务
国内知名的云服务平台都提供了国内知名的大模型API开放服务,只要花钱,无需部署就能访问
| 云平台 | 公司 | 地址 |
|---|---|---|
| 阿里百炼 | 阿里巴巴 | https://bailian.console.aliyun.com |
| 腾讯 TI 平台 | 腾讯 | https://cloud.tencent.com/product/ti |
| 千帆平台 | 百度 | https://console.bce.baidu.com/qianfan/overview |
| SiliconCloud | 硅基流动 | https://siliconflow.cn/zh-cn/siliconcloud |
| 火山方舟 - 火山引擎 | 字节跳动 | https://www.volcengine.com/product/ark |
国内还有很多模型厂商 只提供了自家模型的API服务
| 公司 | 模型 | 地址 |
|---|---|---|
| 深度求索 | deepseek | https://platform.deepseek.com/ |
| 月之暗面 | kimi | https://www.moonshot.cn/ |
| 智谱 | glm | https://open.bigmodel.cn/ |
后续我们会以阿里云百炼平台跟deepseek为例
私有部署
本地部署最简单的一种方案就是使用ollamma,官网地址:https://ollama.com
使用一个云服务
我们先登录注册https://platform.deepseek.com/一个账号 然后充值一元 不然用不了 一元足够学习完这套LangChain课程 然后创建一个APIkey 注意保存 因为关闭后不可查看 他相当于使用ds的身份证
调用API
可以通过http请求或者代码调用 这里就先演示http请求
curl https://api.deepseek.com/chat/completions \
-H "Content-Type: application/json" \
-H "Authorization: Bearer ${DEEPSEEK_API_KEY}" \
-d '{
"model": "deepseek-chat",
"messages": [
{"role": "system", "content": "You are a helpful assistant."},
{"role": "user", "content": "Hello!"}
],
"stream": false
}'
这是deepseek官方给的接口跟请求体body
使用任意一个可以发送网络请求的工具 我这里使用的是ApiFox
操作方法如图所示 请求只能是POST
记得在请求头里填上自己的apikey
注册阿里云百炼
这里依然是创建账号 然后创建一个APIKEY 这个APIKEY后续是可以查看的
阿里云百炼会给新号特别多的token 所以我们就不用充值了
这里就不多追叙了
要调用阿里云的模型 依旧是看官方文档 这里就演示最基础的http
curl -X POST https://dashscope.aliyuncs.com/compatible-mode/v1/chat/completions \
-H "Authorization: Bearer $DASHSCOPE_API_KEY" \
-H "Content-Type: application/json" \
-d '{
"model": "qwen-plus",
"messages": [
{
"role": "system",
"content": "You are a helpful assistant."
},
{
"role": "user",
"content": "你是谁?"
}
]
}'
header带上自己的apikey
模型私有化部署
这里最便捷的一种方式就是使用Ollamahttps://ollama.com
Ollama有两种下载方式
- 一种就是使用命令行下载 但是不推荐 这样会默认安装在C盘

- 第二种就是安装包
安装包下载完成 在OllamaSetup.exe所在目录打开cmd命令行,然后命令如下
OllamaSetup.exe /DIR=你要安装的目录位置
运行命令后,同样会弹出安装窗口 但是安装的位置已经是你自己设置的了
接下来 因为Ollama的模型存放位置默认是在C盘的 我们需要修改
- 打开Ollama客户端

-
点击Settings 更改Model location为自己想存放的位置

模型部署 -
打开Ollama的models目录 选择一个自己想要拉取的大模型

这里有很多可供选择的模型 我这里就选择最新的qwen3.5 -
这里模型有很多的参数版本 如4b 9b 这里的参数越大 模型的能力就越强 我们要根据自己电脑的配置 进行选择 我这里就选择2b的

-
命令就是官网给的命令加上:xb

这里就是ollama run qwen3.5:2b -
或者可以直接使用Ollama客户端进行使用 但是这种方式删除模型 管理模型就不方便了
调用Ollama服务
curl http://localhost:11434/api/chat -d '{
"model": "gemma3",
"messages": [{ "role": "user", "content": "Hello!" }]
}'
Ollama默认是11434端口 依旧使用apifox发送请求测试
接口规范
大模型接口基本都遵循OpenAI规范,所以都大同小异:
- model:必填,模型名称(如 gpt-4o, qwen3:4b, llama3:8b)
- messages:必填,对话列表
- role:
- system:设定 AI 人设(系统提示)
- user:用户输入
- assistant:AI 回复
- tool:工具调用结果
- content:文本内容(多模态可传图片 URL/base64)
- stream:true= 流式(SSE),false= 一次性返回
- temperature:采样温度,越高越随机(0 = 完全确定)
- max_tokens:生成内容的最大 token 数
会话记忆
大模型的接口是没有记忆的 比如说我发一个这样的请求:
但是下一次发请求问AI的时候 发现AI是并没有记住我是谁
所以 为了让AI记住上下文的内容 我们需要把对话上下文拼接在一起 再传给大模型的API 这样大模型才有了会话记忆
总结:我们需要把对话的上下文拼在一起 大模型才会拥有会话记忆
Python环境管理
uv
这里我们强烈建议使用 uv uv是一个轻便管理Python环境的工具
这里是uv中文版网站 https://uv.doczh.com/
使用网站给的命令安装好uv后 我们新建一个文件夹用于测试uv
进入测试文件夹 右击打开cmd 输入此命令
uv就会自动帮你初始化项目
3.13指的是python版本 如果你没有3.13 uv会帮你自动下载
uv就会自动帮你初始化项目
接下来 我们用任意的python解释器打开项目 会发现工程项目下多了许多文件
ai.py是我提前随意写的一个python测试文件 不必理会
pyproject.toml是依赖管理文件夹 将来在uv中下载的任意依赖都会出现在dependecies列表中
例如 我们随便下载一个openai的依赖
在项目终端执行
uv add openai

uv就自动帮我们引入了openai依赖 并且dependencies中也出现的openai依赖的记录
配置镜像源
默认情况下,uv下载依赖是到国外站点:https://test.pypi.org/simple,速度很慢。推荐大家将下载的镜像源改为国内站点。
uv支持项目级配置和系统级配置两种方案,项目级优先级高,但是需要每个项目都配置,比较麻烦。推荐采用系统级配置。
系统配置方式如下:
- Windows系统,在CMD运行如下命令:
setx UV_DEFAULT_INDEX "https://pypi.tuna.tsinghua.edu.cn/simple"
- MacOS或Linux系统:
echo 'export UV_DEFAULT_INDEX=https://pypi.tuna.tsinghua.edu.cn/simple' >> ~/.zshrc && source ~/.zshrc
创建项目
接下来我们就创建一个项目,我们会使用PyCharm作为开发工具,以uv作为项目管理工具。
第一步,打开PyCharm,创建Project:
为了方便大家学习,我们会使用jupyter,所以需要在项目中引入notebook依赖。
在PyCharm中,左侧有一个Terminal按钮,点击即可打开终端:
如图:
在终端中输入命令:
uv add notebook
测试
为了测试环境,我们创建一个notebook试试:
然后在代码框中编写打印HelloWorld的代码,快捷键SHIFT + ENTER即可运行
编程调用大模型
OpenAI SDK
OpenAI作为全球最早,也是最火的大模型公司之一,占据了先发优势。因此其制定的API规范几乎成为了大模型API的默认规范,几乎所有的大模型API都兼容OpenAI的规范。
在任何模型的官方文档中都能看到基于OpenAI提供的SDK的代码示例,例如DeepSeek:
https://api-docs.deepseek.com/zh-cn/
本节我们来学习如何使用OpenAI提供的SDK工具来访问大模型。
首先,我们需要安装OpenAI的SDK,以python为例:
- 使用uv安装:
uv add openai
接下来,就可以使用SDK调用任何兼容OpenAI规范的模型了,只要将base_url和api_key设定为对应的模型提供者的url和api_key即可:
from openai import OpenAI
client = OpenAI(
api_key="sfxxxxx",
base_url="https://api.deepseek.com"
)
print("🚀 正在调用大模型...")
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "你是一名友好的AI助教。"},
{"role": "user", "content": "你好,你是谁?"}
],
stream=False
)
print(response)
环境变量
将api_key直接写在代码中非常危险,所以通常我们都将其写入环境变量,程序运行时加载。
第一步,配置环境变量。
在项目根目录创建一个.env文件:
在其中配置自己的API_KEY,我们以Deepseek为例:
# deepseek
DEEPSEEK_API_KEY=sk-1234567890
# 阿里云
DASHSCOPE_API_KEY=sk-1234567890
第二步,安装python-dotenv。
在项目中,我们通过python-dotenv库来读取环境变量,所以要先安装依赖。
uv add python-dotenv
安装成功后,会在pyproject.toml中看到新添加的依赖:
[project]
name = "lc-course"
version = "0.1.0"
description = "Add your description here"
requires-python = ">=3.13"
dependencies = [
"notebook>=7.5.5",
"openai>=2.28.0",
"python-dotenv>=1.2.2",
]
第三步,读取环境变量。
最后,我们就可以在代码中读取环境变量了:
from openai import OpenAI
from dotenv import load_dotenv
import os
加载环境变量
load_dotenv()
client = OpenAI(
api_key=os.getenv("DEEPSEEK_API_KEY"),
base_url="https://api.deepseek.com"
)
print("🚀 正在调用大模型...")
response = client.chat.completions.create(
model="deepseek-chat",
messages=[
{"role": "system", "content": "你是一名友好的AI助教。"},
{"role": "user", "content": "你好,你是谁?"}
],
stream=False
)
print(response)
LangChain
认识LangChain
LangChain 由 Harrison Chase 创建于2022年10月,是用于开发智能体工程(Agent Engineering)的平台。
官网地址:
https://www.langchain.com/
官网文档:
https://docs.langchain.com/
架构体系
LangChain并不仅仅是一个框架,而是一整个智能体开发平台,包含很多不同的组件。
其中,包含一系列开源的智能体(Agent)开发框架,而且兼容Python和TypeScript两种语言:
- LangChain:用于快速构建智能体,可兼容任何模型提供商。
- LangGraph:从底层一步步控制智能体的构建,包括记忆(Memory)、人机协同(HITL)等
- Deep Agents:用于构建复杂的、处理多步骤的任务的智能体
Deep Agents 底层依赖于LangChain -> LangChain底层依赖于LangGraph
什么是Agent
在人工智能领域,Agent(通常翻译为智能体或代理) 是指一种能够感知环境,进行推理,自主决策并采取行动以实现特定目标的智能系统
传统聊天机器人(ChatBot)是只能对话的AI,而Agent类似于钢铁侠中的贾维斯 它可以帮你实现各种各样的操作 包括订机票 查天气 今年过年的时候 阿里千问请你喝奶茶活动 你给他发送 千问,帮我下单一杯奶茶 他会自动帮你点单 这就是agent的操作
快速入门
准备工作
首先,要使用LangChain必须先安装依赖,命令如下:
uv add langchain
LangChain支持各种不同的模型,而且提供了对应的兼容SDK,不过也都需要安装对应依赖,你可以按需添加:
集成 DeepSeek
uv add langchain-deepseek
集成 OpenAI
uv add langchain-openai
集成 Anthropic
uv add langchain-anthropic
代码示例
接下来就可以开发Agent了,基本步骤如下:
- 加载环境变量
- 定义工具
- 定义Agent
- 调用Agent
Langchain提供了create_agent方法用来快速创建Agent,我们只需要提供好Agent所需的模型(Models)、工具(Tools)即可。
示例代码如下:
# 1.加载环境变量
from dotenv import load_dotenv
load_dotenv()
# 2.定义工具,基础版,通过注释描述工具
@tool
def getWeather(location: str) -> str:
"""
Get the weather in a given location.
Args:
location: city name or coordinates
"""
return f"Current weather in {location} is sunny"
# 3.定义Agent
agent = create_agent(
"deepseek-chat", # 模型名称(必须是LangChain支持的模型)
tools=[getWeather] # 工具集
)
# 4.调用模型
print("🚀 正在调用大模型...")
response = agent.invoke({
"messages": [
{"role": "user", "content": "杭州今天天气如何?"}
]
})
# 5.打印结果
print(response)
LangChain会自动去加载.env里面的环境变量APIKEY,不需要手动指定
上述代码为快速入门使用 只是为了体验agent开发
原本大模型不具备查询天气的能力,所以无法回答天气问题。但是,当我们提供了一个查询添加的Tool以后,它就能自动查询天气来回答问题
Agent是如何做到的呢?
传统的聊天机器人都是一问一答的模式,只能通过预训练的数据进行回答,流程非常简单
而智能体则可以调用工具与外界交互,获取实时信息,工作流程则要复杂很多,是这样的:
流程如下:
- 用户提问(Input):杭州今天天气如何?
- 模型分析(Reasoning):用户询问杭州天气,我不知道,需要调用查询天气的工具get_weather
- 调用工具(Action):调用工具,get_weather,传入城市"杭州"
- 分析结果(Observation):工具返回结果,模型分析结果,判断是否足以回答用户问题
- 是:整理生成响应结果
- 否:重复前面步骤
- 生成结果(Output):根据工具的结果生成响应给用户
模型如何调用tool
那么,模型是如何知道工具的信息的呢?
其实,在大模型提供的API接口中,有一个tools参数,描述了工具的详细信息:
就像上面我们写的代码
@tool
def getWeather(location: str) -> str:
"""
Get the weather in a given location.
Args:
location: city name or coordinates
"""
return f"Current weather in {location} is sunny"
给tool传进去一个名为getWeather 的函数
Get the weather in a given location.
Args:
location: city name or coordinates
这个是对于这个函数的描述 是当给定一个区域需要获取天气时调用 用于提示大模型
所以,LangChain会帮助我们把tool的信息封装为此tool参数,与message一起发送给大模型,大模型就了解tool的详细信息,根据用户需求判断是否需要调用tool,需要调用哪个tool.
那么问题来了,当大模型决定调用某个tool时,该如何调用呢?毕竟,tool是我们定义的,模型是没有调用能力的。
模型确实不能直接调用tool,只能返回字符串。但是它可以把要调用的tool信息、参数信息都以Json格式返回:
这样一来,LangChain就会帮我们解析响应结果中的Function信息,也就是tool信息,就知道了要调用哪个函数,以及参数是什么了。LangChain就会执行该函数,再把得到的结果再次发送给大模型。
简单来说 模型会把想要调用的工具返回给LangChain 然后LangChain帮大模型去调用tool 再把tool的结果发送给大模型进行分析
具体的工作流程如图:
OK,弄明白了Agent的原理,我们不难发现,Agent中最重要的两个部分,就是:
- Model:负责推理分析、思考,相当于Agent的大脑
- Tools:负责执行任务,相当于Agent与外界交互的手脚
当然,Agent中肯定不止这两个部分,接下来,我们就逐一解析Agent创建的各个细节。
初始化模型
模型(Models)
LangChain支持现在市面上大部分常见的大语言模型(LLM),并且提供了各个模型的对应依赖库
初始化方式
langchain提供了两种常见方法用来初始化模型:
- 使用init_chat_model函数,由langchain自动创建模型对象
- 使用不同模型对应的Model类,手动创建模型对象
init_chat_model
在LangChain中开始使用独立模型的最简单方法是使用init_chat_model函数。
调用init_chat_model函数时,你需要从langchain支持的模型提供者(Model Provider)中选择一个模型,而langchain会自动初始化这个模型,非常方便。
例如,我们要使用Deepseek这个模型。
- 首先,我们需要安装模型依赖:
uv add langchain-deepseek
- 然后,我们要确保在项目的.env环境中配置好api_key:

DEEPSEEK_API_KEY=你自己的APIKEY
- 最后,就可以直接使用init_chat_model初始化模型了:
# 导入Langchain的初始化模型的函数
from langchain.chat_models import init_chat_model
# 加载环境变量
from dotenv import load_dotenv
load_dotenv()
# 调用init_chat_model函数初始化模型,参数model用来指定模型名称,Langchain会根据模型名字自动设定base_url,并从环境变量中获取api_key
model = init_chat_model(model="deepseek-chat")
- 测试,我们可以通过打印model的类型,查看生成的结果:
print(type(model)) # <class 'langchain_deepseek.chat_models.ChatDeepSeek'>
可见,采用init_chat_model自动初始化模型时,模型的类型由LangChain通过模型名称自动推断。
Tips: init_chat_model方式初始化的模型 可调用的只有LangChain官网支持的模型 这些模型并不多 两种用户自己根据需要进行选择
自定义模型及参数
init_chat_model默认会根据模型名称自动确定模型的提供者、其base_url,并从env读取api_key,但前提是必须是langchain支持的模型提供者(支持模型参考链接),例如:
- Openai
- Deepseek
- Anthropic
- …
对于其它不支持的模型,我们必须自定义模型参数来访问。
例如,我们要访问阿里云百炼的qwen-max,它就是不被langchain支持的模型,我们必须自定义模型参数来访问。
- 我们需要在环境变量中定义api_key和base_url
- 然后在init_chat_model中指定model、model_provider、base_url和api_key
具体步骤如下: - 首先,在.env中配置好api_key和base_url:
DASHSCOPE_API_KEY=你自己的APIKEY
DASHSCOPE_BASE_URL=https://dashscope.aliyuncs.com/compatible-mode/v1
- 然后,手动读取环境变量中的api_key和base_url:
import os
base_url = os.getenv("DASHSCOPE_BASE_URL")
api_key = os.getenv("DASHSCOPE_API_KEY")
- 最后,调用init_chat_model,初始化模型:
# 初始化模型
model = init_chat_model(
model="qwen-max", # 模型名称,这里可以自定义,我们用的是阿里的qwen-max
model_provider="openai", # 如果是Langchain不支持的模型,需要指定模型提供者(虽然我们用的是阿里,但是阿里兼容openai,所以这里用openai,就是默认采用openai的API规范)
base_url=base_url,
api_key=api_key
)
- 测试,查看生成的模型类型:
print(type(model)) # <class 'langchain_openai.chat_models.base.ChatOpenAI'>
可见,通过参数自定义模型时,模型的类型由model_provider参数类决定
除了修改模型提供者以外,init_chat_model方法允许我们调整模型参数,例如:
- temperature: 控制生成文本的随机性,值越小越确定,值越大越随机
- max_tokens: 控制生成文本的最大长度
- top_p: 控制生成文本的多样性,值越小越多样,值越大越确定
- timeout: 控制生成文本的超时时间
- max_retries: 控制生成文本的最大重试次数
- …
示例:
# 调用init_chat_model函数初始化模型,并设定模型参数
model = init_chat_model(
model="qwen-max",
model_provider="openai",
base_url=base_url,
api_key=api_key,
temperature=1.5,
)
老方案-使用Model类
其实init_chat_model方法底层就是帮我们利用Model类创建对象。但只支持有限的模型。而在langchain的社区,除了langchain官方提供的Model,还有些类是社区提供,更丰富多样。
具体支持的模型,可以查看官网地址:
https://docs.langchain.com/oss/python/integrations/chat
例如,我们使用社区版本的Model类来访问阿里云百炼的通义千问模型:
- 首先,我们需要安装依赖
LangChain社区依赖:
uv add langchain-community
阿里云百炼依赖:
uv add dashscope
- 然后,我们就可以使用Model类初始化模型了
from langchain_community.chat_models.tongyi import ChatTongyi
# 使用Model类初始化模型
model = ChatTongyi(
model="qwen-plus"
# 其它模型参数...
)
- 测试,查看生成的模型类型:
print(type(model)) # <class 'langchain_community.chat_models.tongyi.ChatTongyi'>
调用模型
LangChain提供了两个不同的方法来访问模型:
- invoke:阻塞式访问
- stream:流式访问
invoke
invoke方法是阻塞式调用,需要等待模型生成全部结果才会返回,等待时间较长。
response = model.invoke([
{
"role": "System",
"Content": "你扮演宇智波佐助"
},
{
"role": "User",
"Content": "你是谁?"
}
])
但是 我们平常对话 大部分时间是不需要给AI设置身份的 所以System就不用写 LangChain也给我们提供了简单的调用方法
# 调用invoke方法
response = model.invoke("月亮的首都是哪里?")
# 查看响应结果
print(response)
stream
阻塞式调用需要等待较长时间才能看到AI返回的结果,而流式调用则可以实时看到AI返回的一个个词。
示例:
# 通过.stream方法实现流式访问
stream = model.stream("月亮的首都是哪里?")
# stream调用返回的结果是一个generator,方便我们循环获取结果
print(type(stream))
# 遍历stream结果,实时打印AI的回复
for chunk in stream:
print(chunk.content, end="", flush=True)
当然 stream也是可以正常用message调用的
# 通过.stream函数实现流式访问
stream = model.stream([
{
"role": "system",
"content": "你扮演宇智波佐助,用户是鸣人,模仿佐助对鸣人口吻回答"
},
{
"role": "user",
"content": "你为什么要跟随大蛇丸 佐助! 快回来吧"
}
])
在Agent中使用模型
Langchain提供了一个create_agent方法用来快速创建智能体。当我们创建Agent的时候,可以直接使用创建好的Model,也可以指定模型名,让Langchain自动初始化模型。
create_agent调用时有两种选择:
- 使用初始化好的模型对象
from langchain.agents import create_agent
from langchain_community.chat_models.tongyi import ChatTongyi
# 1.使用Model类初始化模型
model = ChatTongyi(
model="qwen-plus"
# 其它模型参数...
)
# 2.使用初始化好的model创建智能体
agent = create_agent(model=model)
- 使用模型名称,让LangChain自动初始化模型
from langchain.agents import create_agent
# 指定Model名称,由LangChain自动初始化模型
agent = create_agent(model="deepseek-chat")
使用模型名称这种就是底层调用了init_chat_model
调用智能体
智能体也分为阻塞调用和流式调用两种。
- 阻塞式调用,使用invoke方法:
# 2.调用模型,需要传入一个消息列表
response = agent.invoke({
"messages": [{"role": "user", "content": "月亮的首都是哪里?"}]
})
print(response)
- 流式调用,只需要把调用方式改为stream:
# 通过stream函数实现流式访问
messages = agent.stream(
{"messages": [{"role": "user", "content": "你是谁?"}]},
stream_mode="messages"
)
print(type( messages))
接下来依然是 流式打印stream
# 遍历stream结果,实时打印AI的回复
for token, metadata in messages:
if token.content: # Check if there's actual content
print(token.content, end="", flush=True) # Print token
消息
在调用模型时,发送给LLM的消息、LLM返回的消息都包含以下几部分内容:
- role:消息所属角色,可以是system、user、assistant
- content:消息的内容
- metadata(可选):消息的元数据,例如:消息的ID、消耗的token等
之前我们都是自己用dict模拟消息:
response = agent.invoke({
"messages": [{"role": "user", "content": "月亮的首都是哪里?"}]
})
这太麻烦了。在LangChain中发送给LLM的消息、LLM返回的消息都统一被封装为BaseMessage,它是中基本的上下文单元。
消息类型
在LangChain中,我们并不需要自己创建BaseMessage对象,LangChain已经把常见消息根据角色(Role)创建了对应的BaseMessage的子类:
- SystemMessage:role是system,代表系统消息,用于设定模型角色和交互背景
- HumanMessage:role是user,代表用户输入的消息
- AIMessage:role是assistant,代表LLM生成的响应,包含:文本、工具调用、元数据
- ToolMessage:role是tool,代表工具调用时产生的结果
所以,我们可以这样传递消息列表:
from langchain.messages import HumanMessage, AIMessage
from langchain.agents import create_agent
# 创建Agent
agent = create_agent(model="deepseek-chat")
# 调用Agent,发送消息
response = agent.invoke({
"messages": [
HumanMessage(content="你好,我是虎哥"),
AIMessage(content="你好,虎哥,很高兴认识你。"),
HumanMessage(content="我的名字是什么?")
]
})
print(response)
注意看,Agent的返回结果中包含完整的消息列表(Messages):
{‘messages’: [HumanMessage(content=‘你好,我是虎哥’, additional_kwargs={}, response_metadata={}, id=‘f5703ee9-f567-48d6-8e07-e6ddaf24547e’), AIMessage(content=‘你好,虎哥,很高兴认识你。’, additional_kwargs={}, response_metadata={}, id=‘5c654447-828c-43b7-9505-a341e0d21b8a’, tool_calls=[], invalid_tool_calls=[]), HumanMessage(content=‘我的名字是什么?’, additional_kwargs={}, response_metadata={}, id=‘a3390334-85b8-4f5f-8528-782a18671ac9’), AIMessage(content=‘你刚才提到你的名字是“虎哥”。如果这是你希望我称呼你的方式,我会记住的。如果有其他偏好,随时告诉我哦! 😊’, additional_kwargs={‘refusal’: None}, response_metadata={‘token_usage’: {‘completion_tokens’: 33, ‘prompt_tokens’: 26, ‘total_tokens’: 59, ‘completion_tokens_details’: None, ‘prompt_tokens_details’: {‘audio_tokens’: None, ‘cached_tokens’: 0}, ‘prompt_cache_hit_tokens’: 0, ‘prompt_cache_miss_tokens’: 26}, ‘model_provider’: ‘deepseek’, ‘model_name’: ‘deepseek-chat’, ‘system_fingerprint’: ‘fp_eaab8d114b_prod0820_fp8_kvcache’, ‘id’: ‘9ea39267-c54a-4523-82e0-1377435ffde4’, ‘finish_reason’: ‘stop’, ‘logprobs’: None}, id=‘lc_run–019cad79-8b7a-7861-855b-5a87ba11d38c-0’, tool_calls=[], invalid_tool_calls=[], usage_metadata={‘input_tokens’: 26, ‘output_tokens’: 33, ‘total_tokens’: 59, ‘input_token_details’: {‘cache_read’: 0}, ‘output_token_details’: {}})]}
我们可以通过遍历Messages数组,更友好的打印结果:
for message in response['messages']:
message.pretty_print()
多模态消息
之前我们都是向模型发送文本消息,但是 LangChain 也支持向模型发送多模态消息,比如图片、音频、视频、文本等。但前提是必须是多模态模型才支持。
一些支持多模态的模型有:
- qwen3.5-plus
- gpt-5-nano
- …
我们以qwen3.5-plus为例,演示向模型发送图片消息
在线图片
首先,我们演示如何发送一个在线图片给模型,也就是指定模型的url地址。
这是用来演示的图片:
消息格式如下:
{
"role": "user",
"content": [
{"type": "image", "url": "https://xxx.com/a.jpeg"},
{"type": "text", "text": "这些图描绘了什么内容?"}
]
}
图片多模态消息有两种定义方式:
1.经典message写法
message = {
"role": "user",
"content":[
{"type": "text", "text": "描述以下这张图片的内容."},
{"type": "image", "url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"}
]
}
2.使用HumanMessage包装类
# 准备多模态消息
message = HumanMessage([
{"type": "text", "text": "描述以下这张图片的内容."},
{"type": "image", "url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"},
])
完整示例代码:
from langchain.chat_models import init_chat_model
import os
# 1.初始化模型
model = init_chat_model(
model="qwen3.5-plus", # 这里选择qwen3.5-plus,这是一个多模态模型,支持图片、文本、音频、视频
model_provider="openai",
base_url=os.getenv("DASHSCOPE_BASE_URL"),
api_key=os.getenv("DASHSCOPE_API_KEY")
)
# 2.创建智能体
agent = create_agent(model=model)
# 3.组织多模态消息
multimodal_message = HumanMessage(
content=[
{"type": "image",
"url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyrja/dog_and_girl.jpeg"},
{"type": "text", "text": "这些图描绘了什么内容?"}
])
# 4.调用Agent,发送多模态消息
for token, metadata in agent.stream({
"messages": [multimodal_message]
}, stream_mode="messages"):
if token.content:
print(token.content, end="", flush=True)
本地图片
所谓本地图片,就是用户上传的图片数据或者本地存在的图片,而不是图片的url地址。我们需要将图片数据转换成base64字符串,然后发送给模型。
本地图片的消息格式:
{
"role": "user",
"content": [
{"type": "text", "text": "Describe the content of this image."},
{
"type": "image",
"base64": "AAAAIGZ0eXBtcDQyAAAAAGlzb21tcDQyAAACAGlzb2...",
"mime_type": "image/jpeg",
},
]
}
有时候用户会上传图片数据,而不是图片的url地址。我们需要将图片数据转换成base64字符串,然后发送给模型。
代码示例:
import base64
# 例如,有一个用户上传的文件,是字节格式img_bytes,我们先将其进行base64编码
img_b64 = base64.b64encode(img_bytes).decode("utf-8")
# 组织多模态消息
multimodal_question = HumanMessage(content=[
{
"type": "image",
"base64": img_b64,
"mime_type": "image/jpeg",
},
{"type": "text", "text": "给我讲讲图片中的城市"}
])
# 调用Agent,发送消息
response = agent.invoke(
{"messages": [multimodal_question]}
)
print(response['messages'][-1].content)
先把字节数组img_bytes转成base64编码
img_64 = base64.b64encode(img_bytes).decode("utf_8")
然后包装成需要的message格式即可
提示词(Promt)
提示词(Promt) 就是发送给模型的消息,其中SystemMessage是系统提示词(system promt),可以给模型设定角色,聊天的背景,任务说明,对模型生成的内容有很大的影响

系统提示词
使用SystemMessage
可以直接使用SystemMessage来给agent设置系统提示词
示例代码如下:
from langchain.agents import create_agent
from langchain.messages import HumanMessage
# 创建智能体
agent = create_agent(
model = "deepseek-chat"
)
# 调用智能体
for token, metadata in agent.stream(
{"messages": [SystemMessage(content="你以伏黑甚尔的口吻回答我的问题"),HumanMessage(content="你是谁?")]},
stream_mode="messages"
):
print(token.content, end="", flush=True)
使用System Promt(推荐使用)
在创建智能体时,我们可以直接设定system prompt,不必在每次发送消息时指定。
示例代码:
from langchain.agents import create_agent
from langchain.messages import HumanMessage
# 创建智能体
agent = create_agent(
model = "deepseek-chat",
system_prompt= "你以伏黑甚尔的口吻回答我的问题"
)
# 调用智能体
for token, metadata in agent.stream(
{"messages": [HumanMessage(content="你是谁?")]},
stream_mode="messages"
):
print(token.content, end="", flush=True)
提示词工程
通过优化System Prompt从而让模型输出更理想的结果的这一过程,我们称为提示词工程(Prompt Engineering)。
也就是说,提示词优化不是一锤子买卖,而是一个不断优化、测试、再优化的过程。那么,提示词到底该怎么写呢?
从内容来说,提示词通常包含以下几个部分,通常按此顺序排列:
- 身份(Identity):描述AI的职责、沟通风格和总体目标。
- 说明(Instructions):请指导模型如何生成所需的响应。它应该遵循哪些规则?模型应该做什么,以及模型绝对不能做什么?
- 示例(Examples):提供可能的输入示例,以及模型期望的输出。
- 背景信息(Context):向模型提供生成响应所需的任何额外信息,例如RAG的额外知识库数据,或您认为特别相关的任何其他数据。
从格式来说,在编写System Prompt时,您可以使用Markdown格式和XML 标签的组合来帮助模型理解提示和上下文数据的逻辑边界。
- Markdown 的标题和列表有助于标记提示的不同部分,并向模型传达层级结构。它们还可以提高开发过程中提示的可读性。
- XML 标签可以帮助明确区分一段内容(例如用作参考的辅助文档、对话示例等)的起始和结束位置。
设定角色和指令
设定角色和指令时 需要通过一次次地观察agent输出来进行调整
例如只设置用户身份:
# 比如,要开发一个AI编程助手,帮助用户写代码
system_prompt = """
你是一个编程助手,你帮助用户编写Python代码。
"""
# 创建智能体
agent = create_agent(
model = "deepseek-chat",
system_prompt=system_prompt
)
for token, metadata in agent.stream(
{"messages": [HumanMessage(content="怎样定义string变量记录学校名字?")]},
stream_mode="messages"
):
print(token.content, end="", flush=True)
你会发现模型输出了一堆markdown格式的教程 连最基础的代码示例都没有 非常的不直观 通常我们看代码示例就能学会string变量的使用
我们通过只设置了agent身份背景的前提上 加上指令说明去约束agent输出 会达到更好的效果
代码示例:
#%%
system_prompt = """
# 身份
- 你是一个编程助手,你帮助用户编写Python代码。
# 指令
- 定义变量时,使用snake case命名法,而不是camel case命名法。
- 不要返回markdown格式说明,仅仅返回代码即可。
"""
# 创建智能体
agent = create_agent(
model = "deepseek-chat",
system_prompt=system_prompt
)
for token, metadata in agent.stream(
{"messages": [HumanMessage(content="怎样定义string变量记录学校名字,例如`西安电子科技大学`")]},
stream_mode="messages"
):
print(token.content, end="", flush=True)
我们发现 agent输出:
school_name = "西安电子科技大学"
遵从了我们的约束:下划线命名法 而不是驼峰命名法 并且只返回了代码示例
4.2.2 Few-Shot examples(示例)
有的时候我们希望模型按照固定的风格来回答问题,而这种风格又不太好描述,那我们就可以通过举例的方式让模型学习例子来回答。
用户只需在输入提示(Prompt)中提供几个输入-输出示例,模型就能理解任务模式并生成符合预期的输出:
代码示例:
system_prompt = """
# 身份
- 你是一个科幻作家,根据用户的要求创建一个太空之都。
# 示例
user:月球的首都是什么?
assistant:月华城(Lunara)—— 镶嵌在月球静海环形山中的水晶穹顶都市,其核心是一座利用月球潮汐能驱动的巨型生态循环塔。
user:火星的首都是什么?
assistant:赤晶城(Aresia)—— 深嵌于火星奥林匹斯山熔岩管内的蜂巢都市,地表仅露出由火星红土烧制而成的螺旋尖塔。
"""
# 创建智能体
agent = create_agent(
model = "deepseek-chat",
system_prompt=system_prompt
)
for token, metadata in agent.stream(
{"messages": [HumanMessage(content="金星的首都是什么?")]},
stream_mode="messages"
):
print(token.content, end="", flush=True)
agent输出:
**云冕城(Aphrodia)**——悬浮于金星硫酸云层上空的浮岛集群都市,由反重力引擎与太阳帆阵列共同维持,其核心是一座能将大气二氧化碳转化为钻石穹顶的巨型催化塔。
结构化输出
由于传统程序识别结构化的数据会更加方便,所以有时候我们希望LLM也能输出固定结构的内容,方便我们解析。这同样可以通过系统提示词来实现。
基于提示词的结构化输出
代码示例:
system_prompt = """
# 身份
- 你是一个科幻作家,根据用户的要求创建一个太空之都。
# 指令
- 请务必以JSON格式输出,不要加任何markdown样式。
# 示例:
user: 月球的首都是什么?
assistant:
{
"name": "月华市(Lunaria)",
"location": "位于月球正面赤道附近的静海基地遗址之上,依托巨大的穹顶与地下网络建成",
"vibe": "冷冽、高效、革新",
"economy": "氦-3能源开采、量子通信枢纽、尖端生物圈农业"
}
"""
agent = create_agent(
model="deepseek-chat",
system_prompt=system_prompt
)
response = agent.invoke(
{"messages": [HumanMessage(content="金星的首都是什么?")]},
)
print(response['messages'][-1].content)
agent输出:
{
"name": "硫磺城(Sulfura)",
"location": "悬浮于金星浓厚大气层中距地表约50公里的高空,由巨大的反重力浮空平台群构成",
"vibe": "高压、炽热、坚韧",
"economy": "大气资源提炼(二氧化碳、硫酸)、极端环境材料制造、太阳能巨型阵列"
}
基于Model的结构化输出
在LangChain中,实现结构化输出会更加简单。我们无需自己在提示词中添加描述实现结构化输出,而仅仅是两步即可:
- 定义一个数据类型(基于pydantic)
- 创建智能体,设置输出格式
from pydantic import BaseModel
# 首先,我们定义一个类,用来封装模型要输出的数据:
class CapitalInfo(BaseModel):
name: str
location: str
vibe: str
economy: str
在创建agent的时候使用response_format指定结构化输出的格式
# 我们可以创建智能体时设置结构化输出的格式,LangChain会自动帮我们完成提示词改造和响应结果解析。
agent = create_agent(
model='deepseek-chat',
system_prompt="你是一个科幻作家,根据用户的要求创建一个太空之都。",
response_format=CapitalInfo # 设置结构化输出的格式
)
response = agent.invoke(
{"messages": [HumanMessage(content="月球的首都是什么?")]}
)
# 输出结果
print(response)
注意,在输出的结果中,有一个’structured_response’的字段,就是结构化输出的对象:
{'messages': [HumanMessage(content='月球的首都是什么?', additional_kwargs={}, response_metadata={}, id='42747579-7994-4fe9-93bf-970216fb65b4'), AIMessage(content='', additional_kwargs={'refusal': None}, response_metadata={'token_usage': {'completion_tokens': 130, 'prompt_tokens': 355, 'total_tokens': 485, 'completion_tokens_details': None, 'prompt_tokens_details': {'audio_tokens': None, 'cached_tokens': 0}, 'prompt_cache_hit_tokens': 0, 'prompt_cache_miss_tokens': 355}, 'model_provider': 'deepseek', 'model_name': 'deepseek-chat', 'system_fingerprint': 'fp_eaab8d114b_prod0820_fp8_kvcache', 'id': '3dcb8346-67b2-4cf1-b61d-cf9cf8e2dde9', 'finish_reason': 'tool_calls', 'logprobs': None}, id='lc_run--019ca25b-a0e2-77a0-af64-9b1d2c9247f0-0', tool_calls=[{'name': 'CapitalInfo', 'args': {'name': '月宫', 'location': '月球南极-艾特肯盆地边缘', 'vibe': '高科技与东方古典美学融合的宁静都市,拥有透明穹顶下的传统园林和悬浮建筑', 'economy': '氦-3开采、量子计算中心、太空旅游枢纽、月球农业和科学研究'}, 'id': 'call_00_NBnEIMUhLTXZdXRJLZVYCnan', 'type': 'tool_call'}], invalid_tool_calls=[], usage_metadata={'input_tokens': 355, 'output_tokens': 130, 'total_tokens': 485, 'input_token_details': {'cache_read': 0}, 'output_token_details': {}}), ToolMessage(content="Returning structured response: name='月宫' location='月球南极-艾特肯盆地边缘' vibe='高科技与东方古典美学融合的宁静都市,拥有透明穹顶下的传统园林和悬浮建筑' economy='氦-3开采、量子计算中心、太空旅游枢纽、月球农业和科学研究'", name='CapitalInfo', id='1735ea63-403a-468a-a5ea-cc01deeab0b2', tool_call_id='call_00_NBnEIMUhLTXZdXRJLZVYCnan')], *'structured_response': CapitalInfo(name='月宫', location='月球南极-艾特肯盆地边缘', vibe='高科技与东方古典美学融合的宁静都市,拥有透明穹顶下的传统园林和悬浮建筑', economy='氦-3开采、量子计算中心、太空旅游枢纽、月球农业和科学研究')}*
所以,我们这样获取结构化的输出:
city = response['structured_response']
工具
模型(Model) 是Agent的大脑,负责推理分析 而工具(Tools)则是Agent的手脚,负责执行任务,与外界交互
基本用法
我们先通过一个案例快速回顾Agent定义的步骤,以及Agent的工作原理。
定义一个带有工具的Agent分为两步:
- 定义工具
- 定义Agent,绑定工具
首先,使用tool装饰器定义工具:
# 1.使用tool装饰器定义工具
from langchain.tools import tool
@tool
def get_weather(location: str) -> str:
"""
Get the weather in a given location.
Args:
location: city name or coordinates
"""
return f"Current weather in {location} is sunny"
接着,定义Agent,绑定工具:
from langchain.agents import create_agent
from langchain_core.messages import HumanMessage
# 2.创建智能体,并绑定工具
agent = create_agent(
model="deepseek-chat",
tools=[get_weather]
)
# 3.调用Agent
response = agent.invoke(
{"messages": [HumanMessage(content="杭州今天天气如何?")]},
)
for message in response['messages']:
message.pretty_print()
执行结果如下:
================================ Human Message =================================
杭州今天天气如何?
================================== Ai Message ==================================
我来帮您查询杭州今天的天气情况。
Tool Calls:
get_weather (call_00_FETE4MIR9p1Gr6uszgjcko6m)
Call ID: call_00_FETE4MIR9p1Gr6uszgjcko6m
Args:
location: 杭州
================================= Tool Message =================================
Name: get_weather
Current weather in 杭州 is sunny
================================== Ai Message ==================================
根据查询结果,杭州今天的天气是晴朗的。天气很好,适合外出活动!
流程图:
由此可见,所谓的工具,本质就是一个可调用的函数,要想让Agent知道有哪些工具可调用,该如何调用这些工具,就必须把这个函数的详细信息发送给模型。包括:
- 函数名
- 函数的作用
- 函数的参数和返回值信息
所以,定义工具的时候,关键就是把这些信息描述清楚即可。
自定义工具
所谓的工具(Tool),本质就是一个可调用的函数,但是这个函数不是我们自己去调用,而是给模型调用。因此除了定义函数外,我们还需要清晰描述这个工具,让模型知道这个工具如何使用。包括下列信息:
- 工具名
- 工具的作用
- 工具需要的参数
基于tool描述工具
在LangChain中,定义工具需要用到@tool装饰器,我们可以通过装饰器来定义工具名、工具的作用:
from langchain_core.tools import tool
@tool("square_root", description="Calculate the square root of a number")
def tool1(x: float) -> float:
return x ** 0.5
使用函数名和文档注释描述工具
如果不@tool装饰器没有定义工具名和作用描述,此时:
- 工具名:默认就是函数名
- 工具所需的参数:默认就是函数的参数列表
- 工具作用的描述:默认就是函数的文档注释
from langchain_core.tools import tool
# 通过tool装饰器定义工具
@tool
def square_root(x: float) -> float:
"""Calculate the square root of a number"""
return x ** 0.5
参数比较多的工具:包含选择摄氏度还是华氏度 和是否前瞻未来天气
# 定义一个查询天气的tool
@tool
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
"""
Get current weather and optional forecast.
Args:
location: city name or coordinates
units: unit of degrees
include_forecast: does it include the weather forecast
"""
temp = 22 if units == "celsius" else 72
result = f"Current weather in {location}: {temp} degrees {units[0].upper()}"
if include_forecast:
result += "\nNext 5 days: Sunny"
return result
定义Pydanitic Model描述参数
如果函数的参数比较多,而且比较复杂,此时建议通过pydantic model来描述参数列表。
然后使用@tool里面的args_schema参数来指定
依旧是依照上面的例子来改造:
# 通过自定义model来约束入参
from pydantic import BaseModel, Field
from typing import Literal
# 例如一个查询天气的tool
class WeatherInput(BaseModel):
"""查询天气的输入参数."""
location: str = Field(description="City name or coordinates")
units: Literal["celsius", "fahrenheit"] = Field(
default="celsius",
description="Temperature unit preference, default is celsius."
)
include_forecast: bool = Field(
default=False,
description="Include 5-day forecast"
)
# 定义一个查询天气的tool
@tool(args_schema=WeatherInput)
def get_weather(location: str, units: str = "celsius", include_forecast: bool = False) -> str:
"""Get current weather and optional forecast."""
temp = 22 if units == "celsius" else 72
result = f"Current weather in {location}: {temp} degrees {units[0].upper()}"
if include_forecast:
result += "\nNext 5 days: Sunny"
return result
预定义工具
LangChain中提供了很多预定义好的工具,方便我们使用,可使用的预定义工具列表可参考官网:
https://docs.langchain.com/oss/python/integrations/tools
例如,模型本身只能根据本身的训练数据回答问题,无法获取实时信息。但如果我们给它提供了web搜索的工具,那么你的Agent就如同具备了实时web搜索的能力,回答会更加准确。
有一个专门用于给Agent提供Web搜索的工具,叫做Tavily,官网如下:
在LangChain中也提供了对Tavily的支持:
https://docs.langchain.com/oss/python/integrations/tools/tavily_search
注册Tavily账号获取APIKEY
首先,我们要在Tavily官网注册一个账号,可以选择邮箱注册,或者直接用google、github登录:
注册成功后,我们登录后台(https://app.tavily.com/home),即可看到一个默认的API_KEY:
配置环境变量
接下来,我们需要把这个KEY配置到我们的.env文件中:
安装依赖
然后,我们需要安装langchain-tavily的依赖:
# 使用uv的环境
uv add langchain-tavily
使用工具
接下来,就可以使用tavily来做web搜索了:
# 使用tavily作为web搜索工具
from langchain_tavily import TavilySearch
# 初始化工具,并设置参数,具体参数设置参考官网
tool = TavilySearch(
max_results=5,//最大搜索条数
topic="general",//搜索模式为通用搜索
# include_answer=False,
# include_raw_content=False,
# include_images=False,
# include_image_descriptions=False,
# search_depth="basic",
# time_range="day",
# include_domains=None,
# exclude_domains=None
)
如果想配置其他的选项 可以在tavily官网查看
试试看调用工具:
tool.invoke("杭州今天天气如何?")
结合智能体
我们结合智能体来使用Tavily搜索工具:
# 创建智能体,使用预定义工具tavily
agent = create_agent(
model="deepseek-chat",
tools=[tool],
system_prompt="你是一个智能助手,你使用工具来解决用户问题。"
)
# 调用工具
for chunk in agent.stream(
{"messages": [HumanMessage(content="北京接下来5天天气如何?")]},
stream_mode="updates"
):
for step, data in chunk.items():
print(f"step: {step}")
print(f"content: {data['messages'][-1].content_blocks}")
print()
优化
目前的搜索智能体存在两个问题:
- 官方默认的tavily工具过于复杂
- 结果中不包含网页数据源,可信度低
- 解决思路:
自定义tavily工具
- 结构化输出
- 自定义tavily工具
LangChain官方提供的tavily工具包含了完整的参数列表,会导致额外的流量和Token消耗。因此,对于简单的业务,我们建议大家利用tavily自定义工具。
# 先使用官方的客户端做初始化
tavily = TavilySearch(
max_results=5,
topic="general"
)
# 然后自己封装为tool
@tool
def web_search(query: str):
"""Search the web for information"""
return tavily.invoke(query)
默认情况下AI回答的结果不包含信息来源,这样回答的可信度就不高。我们可以自定义结构化输出,让AI在回答时包含信息来源。
from pydantic import BaseModel, Field
# Agent回答内容引用的网页信息
class Reference(BaseModel):
title: str = Field(description="The title of the web page cited in the answer")
url: str = Field(description="The url of the web page cited in the answer")
# Agent的回答内容
class AnswerInfo (BaseModel):
answer: str = Field(description="The final answer for user")
reference: list[Reference] = Field(description="The web pages cited in the answer")
# 创建智能体,使用预定义工具tavily
agent = create_agent(
model="deepseek-chat",
tools=[web_search],
system_prompt="你是一个智能助手,你使用工具来解决用户问题。",
response_format=AnswerInfo
)
# 调用agent
response = agent.invoke(
{"messages": [HumanMessage(content="蒸蚌是什么梗?")]},
)
# 获取结构化输出
print(response['structured_response'])
记忆
对于Agent而言,记忆至关重要,因为它能让代理记住之前的交互情况,从反馈中学习,并适应用户的偏好。随着代理处理的任务愈发复杂,涉及的用户交互也越来越多,这种能力对于提高效率和用户满意度而言变得不可或缺。
记忆的分类
对于智能体而言,记忆分为了两类:
- 短期记忆(short-term memory)
- 长期记忆(long-term memory)
注意,大家不要被字面上的意思误导了,很多人看到名字就误以为:短期记忆就是临时记忆,断电就没了;长期记忆就是永久记忆,持久保存。
对于智能体而言,这是完全错误的理解!!!
简单用一句话概括的话:
- 短期记忆:当前任务或会话的上下文(Working Memory 或 Session Memory)
- 长期记忆:跨任务或会话的经验与知识(Persistent Memory)

比如,一个公司数据分析的Agent。
用户提出需求:
“帮我写Q1的销售分析报告”
Agent:
短期记忆:
- 对话历史
- 查询到Q1的销售数据
- 任务目标及执行状态
长期记忆:
- 公司的KPI算法
- 用户偏好的报告形式
| 分类 | 生命周期 | 内容 | 是否跨任务 | 存储 |
|---|---|---|---|---|
| 短期记忆 | 当前会话(短暂) | 当前任务状态 | ❌ | Redis/内存 |
| 长期记忆 | 跨任务、跨会话(永久) | 知识、经验、用户偏好 | ✅ | DB/Vector DB |
短期记忆
由于短期记忆通常生命周期是当前会话,所以我们也可以称为会话记忆。Agent的会话记忆通常包含三部分:
- 对话历史
- 查询结果
- 任务状态
对于简单的Agent来说,任务没有做拆分,也就不需要记录任务状态,只用考虑会话历史和查询结果就可以了。后续我们会学习如何自定义更复杂的Agent会话记忆。
LangChain提供了自动化的记忆管理方案:
- 首先,LangChain把会话记忆(也就是Messages列表)记录为AgentState的一部分

- AgentState通过Checkpointer对象来保存,每一次与AI的交互都会生成一个快照,记录为一个checkpoint,把同一会话的所有checkpoint组合在一起,就是完整的会话历史了。
- 为了区分不同的会话记忆,不同会话需要设定各自的thread_id,相同会话则使用相同thread_id
- 向Agent发起会话时必须指定自己的thread_id以唤起对应的会话记忆

接下来,我们以LangChain提供的基于内存的Checkpointer为例来演示会话记忆。
InMemorySaver
具体步骤是这样的:
- 导入CheckPointer的内存版实现:
# langchain提供的checkpointer的默认实现,基于内存存储
from langgraph.checkpoint.memory import InMemorySaver
- 创建智能体,设置checkpointer:
from langchain.agents import create_agent
# 创建智能体时指定checkpointer,LangChain会自动帮我们管理历史会话记忆
agent = create_agent(
"deepseek-chat",
checkpointer=InMemorySaver()
)
- 发起调用时,指定thread_id
- 第一次调用,告知AI一些信息
from langchain.messages import HumanMessage
# 设定thread_id,作为会话标识
config = {"configurable": {"thread_id": "thread_1"}}
# 第一次调用,告知AI我的信息
response = agent.invoke(
{"messages": [HumanMessage(content="你好,我叫瓜瓜,我最喜欢猫猫。")]},
config # 调用时添加thread_id,区分不同会话
)
print(response)
-
- 第二次调用,询问AI
# 第二次调用,询问我的信息,这次带上thread_id,唤起记忆
response = agent.invoke(
{"messages": [HumanMessage(content="我最喜欢的动物是什么?")]},
config # 调用时添加thread_id
)
print(response)
由于两次调用使用了相同的thread_id,被认定为是同一次对话,所以LangChain会在请求模型时携带历史对话的Messages,模型就能根据历史消息来正确回答了
注意:
目前我们使用的checkpointer是基于内存的InMemorySaver,LangChain也提供了很多持久化存储的checkpointer,例如:
- SqlLiteSaver :基于sqlite存储
- PostgresSaver :基于Postgres存储
- CosmosDBSaver :使用Azure Cosmos DB的实现
具体可以查看文档:
https://docs.langchain.com/oss/python/langgraph/persistence#checkpointer-libraries
持久化Memory(选学)
LangChain也提供了很多持久化存储的checkpointer,例如:
- SqlLiteSaver :基于sqlite存储
- PostgresSaver :基于Postgres存储
- CosmosDBSaver :使用Azure Cosmos DB的实现
我们以SqlLiteSaver 为例来讲解如何自定义Memory存储方案。
首先,安装对应依赖:
# pip安装
# pip install langgraph-checkpoint-sqlite
# uv安装
uv add langgraph-checkpoint-sqlite
然后,导入以来,并初始化sqlite-checkpointer
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
# 初始化checkpointer
checkpointer = SqliteSaver(sqlite3.connect("checkpoint.db", check_same_thread=False))
# 自动建表
checkpointer.setup()
最后,创建Agent,并设置checkpointer:
# 创建agent
agent = create_agent(
"deepseek-chat",
checkpointer=checkpointer,
)
记忆管理策略
由于会话记忆要保存会话的历史,并且在调用LLM时携带历史消息列表。而当会话越来越长时,历史消息就可能超过LLM的上下文限制。例如,DeepSeek的上下文不能超过128K.
一旦会话历史超过上下文窗口,就会出现上下文丢失的情况,从而导致丢失记忆。而且即便不丢失,太长的上下文容易让模型出现“注意力分散”问题,模型的响应速度、回答质量会大大降低。
为了解决这一问题,通常有以下几种手段:
具体可参照官网:
https://docs.langchain.com/oss/python/langchain/short-term-memory#common-patterns
修剪消息
修剪消息并不是真正的删除消息,在AgentState中的消息列表依然是完整的,只不过发送给LLM之前会进行修剪,只保留一部分消息。
具体示例参考:
https://docs.langchain.com/oss/python/langchain/short-term-memory#trim-messages
删除消息
删除消息与修剪不同:
- 修剪消息:只是从State中选取一部分消息发送给模型
- 删除消息:直接删除State中保存的消息,也就是说消息历史中不再存在!
所以一定要谨慎使用。
具体参考:
https://docs.langchain.com/oss/python/langchain/short-term-memory#delete-messages
总结消息
不管是修剪还是删除,都会导致一部分消息丢失,从而丢失记忆。所以就有了第三种策略:总结消息(Summarize Messages)
它的思路很简单,就是把历史的消息利用大模型总结出摘要,然后把最新的消息拼接在一起作为新的消息列表发送给大模型,这样既不会超出模型的上下文窗口限制,还能尽量保留所有的记忆。
LangChain提供了总结消息的默认实现:SummarizationMiddleware
用法很简单:
- 初始化SummarizationMiddleware和checkpointer
rom langchain.agents import create_agent
from langchain.agents.middleware import SummarizationMiddleware
from langgraph.checkpoint.memory import InMemorySaver
from langchain_core.runnables import RunnableConfig
# 初始化checkpointer
checkpointer = InMemorySaver()
# 初始化中间件
middleware = SummarizationMiddleware(
model="deepseek-chat",
trigger=("messages", 3), # 触发时机,当消息数超过3时,进行总结
keep=("messages", 1) # 保留的会话数,超过2条
)
注意这里SummarizationMiddleware的参数(详细内容参考官网链接):
- model:会话摘要时要使用的模型
- trigger:会话摘要的触发时机,有三种设置:
- fraction (float): 模型上下文大小的比例(0-1)
- tokens (int): 令牌数量
- messages (int): 消息数量
- keep:是指触发摘要后要保留的消息
- fraction (float): 要保留的消息占模型上下文大小的比例(0-1)
- tokens (int): 要保留的消息的令牌数量
- messages (int): 要保留的消息数量
- 创建Agent,设置middleware和checkpointer
# 创建agent
agent = create_agent(
model="deepseek-chat",
middleware=[middleware],
checkpointer=checkpointer,
)
- 调用agent即可
config: RunnableConfig = {"configurable": {"thread_id": "1"}}
# 制造长会话历史
agent.invoke({"messages": "你好,我是瓜瓜."}, config)
agent.invoke({"messages": "我最喜欢的运动是"}, config)
agent.invoke({"messages": "我最喜欢的动物是猫猫"}, config)
# 测试效果
final_response = agent.invoke({"messages": "你还记得我吗?"}, config)
for message in final_response["messages"]:
message.pretty_print()
好了 这里LangChain入门开发教程已经结束了 有兴趣的朋友 可以继续阅读博主的LangChain入门实战小案例
更多推荐



所有评论(0)