LangChain基础使用
文章目录
一、前言
近年来,大语言模型(LLM)展现了惊人的能力,但在实际应用中,直接使用原生LLM往往会遇到一系列棘手的问题:
- 幻觉问题:模型可能生成看似合理但实际错误的信息
- 提示词不统一:不同场景需要反复调整提示词,缺乏标准化
- 模型切换成本高:不同厂商的LLM接口差异大,迁移困难
- 编码集成不便:开发者更希望通过结构化数据(JSON/XML等)接口获取能力,而非纯文本
- 知识截止日期:模型训练数据有时间限制,无法获取实时信息
- 专业领域知识薄弱:在医学、法律等垂直领域,模型结果仅能作为参考,可信度不足
解决方案
上述问题的核心在于:大模型本身缺乏外部知识检索、工具调用和状态管理能力。目前主流解决思路包括:
- RAG(检索增强生成):为模型提供外部知识库,减少幻觉
- Agent(智能体):让模型能够主动调用工具、搜索网络、执行操作
而要实现这些能力,需要一个强大的框架来协调模型、数据和工具——这就是 LangChain 和 LangGraph 诞生的背景。
1.解决痛点
LangChain能解决提到的每一个痛点,具体如下:
| 痛点 | LangChain 的解决方案 |
|---|---|
| 幻觉问题 | 通过 RAG(检索增强生成) 流程,将外部知识库(文档、数据库)接入模型,让模型在回答时参考真实数据,从而显著降低幻觉。LangChain 提供了完整的文档加载、分割、向量化、检索链,开箱即用。 |
| 提示词不统一 | 通过 SystemPrompt 模板 和 提示词工程 的最佳实践,将提示词结构化(身份、指令、示例、上下文)。LangChain 的 ChatPromptTemplate 支持动态插入变量,便于在不同场景复用同一套提示词模板。 |
| 模型切换成本高 | LangChain 提供了 统一的 API 接口(如 invoke、stream),所有模型(OpenAI、DeepSeek、Anthropic 等)都遵循相同调用方式。只需修改模型名称或配置,无需重构业务代码。 |
| 编码集成不便 | LangChain 支持 结构化输出,通过 with_structured_output 或 output_parsers,可以强制模型返回 JSON、XML 或 Pydantic 对象。开发者可以直接操作结构化数据,而非解析纯文本。 |
| 知识截止日期 | 通过 工具(Tool) 机制,模型可以调用外部搜索工具(如 Tavily、Google Search),实时获取最新网络信息,突破训练数据的时间限制。 |
| 专业领域知识薄弱 | 结合 RAG + 领域知识库,将医学、法律等专业文档向量化后作为上下文注入模型,使模型基于权威资料作答。同时可通过 自定义工具 调用领域专用 API(如药品查询、法规检索),进一步提升可信度。 |
2.技术特点
LangChain 的核心设计哲学是 组件化与可组合性。它将复杂的大模型应用拆解为多个标准化组件,并通过 链式(Chain) 的方式将这些组件串联起来,形成一个可执行的工作流。
- 组件化:所有功能(模型、提示词、检索、工具、记忆)均封装为独立组件,可插拔替换。
- 链式调用:通过 | 管道运算符或 Chain 类将组件串联,形成清晰的执行流程。
- 标准化接口:所有组件都遵循 invoke、stream、batch 等统一方法,学习成本低。
- 可观测性:支持回调机制(Callbacks),便于日志记录、监控和调试。
- 生态丰富:内置大量文档加载器、向量数据库、工具、输出解析器,开箱即用。
- 与 LangGraph 协同:LangChain 提供基础链式能力,LangGraph 在此基础上增加了状态管理和循环控制,适用于复杂 Agent 场景。
3.传统LLM与Agent的区别
| 特性 | 传统聊天机器人 / LLM | AI Agent |
|---|---|---|
| 交互模式 | 被动响应,问一句答一句 | 下达命令,自主执行 |
| 执行力 | 停留在文本生成层面 | 能操作软件、发送邮件、联网搜索 |
| 自主性 | 需要人类给出详细步骤 | 只需给定最终目标,自主寻找路径 |
二、LangChain
LangChain 是一个专门用于开发由大语言模型驱动的应用程序的框架。它将自然语言处理(NLP)流程拆解为标准化组件,让开发者能够自由组合并高效定制工作流。
核心组件与工作流程
典型的RAG流程在LangChain中体现为以下步骤:
- 读取文档:支持PDF、Word、网页等多种格式
- 分割文本:将长文档切分为语义完整的块(Chunk)
- 向量化(Embedding):将文本块转换为向量表示
- 存储向量:存入向量数据库(如Chroma、FAISS)
- 接收用户问题:将用户问题也转换为向量
- 检索相关文本:通过相似度搜索找到最相关的文本块
- 组合提示:将用户问题与检索到的上下文一起发送给LLM
- 解析输出:提取并返回最终答案
1.模型调用
(1)invoke方式
【解释一句话】
from langchain.chat_models import ChatDeepSeek
model = ChatDeepSeek(model="deepseek-chat", temperature=0.9)
response = model.invoke([
SystemMessage(content="你扮演火箭队的武藏。"),
HumanMessage(content="你是谁?")
])
print(response.content)
(2)stream方式
【解释一句话】
for chunk in model.stream("你好,你是谁?"):
print(chunk.content, end="", flush=True)
2.消息
在 LangChain 中,发送给 LLM 的消息和 LLM 返回的消息都统一封装为 BaseMessage,它是智能体中基本的上下文单元。LangChain 已经根据角色(Role)创建了对应的子类:
- SystemMessage:role=“system”,用于设定模型角色和交互背景。
- HumanMessage:role=“user”,代表用户输入。
- AIMessage:role=“assistant”,代表 LLM 生成的响应(包含文本、工具调用、元数据)。
- ToolMessage:role=“tool”,代表工具调用时产生的结果。
from langchain.agents import create_agent
from langchain.tools import tool
from langchain_core.messages import SystemMessage, HumanMessage, AIMessage
# 定义工具
@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
agent = create_agent(model="deepseek-chat", tools=[get_weather])
# 调用 Agent,发送消息
response = agent.invoke({
"messages": [
SystemMessage(content="请使用工具来获取天气信息。"),
HumanMessage(content="你好,我是虎哥。"),
AIMessage(content="你好,虎哥,很高兴认识你。"),
HumanMessage(content="北京今天天气如何?")
]
})
print(response)
除了文本消息,LangChain 也支持向模型发送多模态消息(图片、音频、视频等),但前提是模型本身支持多模态,例如:
- qwen3.5-plus
- gpt-5-nano
# 准备多模态消息
message = HumanMessage([
{"type": "text", "text": "描述以下这张图片的内容。"},
{"type": "image", "url": "https://help-static-aliyun-doc.aliyuncs.com/file-manage-files/zh-CN/20241022/emyria/dog_and_girl.jpeg"},
])
stream = agent.stream(
{"messages": [message]},
stream_mode="messages"
)
for chunk, metadata in stream:
if chunk.content:
print(chunk.content, end="", flush=True)
3.提示词
在所有消息中,SystemMessage 最为重要,它设定了模型的角色和聊天背景,会影响后续所有对话。我们将其称为 系统提示词(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)
提示词工程(Prompt Engineering):所谓提示词工程,就是通过优化提示词使模型输出的结果更符合业务需要的过程。
一般来说,系统提示词会包含以下几个部分,建议按此顺序排列:
- 身份角色(Identity):描述 AI 的职责、沟通风格和总体目标。
- 指令说明(Instructions):指导模型如何生成响应,应遵循哪些规则,模型应该做什么以及绝对不能做什么。
- 对话示例(Examples):提供可能的输入示例及期望的输出。
- 背景信息(Context):提供生成响应所需的额外信息,例如 RAG 检索到的知识库数据。
- 在编写系统提示词时,可以使用 Markdown 格式和 XML 标签 的组合来帮助模型理解逻辑边界:
- Markdown 的标题和列表有助于标记提示的不同部分,并传达层级结构,同时提高可读性。
- XML 标签(如 <context>、<example>)可以明确区分不同内容的起始和结束位置。
# 身份
你是一名资深的AI助教,擅长用通俗易懂的方式解释复杂概念。
# 指令
- 回答必须简洁、准确。
- 如果问题涉及实时数据,请告知用户需要查询最新信息。
- 禁止编造事实。
# 对话示例
用户:什么是RAG?
AI:RAG是检索增强生成,它通过在生成答案前先检索外部知识库来减少幻觉。
# 上下文
<context>
RAG全称Retrieval-Augmented Generation,由Facebook AI在2020年提出。
</context>
4.工具Tool
所谓的工具(Tool),本质上是一个可调用的函数,但这个函数不是由我们直接调用,而是交给模型来调用。因此,除了定义函数本身,我们还需要清晰描述这个工具,让模型知道它的用途和用法。描述信息通常包括:
- 工具名
- 工具的作用
- 工具需要的参数
(1)基于 @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
(2)使用函数名和文档注释自动生成描述
如果不 @tool 装饰器没有定义工具名和作用描述,此时:
- 工具名:默认就是函数名
- 工具所需的参数:默认就是函数的参数列表
- 工具作用的描述:默认就是函数的文档注释
# 定义一个查询天气的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
(3)预定义工具
LangChain中提供了很多预定义的Tool,方便我们使用。例如 tavily 就是一个用来做web搜索的工具,使用步骤如下,
- 注册账号,创建API_KEY;
- 配置环境变量:TAVILY_APIKEY;
- 安装依赖:uv addlangchain-tavily;
- 初始化工具并调用;
from langchain_tavily import TavilySearch
# 初始化搜索工具
search_tool = TavilySearch(
max_results=5,
topic="general", # 可选:general, news, finance
# 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
)
# 直接调用
result = search_tool.invoke("蒸蚌是什么梗?")
print(result)
- 直接调用
# 直接调用
result = search_tool.invoke("蒸蚌是什么梗?")
print(result)
5.记忆
Agent的记忆(Memory)分两类:
- 短期记忆(short-term memory):当前任务或会话的上下文
- 长期记忆(long-term memory):跨任务或会话的经验与知识
(1)短期记忆
在 LangChain 短期记忆是通过 Agentstate 实现的,而会话历史(也就是消息列表)是 AgentState 的一部分。
LangChain提供了checkpointer对象来保存AgentState,每一次用户与AI的交互都会生成一个快照,记录为一个checkpoint。同一会话的多个 checkpoint 形成一个组,用同一个thread_id来标记。

