LangChain 学习记录
AIGC RAG AIGC 20

introduction | LangChain中文网

langchain-ai/langchain: 🦜🔗 Build context-aware reasoning applications

  1. LangChain:Model I/O, Chain, Agent,组成应用程序认知架构的链、代理和检索策略

  2. LangGraph:将多个Chain,agent构建成图结构,能够处理更加复杂的问题

  3. LangSimth:评估,监控,从开发到生成的过渡

  4. LangGraph Cloud: 将的 LangGraph 应用程序转变为生产就绪的 API 和助手,供前端或其他调用

  5. LangServe:部署应用

LangChain可以从六个方面进行学习,即Model I/O, Retrieval, Chains, Memory, Agents, Callbacks.

一、Model I/O

在 LangChain 中,Model I/O(模型输入输出) 是连接用户与大语言模型(LLM)的核心模块,负责 处理输入(将用户需求转换为模型可理解的格式)、调用模型(与 LLM 交互)、处理输出(将模型返回结果转换为用户友好的形式) 三大核心任务。它是 LangChain 中所有涉及 LLM 调用场景(如 Chain、Agent、RAG)的基础,确保模型与用户、其他组件(如 Memory、Tools)之间的顺畅协作。

一、Model I/O 的核心价值与流程

Model I/O 的本质是 “模型交互的中间层”,解决了以下关键问题:

  • 输入标准化:将用户的自然语言、结构化数据(如对话历史)转换为符合 LLM 要求的 Prompt 格式;

  • 模型适配:统一不同 LLM 的调用接口(如 OpenAI、本地模型、Anthropic),屏蔽底层 API 差异;

  • 输出解析:将 LLM 生成的原始文本转换为结构化数据(如 JSON、列表),便于后续处理(如工具调用、结果展示)。

其核心流程可概括为:

plaintext

用户输入 → [输入处理(Prompt 构建)] → [模型调用(LLM 推理)] → [输出处理(结果解析)] → 用户/下游组件

二、Model I/O 的核心组件

LangChain 的 Model I/O 模块包含三个核心组件,分别对应上述流程的三个阶段:

组件

作用

核心实现

Prompts

处理输入:定义 Prompt 模板,将用户输入、上下文(如对话历史)、变量动态填充为完整的模型输入。

PromptTemplate(基础模板)、ChatPromptTemplate(对话式模板)、FewShotPromptTemplate(少样本模板)等。

Language Models

调用模型:封装不同类型的 LLM / 聊天模型,提供统一的调用接口(同步 / 异步)。

LLM(文本生成模型,如 GPT-3)、ChatModel(对话模型,如 GPT-4、DeepSeek)、HuggingFacePipeline(本地模型)等。

Output Parsers

处理输出:将模型生成的原始文本解析为结构化数据(如字典、列表、自定义对象)。

StructuredOutputParser(结构化解析)、JsonOutputParser(JSON 解析)、PydanticOutputParser(基于 Pydantic 模型解析)等。

三、核心组件详解与实战

1. Prompts:构建模型输入

Prompts 的核心是 “模板化”—— 通过预定义模板,动态插入变量(如用户输入、上下文),生成符合 LLM 预期的输入文本。

(1)基础模板:PromptTemplate

适用于非对话式 LLM(如文本生成模型),支持简单变量替换。

from langchain.prompts import PromptTemplate

# 定义模板(用 {变量名} 标记占位符)
template = "请用一句话总结以下内容:{text}"
# 创建 PromptTemplate 实例
prompt = PromptTemplate(
    input_variables=["text"],  # 声明变量名
    template=template
)

# 填充变量,生成最终 Prompt
user_text = "LangChain 是一个用于构建 LLM 应用的框架,它提供了 Model I/O、Memory、Agent 等模块。"
final_prompt = prompt.format(text=user_text)
print("最终输入到 LLM 的文本:")
print(final_prompt)

(2)对话模板:ChatPromptTemplate

适用于对话式模型(如 GPT-4、DeepSeek),支持区分 “系统提示”“用户输入”“AI 回复” 等角色,更符合聊天模型的交互逻辑。

from langchain.prompts import ChatPromptTemplate
from langchain.schema import HumanMessage, SystemMessage, AIMessage

# 定义对话模板(按角色区分消息)
chat_template = ChatPromptTemplate.from_messages([
    ("system", "你是一个专业的技术助手,回答需简洁准确。"),  # 系统提示(定义 AI 角色)
    ("human", "什么是 {concept}?"),  # 用户输入(带变量 {concept})
    ("ai", "我之前解释过 {related_concept},它是 {concept} 的相关技术。"),  # 历史 AI 回复(可选)
])

# 填充变量,生成对话消息列表
messages = chat_template.format_messages(
    concept="Model I/O",
    related_concept="Prompt 工程"
)

# 打印生成的消息(每个消息包含角色和内容)
for msg in messages:
    print(f"{msg.type}: {msg.content}")

2. Language Models:调用 LLM

LangChain 封装了多种 LLM 类型,提供统一的调用接口(predict/generate 方法),无需关心底层模型的 API 差异。

