一、简述
1 什么是LangChain
LangChain由 Harrison Chase 创建于2022年10月,它是围绕LLMs(大语言模型)建立的一个框架,它的核心理念是为各种LLMs实现通用的接口,把LLMs相关的组件"链接"在一起,简化LLMs应用的开发难度,方便开发者快速地开发复杂的LLMs应用。
LangChain 最初是于 2022 年 10 月开源的一个项目,在 GitHub 上获得大量关注之后迅速转变为一家初创公司。2017 年 Harrison Chase 还在哈佛上大学,如今已是硅谷的一家热门初创公司的 CEO,这对他来说是一次重大而迅速的跃迁。Insider 独家报道,人工智能初创公司 LangChain 在种子轮一周后,再次获得红杉领投的 2000 万至 2500 万美元融资,估值达到 2 亿美元。
LangChain官网 :python.langchain.com/docs/introd...
LangChain官网中文版 :www.langchain.com.cn/
LangChain 简化了 LLM 应用程序生命周期的每个阶段:
- 开发:使用 LangChain 的开源组件和第三方集成构建应用程序。使用 LangGraph 构建具有一流流式和人机交互支持的状态智能体。
- 生产化:使用 LangSmith 检查、监控和评估应用程序,方便持续优化和部署。
- 部署:使用 LangGraph平台 将应用程序转变为可用于生产的 API 和助手。
2 主要组件

- Models:模型,各种类型的模型和模型集成,比如GPT-4
- Prompts:提示,包括提示管理、提示优化和提示序列化
- Chains:链,一系列对各种组件的调用
- Memory:记忆,用来保存和模型交互时的上下文状态
- Indexes:索引,用来结构化文档,以便和模型交互
- Agents:代理,决定模型采取哪些行动,执行并且观察流程,直到完成为止
3 LangChain核心包
-
langchain-core:聊天模型和其他组件的基础抽象。
-
集成包(例如 langchain-openai、langchain-anthropic 等):重要的集成被拆分为轻量级的独立包,由 LangChain 团队和集成方共同维护。
-
langchain:包含链(chains)、智能体(agents)和检索策略,这些构成了应用的认知架构。
-
langchain-community:由社区维护的第三方集成。
-
langgraph:一个编排框架,用于将 LangChain 组件组合成可用于生产的应用,支持持久化、流式处理及其他关键特性。
4 环境准备
本课程以LangChain+Qwen进行学习,需要提前安装
pip install openai
pip install langchain
pip install modelscope
借助阿里云-百炼平台(需要申请API Key 以及Secret Key):
二、Models
现在市面上的模型多如牛毛,各种各样的模型不断出现,LangChain模型组件提供了与各种模型的集成,并为所有模型提供一个精简的统一接口。
LangChain目前支持三种类型的模型:LLMs、Chat Models(聊天模型)、Embeddings Models(嵌入模型)。
- LLMs:大语言模型接收文本字符作为输入,返回的也是文本字符。
- 聊天模型:基于LLMs, 不同的是它接收聊天消息(一种特定格式的数据)作为输入,返回的也是聊天消息。
- 文本嵌入模型:文本嵌入模型接收文本作为输入, 返回的是浮点数列表。

LangChain支持的三类模型,它们的使用场景不同,输入和输出不同,开发者需要根据项目需要选择相应的模型。
1 LLMs
LLMs(大语言模型)使用场景最多,常用大模型的下载库:
下面Qwen为例进行讲解。
模型调用有2种方式:
- 通过ChatOpenAI进行调用
python
from langchain_openai import ChatOpenAI
import os
# 实例化模型
model = ChatOpenAI(
base_url=os.environ.get('base_url','https://dashscope.aliyuncs.com/compatible-mode/v1'),
model='qwen-plus',
openai_api_key=os.environ.get('api_key','sk-XXXXXX'),
max_tokens=1000,
temperature=0
)
# 获取问答结果
result = model.invoke("帮我讲个笑话吧")
print(result.content)
- 通过Ollama进行调用 Ollama支持模型
python
# from langchain_community.llms import Ollama # Langchain 0.x版本使用
from langchain_ollama import OllamaLLM
# 实例化模型
# model = Ollama(model="qwen2.5:7b") # Langchain 0.x版本使用
model = OllamaLLM(model="qwen2.5:7b")
# 获取问答结果
result = model.invoke("请给我讲个笑话吧")
print(result)
2 Chat Models
聊天模型,聊天消息包含下面几种类型,使用时需要按照约定传入合适的值:
-
AIMessage: 就是 AI 输出的消息,可以是针对问题的回答.
-
HumanMessage: 人类消息就是用户信息,由人给出的信息发送给LLMs的提示信息,比如"实现一个快速排序方法".
-
SystemMessage: 可以用于指定模型具体所处的环境和背景,如角色扮演等。你可以在这里给出具体的指示,比如"作为一个代码专家",或者"返回json格式".
-
ChatMessage: Chat 消息可以接受任意角色的参数,但是在大多数时间,我们应该使用上面的三种类型.
LangChain支持大量的chat 模型,可以通过官网查询:
也可以通过langchain源码查看