- 添加会话记忆(短期记忆)分为三步:
- 导入并初始化 Checkpointer
- 创建Agent,指定 Checkpointer
- 调用Agent,指定 thread_id
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
agent = create_agent(
"deepseek-chat",
checkpointer=InMemorySaver()
)
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)
# 第二次调用,询问我的信息,这次带上thread_id,唤起记忆
response = agent.invoke(
{"messages": [HumanMessage(content="我最喜欢的动物是什么?")]},
config # 调用时添加thread_id
)
print(response)
(2)记忆的持久化存储
内存型 checkpointer 在程序重启后会丢失数据。如果需要持久化存储,这里选择使用 Sqlite 作为后端。首先需要按照langgraph-checkpoint-sqlite依赖:
uv add langgraph-checkpoint-sqlite
接着,按照以下步骤使用:
- 导入依赖
- 连接 SQLite 数据库并初始化 SqliteSaver
- 执行 setup() 自动创建表结构。
- 创建 Agent 时指定该 checkpointer。
import sqlite3
from langgraph.checkpoint.sqlite import SqliteSaver
# 连接sqlite
connection = sqlite3.connect("resources/checkpoint.db", check_same_thread=False)
# 初始化checkpointer
checkpointer = SqliteSaver(connection)
# 自动建表
checkpointer.setup()
# 创建agent
agent = create_agent(
"deepseek-chat",
checkpointer=checkpointer,
)
- 使用方式与内存型一致
from langchain.messages import HumanMessage
# 设定thread_id,作为会话标识
config = {"configurable": {"thread_id": "thread_2"}}
# 第一次调用,告知AI我的信息
response = agent.invoke(
{"messages": [HumanMessage(content="你好,我叫虎哥,我最喜欢猫猫。")]},
config # 调用时添加thread_id
)
print(response)
# 第二次调用,询问我的信息,这次带上thread_id,唤起记忆
response = agent.invoke(
{"messages": [HumanMessage(content="我最喜欢的动物是什么?")]},
config # 调用时添加thread_id
)
print(response)
(3)记忆管理策略
多轮对话会导致消息历史不断累积,可能超出模型的上下文长度限制(例如 DeepSeek 的上下文上限为 128K)。LangChain 提供了多种记忆管理策略来解决这一问题。
- 修剪:保留最近的 N 条消息,丢弃更早的消息,但可能丢失重要历史信息;
- 删除:直接删除某个 checkpoint 快照,无法回滚,但风险较大,不可恢复;
- 总结摘要:对早期消息进行总结,生成摘要,替代原始消息,但摘要可能丢失部分细节;
- 自定义:自定义策略,例如根据用户问题筛选历史消息
下面以摘要中间件(SummarizationMiddleware) 为例,展示如何使用摘要策:
from 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条
)
# 创建agent
agent = create_agent(
model="deepseek-chat",
middleware=[middleware],
checkpointer=checkpointer,
)
config: RunnableConfig = {"configurable": {"thread_id": "thread_3"}}
# 制造长会话历史
agent.invoke({"messages": [HumanMessage(content="你好,我是虎哥。")]}, config)
agent.invoke({"messages": [HumanMessage(content="我最喜欢的运动是乒乓")]}, config)
agent.invoke({"messages": [HumanMessage(content="我最喜欢的动物是猫猫")]}, config)
# 测试效果
final_response = agent.invoke({"messages": HumanMessage(content="你还记得我吗?")}, config)
更多推荐


所有评论(0)