(1)调用聊天模型(ChatModel

适用于对话式模型(如 GPT-4、DeepSeek),输入为消息列表(SystemMessage/HumanMessage 等),输出为 AIMessage

from langchain_huggingface import HuggingFacePipeline
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
from langchain.schema import HumanMessage

# 初始化本地聊天模型(以 DeepSeek-7B 为例)
def init_chat_model():
    model_name = "deepseek-ai/deepseek-llm-7b-chat"
    tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(
        model_name, trust_remote_code=True, device_map="auto", load_in_4bit=True
    )
    # 构建文本生成管道
    pipe = pipeline(
        "text-generation",
        model=model,
        tokenizer=tokenizer,
        max_new_tokens=200,
        temperature=0.3
    )
    return HuggingFacePipeline(pipeline=pipe)

# 初始化模型
chat_model = init_chat_model()

# 调用模型(输入为消息列表)
messages = [
    HumanMessage(content="用3句话解释 LangChain 的 Model I/O 模块")
]
response = chat_model.invoke(messages)  # 同步调用
print("模型输出:", response.content)

(2)调用文本生成模型(LLM

适用于非对话式模型(如 GPT-3、Llama 2),输入为纯文本,输出为生成的文本。

from langchain_community.llms import Ollama  # 以 Ollama 部署的 Llama 2 为例

# 初始化 LLM(通过 Ollama 调用本地 Llama 2)
llm = Ollama(model="llama2", temperature=0.3)

# 调用模型(输入为纯文本)
prompt = "总结 LangChain 中 Model I/O 的作用"
response = llm.invoke(prompt)  # 同步调用
print("模型输出:", response)

3. Output Parsers:解析模型输出

LLM 生成的原始文本通常是无结构的字符串,Output Parsers 可将其转换为结构化数据(如 JSON、列表、Pydantic 模型),便于后续处理(如提取关键信息、调用工具)。

(1)JsonOutputParser:解析为 JSON

from langchain.output_parsers import JsonOutputParser
from langchain.prompts import PromptTemplate
from langchain_huggingface import HuggingFacePipeline

# 初始化模型(复用之前的 chat_model)
chat_model = init_chat_model()

# 定义输出格式(JSON 结构)
parser = JsonOutputParser()
# 获取格式说明(用于提示 LLM 按指定格式输出)
format_instructions = parser.get_format_instructions()

# 定义 Prompt 模板(包含格式说明)
prompt = PromptTemplate(
    template="请解析以下内容为 JSON,包含 'name' 和 'category' 字段。\n{format_instructions}\n内容:{text}",
    input_variables=["text"],
    partial_variables={"format_instructions": format_instructions}  # 注入格式说明
)

# 生成 Prompt 并调用模型
text = "苹果是一种常见的水果,味道甜美。"
final_prompt = prompt.format(text=text)
response = chat_model.invoke([HumanMessage(content=final_prompt)])

# 解析输出为 JSON
parsed_output = parser.parse(response.content)
print("解析后的 JSON:", parsed_output)
print("提取的类别:", parsed_output["category"])  # 访问 JSON 字段

(2)PydanticOutputParser:解析为 Pydantic 模型

更灵活的结构化解析,支持字段验证(如类型、约束),适合复杂场景。

from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List

# 定义 Pydantic 模型(结构化输出格式)
class Product(BaseModel):
    name: str = Field(description="产品名称")
    price: float = Field(description="产品价格(数字)")
    tags: List[str] = Field(description="产品标签列表")

# 初始化解析器
parser = PydanticOutputParser(pydantic_object=Product)
format_instructions = parser.get_format_instructions()

# 定义 Prompt 模板
prompt = PromptTemplate(
    template="请解析以下产品信息为指定格式。\n{format_instructions}\n信息:{info}",
    input_variables=["info"],
    partial_variables={"format_instructions": format_instructions}
)

# 生成 Prompt 并调用模型
product_info = "产品名称:智能手表,价格:1299元,标签:电子设备、可穿戴、健康监测"
final_prompt = prompt.format(info=product_info)
response = chat_model.invoke([HumanMessage(content=final_prompt)])

# 解析为 Product 对象
product = parser.parse(response.content)
print("解析后的产品对象:", product)
print("产品价格:", product.price)  # 直接访问对象字段(自动转换为 float 类型)

四、Model I/O 与其他模块的协作(见下文)

Model I/O 是 LangChain 的 “核心枢纽”,与其他模块(Memory、Retrieval、Agent)紧密协作:

  • 与 Memory 协作Prompts 结合 Memory 中的对话历史,生成包含上下文的 Prompt(如多轮对话中,将历史消息插入 ChatPromptTemplate);

  • 与 Retrieval 协作Prompts 将检索到的文档(Document)作为 “上下文” 插入模板,让 LLM 基于外部知识生成回答(RAG 流程);

  • 与 Agent 协作Output Parsers 将 LLM 生成的工具调用指令(如 {"action": "tool_name", "parameters": {...}})解析为结构化数据,指导 Agent 执行工具调用。

五、最佳实践

  1. Prompt 工程优化

    • 明确任务要求(如 “用 3 句话总结”“输出 JSON 格式”);

    • 加入示例(少样本学习)提升模型输出质量;

    • 控制 Prompt 长度,避免超出 LLM 上下文窗口。

  2. 模型选择适配

    • 对话场景用 ChatModel(如 GPT-4、DeepSeek),文本生成场景用 LLM(如 Llama 2);

    • 本地部署优先选择轻量级模型(如 7B 参数模型),平衡性能与效率。

  3. 输出解析容错

    • 模型可能生成不符合格式的输出,需用 try-except 捕获解析错误,并在 Prompt 中强化格式要求;

    • 复杂结构优先用 PydanticOutputParser,利用其字段验证功能过滤无效数据。

Function Calling | 禧语许

二、Retrieval

在 LangChain 中,Retrieval(检索) 是实现 RAG(Retrieval-Augmented Generation,检索增强生成) 的核心模块,其核心作用是 从外部知识库(如文档、数据库、API)中精准提取与用户查询相关的信息,并将这些信息作为上下文传递给 LLM,让 LLM 基于 “外部知识” 生成更准确、更具时效性的回答(而非仅依赖 LLM 训练时的内置知识)。

一、Retrieval 的核心价值与应用场景

Retrieval 解决了 LLM 的两大核心痛点:

  1. 知识时效性:LLM 训练数据有截止日期(如 GPT-4 截止到 2023 年),无法回答最新信息(如 2024 年新政策、新品发布);

  2. 知识局限性:LLM 无法掌握领域专属知识(如企业内部文档、学术论文、行业数据)。

通过 Retrieval,LangChain 可将 LLM 与任意外部知识库结合,典型应用场景包括:

  • 企业内部文档问答(如员工手册、产品手册查询);

  • 学术论文 / 技术文档解析(如根据论文内容回答研究问题);

  • 实时信息问答(如结合搜索引擎 API 回答最新新闻、天气);

  • 多源数据整合(如从 CSV 表格、SQL 数据库中检索数据并生成分析报告)。

二、Retrieval 的核心组件与工作流程

LangChain 的 Retrieval 模块围绕 “从知识库中高效检索相关信息” 设计,核心组件包括 Retriever(检索器)Vector Store(向量存储)Document Loader(文档加载器),完整工作流程如下:

1. 核心组件

组件

作用

关键实现

Document Loader

将外部数据(如 PDF、TXT、CSV、URL)加载为 LangChain 标准的 Document 对象(包含 page_content 内容和 metadata 元数据)。

PyPDFLoader(PDF)、TextLoader(TXT)、WebBaseLoader(网页)、SQLLoader(SQL)等。

Text Splitter

将长文档分割为短 “文本块(Chunk)”—— 避免单个文档过长导致嵌入(Embedding)效果差,同时适配 LLM 上下文窗口长度。

RecursiveCharacterTextSplitter(按字符递归分割,优先按段落 / 句子拆分,最常用)、CharacterTextSplitter(按固定字符数拆分)等。

Embedding

将文本块(Chunk)转换为 向量(Vector)—— 通过语义相似度量化文本内容(如 “猫” 和 “猫咪” 的向量距离很近)。

HuggingFaceEmbeddings(本地模型,如 BGE、Sentence-BERT)、OpenAIEmbeddings(API 模型)等。

Vector Store

存储文本块的向量及对应原文,提供 “按向量相似度查询” 的能力(即 “给定查询向量,返回最相似的 N 个文本块”)。

Chroma(轻量级本地向量库,无需服务)、Pinecone(云向量库,支持大规模数据)、FAISS(Facebook 开源向量库,适合单机)等。

Retriever

检索器 —— 封装 Vector Store 的查询逻辑,对外提供统一的检索接口(如 get_relevant_documents(query)),是 Retrieval 流程的 “入口”。

VectorStoreRetriever(基于向量库的检索器,最常用)、BM25Retriever(基于关键词的检索器)、EnsembleRetriever(融合多种检索策略)等。

2. 完整工作流程(RAG 流程)

Retrieval 是 RAG 的 “检索阶段”,与 “生成阶段”(LLM 生成回答)共同构成完整 RAG 流程,分为 离线准备在线问答 两步:

步骤 1:离线准备(知识库构建)

  1. 加载文档:用 Document Loader 将外部数据(如 PDF、TXT)加载为 Document 对象;

  2. 文本分块:用 Text Splitter 将长 Document 拆分为短文本块(如每块 500 字符,重叠 50 字符);

  3. 生成向量:用 Embedding 模型将每个文本块转换为向量;

  4. 存储向量:将向量和对应文本块存入 Vector Store,形成可检索的知识库。

步骤 2:在线问答(检索 + 生成)

  1. 用户查询:用户输入问题(如 “产品 A 的保修期是多久?”);

  2. 检索相关文本:用 Retriever 将用户查询转换为向量,在 Vector Store 中查询最相似的 N 个文本块(如 Top 3);

  3. 构建 Prompt:将检索到的文本块作为 “上下文”,与用户查询拼接成 Prompt(如 “根据上下文回答:{context}\n 问题:{query}”);

  4. LLM 生成回答:将 Prompt 传入 LLM,生成基于外部知识库的回答。

具体代码可以参考:RAG应用实践 | 禧语许

https://github.com/XuWink/SimpleRAG.git

三、Chains

设计理念:通过很多小的chain构成Chains,形成一个可行的项目

3.1 基于LCEC的

LangChain 表达式语言 (LCEL) |🦜️🔗 朗链

LangChain Expression Language(LCEL)通过 RunnableSequence(顺序执行)RunnableParallel(并行执行) 实现这种“调用链”。
在 LangChain 中,每个步骤都是一个 Runnable 对象,包括:

  • 模型(LLM)

  • Prompt 模板

  • 外部函数或工具(Tool)

  • 输出解析器(Parser)

这些 Runnables 可以通过两种方式组合:

  • RunnableSequence:顺序执行(pipeline)

  • RunnableParallel:并行执行(map)

  1. 使用 RunnableSequence

from langchain_core.runnables import RunnableSequence
chain = RunnableSequence([runnable1, runnable2])
final_output = chain.invoke(some_input)

# 等价于下面的过程
output1 = runnable1.invoke(some_input)
final_output = runnable2.invoke(output1)
  1. 使用RunnableParallel

from langchain_core.runnables import RunnableParallel
chain = RunnableParallel({
    "key1": runnable1,
    "key2": runnable2,
})

# 例如
from langchain_core.runnables import RunnableParallel

cities = ["杭州", "北京", "上海"]
parallel_chain = RunnableParallel(
    **{city: RunnableLambda(lambda _: get_weather(city)) for city in cities}
)
weather_results = parallel_chain.invoke(None)
print(weather_results)  # {'杭州': '晴,气温 26°C', '北京': '多云,气温 20°C', '上海': '小雨,气温 22°C'}

  1. 使用 | pipe运算符

chain = runnable1 | runnable2
# 等价于:
chain = RunnableSequence([runnable1, runnable2])
# 或:
chain = runnable1.pipe(runnable2)

  1. LCEL 应用自动类型强制,使链的组成更容易

mapping = {
    "key1": runnable1,
    "key2": runnable2,
}

chain = mapping | runnable3

会自动转换成:

chain = RunnableSequence([RunnableParallel(mapping), runnable3])

  1. 在 LCEL 表达式中,函数会自动转换为 .RunnableLambda

def some_func(x):
    return x

chain = some_func | runnable1

# 自动转换为:
chain = RunnableSequence([RunnableLambda(some_func), runnable1])

  1. routerchain

(10 封私信 / 80 条消息) Langchain Chain - RouterChain 根据输入相关性进行路由的路由链 - 知乎

根据用户的数据,自动选择下游多个链中对应的一个。

1. 创建子链和默认链

2. 创建路由链,同时路由链的路由模板需要根据子链进行特殊设计

3. 创建MultiPromptChain,将路由链、默认链和子链组装成一个整体

from langchain.chains.router.llm_router import RouterOutputParser
from langchain.chains.router.llm_router import LLMRouterChain
from langchain.llms import OpenAI
import os

router_prompt = PromptTemplate(
    template=router_template,
    input_variables=["input"],
    output_parser=RouterOutputParser(),
)

#创建一个OpenAI作为大语言模型的基底
llm = OpenAI()
os.environ["OPENAI_API_KEY"]="你的key"
# 创建路由链
router_chain = LLMRouterChain.from_llm(llm, router_prompt)

# 生成目标链
candadite_chains = {}
# 遍历路由目录,生成各子链并放入候选链字典
for p_info in prompt_infos:
    name = p_info["name"]
    prompt_template = p_info["prompt_template"]
    prompt = PromptTemplate(template=prompt_template, input_variables=["input"])
    chain = LLMChain(llm=llm, prompt=prompt)
    candadite_chains[name] = chain

# 生成默认链
default_chain = ConversationChain(llm=llm, output_key="text")

# MultiPromptChain所必须的三大要素:router_chain, destination_chain 以及 default_chain
chain = MultiPromptChain(
    router_chain=router_chain,
    destination_chains=candadite_chains,
    default_chain=default_chain,
    verbose=True,
)

print(chain.run("帮我写一首关于春天的诗"))

四、Memory

玩转 LangChain Memory 模块:四种记忆类型详解及应用场景全覆盖-CSDN博客

在 LangChain 中,Memory(记忆) 是实现对话连贯性、上下文感知的核心组件,其核心作用是 存储和管理对话历史 / 上下文信息,让 LLM(大语言模型)能基于历史交互生成更连贯、更贴合场景的回复。

LangChain 提供了丰富的 Memory 实现,覆盖不同场景(如单轮 / 多轮对话、结构化 / 非结构化记忆、持久化存储等),且支持与 Chain(链)、Agent(智能体)无缝集成。

根据存储内容、生命周期和使用场景,Memory 可分为以下几类:

分类维度

具体类型

适用场景

存储内容

对话历史记忆(Conversation Memory)

多轮对话(如客服、聊天机器人)

实体记忆(Entity Memory)

存储用户 / 对象的关键属性(如 “用户年龄 25 岁”)

知识记忆(Knowledge Memory)

结合外部知识库的上下文(如 RAG + 对话)

生命周期

临时记忆(Transient Memory)

单次对话会话(会话结束后清空)

持久化记忆(Persistent Memory)

跨会话存储(如用户长期偏好、历史订单)

数据结构

非结构化记忆(如纯文本对话历史)

简单对话场景

结构化记忆(如 Key-Value 存储、JSON、数据库)

复杂场景(需精准提取 / 查询上下文)

LangChain 中最常用的是 对话类 Memory,以下介绍高频使用的实现及代码示例(基于 LangChain v0.2+ 版本)

1. 基础款:ConversationBufferMemory(全量存储对话历史)

  • 原理:以纯文本形式全量存储所有对话历史(用户输入 + AI 回复),生成回复时将完整历史传入 LLM。

  • 优点:简单直观,适合短对话;

  • 缺点:对话过长时会导致 LLM 输入超限(Context Window 不足),且效率低

from langchain_community.chat_models import ChatOpenAI  # 也可替换为本地LLM(如DeepSeek)
from langchain.chains import ConversationChain
from langchain.memory import ConversationBufferMemory

# 1. 初始化LLM(以OpenAI为例,本地LLM需替换为HuggingFacePipeline等)
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.3)

# 2. 初始化Memory:存储全量对话历史,可指定输出格式(如"human"和"ai"的键名)
memory = ConversationBufferMemory(
    memory_key="history",  # 对话历史在Prompt中的变量名
    return_messages=True   # 若为True,返回Message对象;False返回纯文本字符串
)

# 3. 初始化对话链(将LLM与Memory绑定)
conversation_chain = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=True  # 开启 verbose,可查看完整Prompt(含对话历史)
)

