LangChain大模型开发
现在市面上的模型多如牛毛,各种各样的模型不断出现,LangChain模型组件提供了与各种模型的集成,并为所有模型提供一个精简的统一接口。
一、LangChain调用模型
LangChain目前支持三种类型的模型:LLMS(大语言模型)、Chat Models(聊天模型)、Embeddings Models(嵌入模型)
- LLMS:是技术范畴的统称,指基于大参数量、海量文本训练的 Transformer 架构模型,核心能力是理解和生成自然语言,主要服务于文本生成场景
- 聊天模型:是应用范畴的细分,是专为对话场景优化的 LLMS,核心能力是模拟人类对话的轮次交互,主要服务于聊天场景
- 文本嵌入模型:文本嵌入模型接收文本作为输入,得到文本的向量.
LangChain支持的三类模型,它们的使用场景不同,输入和输出不同,开发者需要根据项目需要选择相应。
LLMS使用常用大模型的下载库:
• https://huggingface.co/models
• https://modelscope.cn/models
LangChain调用三种模型的API如下:

1、LangChain调用大语言模型LLM
同时LangChain支持对许多模型的调用,以通义干问为例:
from langchain_community.llms.tongyi import Tongyi
llm = Tongyi(model= 'qwen-max')
res = llm.invoke("帮我讲个笑话吧") #一次性返回完整结果
print(res)
res = llm.stream("帮我讲个笑话吧") #流式输出
for chunk in res:
print(chunk, end='', flush=True)
LangChain也支持Ollama本地模型的访问:
from langchain_ollama import OllamaLLM
model = OllamaLLM(model='gemma4:e4b') #一次性返回完整结果
res = model.invoke("你是谁?能做什么")
print(res)
res = model.stream("你是谁?简单回答我") #流式输出
for chunk in res:
print(chunk, end='', flush=True)
2、LangChain调用聊天模型ChatModel
聊天模型需要传入下面几种类型:
- SystemMessage:Prompt,可以用于指定模型具体所处的环境和背景,如角色扮演等。你可以在这里给出具体的指示,比如“作为一个代码专家",或者"返回json格式".
- HumanMessage:人类消息就是用户信息,由人给出的信息发送给LLMS的提示信息,比如“实现一个快速排序方法”.
- AIMessage:就是 AI 输出的消息,可以是针对问题的回答.(OpenAI库中的assistant角色)
LangChain调用通义千问聊天模型:
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
chatModel = ChatTongyi(model='qwen3-max')
humanMessage = [
SystemMessage(content='你是一名文笔细腻的现代诗人'),
HumanMessage(content= '给我写一首简单的现代诗'),
AIMessage(content='你,一会看我,一会看云。我觉得,你看我时很远,你看云时很近。'),
HumanMessage(content='给予上一首类似的情感,再来一首'),
]
for chunk in chatModel.stream(input=humanMessage):
print(chunk.content, end='', flush=True)
LangChain调用Ollama本地聊天模型:
from langchain_ollama import ChatOllama
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
chatModel = ChatOllama(model='gemma4:e4b')
humanMessage = [
SystemMessage(content='你是一名文笔细腻的现代诗人'),
HumanMessage(content= '给我写一首简单的现代诗'),
AIMessage(content='你,一会看我,一会看云。我觉得,你看我时很远,你看云时很近。'),
HumanMessage(content='给予上一首类似的情感,再来一首'),
]
for chunk in chatModel.stream(input=humanMessage):
print(chunk.content, end='', flush=True)
3、LangChain聊天消息的简写形式
SystemMessage、HumanMessage、AIMessage可以用system、human、ai来替换。
- SystemMessage、HumanMessage、AIMessage是静态的。代码中直接得到了Message类的类对象
- system、human、ai是动态的,代码运行时由LangChain内部机制转换为Message类对象。
动态运行的好处为:
- 它可以避免导包,写起来更简单
- 由于是动态的,代码运行时实时转换,所以简写形式支持内部填充{变量}占位,可在运行时填充具体值,可以在提示词模版中使用。
from langchain_core.messages import HumanMessage, SystemMessage, AIMessage
HumanMessage = [
SystemMessage(content='你是一名文笔细腻的现代诗人'),
HumanMessage(content= '给我写一首简单的现代诗'),
AIMessage(content='你,一会看我,一会看云。我觉得,你看我时很远,你看云时很近。'),
HumanMessage(content='给予上一首类似的情感,再来一首'),
]
messages = [
('system', '你是一名:{文笔细腻的现代诗人}'),
('human', '给我:{写一首简单的现代诗}'),
('ai', '{你,一会看我,一会看云。我觉得,你看我时很远,你看云时很近。}'),
('human', '给予上一首类似的情感,再来一首'),
]
print(chatModel.invoke(input=messages))
4、LangChain调用文本嵌入模型
Embeddings Models嵌入模型的作用:将字符串作为输入,返回一个浮点数的列表(向量)、
在NLP中,Embedding的作用就是将数据进行文本向量化。
阿里千问文本嵌入模型的使用:
#本文嵌入模型
from langchain_community.embeddings import DashScopeEmbeddings
embedder = DashScopeEmbeddings()
print(embedder.embed_query('你好')) #单次转换
print(embedder.embed_documents(['你好', '喝水了吗', '一起打游戏'])) #批量转换
Ollama文本嵌入模型的使用:
#Ollama本地
from langchain_ollama import OllamaEmbeddings
embedder = OllamaEmbeddings(model='qwen3-embedding')
print(embedder.embed_query('你好')) #单次转换
print(embedder.embed_documents(['你好', '喝水了吗', '一起打游戏'])) #批量转换
二、LangChain提示词模型
提示词优化在模型应用中非常重要,LangChain提供了PromptTempleta类,用于协助优化提示词。
0. 模版类的format和invoke方法
PromptTemplate、FewShotPromptTemplate、ChatPromptTemplate都拥有format和invoke这2类方法。通过占位符的方式设置提示词