- SystemMessage+HumanMessage+AIMessage
python
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
# from langchain_community.chat_models import ChatOllama # Langchain 0.x版本使用
from langchain_ollama import ChatOllama
# 实例化模型
model = ChatOllama(model="qwen2.5:7b")
# 定义提示词
messages = [
SystemMessage(content="现在你是一个著名的诗人"),
HumanMessage(content="给我写一首唐诗")]
# 获取问答结果
result = model.invoke(messages)
# print(result)
print(result.content)
3 Embeddings Models
Embeddings Models(嵌入模型)特点:将字符串作为输入,返回一个浮动数的列表。在NLP中,Embedding的作用就是将数据进行文本向量化。
不同的Embedding模型对多语言支持和文本类型有不同的特点:
- 多语言支持 :
text-embedding-ada-002:支持多种语言,但对中文等亚洲语言的支持相对较弱bge-large-zh:对中文有很好的支持multilingual-e5-large:对多语言都有较好的支持- mxbai-embed-large:
- 文本类型适用性 :
- 代码文本:建议使用专门的代码Embedding模型,如
CodeBERT - 通用文本:可以使用
text-embedding-ada-002或bge-large-zh - 专业领域文本:建议使用该领域的专门模型
- 代码文本:建议使用专门的代码Embedding模型,如
可以参考MTEB(大规模文本嵌入基准)排行榜以获取最新模型效果:huggingface.co/spaces/mteb...
接下来以一个文本嵌入模型的例子进行说明:
python
# from langchain_community.embeddings import OllamaEmbeddings # Langchain 0.x版本使用
from langchain_ollama import OllamaEmbeddings
# 初始化Ollama嵌入模型,使用mxbai-embed-large模型,温度设置为0
model = OllamaEmbeddings(model="mxbai-embed-large", temperature=0)
# 对单个查询文本进行嵌入编码
res1 = model.embed_query('这是第一个测试文档')
print(f'result1->{res1}')
print(f'result1的长度->{len(res1)}')
# 对多个文档进行批量嵌入编码
res2 = model.embed_documents(['这是第一个测试文档', '这是第二个测试文档'])
print(res2)
运行结果:

三、Prompts
1 通用prompt
Prompt是指当用户输入信息给模型时加入的提示,这个提示的形式可以是zero-shot或者few-shot等方式,目的是让模型理解更为复杂的业务场景以便更好的解决问题。
提示模板:如果你有了一个起作用的提示,你可能想把它作为一个模板用于解决其他问题,LangChain就提供了PromptTemplates组件,它可以帮助你更方便的构建提示。
- zero-shot提示方式
python
# from langchain import PromptTemplate # Langchain 0.x版本使用
from langchain_core.prompts import PromptTemplate
# from langchain_community.llms import Ollama # Langchain 0.x版本使用
from langchain_ollama import OllamaLLM
# model = Ollama(model="qwen2.5:7b") # Langchain 0.x版本使用
model = OllamaLLM(model="qwen2.5:7b")
# 定义模板
template = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字"
prompt = PromptTemplate(
input_variables=["lastname"],
template=template)
prompt_text = prompt.format(lastname="王")
print(prompt_text)
# result: 我的邻居姓王,他生了个儿子,给他儿子起个名字
result = model.invoke(prompt_text)
print(result)
运行结果:

- few-shot提示方式
python
# from langchain import PromptTemplate, FewShotPromptTemplate # Langchain 0.x版本使用
from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate
from langchain_ollama import OllamaLLM
model = OllamaLLM(model="qwen2.5:7b")
examples = [
{"word": "开心", "antonym": "难过"},
{"word": "高", "antonym": "矮"}]
example_template = """
单词: {word}
反义词: {antonym}\\n
"""
example_prompt = PromptTemplate(
input_variables=["word", "antonym"],
template=example_template,
)
few_shot_prompt = FewShotPromptTemplate(
examples=examples,
example_prompt=example_prompt,
prefix="给出每个单词的反义词",
suffix="单词: {input}\\n反义词:",
input_variables=["input"],
example_separator="\\n",
)
prompt_text = few_shot_prompt.format(input="粗")
print(model.invoke(prompt_text))
# 细
2 ChatPrompts
适合交互式对话应用,如聊天机器人、智能客服等,这些应用需要处理用户和LLM之间的多轮对话。
ChatPromptTemplate
SystemMessagePromptTemplate
HumanMessagePromptTemplate
history=("system","......"),('human',"......"),("ai","......")
- 直接提问
提示模板就是把一些常见的提示整理成模板,用户只需要修改模板中特定的词语,就能快速准确地告诉模型自己的需求。我们看个例子:
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 定义提示词模版
template_str = "帮我讲个关于{name}笑话吧"
prompt_template = ChatPromptTemplate.from_template(template_str)
prompt = prompt_template.format_messages(name="气球")
print(f'prompt-->{prompt}')
# 调用模型
result = model.invoke(prompt)
print(f'result-->{result}')
- zero-shot提示方式
python
from langchain_core.prompts import ChatPromptTemplate, HumanMessagePromptTemplate
from langchain_core.messages import SystemMessage
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 系统信息
system_prompt = SystemMessage("你是取名专家。")
# 用户信息模版
human_str = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字。"
human_template = HumanMessagePromptTemplate.from_template(human_str)
# 组装
chat_template = ChatPromptTemplate.from_messages([system_prompt, human_template])
# 生成最终的提示词
prompt = chat_template.format_messages(lastname="王")
print(f'prompt-->{prompt}')
# 调用模型
result = model.invoke(prompt) # 返回结果出了content外,还有元数据信息
print(f'result-->{result}')
运行结果:

- few-shot提示方式
python
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 创建 prompt 模版
prompt_template = ChatPromptTemplate.from_messages(
[
("system", "给出每个单词的反义词"),
MessagesPlaceholder("history"),
("human", "{question}")
]
)
# 创建 few-shot prompt
# history = [
# HumanMessage(content="开心"),
# AIMessage(content="难过"),
# HumanMessage(content="高"),
# AIMessage(content="矮")
# ]
history = [("human", "开心"), ("ai", "难过"),("human", "高"), ("ai", "矮")]
prompt = prompt_template.format_messages(history=history, question="富有")
print(f"prompt-->{prompt}")
# 调用模型
result = model.invoke(prompt)
print(f'result-->{result}')
运行结果:

四、Chains
在LangChain中,Chains描述了将LLM与其他组件结合起来完成一个应用程序的过程。 针对上一小节的提示模版例子,zero-shot里面,我们可以用链来连接提示模版组件和模型,进而可以实现代码的更改,主要使用LCEL方法。
LCEL(Lang Chain Expression Language) 是一种声明式的方法,用于轻松组合链条。
LCEL的基本语法规则是使用|符号将不同的组件连接起来,形成一个链式结构。|符号类似于Unix的管道操作符,它将一个组件的输出作为下一个组件的输入,从而实现数据的传递和处理。
上一个组件的输出作为下一个组件的输入,输出和输入的类型必须保持一致,否则不能连接。
python
from langchain import PromptTemplate
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
# 定义模板
template = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字"
prompt = PromptTemplate(
input_variables=["lastname"],
template=template)
# 实例化模型
llm = OllamaLLM(model="qwen2.5:7b")
chain = prompt | llm
# 执行链
print(chain.invoke("王"))
如果你想将第一个模型输出的结果,直接作为第二个模型的输入,直接使用管道符, 代码如下:
python
from langchain_core.prompts import PromptTemplate
from langchain_ollama import OllamaLLM
# 创建第一条链
template = "我的邻居姓{lastname},他生了个儿子,给他儿子起个名字"
first_prompt = PromptTemplate(
input_variables=["lastname"],
template=template,)
# 实例化模型
llm = OllamaLLM(model="qwen2.5:7b")
first_chain = first_prompt | llm
# 创建第二条链
second_prompt = PromptTemplate(
input_variables=["child_name"],
template="邻居的儿子名字叫{child_name},给他起一个小名")
second_chain = second_prompt | llm
# 链接两条链
overall_chain = first_chain | second_chain
print(overall_chain)
print('*'*80)
# 执行链,只需要传入第一个参数
catchphrase = overall_chain.invoke("王")
print(catchphrase)
运行结果:


五、output_parsers
LLM 的输出是自然语言文本,但在应用开发中,我们经常需要将这些文本转换为结构化的数据格式,如列表、字典或对象。LangChain 输出解析器负责获取 LLM 的输出并将其转换为更合适的格式。
部分解析器如下:
| 解析器名称 | 核心功能 | 输出的 Python 类型 | 工业级应用场景 |
|---|---|---|---|
| StrOutputParser | 默认解析器。将 LLM 的输出直接解析为字符串。 | str | 只需要原始回答时(如问答任务、对话场景) |
| CommaSeparatedListParser | 将 LLM 输出的、用逗号分隔的文本解析为列表。 | liststr | 列表/枚举型输出 |
| JsonOutputParser | 极其常用。将 LLM 输出的 JSON 字符串解析为 Python 字典。 | dict | 结构化 JSON 输出 |
| PydanticOutputParser | 极其常用。将 LLM 输出解析为预先定义的 Pydantic 对象,提供类型安全和数据验证。 | 自定义的 pydantic.BaseModel 对象 | 输出需要严格结构化(JSON-like)数据时 |
| DatetimeOutputParser | 从文本中智能地解析出日期和时间信息。 | datetime.datetime | 需要时间格式时 |
1 字符串解析器
StrOutputParser,最简单的解析器,用于提取模型返回的原始文本:
python
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 创建简单链
prompt = ChatPromptTemplate.from_template("解释{topic}是什么?回答控制20字以内")
chain1 = prompt | model
result1 = chain1.invoke({"topic": "ai"})
print(f"result1-->{result1}")
# 添加字符串解析器
parser = StrOutputParser()
chain2 = prompt | model | parser
result2 = chain2.invoke({"topic": "ai"})
print(f"result2-->{result2}")
2 列表解析器
CommaSeparatedListOutputParser,将逗号分隔的文本转换为Python列表:
python
from langchain_core.output_parsers import CommaSeparatedListOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 创建列表解析器
parser = CommaSeparatedListOutputParser()
# 创建带格式说明的提示模板
format_instructions = parser.get_format_instructions()
prompt = ChatPromptTemplate.from_template(
"用中文列出{topic}的五个最重要特点。\n{format_instructions}"
)
# 组合组件
chain = prompt | model | parser
# 调用链
result = chain.invoke({
"topic": "大模型",
"format_instructions": format_instructions
})
print(result)
3 JSON解析器
JsonOutputParser,将JSON格式文本转换为Python字典或列表:
python
from langchain_core.output_parsers import JsonOutputParser
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 创建JSON解析器
json_parser = JsonOutputParser()
# 创建带格式说明的提示模板
json_format_instructions = json_parser.get_format_instructions()
json_prompt = ChatPromptTemplate.from_template(
"生成一个包含{person}基本信息的JSON。应包括姓名、职业、年龄和技能列表, 不要包含任何注释或额外说明。\n{format_instructions}"
)
# 组合组件
json_chain = json_prompt | model | json_parser
# 调用链
result = json_chain.invoke({
"person": "雷军",
"format_instructions": json_format_instructions
})
print(result)
运行结果:

4 Pydantic解析器
PydanticOutputParser,使用Pydantic模型定义输出结构:
python
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
# 定义Pydantic模型
class Movie(BaseModel):
title: str = Field(description="电影标题")
director: str = Field(description="导演姓名")
year: int = Field(description="上映年份")
genre: List[str] = Field(description="电影类型")
rating: float = Field(description="评分(1-10)")
# 创建Pydantic解析器
pydantic_parser = PydanticOutputParser(pydantic_object=Movie)
# 创建带格式说明的提示模板
format_instructions = pydantic_parser.get_format_instructions()
pydantic_prompt = ChatPromptTemplate.from_template(
"生成一部{genre}电影的信息。\n{format_instructions}"
)
# 组合组件
pydantic_chain = pydantic_prompt | model | pydantic_parser
# 调用链
movie_data = pydantic_chain.invoke({
"genre": "科幻",
"format_instructions": format_instructions
})
print(movie_data)
运行结果:

5 自定义解析器
创建自定义输出解析器
python
from langchain_core.output_parsers import BaseOutputParser
from typing import Dict, Any
from langchain_core.prompts import ChatPromptTemplate
from langchain_ollama import OllamaLLM
# 实例化模型
model = OllamaLLM(model="qwen2.5:7b")
class CustomKeyValueParser(BaseOutputParser[Dict[str, Any]]):
"""解析形如'key: value'的文本"""
def parse(self, text: str) -> Dict[str, Any]:
"""从文本中解析键值对"""
result = {}
lines = text.strip().split('\n')
for line in lines:
if ':' in line:
key, value = line.split(':', 1)
result[key.strip()] = value.strip()
return result
def get_format_instructions(self) -> str:
"""提供格式指导给模型"""
return """请以'键: 值'的格式返回信息,每行一个键值对。
例如:
名称: 爱因斯坦
职业: 物理学家
贡献: 相对论"""
# 使用自定义解析器
custom_parser = CustomKeyValueParser()
custom_prompt = ChatPromptTemplate.from_template(
"提供关于{person}的基本信息。\n{format_instructions}"
)
# 组合组件
custom_chain = custom_prompt | model | custom_parser
# 调用模型
result = custom_chain.invoke({
"person": "屠呦呦",
"format_instructions": custom_parser.get_format_instructions()
})
print(result)
运行结果:

六、Memory
大模型本身不具备上下文的概念,它并不保存上次交互的内容,ChatGPT之所以能够和人正常沟通对话,因为它进行了一层封装,将历史记录回传给了模型。
因此 LangChain 也提供了Memory组件, Memory分为两种类型: 短期记忆和长期记忆 。短期记忆一般指单一会话时传递数据,长期记忆则是处理多个会话时获取和更新信息。
- 使用 ChatMessageHistory 手动添加上下文
python
from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.messages import messages_to_dict, messages_from_dict
import json
# 1.创建一个ChatMessageHistory对象,用来存储对话信息
history = ChatMessageHistory()
# 添加用户消息
history.add_user_message("在吗?")
# 添加大模型消息
history.add_ai_message("在")
# 打印所有消息
print(f'history.messages-->{history.messages}')
# 2.可以将history.messages中的信息保存到字典中,然后保存到数据库或者文件中,方便后续读取
# 2.1 messages_to_dict()方法将history.messages中的信息转换成字典
dicts = messages_to_dict(history.messages)
print(f'dicts-->{dicts}')
# 2.2 这里将dicts保存到文件中
with open('history.json', 'w', encoding='utf-8') as f:
f.write(json.dumps(dicts, indent=2, ensure_ascii=False))
# 3.从文件中读取出字典,然后将字典转换成消息
# 3.1 读取文件
messages = json.load(open('history.json', 'r', encoding='utf-8'))
# 3.2 然后将字典转换成消息
chat_messages = messages_from_dict(messages)
print(f'chat_messages-->{chat_messages}')
运行结果:

LangChain v1.0 版本对 Memory(记忆) 组件进行了深度优化与工程化重构,使其不仅支持多轮对话上下文管理,更成为构建生产级智能体(Agent)不可或缺的核心能力之一。在 LangChain 从"实验性框架"迈向"企业级平台"的关键转型中,Memory 模块被赋予了更清晰的职责边界、更灵活的策略机制以及更强的可扩展性,从而支撑复杂任务场景下的长期记忆、状态追踪与个性化交互。
1 短期记忆(Short-term Memory)
短期记忆的本质是线程级状态管理]。在 LangGraph 中:
短期记忆 = Agent 状态(State) + 检查点持久化(Checkpointer) + 线程标识(thread_id)。
(1)状态(State)
通常是一个包含 messages 字段的字典或 Pydantic 模型(如 MessagesState 或自定义 CustomState),用于存储当前会话的所有消息、中间变量、工具调用结果等。
(2)检查点器(Checkpointer)
负责将状态序列化并持久化到内存、SQLite、PostgreSQL 等后端。每次状态变更(如新增一条消息)都会触发一次检查点保存。
(3)会话ID(thread_id)
作为会话的唯一标识符,确保不同用户或不同对话之间的状态完全隔离。即使多个用户并发交互,也不会发生记忆混淆。
python
from langchain.agents import create_agent
from langgraph.checkpoint.memory import InMemorySaver
from langchain.messages import HumanMessage
from langchain_ollama import OllamaLLM
import time
# 初始化大模型,这里使用的是 Qwen 的聊天模型
model = OllamaLLM(model="qwen2.5:7b")
# 创建内存保存器 (Checkpointer)
# 这是 LangGraph 的核心概念之一,用于在对话过程中保存和恢复状态(记忆)
checkpointer = InMemorySaver()
# 定义 Agent 的配置
# "thread_id" (线程ID) 非常关键:它相当于对话的 ID,用于区分不同的会话记忆
# 使用时间戳作为 ID,确保每次运行脚本时的线程 ID 不同(但在同一次运行中复用)
timestamp = time.time()
config = {
"configurable": {"thread_id": f"{int(timestamp)}"}
}
# 创建 Agent 实例
# model: 指定使用的底层大模型
# tools: 工具列表,这里为空,表示 Agent 只能进行对话,不能调用外部工具(如搜索、计算器等)
# checkpointer: 传入上面的内存保存器,赋予 Agent 记忆能力
# system_prompt: 设定 Agent 的角色和行为模式
agent = create_agent(
model=model,
tools=[],
checkpointer=checkpointer,
system_prompt="你是一个翻译官,擅长中英互译。"
)
# 第一轮对话输入
input_1 = {
"messages": [HumanMessage("你好,我叫小呆。")]
}
# 调用 Agent (Response A)
# 使用 config,这意味着这次对话会记录在 thread_id 对应的内存中
# Agent 会记住 "我叫小呆" 这个信息
response_a = agent.invoke(
input=input_1,
config=config
)
# 第二轮对话输入(测试是否记得之前的信息)
input_2 = {
"messages": [HumanMessage("你好,我叫什么?")]
}
# 定义一个新的配置 (config_2),使用完全不同的 thread_id
# 这代表开启了一个全新的、独立的对话会话
config_2 = {
"configurable": {"thread_id": f"{int(time.time())}"} # 这里生成了一个新的时间戳 ID
}
# 调用 Agent (Response B)
# 使用新的 config_2,Agent 看不到 config_1 中的历史记录
# 因此,Agent 应该不知道用户叫 "小呆",因为它处于一个新的 "线程" 中
response_b = agent.invoke(
input=input_2,
config=config_2
)
# 调用 Agent (Response C)
# 再次使用最初的 config
# 这会恢复到第一个会话的记忆中
# Agent 应该能回想起用户叫 "小呆",因为它共享同一个 thread_id
response_c = agent.invoke(
input=input_2,
config=config
)
# 输出结果进行对比
print("Response A (第一次对话):")
print(response_a)
print("\nResponse B (新线程,无记忆):")
print(response_b)
print("\nResponse C (回到原线程,有记忆):")
print(response_c)
代码运行逻辑:
- checkpointer (记忆核心):这是 LangGraph 区别于传统简单 API 调用的关键。它允许 Agent 在多次 invoke 调用之间保存状态。
- thread_id (记忆索引) :你可以把 thread_id 想象成数据库中的主键。
- Response A:向 ID 为 100 的记录里写入了名字。
- Response B:向 ID 为 200 的新记录提问,因为 200 是空的,所以 Agent 不知道名字。
- Response C:又回到 ID 为 100 的记录提问,Agent 读取了历史记录,所以知道名字。
- System Prompt:虽然输入是在对话,但因为设定了 "你是一个翻译官",Agent 可能会试图在回答的同时进行翻译,或者在回复格式上符合翻译官的身份。
2 长期记忆
对于需要长期运行和可靠记忆的应用,推荐使用数据库进行持久化。详见后面阶段LangGraph部分。
七、Indexes
Indexes组件的目的是让LangChain具备处理文档处理的能力,包括:文档加载、检索等。注意,这里的文档不局限于txt、pdf等文本类内容,还涵盖email、区块链、视频等内容。
Indexes组件主要包含类型:
- 文档加载器
- 文本分割器
- VectorStores
- 检索器
1 文档加载器
文档加载器主要基于Unstructured 包,Unstructured 是一个python包,可以把各种类型的文件转换成文本。文档加载器使用起来很简单,只需要引入相应的loader工具。
LangChain支持的文档加载器 (部分):