# 4. 多轮对话交互
response1 = conversation_chain.predict(input="介绍一下LangChain的Memory组件")
print("AI回复1:", response1)

# 第二轮对话:依赖上一轮的上下文
response2 = conversation_chain.predict(input="它有哪些常见的实现方式?")
print("AI回复2:", response2)

# 查看当前存储的对话历史
print("\n当前对话历史:")
print(memory.load_memory_variables({}))  # load_memory_variables() 读取记忆

2. 优化款:ConversationBufferWindowMemory(滑动窗口记忆)

  • 原理:只存储最近 N 轮对话历史(类似 “滑动窗口”),避免历史过长导致 LLM 输入超限。

  • 核心参数k —— 保留的对话轮数(如 k=3 表示只保留最近 3 轮)。

  • 适用场景:中等长度对话,平衡上下文完整性和输入长度。

from langchain.memory import ConversationBufferWindowMemory

# 初始化滑动窗口Memory:只保留最近2轮对话
memory = ConversationBufferWindowMemory(
    memory_key="history",
    return_messages=True,
    k=2  # 关键参数:保留2轮对话(1轮=用户输入+AI回复)
)

# 绑定到对话链
conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)

# 多轮对话
conversation_chain.predict(input="第一轮:什么是LangChain?")
conversation_chain.predict(input="第二轮:它的核心模块有哪些?")
conversation_chain.predict(input="第三轮:Memory模块属于核心模块吗?")