format和invoke的区别:
| 区别 | format | invke |
|---|---|---|
| 功能 | 解析占位符,用纯字符串替换生成提示词。静态注入 | Runnable接口的标准方法,解析占位符生成提示词。动态注入 |
| 返回值 | 字符串 | PromptValue类对象 |
| 传参 | .format(k=v, k=v, ...) | .invoke({"k":v, "k":v, ...}) |
| 解析 | 解析{}占位符 | 解析{}占位符和MessagePlaceholder结构化占位符(ChatPromptTemplate使用) |
template = PromptTemplate.from_template("我是:{lastname}")
res = template.format(lastname="李逗逗")
res2 = template.invoke({"lastname": "李逗逗"})
1. 通用提示词模版 PromptTemplate
PromptTemplate表示提示词模版,可以构建一个自定义的基础提示词模版,支持变量的注入,最终生成所需的提示词。它相比手动拼接字符串的好处:
- 使用Template模板构建提示词,在大型工程中更容易做标准化模板
- Template模板类,支持LangChian框架的链式调用(Runnable接口)
- PromptTempLate
- FewShotPromptTemplate
- ChatPromptTemplat
标准写法:通过from_template设置提示词
from langchain_core.prompts import PromptTemplate
from langchain_community.llms.tongyi import Tongyi
prompt_template = PromptTemplate.from_template(
"我姓{lastname}, 是{gender}, 帮忙起一个名字,请简略回答"
)
prompt_text = prompt_template.format(lastname='桑', gender='女性')
model = Tongyi(model='qwen-max')
response = model.invoke(prompt_text)
print(response)
基于Chain链的写法:
prompt_template = PromptTemplate.from_template(
"我姓{lastname}, 是{gender}, 帮忙起一个名字,请简略回答"
)
model = Tongyi(model='qwen-max')
chain = prompt_template | model
response = chain.invoke(input={"lastname": "桑", "gender": "女性"})
print(response)
- zero-shot(零样本学习),也就是单纯prompt,可以基于PromptTemplate直接完成
- few-shot(少样本学习),需要提供实例的话,需要转换为FewShotPromptTemplate完成。
2. 少样本学习提示词模版 FewShotPromptTemplate
FewShotPromptTemplate通过5个参数设置提示词模板
- example_prompt:示例数据的模板
- examples:示例数据,list,嵌套字典
- prefix:前提示词,示例数据前内容
- suffix:后提示词,示例数据的后内容
- input_variables:list,动态输入的变量列表,用于前缀或后缀中需要注入的变量名
from langchain_core.prompts import FewShotPromptTemplate, PromptTemplate
from langchain_community.llms.tongyi import Tongyi
example_prompt = PromptTemplate.from_template("单词:{word}, 反义词:{antonym}")
example_data = [
{"word": "大", "antonym": "小"},
{"word": "上", "antonym": "下"},
]
few_shot_prompt = FewShotPromptTemplate(
prefix="给予给定词的反义词,有如下示例:",
example_prompt=example_prompt,
examples=example_data,
suffix="基于示例告诉我:{input_word} 的反义词",
input_variables=['input_word'],
)
#获得最终的提示词
prompt = few_shot_prompt.invoke({"input_word": "左"}).to_string()
# prompt_text = few_shot_prompt.format(input_word = "左")
model = Tongyi(model="qwen-max")
response = model.invoke(input = prompt)
print(response)
3. 聊天提示词模板 ChatPromptTemplate
- PromptTemplate: 通用提示词模板,支持动态注入信息。只能传入一条消息。
- FewShotPromptTemplate: 支持基于模板注入任意数量的示例信息。
- ChatPromptTemplate: 支持注入任意数量的历史会话信息。
ChatPromptTemplate通过from_messages方法,从列表中获取多轮次会话作为聊天的基础模板。
MessagePlaceholder作为占位,提供了占位符,必须基于invoke动态注入历史对话记录。历史对话信息并不是静态的,随着对话不断增加,所以历史会话信息需要支持动态注入。
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
chat_prompt_template = ChatPromptTemplate.from_messages(
[
("system", "..."),
("ai", "..."),
MessagesPlaceholder("history"),
("human", "..."),
]
)
history_data = [
("human", "..."),
("ai", "..."),
("human", "..."),
("ai", "...")
]
prompt = chat_prompt_template.invoke({"history": history_data})
model = Tongyi(model="qwen-max")
print(model.invoke(input=prompt))
三、Chain的基础使用
1. chain简介
Chain的作用:将组件串联,上一个组件的输出作为下一个组件的输入。 这是LangChain链,尤其是|管道链的核心工作原理,也是链式调用的核心:实现数据的自动化流程转与组件的协同工作。
Chain的语法:chain = prompt_template | model | ...
Chain的组件能串联的核心前提是:Runnable的子类对象才能入链(以及Callable、Maping接口的子类对象也能入链)。