示例代码:
需要安装的包:
pip install unstructured
pip install langchain-unstructured
python
from langchain_unstructured import UnstructuredLoader
# 创建 UnstructuredLoader 对象
loader = UnstructuredLoader('./data/衣服属性.txt', encoding='utf8')
docs = loader.load()
print(f'docs-->{docs}')
print(f'len-->{len(docs)}')
print(f'第一行数据-->{docs[0].page_content}')
print('*' * 100)
from langchain_community.document_loaders import TextLoader
# 创建 TextLoader 对象
loader = TextLoader('./data/衣服属性.txt', encoding='utf8')
docs = loader.load()
print(f'docs-->{docs}')
print(f'len-->{len(docs)}')
print('第一行数据-->{}'.format(docs[0].page_content.split('\n')[0]))
运行结果:

2 文档分割器
由于模型对输入的字符长度有限制,我们在碰到很长的文本时,需要把文本分割成多个小的文本片段。
文本分割最简单的方式是按照字符长度进行分割,但是这会带来很多问题,比如说如果文本是一段代码,一个函数被分割到两段之后就成了没有意义的字符,所以整体的原则是把语义相关的文本片段放在一起。
LangChain中最基本的文本分割器是CharacterTextSplitter ,它按照指定的分隔符(默认"\n\n")进行分割,并且考虑文本片段的最大长度。我们看个例子:
python
from langchain_core.documents import Document
from langchain_text_splitters import CharacterTextSplitter
# 创建分词器 separator参数指的是分割的分隔符,chunk_size指的是分割出来的每个块的大小,chunk_overlap指的是每个块之间重叠的大小
text_splitter = CharacterTextSplitter(separator=" ", chunk_size=5, chunk_overlap=1)
# 一句话分割
result1 = text_splitter.split_text("a b c d e f")
print(f'result1--->{result1}')
# 多句话分割
result2 = text_splitter.create_documents(["a b c d e f", "e f g h"])
print(f'result2--->{result2}')
# 多句话分割
result3 = text_splitter.split_documents([Document(page_content="a b c d e f", metadata={"id": "1"})])
print(f'result3--->{result3}')
运行结果:

除了CharacterTextSplitter分割器,LangChain还支持其他文档分割器 (部分):
| 分割器名称 | 功能描述 | 类型 | 工业场景应用 |
|---|---|---|---|
| CharacterTextSplitter | 简单按指定分隔符(如换行、逗号)直接分割。 | 基础字符解析 | 简单字符串或 CSV 数据处理,如传感器数据日志。 |
| RecursiveCharacterTextSplitter | 递归按字符分割,先尝试自然边界(如段落、句子),太大则继续细分。 | 通用字符解析 | 通用文本处理,如日志、报告、PDF 文档分割,便于 RAG 检索。 |
| TokenTextSplitter | 按 token(词元)分割,支持 LLM token 计数。 | Token 基于解析 | LLM 输入优化,如处理 API 响应或长查询,控制 token 限制。 |
| SentenceTextSplitter | 按句子边界分割,使用 NLP 识别句子(包括标点)。 | 语义解析 | 自然语言文本,如文章或对话分析,保持句子完整。 |
| SpacyTextSplitter | 使用 SpaCy NLP 库按句子或实体分割(需安装 SpaCy)。 | 语义解析 | 高级 NLP 场景,如实体提取或生物医学文本。 |
| NLTKTextSplitter | 使用 NLTK 库按句子或词分割(需安装 NLTK)。 | 语义解析 | 文本研究或分析,如时间序列数据描述。 |
| MarkdownHeaderTextSplitter | 按 Markdown 结构(如标题、列表)智能分割。 | 结构化解析 | Markdown 文档分割,保留语义结构,用于知识库构建。 |
| HTMLSplitter | 按 HTML 标签(如 、)分割网页内容。 | 结构化解析 | 网页数据爬取,如在线技术文档或新闻提取。 |
| LatexTextSplitter | 按 LaTeX 结构(如章节、公式)分割。 | 结构化解析 | 学术论文或数学文档处理。 |
| PythonCodeTextSplitter | 按 Python 代码结构(如函数、类)分割。 | 代码解析 | 源代码文件分析,如脚本调试或代码库管理。 |
下面就几个重要的分割器进行讲解。
- 递归字符文本分割器(RecursiveCharacterTextSplitter)
递归字符文本分割器是一种更智能的分割方法,它尝试在特定分隔符处分割文本,以保持更好的语义完整性。 特点:
- 尝试在自然断点处分割文本
- 比简单的字符分割更能保持语义完整性
- 适用于结构化程度较高的文本,如 Markdown、HTML 等
运行流程:
- 首先尝试使用第一个分隔符(如 "\n\n")分割文本
- 如果分割后的块仍然过大,则使用下一个分隔符继续分割
- 重复此过程,直到达到指定的 chunk_size 或用完所有分隔符
示例代码
pythonfrom langchain_text_splitters import RecursiveCharacterTextSplitter text_splitter = RecursiveCharacterTextSplitter( chunk_size=20, # 每个块最多 100 个字符 chunk_overlap=6, # 相邻分块会共享 20 个字符 length_function=len, # 用字符数来衡量长度 separators=["\n\n", "\n", " ", ""] # 会优先尝试按 \n\n 分段,如果太长,再按 \n → 空格 → 逐字符切分。 ) text = """ 人工智能正在快速发展,尤其是大语言模型的应用,正在改变人类的工作方式。 它们可以帮助人们进行写作、代码生成、甚至是科研探索。 相比之下,新能源的发展同样重要。 电动车和太阳能正在逐渐替代传统能源,减少碳排放,对全球环境保护至关重要。 """ docs = text_splitter.split_text(text) print(docs)运行结果
- 语义文档分割器(SemanticChunker)
语义文档分割器使用语义理解来分割文本,这是一种更高级的分割方法。 特点:
- 基于语义相似性分割文本
- 能够更好地保持语义完整性
- 计算成本较高,处理大量文本时可能效率较低
- 适用于需要高度语义理解的场景
示例代码
pythonfrom langchain_experimental.text_splitter import SemanticChunker from langchain_ollama import OllamaEmbeddings embed = OllamaEmbeddings(model="mxbai-embed-large") text_splitter = SemanticChunker( embeddings=embed, breakpoint_threshold_type='percentile', # 更低的百分位会更"积极"地切分(数值越小 => 切得越多) breakpoint_threshold_amount=70.0, # 句子拆分正则为同时识别中/英文终结符 sentence_split_regex=r'(?<=[。!?.!?])\s*', # 每个切分后片段(chunk)至少要有多少个字符 min_chunk_size=10 ) text = """ 人工智能正在快速发展,尤其是大语言模型的应用,正在改变人类的工作方式。 它们可以帮助人们进行写作、代码生成、甚至是科研探索。 相比之下,新能源的发展同样重要。 电动车和太阳能正在逐渐替代传统能源,减少碳排放,对全球环境保护至关重要。 """ docs = text_splitter.split_text(text) for i, d in enumerate(docs): print(f"------ Chunk {i+1} ------") print(d.strip()) print()运行结果
- MarkdownHeaderTextSplitter(Markdown文档切割器)
适用于Markdown文档,按照标题进行拆分
示例代码
pythonfrom langchain_text_splitters import MarkdownHeaderTextSplitter headers_to_split_on = [ ("#", "Header 1"), ("##", "Header 2"), ("###", "Header 3"), ] markdown_splitter = MarkdownHeaderTextSplitter(headers_to_split_on=headers_to_split_on) markdown_text = "# Header 1\nSome text\n## Header 2\nMore text\n### Header 3\nEven more text" docs = markdown_splitter.split_text(markdown_text) print(docs)运行结果
其他拓展知识可以参考:blog.csdn.net/qq_28540861...
3 VectorStores
VectorStores是一种特殊类型的数据库,它的作用是存储由嵌入创建的向量,提供相似查询等功能。
LangChain支持的VectorStore有python.langchain.com/docs/integr...,常见的如下:

我们使用其中一个
Chroma组件作为例子:pip install chromadb
pip install langchain-chroma
python
from langchain_text_splitters import CharacterTextSplitter
from langchain_chroma import Chroma
from langchain_community.document_loaders import TextLoader
from langchain_ollama import OllamaEmbeddings
# 1.加载文档
# 创建 TextLoader 对象
loader = TextLoader('./data/pku.txt', encoding='utf-8')
# 加载文档
docs = loader.load()
# print(f'docs-->{docs}')
# 2.将文档进行分块
# 创建 CharacterTextSplitter 对象
text_splitter = CharacterTextSplitter(separator="\n\n", chunk_size=200, chunk_overlap=30)
# 分块
split_docs = text_splitter.split_documents(docs)
print(f'split_docs-->{split_docs}')
# 3.将分割后的文档存储到向量数据库中
# 加载embedding模型
embedding = OllamaEmbeddings(model="mxbai-embed-large")
# 创建向量数据库,需要指定 存储的文档和向量模型名称以及持久化目录
chromadaDB = Chroma.from_documents(documents=split_docs,
embedding=embedding,
persist_directory='./chroma_db')
# 假如你的向量数据库已经存在,那么可以直接加载
# chromadaDB = Chroma(persist_directory='./chroma_db', embedding_function=embedding)
# 4.使用向量数据库进行查询
query = "1937年北京大学发生了什么?"
result = chromadaDB.similarity_search(query, k=2)
print(f'result-->{result}')
运行结果:

4 检索器
4.1 LangChain的检索器定义
检索器是 LangChain 中负责信息检索的模块,通常与 索引(Indexes) 模块(如向量存储、嵌入模型)结合使用。它的核心功能是:
- 输入:接收用户查询(通常是文本)。
- 处理:根据查询从数据源中检索相关内容。
- 输出:返回一组相关文档或文本片段(通常是 Document 对象列表)。
检索器在以下场景中扮演关键角色:
- 问答系统:从文档或知识库中检索答案的上下文。
- 语义搜索:根据查询的语义返回相关结果。
- 上下文增强:为语言模型提供外部知识,解决其知识局限。
4.2 检索器的工作原理
检索器通常与 向量存储(Vector Stores) 配合,通过嵌入模型(Embedding Models)将查询和文档转为向量,基于相似性进行检索。工作流程可以分为以下步骤:
- 查询嵌入:将用户查询通过嵌入模型(如 OpenAIEmbeddings)转为向量表示
- 相似性搜索:在向量存储中查找与查询向量最相似的文档向量。
- 文档返回:返回匹配的文档(包含内容、元数据等)。
- 后处理(可选):对检索结果进行排序、过滤或重新排名。
检索器的核心依赖:
- 嵌入模型:将文本转为向量(如 OpenAIEmbeddings, HuggingFaceEmbeddings)。
- 向量存储:存储文档向量(如 Chroma、FAISS、Pinecone)。
- 相似性度量:如余弦相似度、欧几里得距离
4.3 检索器类型
langchain支持很多检索器python.langchain.com/docs/integr...,部分如下:

此处我们讲解VectorStoreRetriever。
在 LangChain 中,as_retriever() 方法的 search_type 参数决定了向量检索的具体算法和行为。
python
retriever = vector_store.as_retriever(
search_type="similarity", # 可选 "similarity"|"mmr"|"similarity_score_threshold"
search_kwargs={
"k": 5, # 返回结果数量
"score_threshold": 0.7, # 仅当search_type="similarity_score_threshold"时有效,低于阈值的都丢弃。
"filter": {"source": "重要文档.pdf"}, # 元数据过滤,只会检索满足条件的文档。
"lambda_mult": 0.25 # 仅MMR搜索有效(控制多样性):接近 0 则更强调和查询的相关性;接近 1 则更强调结果之间的差异性
}
)
以下是三种搜索类型的对比:

示例代码:
python
from langchain_chroma import Chroma
from langchain_ollama import OllamaEmbeddings
embedding = OllamaEmbeddings(model="mxbai-embed-large")
# 向量数据库已经存在,那么可以直接加载
chromadaDB = Chroma(persist_directory='./chroma_db', embedding_function=embedding)
# 使用向量数据库进行查询
query = "1937年北京大学发生了什么?"
# result = chromadaDB.similarity_search(query, k=2)
# 使用 as_retriever 方法返回 Retriever 对象,然后调用 invoke 方法进行查询
retriever = chromadaDB.as_retriever(search_kwargs={"k":2})
result = retriever.invoke(query)
print(f'result-->{result}')
运行结果:

拓展:
vectordb.as_retriever() 和 vectordb.similarity_search() 都是用于从向量数据库中检索相关文档的方法,它们有什么异同:
-
相同
- 核心功能:两者都基于向量相似度(如余弦相似度)从向量数据库中检索与查询最相关的文档。
- 底层技术:通常使用相同的嵌入模型和相似度计算方式(如 FAISS、Chroma、Pinecone 等)。
-
不同
4.4 拓展
其他几种常用的检索器介绍如下。
- TFIDFRetriever
功能:基于 TF-IDF(词频-逆文档频率)的检索器。
特点:
- 使用 TF-IDF 向量表示文档和查询。
- 适合快速构建原型。
- 不支持语义搜索。
适用场景:
- 文本搜索
- 关键词提取
示例代码:
pythonfrom langchain_community.retrievers import TFIDFRetriever from langchain_core.documents import Document docs = [ Document(page_content="量子计算是一种基于量子力学的计算范式。"), Document(page_content="人工智能是模拟人类智能的技术。") ] retriever = TFIDFRetriever.from_documents(docs) retriever.k = 1 # 返回 1 个结果 results = retriever.invoke("量子计算") print(results)
- BM25Retriever
BM25算法:BM25 是对 TF-IDF 的改进版本,在 TF-IDF 基础上做了归一化(防止长文档优势)与饱和控制(防止词频无限增长)。
功能:基于 BM25 算法的关键词检索器,适合基于词频的搜索。特点:
- 不依赖嵌入模型,使用词频和逆文档频率(TF-IDF)计算相关性。
- 适合关键词匹配场景,计算成本低。
- 不支持语义搜索,效果依赖文本的字面匹配。
适用场景:
- 传统搜索场景。
- 关键词驱动的问答。
环境依赖:
pip install rank_bm25
示例代码:
pythonfrom langchain_community.retrievers import BM25Retriever from langchain_core.documents import Document docs = [ Document(page_content="量子计算是一种基于量子力学的计算范式。"), Document(page_content="人工智能是模拟人类智能的技术。") ] retriever = BM25Retriever.from_documents(docs) retriever.k = 1 # 返回 1 个结果 results = retriever.invoke("量子计算") print(results)
- MultiQueryRetriever
功能:它借助 LLM 自动生成多个语义等价的改写查询,把用户的问题扩展成多个角度,然后对每个改写进行检索,最后合并结果,以提高召回率。
特点:
- 使用语言模型生成查询的多种表达方式
- 从向量存储中检索所有变体的结果并合并
- 提高召回率,适合复杂查询
适用场景:
- 查询表达不明确或需要覆盖多种语义
- 提高检索的全面性
- EnsembleRetriever
功能:结合多种检索器(如 BM25 和向量存储),融合结果。
特点:
- 结合关键词搜索和语义搜索的优点
- 支持加权融合,调整不同检索器的权重
- 提高召回率和精准度
适用场景:
- 需要综合关键词和语义的搜索
- 复杂查询场景
- ContextualCompressionRetriever
功能:对检索结果进行压缩,提取最相关的内容
特点:
- 使用语言模型对检索到的文档进行重新排序或精炼
- 减少无关内容,提高结果质量
- 增加计算开销,但提升精准度
适用场景:
- 文档内容冗长,需要提取关键信息
- 提高问答系统的答案质量
- Custom Retriever
功能:开发者可以自定义检索逻辑,适配特定数据源或算法
特点:
- 继承 BaseRetriever 类,实现 get_relevant_documents 方法
- 支持任意数据源(如数据库、API)
适用场景:
- 特定领域的数据源(如内部数据库)
- 自定义检索算法
八、Agents
1 Agent 概念
Agent(智能体)是一种能够感知环境、进行决策和执行动作的智能实体。不同于传统的人工智能,Agent 具备通过主动思考、调用工具去逐步完成给定目标的能力。
为什么要调用工具?
- 因为大模型虽然非常强大,但是也具备一定的局限性,比如不能回答实时信息、处理数学逻辑问题仍然非常的初级等等。因此,可以借助第三方工具来辅助大模型的应用。
从大模型的角度来看,Agent其实就是基于大模型的语义理解和推理能力,让大模型拥有解决复杂问题时的任务规划能力,并调用外部工具来执行各种任务,利用向量数据库保留"记忆"的一个智能体。
Agent = 大模型 + 任务规划(Planning) + 使用外部工具执行任务(Tools&Action) + 记忆(Memory)
把Agent比作一个人的话,他应该有大脑(语义理解、存储记忆、推理规划、专业知识)、五官(接收文本、视觉输入、听觉输入等)、四肢(使用工具完成各种具体任务)等主要部件
工作流程概述:
- 用户提出任务。
- Agent 启动: 将用户输入与预设的"提示词模板"结合,并结合当前的"上下文"和"变量",形成一个完整的输入发送给大模型。
- 大模型思考与决策 (循环):
- 大模型接收输入后,根据其内置的逻辑和提示词的指导,进行"思考"。
- 它会判断完成当前任务是需要继续使用工具来获取更多信息/执行操作,还是已经可以直接生成最终答案。
- 如果需要工具: 大模型会根据任务需求和对工具的描述,选择合适的工具,并生成执行该工具所需的输入参数。这些参数通常会从上下文或变量中提取。
- 工具执行: 选定的工具被调用,执行其功能。
- 结果反馈: 工具执行的结果会返回给 Agent,并被用来更新"上下文"和"变量"。
- 循环: Agent 将更新后的"上下文"、"变量"以及工具执行结果再次反馈给大模型,大模型继续进行新一轮的"思考-行动"循环,直到任务完成。
- 如果直接回答: 当大模型判断任务已完成,或无需额外工具即可回答时,它会生成最终的答案。
- 用户输出: 最终答案被呈现给用户。
2 Agent关键组成部分

-
任务规划(Planning)
Agent需要提前将一项复杂任务拆解为多个更小、更易于处理的子任务,从而实现对复杂任务的高效处理
-
长短期记忆(Memory)
聊天上下文,长期保留和回忆信息
-
工具&执行(Tools&Action)
根据拆分好的子任务,调用外部提供好的专业API解决专业问题,完成一个个具体的子任务,并把处理结果返回给大模型
3 langchain实现Agent
LangChain 提供了不同类型的代理(主要罗列一下三种):
-
Zero-shot ReAct Description
-
基于 ReAct 框架 (推理 + 行动),仅依赖工具的 描述 来决定调用哪个工具。
-
特点:无需额外示例(zero-shot),但 不具备记忆能力,每次推理都独立进行。
-
使用场景:简单任务,工具选择完全靠工具描述即可。
-
-
Structured Chat Zero-shot ReAct Description
-
同样基于 ReAct 框架 ,但可以处理 结构化输入,即支持带多个参数的工具(类似函数调用)。
-
特点:不仅能像第一种代理那样根据描述选择工具,还能正确组织并传递复杂参数。
-
使用场景:调用接口类工具、需要多参数输入的任务。
-
-
Conversational ReAct Description
-
在 ReAct 框架基础上,增强了 对话记忆能力。
-
特点:能根据上下文对话历史来做出工具选择和回应,更适合持续性对话场景。
-
使用场景:多轮对话,用户可能引用之前的内容或需要长期上下文跟踪。
-
LangChain 中集成了很多工具,可以通过下面的方式进行查询:
python
from langchain_community.agent_toolkits.load_tools import get_all_tool_names
results = get_all_tool_names()
print(results)
运行结果:
css
['sleep', 'wolfram-alpha', 'google-search', 'google-search-results-json', 'searx-search-results-json', 'bing-search', 'metaphor-search', 'ddg-search', 'google-books', 'google-lens', 'google-serper', 'google-scholar', 'google-finance', 'google-trends', 'google-jobs', 'google-serper-results-json', 'searchapi', 'searchapi-results-json', 'serpapi', 'dalle-image-generator', 'twilio', 'searx-search', 'merriam-webster', 'wikipedia', 'arxiv', 'golden-query', 'pubmed', 'human', 'awslambda', 'stackexchange', 'sceneXplain', 'graphql', 'openweathermap-api', 'dataforseo-api-search', 'dataforseo-api-search-json', 'eleven_labs_text2speech', 'google_cloud_texttospeech', 'read_file', 'reddit_search', 'news-api', 'tmdb-api', 'podcast-api', 'memorize', 'llm-math', 'open-meteo-api', 'requests', 'requests_get', 'requests_post', 'requests_patch', 'requests_put', 'requests_delete', 'terminal']
接下来,通过一个示例来学习Agent的基本使用。
- 问题1:计算一下300的25%是多少?
- 问题2:请帮我介绍一下故宫
示例代码:
需要提前安装 wikipedia
pip install wikipedia
python
from langchain_core.messages import HumanMessage
from langgraph.checkpoint.memory import MemorySaver
from langchain.agents import create_agent
from langchain_community.agent_toolkits.load_tools import load_tools
from langchain_openai import ChatOpenAI
import os
# 实例化模型
model = ChatOpenAI(
base_url=os.environ.get('base_url','https://dashscope.aliyuncs.com/compatible-mode/v1'),
model='qwen-plus',
openai_api_key=os.environ.get('api_key','sk-XXXXXXXX'),
max_tokens=1000,
temperature=0
)
# Create the agent
memory = MemorySaver()
tools = load_tools(['wikipedia', 'llm-math'], llm=model)
agent_executor = create_agent(model, tools, checkpointer=memory)
# Use the agent
config = {"configurable": {"thread_id": "abc123"}}
for chunk in agent_executor.stream(
{"messages": [HumanMessage(content="计算一下300的25%是多少?")]}, config
):
print(chunk)
print("----")
# for chunk in agent_executor.stream(
# {"messages": [HumanMessage(content="大熊猫是哪个国家的国宝?")]}, config
# ):
# print(chunk)
# print("----")
运行结果:

九、LangChain使用场景
- 个人助手
- 基于文档的问答系统
- 聊天机器人
- Tabular数据查询
- API交互
- 信息提取
- 文档总结