# 查看记忆:只保留第2、3轮对话(k=2)
print("\n滑动窗口记忆内容:")
print(memory.load_memory_variables({}))

3. 高效款:ConversationSummaryMemory(对话摘要记忆)

  • 原理:不存储全量历史,而是对早期对话生成 “摘要”,仅保留摘要 + 最近几轮对话,大幅减少输入长度。

  • 优点:适合长对话,兼顾上下文连贯性和效率;

  • 注意:需要额外调用 LLM 生成摘要(会增加少量 token 消耗)。

from langchain.memory import ConversationSummaryMemory

# 初始化摘要Memory:需传入LLM(用于生成对话摘要)
memory = ConversationSummaryMemory(
    memory_key="history",
    return_messages=True,
    llm=llm  # 用于生成摘要的LLM(建议与对话LLM一致)
)

# 长对话测试
conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)

# 多轮对话(早期对话会被总结)
conversation_chain.predict(input="LangChain是一个用于构建LLM应用的框架,它提供了很多组件。")
conversation_chain.predict(input="其中Memory组件负责存储对话历史,让LLM能记住上下文。")
conversation_chain.predict(input="除了Memory,还有Chain、Agent、DocumentLoader等组件。")
conversation_chain.predict(input="刚才提到的Memory组件,有哪些优化存储的实现?")