chain变量是RunnableSequence(RunnableSerializable子类)类型, 而得到这个类型的原因就是Runnable基类内部对__or__魔术方法的改写。对于传入的数据始终返回RunnableSequence,这就是链的基础架构。
def __or__(
self,
other: Union[
Runnable[Any, Other],
Callable[[Iterator[Any]], Iterator[Other]],
Callable[[AsyncIterator[Any]], AsyncIterator[Other]],
Callable[[Any], Other],
Mapping[str, Union[Runnable[Any, Other], Callable[[Any], Other], Any]],
],
) -> RunnableSerializable[Input, Other]:
return RunnableSequence(self, coerce_to_runnable(other))
2. chain链式调用
- 通过|链接提示词模板和模型对象
- 返回值chain对象是RunnableSerializable类型。RunnableSerializable是Runnable的直接子类,也是绝大多数组件的父类。
- 通过chain通过invoke和stream触发整个链条的执行,进行阻塞执行或流式执行
1. 非链式调用
prompt = chat_prompt_template.invoke({"history": history_data})
model = Tongyi(model="qwen-max")
model.invoke(input=prompt)
2. 链式调用
model = ChatTongyi(model="qwen-max")
chain: RunnableSerializable = chat_prompt_template | model
res = chain.invoke({"history": history_data}) #阻塞执行
print(res.content)
res2 = chain.stream({"history": history_data})
for chunk in res2:
print(chunk.content, end='', flush=True) #流式执行
chain的执行是上一个组件的输出作为下一个组件的输入,所以chain = chat_prompt_template | model 的数据传递流程:

四、LangChain解析器
1. StrOutputParser字符串解析器
如果我们想第一次模型的输出结果,作为第二次询问模型:
这样的链构建没有问题,但会运行报错:
ValueError: Invalid input type <class 'Langchain_core.messages.ai.AIMessage'>. Must be a PromptValue,str,or List of BaseMessages.
错误原因是原因是prompt是PromptValue类型,而model的输出是AIMessage类型。不是模型的规定输入。模型规定的输入类型是
- PromptValue
- 字符串
- 序列Sequence[MessageLikeRepresentation]
StrOutputParser简介
StrOutputParser是LangChain的内置的字符串解析器
- 可以将AIMessage解析为简单的字符串,符合了模型invoke方法的输入要求
- 是Runnable接口的子类(可以加入链)
parser = StrOutputParser()
chain = prompt | model | parser | model
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi
parser = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
prompt = PromptTemplate.from_template(
"我姓:{lastname}, 是{gender},请给我起名,仅告知名字"
)
chain = prompt | model | parser | model | parser
res = chain.invoke({"lastname": "桑", "gender": "女性"})
print(res)
2、JsonOutputParser解析器
strOutputParser字符串解析器模型输出结果进行简单的字符串解析,便输入模型中,这样的做法并不标准,因为上一个模型的输出没有被处理就输入到下一个模型中。
正常模型的处理逻辑为:
invoke -> stream初始输入 -> 提示词模板 -> 模型 -> **数据处理 -> 提示词模板** -> 模型 ->解析器 ->结果