# 查看记忆:早期对话已被总结,最近对话保留原文
print("\n摘要记忆内容:")
print(memory.load_memory_variables({}))

4. 结构化款:ConversationEntityMemory(实体记忆)

  • 原理:不仅存储对话历史,还会提取对话中的 “实体”(如人名、产品名、属性),以 Key-Value 形式结构化存储(如 “产品 A:价格 100 元,产地北京”)。

  • 适用场景:需要精准跟踪实体属性的场景(如电商客服、CRM 对话)。

from langchain.memory import ConversationEntityMemory
from langchain.memory.prompt import ENTITY_MEMORY_CONVERSATION_TEMPLATE

# 初始化实体Memory
memory = ConversationEntityMemory(
    llm=llm,
    memory_key="history",
    return_messages=True
)

# 绑定对话链(使用实体记忆专用的Prompt模板)
conversation_chain = ConversationChain(
    llm=llm,
    memory=memory,
    prompt=ENTITY_MEMORY_CONVERSATION_TEMPLATE,  # 关键:使用实体模板
    verbose=True
)

# 对话中包含实体属性
conversation_chain.predict(input="我想买产品A,它的价格是多少?")
conversation_chain.predict(input="产品A的保修期有多久?")
conversation_chain.predict(input="那产品B呢?价格比A贵吗?")

# 查看提取的实体(结构化存储)
print("\n提取的实体信息:")
print(memory.entity_store.store)  # entity_store.store 存储实体的Key-Value

5. RAG + Memory(带记忆的检索问答)

在 RAG(检索增强生成)中加入 Memory,让系统能记住 “用户之前问过的问题”“已检索过的文档”,避免重复检索或上下文断裂。

from langchain.chains import RetrievalQAWithSourcesChain
from langchain_community.vectorstores import Chroma
from langchain_huggingface import HuggingFaceEmbeddings

# 1. 初始化向量库(RAG基础)
embeddings = HuggingFaceEmbeddings(model_name="BAAI/bge-large-zh-v1.5")
vector_db = Chroma(persist_directory="chroma_vector_db", embedding_function=embeddings)
retriever = vector_db.as_retriever(top_k=3)

# 2. 初始化Memory(存储对话历史)
memory = ConversationBufferWindowMemory(
    memory_key="history",
    return_messages=True,
    k=2  # 保留最近2轮对话
)

# 3. 初始化带记忆的RAG链
rag_chain = RetrievalQAWithSourcesChain.from_chain_type(
    llm=llm,
    chain_type="stuff",  # 简单拼接上下文
    retriever=retriever,
    memory=memory,
    verbose=True,
    return_source_documents=True  # 返回检索到的文档来源
)

# 4. 多轮RAG问答(基于记忆关联上下文)
response1 = rag_chain({"question": "文档中提到的产品A价格是多少?"})
print("回答1:", response1["answer"])

# 第二轮:依赖上一轮的“产品A”上下文
response2 = rag_chain({"question": "它的折扣活动持续到什么时候?"})
print("回答2:", response2["answer"])

6. Agent + Memory(带记忆的智能体)

Agent 在执行任务时(如 “查询天气→规划出行路线”),需要 Memory 记录已完成的动作、获取的信息,避免重复操作。

from langchain.agents import create_structured_chat_agent
from langchain.agents import AgentExecutor
from langchain.tools import Tool
from langchain import hub

# 1. 定义工具(示例:模拟查询天气工具)
def get_weather(city: str) -> str:
    """查询指定城市的天气"""
    return f"{city}今天天气晴朗,气温25℃。"

tools = [
    Tool(
        name="WeatherQuery",
        func=get_weather,
        description="用于查询城市天气,输入参数为城市名(如“北京”)"
    )
]

# 2. 初始化Memory(记录Agent的动作和对话)
memory = ConversationBufferMemory(
    memory_key="chat_history",
    return_messages=True,
    output_key="output"  # 匹配Agent的输出键
)

# 3. 加载Agent模板(结构化对话模板)
prompt = hub.pull("hwchase17/structured-chat-agent")

# 4. 初始化带记忆的Agent
agent = create_structured_chat_agent(llm=llm, tools=tools, prompt=prompt)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True,
    handle_parsing_errors=True
)

# 5. Agent执行任务(记忆会记录“查询过北京天气”)
agent_executor.invoke({"input": "查询北京的天气"})
agent_executor.invoke({"input": "那明天适合去公园吗?"})  # 依赖上一轮的“北京天气”上下文

7. Memory 持久化(跨会话存储)

默认的 Memory 是临时存储(会话结束后内存清空),若需跨会话保留(如 “用户下次登录仍能获取历史对话”),需将 Memory 持久化到外部存储(如数据库、文件)。

LangChain 支持多种持久化方式,以下是 基于 SQLite 数据库 的示例:

from langchain.memory import ConversationBufferMemory
from langchain_community.utilities import SQLDatabase
from langchain.memory.chat_message_histories import SQLChatMessageHistory

# 1. 连接SQLite数据库(本地文件数据库)
db = SQLDatabase.from_uri("sqlite:///chat_history.db")

# 2. 初始化数据库存储的对话历史(需指定session_id,区分不同用户/会话)
chat_history = SQLChatMessageHistory(
    session_id="user_123",  # 每个用户/会话的唯一ID
    connection_string="sqlite:///chat_history.db"
)

# 3. 初始化Memory,绑定数据库存储
memory = ConversationBufferMemory(
    memory_key="history",
    return_messages=True,
    chat_memory=chat_history  # 关键:使用数据库存储的对话历史
)

# 4. 对话链(跨会话后,memory会从数据库读取历史)
conversation_chain = ConversationChain(llm=llm, memory=memory, verbose=True)

# 对话(内容会自动存入SQLite)
conversation_chain.predict(input="我是用户123,今天咨询产品A的价格。")

# 下次启动程序时,只需传入相同的session_id,即可读取历史对话

五、Agents

在 LangChain 中,Agent(智能体) 是一个能根据用户需求 自主规划、调用工具、处理复杂任务 的核心组件。与普通的 Chain(链)不同,Agent 具备 决策能力—— 它能分析问题、判断是否需要工具、选择合适工具、执行并反思结果,最终完成单轮链无法处理的复杂任务(例如 “查询北京明天天气,再推荐适合的景点”“根据最近股价分析某公司投资价值”)。

一、Agent 的核心原理与组成

1. 核心能力

  • 目标拆解:将复杂任务分解为可执行的子步骤(例如 “规划旅行”→“查天气→订机票→推荐酒店”);

  • 工具调用:根据子步骤选择合适工具(如搜索引擎、数据库、计算器等);

  • 结果反思:评估工具返回的结果是否满足需求,若不满足则调整策略(如重新调用工具、补充信息);

  • 自然语言交互:用自然语言接收输入、输出最终结果,无需用户掌握技术细节。

2. 核心组成部分

组件

作用

示例

LLM(大脑)

提供推理、决策能力,是 Agent 的 “智能核心”

GPT-3.5/4、DeepSeek、Llama 等

Tools(工具集)

Agent 可调用的外部功能 / API,扩展其能力范围

搜索引擎(SerpAPI)、数据库查询、文件读写、计算器、自定义函数等

Agent 类型

定义决策逻辑和工具调用方式(不同类型适用于不同场景)

结构化工具调用(Structured Chat Agent)、反应式 Agent(ReAct)等

Memory(记忆)

存储对话历史和中间结果,支持多轮交互和上下文关联

对话历史记忆(ConversationBufferMemory)、实体记忆(EntityMemory)

Prompt(提示)

指导 LLM 进行决策的模板,包含工具调用格式、思考步骤等规范

定义 “如何选择工具”“如何解析结果”“如何生成最终回答”

二、常见 Agent 类型及适用场景

LangChain 提供了多种 Agent 实现,核心差异在于 决策逻辑工具调用方式,以下是最常用的 3 类:

Agent 类型

核心逻辑

适用场景

优点

缺点

ReAct Agent

遵循 “思考(Reason)→ 行动(Act)→ 观察(Observe)” 循环,显式输出思考过程

复杂推理任务、需要透明化决策过程

推理过程可解释,适合复杂逻辑

长任务可能效率低,思考步骤消耗 Token 多

Structured Chat Agent

支持结构化工具调用(如 JSON 格式),适合多参数工具

工具参数复杂(如 “查询日期 + 地点 + 类型”)

工具调用更精准,减少参数错误

对 Prompt 格式要求严格

AutoGPT Agent

自主设定子目标、迭代执行,模拟人类规划流程

长期任务、多步骤自动化(如 “写一篇研究报告”)

高度自动化,无需人工干预

可能偏离目标,需要严格的终止条件

from langchain.agents import create_structured_chat_agent, AgentExecutor
from langchain.tools import Tool
from langchain import hub
from langchain.memory import ConversationBufferMemory
from langchain_huggingface import HuggingFacePipeline
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline

# ========================== 1. 初始化 LLM(本地模型示例:DeepSeek-7B) ==========================
def init_llm():
    model_name = "deepseek-ai/deepseek-llm-7b-chat"
    tokenizer = AutoTokenizer.from_pretrained(model_name, trust_remote_code=True)
    model = AutoModelForCausalLM.from_pretrained(
        model_name,
        trust_remote_code=True,
        device_map="auto",
        load_in_4bit=True  # 4bit量化,节省显存
    )
    # 构建文本生成管道
    pipe = pipeline(
        "text-generation",
        model=model,
        tokenizer=tokenizer,
        max_new_tokens=512,
        temperature=0.3,
        return_full_text=False
    )
    return HuggingFacePipeline(pipeline=pipe)

llm = init_llm()

# ========================== 2. 定义工具(Tools) ==========================
# 工具1:查询天气(模拟)
def get_weather(city: str, date: str) -> str:
    """
    查询指定城市和日期的天气情况。
    参数:
        city: 城市名称(如“北京”“上海”)
        date: 日期(如“2023-10-01”或“明天”)
    返回:
        天气描述字符串
    """
    # 实际场景中可替换为真实天气API调用
    return f"{city} {date}的天气为:晴朗,气温25℃,微风。"

# 工具2:计算器(支持简单数学运算)
def calculate(expression: str) -> str:
    """
    计算数学表达式的结果(仅支持加减乘除和括号)。
    参数:
        expression: 数学表达式(如“1+2*3”“(5+8)/2”)
    返回:
        计算结果字符串
    """
    try:
        # 注意:实际使用需限制表达式安全性(避免代码注入)
        result = eval(expression)
        return f"计算结果:{expression} = {result}"
    except Exception as e:
        return f"计算错误:{str(e)}"

# 工具列表:将函数包装为 Tool 对象(name 是工具名,func 是函数,description 是工具描述)
tools = [
    Tool(
        name="WeatherQuery",
        func=get_weather,
        description="用于查询指定城市和日期的天气,需要传入city(城市名)和date(日期)两个参数。"
    ),
    Tool(
        name="Calculator",
        func=calculate,
        description="用于计算数学表达式,需要传入expression(如'1+2*3')参数。"
    )
]

# ========================== 3. 初始化 Memory(对话记忆) ==========================
memory = ConversationBufferMemory(
    memory_key="chat_history",  # 记忆在Prompt中的变量名
    return_messages=True,       # 返回Message对象(而非纯文本)
    output_key="output"         # 与Agent输出键匹配,确保记忆正确存储
)

# ========================== 4. 加载 Agent 提示模板 ==========================
# 从 LangChain Hub 加载结构化对话Agent的默认模板(也可自定义)
prompt = hub.pull("hwchase17/structured-chat-agent")

# ========================== 5. 创建 Agent 和执行器 ==========================
# 创建结构化聊天Agent
agent = create_structured_chat_agent(
    llm=llm,
    tools=tools,
    prompt=prompt
)

# 创建Agent执行器(包装Agent,负责运行和处理输出)
agent_executor = AgentExecutor(
    agent=agent,
    tools=tools,
    memory=memory,
    verbose=True,  # 开启详细日志,查看Agent思考和工具调用过程
    handle_parsing_errors=True  # 自动处理工具调用格式错误
)

# ========================== 6. 测试 Agent ==========================
print("开始与Agent对话(输入'退出'结束):")
while True:
    user_input = input("\n你:").strip()
    if user_input.lower() in ["退出", "q"]:
        print("Agent:再见!")
        break
    # 调用Agent处理输入
    response = agent_executor.invoke({"input": user_input})
    print(f"Agent:{response['output']}")

六、Callbacks

在 LangChain 中,Callbacks(回调) 是一套用于 实时监控、干预、记录 LangChain 流程(如 Chain 运行、Agent 工具调用、LLM 推理) 的核心机制。它允许开发者在关键节点(如 “LLM 开始生成”“工具调用前”“流程结束”)插入自定义逻辑,实现日志记录、性能监控、结果修改、用户交互等功能,是 LangChain 灵活性和可扩展性的重要体现。

一、Callbacks 的核心价值与应用场景

Callbacks 的本质是 “事件驱动的钩子(Hook)”——LangChain 在执行流程中会触发一系列预设 “事件”,开发者通过注册 “回调函数” 来响应这些事件。其核心应用场景包括:

应用场景

具体需求

日志与监控

记录 LLM 输入输出、工具调用参数、流程耗时,用于调试或审计。

性能追踪

统计 LLM 推理时间、工具调用延迟,分析流程瓶颈。

结果干预

在 LLM 生成结果后修改内容(如敏感信息过滤),或在工具调用前校验参数。

用户交互

实时向用户展示流程进度(如 “正在调用天气工具...”“LLM 生成中...”)。

持久化存储

将对话历史、工具结果自动存入数据库(如 SQLite、Redis)。

异常处理

在流程报错时触发自定义逻辑(如重试、降级、告警)。

二、LangChain 的核心事件与 Callback 类型

LangChain 定义了一套标准化的 “事件体系”,覆盖从 LLM 到 Agent 的全流程。不同组件(LLM、Chain、Agent、Tool)对应不同的事件,常见核心事件如下:

1. 通用事件(全组件适用)

事件名称

触发时机

on_chain_start

任何 Chain(包括 Agent、LLMChain 等)开始执行时。

on_chain_end

任何 Chain 执行成功结束时。

on_chain_error

任何 Chain 执行报错时。

2. LLM 专属事件

事件名称

触发时机

on_llm_start