所以,对于第一个模型的输出,需要完成AIMessage转换为dict字典,注入到第二个提示词模板中,形成新的提示词PromptValue输出
- 模型输出的结果为AIMessage
- 提示词模板要求输入的类型为dict字典
- 提示词模板输出类型是:PromptValue
JsonOutputParser简介
JsonOutputParser的作用是将AIMessage转换为字典类型(AIMessage -> Dict(JSON)),AIMessage的输出必须是json格式
from langchain_core.output_parsers import StrOutputParser, JsonOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi
str_parser = StrOutputParser()
json_parser = JsonOutputParser()
first_prompt = PromptTemplate.from_template(
"我姓:{lastname}, 性别是: {gender},请帮我起名,并封装为JSON格式返回给我,"
"要求key是name,value就是起的名字。请严格遵守格式要求"
)
second_prompt = PromptTemplate.from_template(
"姓名{name}, 请帮我解析含义"
)
model = ChatTongyi(model="qwen3-max")
# chain = first_prompt | model | str_parser #第一个模型输出内容为{"name":"桑xx"}
chain = first_prompt | model | json_parser | second_prompt | model | str_parser
res = chain.invoke({"lastname": "桑", "gender": "女性"})
print(res)
3. 自定义函数加入链
除了JsonOutputParser这类固定功能的解析器之外。我们也可以自己编写Lambda匿名函数来完成自定义逻辑的数据转换,想怎么转换就怎么转换,更自由。
想要实现自定义链函数,可以基于RunnableLambda类实现。RunnableLambda类是LangChain内置的,将普通函数等转换为Runnable接口实例,方便自定义函数加入chain。
语法:RunnableLambda(函数对象或lambda匿名函数)
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnableLambda
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi
str_parser = StrOutputParser()
#AIMessage.content是str类型
#my_func返回的内容是{'name': 'xxx'}, 类型是 <class 'dict'>
my_func = RunnableLambda(lambda ai_message: {"name": ai_message.content})
first_prompt = PromptTemplate.from_template(
"我姓:{lastname}, 性别是: {gender},请帮我起名,仅告诉我名字"
)
second_prompt = PromptTemplate.from_template(
"姓名{name}, 请帮我简单解析含义"
)
model = ChatTongyi(model="qwen3-max")
chain = first_prompt | model | my_func | second_prompt | model |str_parser
res = chain.invoke({"lastname": "桑", "gender": "女性"})
print(res, type(res))
跳过RunnableLambda类,直接让函数加入链也是可以的。因为Runnable接口类在实现__or__
的时候,支持Callable接口的实例。
- 而函数就是Callable接口的实例。

chain = first_prompt | model | (lambda ai_message: {"name": ai_message.content}) | second_prompt | model |str_parser
如上代码示例,|符号(底层是重写了_or_)组链,是支持函数加入的。其本质是将函数自动转换为RunnableLambda
五、Memory会话记忆
如果想要封装消息,可以借用提示词模板自行封装消息,也可以借助LangChain内置的历史记录history功能。帮助模型在有历史记录的情况下回答。
- RunnableWithMessageHistory。在原有链的基础上创建带有历史记录功能的新Chain(新Runnable实例),这个chain可以利用LangChain的会话记忆类,自动存取会话历史记录
from langchain_core.runnables.history import RunnableWithMessageHistory
new_conversation_chain = RunnableWithMessageHistory(
some_chain, #附加历史消息的Runnable,通常是chain
None, #根据会话ID,返回历史会话记录的函数,
input_messages_key="input", #声明哪个是用户输入消息,占位符
history_messages_key="history" #声明哪个是历史消息,占位符
)
1. Memory临时会话记忆
- InMemoryChatMessageHistory方法可以实现内存中存储历史对话。它可以创建BaseChatMessageHistory的子类,BaseChatMessageHistory专门用于存放会话历史记录
#存储多个会话ID锁对应的历史会话记录
chat_history_store = {}
#根据会话ID,获取历史会话记录。
#传入为会话ID(字符串类型)
def get_history(session_id):
if session_id not in chat_history_store:
chat_history_store[session_id] = InMemoryChatMessageHistory()
return chat_history_store[session_id]
代码实现:
from langchain_core.chat_history import InMemoryChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import PromptTemplate
from langchain_community.chat_models.tongyi import ChatTongyi
def print_prompt(full_prompt):
print("=" * 20, full_prompt.to_string(), "=" * 20)
return full_prompt
# prompt = PromptTemplate.from_template(
# "你需要根据历史对话回应用户的问题,对话历史:{history}。当前用户输入:{input}, 请给出回应"
# )
prompt = ChatPromptTemplate.from_messages(
[
("system", "你需要根据历史对话回应用户的问题"),
MessagesPlaceholder("history"),
("human", "请给出回应:{input}"),
]
)
str_parse = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
base_chain = prompt | print_prompt | model | str_parse
#获取历史对话记录
chat_history_store = {}
def get_history(session_id):
if session_id not in chat_history_store:
chat_history_store[session_id] = InMemoryChatMessageHistory()
return chat_history_store[session_id]
#创建带有历史记录功能的新Chain
new_conversation_chain = RunnableWithMessageHistory(
base_chain,
get_history,
input_messages_key="input",
history_messages_key="history"
)
if __name__ == "__main__":
# 固定格式,添加LangChain的会话配置,表明当前程序所属的会话ID
session_config = {"configurable": {"session_id": "user_001"}}
print(new_conversation_chain.invoke({"input": "我有一只猫"}, session_config))
print(new_conversation_chain.invoke({"input": "我有两只狗"}, session_config))
print(new_conversation_chain.invoke({"input": "我共有几只宠物?"}, session_config))
第一次提问,会话历史是空的
第二次提问,会话历史是:
对话历史:[HumanMessage(content='我有一只猫', additional_kwargs={}, response_metadata={}), AIMessage(content='哇,真棒!你有一只猫呀~', additional_kwargs={}, response_metadata={})]
第三次提问,会话历史是:
对话历史:[HumanMessage(content='我有一只猫', additional_kwargs={}, response_metadata={}), AIMessage(content='哇,真棒!你有一只猫呀~', additional_kwargs={}, response_metadata={}),
HumanMessage(content='我有两只狗', additional_kwargs={}, response_metadata={}), AIMessage(content='哎呀,原来你不仅有一只猫,还有两只狗呀!', additional_kwargs={}, response_metadata={})]
2、FileChatMessageHistory长期会话记忆
基于FileChatMessageHistory,可以实现一个基于Json格式和本地文件的会话记录保存
- 文件存储的会话记录,以session_id为文件名,不同session_id有不同的文件存储消息
FileChatMessageHistory继承了BaseChatMessageHistory类,需要实现3个方法:
- add_message: 添加消息到文件,同步模式
- messages:从文件中获取消息,同步模式
- clear:清除消息,同步模式
其中消息的存储包含2个自带的方法:
- AIMessage、HumanMessage和SystemMessage都是BaseMessage的子类
- message_to_dict: 单个消息消息对象转为字典(BaseMessage -> dict字典)
[
{
"type": "human",
"data": {
"content": "我又一只猫",
"additional_kwargs": {
},
"response_metadata": {
},
"type": "human",
"name": null,
"id": null,
"example": false
}
}
]
- message_from_dict: 将字典消息解析为BaseMessage消息。[字典、字典...] -> [消息、消息...]
官方FileChatMessageHistory示例
import json, os
from langchain_core.messages import messages_from_dict, message_to_dict
class FileChatMessageHistory(BaseChatMessageHistory):
storage_path: str
session_id: str
@property
def messages(self) -> list[BaseMessage]:
try:
with open(
os.path.join(self.storage_path, self.session_id),
"r",
encoding="utf-8",
) as f:
messages_data = json.load(f)
return messages_from_dict(messages_data)
except FileNotFoundError:
return []
def add_messages(self, messages: Sequence[BaseMessage]) -> None:
all_messages = list(self.messages) # Existing messages
all_messages.extend(messages) # Add new messages
serialized = [message_to_dict(message) for message in all_messages]
file_path = os.path.join(self.storage_path, self.session_id)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
json.dump(serialized, f)
def clear(self) -> None:
file_path = os.path.join(self.storage_path, self.session_id)
os.makedirs(os.path.dirname(file_path), exist_ok=True)
with open(file_path, "w", encoding="utf-8") as f:
json.dump([], f)
代码实现:
import json, os
from typing import Sequence
from langchain_community.chat_models import ChatTongyi
from langchain_core.messages import message_to_dict, messages_from_dict, BaseMessage
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain_core.runnables import RunnableWithMessageHistory
# AIMessage、HumanMessage和SystemMessage都是BaseMessage的子类
# message_to_dict: 单个消息对象(BaseMessage类示例 -> dict字典)
# message_from_dict: [字典、字典...] -> [消息、消息...]
class FileChatMessageHistory(BaseChatMessageHistory):
def __init__(self, session_id, storage_path):
self.session_id = session_id # 会话ID
self.storage_path = storage_path # 会话记录存储的路径
# 完整的文件路径
self.file_path = os.path.join(self.storage_path, self.session_id)
# 确保文件夹是存在的
os.makedirs(os.path.dirname(self.file_path), exist_ok=True)
def add_messages(self, messages: Sequence[BaseMessage]) -> None:
# Sequence序列 类似list、tuple
all_messages = list(self.messages)
all_messages.extend(messages)
# 将数据同步存储到本地文件中
# 类对象写入文件中,会变成一堆二进制
# 为例方便查看,message_to_dict可以将BaseMessage消息转为字典,在借助json将字典以字符串写入文件n
# new_messages = [] # for message in messages: # m = message_to_dict(message) # new_messages.append(m) new_messages = [message_to_dict(message) for message in all_messages]
# 将数据写入文件
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump(new_messages, f)
@property # @property装饰器将messages方法变成成员属性用
def messages(self) -> Sequence[BaseMessage]:
# 文件存储的是:list[消息字典]
try:
with open(self.file_path, "r", encoding="utf-8") as f:
messages_data = json.load(f)
return messages_from_dict(messages_data)
except FileNotFoundError:
return []
#文件存储的是:list[消息字典]
def clear(self) -> None:
with open(self.file_path, "w", encoding="utf-8") as f:
json.dump([], f)
def print_prompt(full_prompt):
print("=" * 20, full_prompt.to_string(), "=" * 20)
return full_prompt
prompt = ChatPromptTemplate.from_messages(
[
("system", "你需要根据历史对话回应用户的问题"),
MessagesPlaceholder("history"),
("human", "请给出回应:{input}"),
]
)
str_parse = StrOutputParser()
model = ChatTongyi(model="qwen3-max")
base_chain = prompt | print_prompt | model | str_parse
#获取历史对话记录
def get_history(session_id):
return FileChatMessageHistory(session_id, storage_path="./chat_history")
#创建带有历史记录功能的新Chain
new_conversation_chain = RunnableWithMessageHistory(
base_chain, # 附加历史消息的Runnable,通常是chain
get_history, # 获取历史会话的函数
input_messages_key="input", # 声明模版prompt中,哪个是用户输入消息,占位符
history_messages_key="history" # 声明模版prompt中,哪个是历史消息,占位符
)
if __name__ == "__main__":
# 固定格式,添加LangChain的会话配置,表明当前程序所属的会话ID
session_config = {"configurable": {"session_id": "user_001"}}
# print(new_conversation_chain.invoke({"input": "我有一只猫"}, session_config))
# print(new_conversation_chain.invoke({"input": "我有两只狗"}, session_config))
print(new_conversation_chain.invoke({"input": "我共有几只宠物?"}, session_config))
六、LangChain文档加载器
文档加载器提供了一套标准接口,用于将不同来源(如CSV、PDF 或 JSON等)的数据读取为
LangChain的Class Document文档格式。这确保了无论数据来源如何,都能对其进行一致性处理。
文档加载器可以内置或自行实现,均实现了BaseLoader接口。返回Document对象的list
Class Document,是LangChain内文档的统一载体,所有文档加载器最终返回此类的实例。Document类主要记录了:
- page_content: 文档内容
- metadata: 文档元数据(字典)
一个基础的Document类实例,基于如下代码创建:
from langchain_core.documents import Document
document = Document(
page_content="Hello, world!", metadata={"source":"https://example.com"}
)
LangChain内置了许多文档加载器,不同的文档加载器有不同的参数,但因为都继承与BaseLoder类,返回都实现了统一的接口
- load(): 一次性加载全部文档,返回list格式,包含多个docement
- lazy_load(): 批量单个加载Document,对大型数据集很有用,避免内存溢出