LLM 开始生成文本前(即发送请求给 LLM 时)。

on_llm_end

LLM 生成文本成功结束时(即收到 LLM 响应时)。

on_llm_error

LLM 生成失败时(如网络错误、API 限流)。

on_llm_new_token

流式生成时,LLM 每返回一个新 Token 触发(仅支持流式 LLM)。

3. 工具调用专属事件

事件名称

触发时机

on_tool_start

Agent 开始调用工具前。

on_tool_end

工具调用成功结束时。

on_tool_error

工具调用失败时(如参数错误、函数报错)。

4. Agent 专属事件

事件名称

触发时机

on_agent_action

Agent 决定执行某个动作(如调用工具、思考下一步)时。

on_agent_finish

Agent 完成所有任务,返回最终结果时。

5. Callback 类型分类

根据实现方式,LangChain 的 Callback 主要分为两类:

  • 基础 Callback:继承 BaseCallbackHandler 类,重写对应事件方法(如 on_llm_start),自定义逻辑。

  • 集成 Callback:LangChain 内置的、与第三方工具集成的 Callback(如 FileCallbackHandler 写入日志文件、WandbCallbackHandler 对接 Weights & Biases 监控)。

基础 Callback - 日志记录(监控 Chain 全流程)

需求:记录 Chain 执行的每个关键节点(开始 / 结束 / 错误)、LLM 输入输出、工具调用参数,输出到控制台。

from langchain.callbacks.base import BaseCallbackHandler
from langchain.chains import ConversationChain
from langchain_community.chat_models import ChatOpenAI
from langchain.memory import ConversationBufferMemory
from datetime import datetime

# 1. 自定义 Callback 类:继承 BaseCallbackHandler,重写事件方法
class CustomLoggingCallback(BaseCallbackHandler):
    def __init__(self):
        self.start_time = None  # 用于记录流程耗时

    # -------------------------- 通用 Chain 事件 重写 --------------------------
    def on_chain_start(self, serialized, inputs, **kwargs):
        """Chain 开始执行时触发"""
        self.start_time = datetime.now()
        chain_name = serialized.get("name", "UnknownChain")  # 获取 Chain 名称
        print(f"\n[{datetime.now()}] 🚀 {chain_name} 开始执行")
        print(f"[输入参数]:{inputs}")

    def on_chain_end(self, outputs, **kwargs):
        """Chain 执行结束时触发"""
        duration = (datetime.now() - self.start_time).total_seconds()  # 计算耗时
        print(f"[{datetime.now()}] ✅ Chain 执行结束(耗时:{duration:.2f}s)")
        print(f"[输出结果]:{outputs}")

    def on_chain_error(self, error, **kwargs):
        """Chain 执行报错时触发"""
        print(f"[{datetime.now()}] ❌ Chain 执行错误:{str(error)[:100]}")

    # -------------------------- LLM 专属事件 --------------------------
    def on_llm_start(self, serialized, prompts, **kwargs):
        """LLM 开始生成时触发"""
        llm_name = serialized.get("name", "UnknownLLM")
        print(f"[{datetime.now()}] 🧠 {llm_name} 开始生成(Prompt 前100字符):{prompts[0][:100]}...")

    def on_llm_end(self, response, **kwargs):
        """LLM 生成结束时触发"""
        llm_output = response.generations[0][0].text  # 获取 LLM 输出
        print(f"[{datetime.now()}] 📝 LLM 生成结束(输出前100字符):{llm_output[:100]}...")

    # -------------------------- 工具调用事件 --------------------------
    def on_tool_start(self, serialized, input_str, **kwargs):
        """工具开始调用时触发"""
        tool_name = serialized.get("name", "UnknownTool")
        print(f"[{datetime.now()}] 🔧 {tool_name} 开始调用(参数):{input_str}")

    def on_tool_end(self, output, **kwargs):
        """工具调用结束时触发"""
        print(f"[{datetime.now()}] 📤 工具调用结束(输出):{output}")

# 2. 初始化组件:LLM + Memory + Chain
llm = ChatOpenAI(model_name="gpt-3.5-turbo", temperature=0.3)
memory = ConversationBufferMemory(memory_key="history")

# 3. 注册 Callback:创建 Chain 时通过 `callbacks` 参数传入
conversation_chain = ConversationChain(
    llm=llm,
    memory=memory,
    verbose=False,  # 关闭 LangChain 默认日志,使用自定义 Callback
    callbacks=[CustomLoggingCallback()]  # 注册自定义 Callback
)

# 4. 测试 Chain:触发 Callback 事件
conversation_chain.predict(input="介绍 LangChain 的 Callbacks 组件")

输出效果:

[2024-05-20 15:30:00] 🚀 ConversationChain 开始执行
[输入参数]:{'input': '介绍 LangChain 的 Callbacks 组件', 'history': ''}
[2024-05-20 15:30:00] 🧠 ChatOpenAI 开始生成(Prompt 前100字符):The following is a friendly conversation between a human and an AI. The AI is talkative and provides lots of specific det...
[2024-05-20 15:30:02] 📝 LLM 生成结束(输出前100字符):LangChain 的 Callbacks(回调)是一套用于监控、干预和扩展 LangChain 流程的机制。它允许开发者在关键节点(如 LLM 生成、工具调用)插入自定义逻辑...
[2024-05-20 15:30:02] ✅ Chain 执行结束(耗时:2.15s)
[输出结果]:{'response': 'LangChain 的 Callbacks(回调)是一套用于监控、干预和扩展 LangChain 流程的机制...'}

1

LangChain 学习记录
https://www.bytecanvas.top/archives/u6P8bDbB
作者
禧语许
发布于
更新于
许可