1. CSVLoader
CSVLoader语法:
loader = CSVLoader(
file_path="./data/stu.csv",
csv_args={
"delimiter": ",", #指定分隔符
"quotechar": "'", #指定分隔符文本的包裹的符号,如果文本被这个符号包裹表示一列
"fieldnames": ['name', 'age', 'gender', '爱好'], # 如果数据原本有表头,就不要下面的代码,如果没有可以使用
“source_column”: "source" #指定本条数据的来源是哪一列
},
encoding="utf-8",
)
示例:
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader(
file_path="./data/stu.csv",
csv_args={
"delimiter": ",", #指定分隔符
"quotechar": "'" #指定分隔符文本的包裹的符号
# ,"fieldnames": ['name', 'age', 'gender', '爱好'] ## 如果数据原本有表头,就不要下面的代码,如果没有可以使用
},
encoding="utf-8",
)
# # 1. 批量加载
# documents = loader.load()
# for document in documents:
# print(document, type(document))
# 2.懒加载,使用迭代器加载
for document in loader.lazy_load():
print(document, type(document))
2. JSONLoader
JSONLoader用于将Json数据加载为Document对象。LangChain底层对Json的解析基于jq工具实现,封装为Document,抽取的内容使用jq_schema语法。
安装jq库:`pip install jq
JSONLoader语法:
from langchain_community.document_loaders import JSONLoader
loader = JSONLoader(
file_path="", # 文件路径
jq_schema=".", # 提取的内容,jq_schema语法
text_content=False, # 提取的内容是否是字符串,默认为True
json_content=True, # 是否是JsonLines文件,即每一行都是JSON的文件
)
示例1:
{
"name": "周杰轮",
"age": 11,
"hobby": [
"唱",
"跳",
"RAP"
],
"other": {
"addr": "深圳",
"tel": "12332112321"
}
}
jq_schema语法:
- .表示整个JSON对象
- []表示数据
- .name表示抽取name的值,也就是周杰伦
- .hobby[1]表示抽取唱
- .other.addr表示抽取“深圳”
from langchain_community.document_loaders import JSONLoader
loader = JSONLoader(
file_path="./data/stu.json", # 文件路径
jq_schema=".name", # 提取的内容,jq_schema语法
)
document = loader.load()
print(document)
示例2:
[{"name":"周杰轮","age":11,"gender":"男"},
{"name":"蔡依临","age":12,"gender":"女"},
{"name":"王力鸿","age":11,"gender":"男"}]
jq_schema语法:
- .[]. 抽取了这3个字典
- .[].name 表示抽取全部的name的值
loader2 = JSONLoader(
file_path="./data/stus.json", # 文件路径
jq_schema=".[].name", # 提取的内容,jq_schema语法
)
document2 = loader2.load()
print(document2)
示例3:
{"name": "周杰轮", "age": 11, "gender": "男"}
{"name": "蔡依临", "age": 12, "gender": "女"}
{"name": "王力鸿", "age": 11, "gender": "男"}
JsonLines文件,每一行都是一个Json
loader3 = JSONLoader(
file_path="./data/stu_json_lines.json",
jq_schema=".name",
text_content=False,
json_lines=True
)
document3 = loader3.load()
print(document3)
3. TextLoader和文档分割器
TextLoader的作用是:读取文本文件(.txt), 将全部内容放入一个Document对象中,即返回的Document数量为1
from langchain_community.document_loaders import TextLoader
loader = TextLoader(
"./data/Python基础语法.txt", #文件路径
encoding="utf-8",
)
document = loader.load()
print(document, len(document))
#结果为1
如果文档很大,则文本全都加载到一个Document中不合适
RecursivecharacterTextplitter递归字符文本分割器,主要用于按自然段落分割大文档。
它返回了Document对象的list。[Document, Document,...]
是LangChain官方推荐的默认字符分割器。它在保持上下文完整性和控制片段大小之间实现了良好平衡,开箱即用效果佳。
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
loader = TextLoader(
"./data/Python基础语法.txt", # 文件路径
encoding="utf-8",
)
document = loader.load()
splitter = RecursiveCharacterTextSplitter(
chunk_size=500, # 分段的最大字符数量
chunk_overlap=50, # 分段的之间允许重叠的字符数
separators=["\n\n", "\n", "。", "!", "?", ".", "!", "?", " ", ""],
length_function=len, # 字符统计依据(函数)
)
split_docs = splitter.split_documents(document)
print(len(split_docs))
for split_doc in split_docs:
print("=" * 20)
print(split_doc)
4. PyPDFLoader加载器
PyPDFLoader可以加载PDF中文字内容。返回了Document对象的list
安装pypdf库:pip install pypdf
使用语法:
from langchain_community.document_loaders import PyPDFLoader
loader = PyPDFLoader(
file_path="./data/pdf1.pdf", #文件路径必填
mode="page", #读取模式:Page按页面读取,single按单个文档读取
password="password", #文件密码
)
七、向量存储 Vector stores
LangChain支持向量存储,存储嵌入数据,并执行相似性搜索

典型的RAG流程包含了向量存储和应用。主要涉及:
- 向量嵌入算法,将文本转为向量
- 创建向量数据存储,基于向量存储,实现:
- add_documents:存入向量
- delete:删除向量
- similarity_search: 向量相似度检索
1. InMemoryVectorStore向量存储
安装chroma包:pip install langchain-chroma chromadb
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
#初始化
vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings())
# 添加文档到向量存储,并指定 id# 注意:这里的需要是 Document 对象的list
vector_store.add_documents(documents=[doc1, doc2], ids=["id1", "id2"])
#添加字符到向量库,传入类型是list[str], 自动转换为向量存储到内存中,id如果不传向量库也会自动赋值
vector_store.add_texts(["str", "str2",... ])
# 删除文档 (通过指定的 id 删除)
vector_store.delete(ids=["id1"])
# 相似性搜索,返回的是Document对象的list
similar_docs = vector_store.similarity_search("your query here", 4,filter = {"key":"value"})
示例:
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader(
file_path="./data/info.csv",
encoding="utf-8",
source_column="source", # 指定本条数据的来源是哪里
)
documents = loader.load()
# 向量存储的增加、删除和检索
vector_store = InMemoryVectorStore(
embedding=DashScopeEmbeddings()
)
vector_store.add_documents(
documents=documents,
ids=["id" + str(i) for i in range(1, len(documents) + 1)]
)
vector_store.delete(["id1", "id2"])
result = vector_store.similarity_search(
"python是不是简单易学",
3 #检索几个结果
)
print(result)
2. 外部(Chroma)向量持久化存储:
外部存储数据库,长期记忆
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_chroma import Chroma
# 初始化 Chroma 向量存储,类似于文件会话记忆
vector_store = Chroma(
collection_name="example_collection", #数据库的表
embedding_function=DashScopeEmbeddings(), #Embedding 模型
persist_directory="./chroma_langchain_db", #存储路径,外部数据存储的地方
)
示例:
from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_community.document_loaders import CSVLoader
loader = CSVLoader(
file_path="./data/info.csv",
encoding="utf-8",
source_column="source", # 指定本条数据的来源列是那个
)
documents = loader.load()
# 向量存储的增加、删除和检索
vector_store = Chroma(
collection_name="test", #当前向量存储的名字,类似于数据库的表名
embedding_function=DashScopeEmbeddings(),
persist_directory="./chroma_db" #指定数据存放的文件夹
)
vector_store.add_documents(
documents=documents,
ids=["id" + str(i) for i in range(1, len(documents) + 1)]
)
vector_store.delete(["id1", "id2"])
result = vector_store.similarity_search(
"python是不是简单易学",
3 #检索几个结果
)
print(result)
本地存储,一次存储,随时使用:
from langchain_chroma import Chroma
from langchain_community.embeddings import DashScopeEmbeddings
# 向量存储的增加、删除和检索
vector_store = Chroma(
collection_name="test", #当前向量存储的名字,类似于数据库的表名
embedding_function=DashScopeEmbeddings(),
persist_directory="./chroma_db" #指定数据存放的文件夹
)
result = vector_store.similarity_search(
"努力努力,改变自己",
3, # 检索几个结果
filter={"source": "黑马程序员"} #检索过滤
)
print(result)
八、检索向量并构建提示词
简单实现在向量库中检索到的参考资料+用户提问,组合成提示词,对大模型进行提问
"""
提示词:用户的提问 + 向量库中检索到的参考资料
"""
from langchain_community.chat_models.tongyi import ChatTongyi
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
def print_prompt(prompt):
print(prompt.to_string())
print("=" * 20)
return prompt
# 建立向量库
vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4"))
vector_store.add_texts(
["减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哦"]
)
input_text = "怎么减肥?"
# 检索向量库
result = vector_store.similarity_search(input_text, 2)
# 根据用户输入进行向量库检索,将检索的结果构建提示词
reference_text = "["
for doc in result:
reference_text += doc.page_content
reference_text += "]"
#提问模型
model = ChatTongyi(model="qwen3-max")
prompt = ChatPromptTemplate(
[
("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
("human", "用户提问:{input}"),
]
)
chain = prompt | print_prompt | model | StrOutputParser()
res = chain.invoke({"input": input_text, "context": reference_text})
print(res)
九、RunnablePassthrough的使用
RunnablePassthrough和as_retriever方法用于将向量检索加入chain中。
向量存储对象不是Runnable接口的子类,LangChain中向量存储的对象,有一个as_retriever方法,可以返回一个Runnable接口的子类实例对象 Retriever
retriever = vector_store.as_retriever(search_kwargs={"k":2})
Retriever用于根据用户的提问,在向量库中检索结果
- retriever:
- 输入:用户的提问,类型是str
- 输出:向量库的检索结果,类型是list[Document]
- prompt:
- 输入:用户的提问+向量库的检索结果, 类型dict
- 输出:完整的提示词,类型是PromptValue
- 输入:用户的提问+向量库的检索结果, 类型dict
利用retriever进行用户检索,再结合用户提问形成提示词的复合执行chain为:
- {}字典可以入链,因为chain接受的类型为Runnable、函数和Mapping类型(字典的父类)
- RunnablePassthrough相当于占位符,把invoke整个链条的输入截流一份到这个为止
- retriever相当于第一个链中的执行组件,所以invoke原本的输入就传给retriever
- 而第一个{}的执行结果,整体作为输入传递个prompt
- retriever执行结果是list[Document],format_func将此类型转换为str
chain = (
{"input":RunnablePassthrough(), "context":retriever | format_func} | prompt | model | StrOutputParser()
)
chain.invoke(...)
示例代码
"""
提示词:用户的提问 + 向量库中检索到的参考资料
"""
from langchain_community.chat_models import ChatTongyi
from langchain_core.documents import Document
from langchain_core.runnables import RunnablePassthrough
from langchain_core.vectorstores import InMemoryVectorStore
from langchain_community.embeddings import DashScopeEmbeddings
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
def print_prompt(prompt):
print(prompt.to_string())
print("=" * 20)
return prompt
def format_func(docs: list[Document]):
if not docs:
return "无相关参考资料"
formatted_str = "["
for doc in docs:
formatted_str += doc.page_content
formatted_str += "]"
return formatted_str
# 建立向量库
vector_store = InMemoryVectorStore(embedding=DashScopeEmbeddings(model="text-embedding-v4"))
vector_store.add_texts(
["减肥就是要少吃多练", "在减脂期间吃东西很重要,清淡少油控制卡路里摄入并运动起来", "跑步是很好的运动哦"]
)
input_text = "怎么减肥?"
# 检索向量库chain
retriever = vector_store.as_retriever(search_kwargs={"k": 2})
# 提问模型
model = ChatTongyi(model="qwen3-max")
prompt = ChatPromptTemplate(
[
("system", "以我提供的已知参考资料为主,简洁和专业的回答用户问题。参考资料:{context}。"),
("human", "用户提问:{input}"),
]
)
chain = (
{"input": RunnablePassthrough(), "context": retriever | format_func} | prompt | print_prompt | model | StrOutputParser()
)
res = chain.invoke(input_text)
print(res)

浙公网安备 33010602011771号