LangChain 深入:链条、代理与记忆的高级用法
链条的进阶:从线性到动态控制
链条(Chain)是 LangChain 中最基础的执行单元。它把多个组件串联成一条流水线,让数据在模型、工具和解析器之间流动。初学者通常从 LLMChain 入门,但真正的威力藏在那些能按条件分叉、并行运行或多步推理的链条中。
自定义链条:封装你自己的业务逻辑
当内置链条无法满足需求时,你可以通过继承 Chain 基类来实现完全自定义的执行流程。自定义链条需要实现两个关键方法:
input_keys:声明链条需要哪些输入键。_call:定义实际的执行逻辑。
下面的例子创建了一条“情感分析增强回答”链条:先将用户问题送入情感分类器,若情感为消极则追加安抚性前缀,再调用大模型生成答案。
from langchain.chains.base import Chain
from typing import Dict, List, Optional
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
class SentimentAwareChain(Chain):
llm: OpenAI
sentiment_prompt: PromptTemplate
answer_prompt: PromptTemplate
@property
def input_keys(self) -> List[str]:
return ["question"]
@property
def output_keys(self) -> List[str]:
return ["response"]
def _call(self, inputs: Dict[str, str]) -> Dict[str, str]:
question = inputs["question"]
# 情感分析阶段
sentiment = self.llm(self.sentiment_prompt.format(question=question))
# 根据情感选择回答模板
if "negative" in sentiment.lower():
prefix = "我理解你可能感到困扰。"
else:
prefix = ""
full_prompt = prefix + self.answer_prompt.format(question=question)
response = self.llm(full_prompt)
return {"response": response}
路由链条:让模型自动选择执行路径
RouterChain 允许模型根据输入动态选择下游链条。这在构建“意图分发系统”时特别有用——用户一句话可能对应查询订单、咨询售后或闲聊,你希望系统自动路由到对应处理逻辑。
LangChain 提供了多种路由策略,包括基于 LLM 判断的 LLMRouterChain 和基于嵌入相似度的 EmbeddingRouterChain。下面展示 LLM 路由器的典型配置:
from langchain.chains.router import LLMRouterChain, RouterOutputParser
from langchain.chains.router.multi_prompt_prompt import MULTI_PROMPT_ROUTER_TEMPLATE
# 定义不同意图对应的提示词与链条
order_template = "你是订单助手。用户问题:{input}"
chat_template = "你是闲聊伙伴。用户说:{input}"
destinations = {
"order": {
"name": "订单查询",
"description": "处理订单、物流相关问题",
"prompt_template": order_template,
},
"chat": {
"name": "日常聊天",
"description": "处理问候、天气等闲聊",
"prompt_template": chat_template,
},
}
# 构建路由器链条
router_prompt = MULTI_PROMPT_ROUTER_TEMPLATE.format(destinations=...)
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
当用户输入“我的快递到哪了?”,路由器会将其解析为 order 意图,并将请求传递给对应的订单处理链条。
序列化与并行:组合多个链条
SequentialChain 让你将多个链条按顺序串联,前一个链条的输出会自动成为下一个的输入。当任务天然是流水线式时(例如:生成大纲 → 扩写段落 → 润色校对),这种模式非常高效。
对于相互独立的子任务,可以使用 SimpleChain 手动并行调用,再将结果汇总。使用 asyncio.gather 即可轻松实现并发,大幅降低响应延迟。
import asyncio
async def parallel_chains(question):
summary_chain = load_summary_chain()
translation_chain = load_translation_chain()
# 并行运行
summary_task = summary_chain.arun(question)
translation_task = translation_chain.arun(question)
summary, translation = await asyncio.gather(summary_task, translation_task)
return {"summary": summary, "translation": translation}
高级代理:赋予模型行动能力
代理(Agent)是 LangChain 中真正让 LLM“行动”起来的组件。它不只会生成文本,还会规划步骤、选择工具、执行操作并观察结果,直到完成目标。深入理解代理的类型与自定义工具,可以让你构建出自主决策的应用。
代理的决策核心:ReAct 与 OpenAI Functions
最早的代理基于 ReAct(Reasoning + Acting)范式,模型交替生成“思考-行动-观察”链路。LangChain 的 ZERO_SHOT_REACT_DESCRIPTION 代理类型便是此模式的经典实现。它仅靠工具的描述就能零样本决定调用哪个工具。
OpenAI 函数调用模型(如 gpt-3.5-turbo-1106)则提供了更结构化的代理模式。通过 OPENAI_FUNCTIONS 代理类型,工具被转换为函数定义注入请求,模型直接返回 JSON 函数调用,彻底消除了解析错误。
from langchain.agents import initialize_agent, AgentType
# 使用 OpenAI Functions 代理
agent = initialize_agent(
tools=[search_tool, calculator_tool],
llm=chat_model,
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True,
)
当问题为“2023年特斯拉的净利润除以4是多少?”,代理会先调用搜索工具得到净利润数值,再调用计算器工具进行除法,最终返回准确答案。
自定义工具:让代理接入任何 API
工具是代理的手和脚。LangChain 的 @tool 装饰器可以将任意 Python 函数快速封装为工具,只需要清晰的函数名和 docstring。
from langchain.agents import tool
@tool
def get_weather(city: str) -> str:
"""查询指定城市的实时天气。输入应为城市中文名称,如'北京'"""
# 调用实际的天气 API
weather_data = weather_api.call(city)
return f"{city}当前气温{weather_data['temp']}℃,{weather_data['desc']}"
# 注册工具后,代理就能自动根据用户意图调用它
对于更复杂的输入,可以使用 Pydantic 模型定义参数模式,确保代理传递的数据完全符合 API 要求。
代理的陷阱与对策:中断、回退与安全
在实际应用中,代理容易陷入无限循环、工具调用失败或产生有害操作。LangChain 提供了多层防护:
max_iterations:限制最大推理步骤,防止无限循环。handle_parsing_errors=True:当输出格式错误时自动重试。early_stopping_method:在generate方法中设置停止策略,避免模型空转。- 为敏感工具添加人工审批回调,在真正执行前请求用户确认。
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
max_iterations=5,
handle_parsing_errors=True,
early_stopping_method="force",
callbacks=[human_approval_callback],
)
记忆的深层设计:让对话持续而有上下文
记忆(Memory)是构建有状态对话应用的核心。LangChain 提供了多种记忆实现,从简单的缓冲区到跨会话的持久化存储,你可以根据场景选择合适的策略。
记忆的演变:从缓冲区到智能摘要
ConversationBufferMemory 忠实地保存全部对话历史,适合短对话,但长对话会迅速撑爆上下文窗口。ConversationBufferWindowMemory 通过滑动窗口只保留最近 K 轮对话,缓解了 token 消耗。
更优雅的方案是 ConversationSummaryMemory,它会在对话变长时自动调用 LLM 对历史进行摘要,只保留摘要信息。这对于深度对话、客服场景非常有效。
from langchain.memory import ConversationSummaryMemory, ConversationBufferWindowMemory
summary_memory = ConversationSummaryMemory(llm=llm, max_token_limit=200)
# 当对话积累超过一定长度,旧内容会被压缩为摘要
实体记忆:记住对话中提及的“人”与“事”
ConversationEntityMemory 会从对话流中提取实体(人物、地点、概念等),并为每个实体建立独立的知识卡片。在后续对话中,模型可以精确引用这些实体信息,而不需要全量历史。
例如,聊过“张三喜欢喝美式咖啡”后,当用户说“给上次那个人点他常喝的”,代理能从实体存储中检索出“张三”和“美式咖啡”并完成操作。
持久化与多会话记忆
生产环境需要记忆跨进程、跨重启保持。LangChain 允许你对接外部存储作为记忆后端。使用 RedisChatMessageHistory、DynamoDBChatMessageHistory 或自定义 BaseChatMessageHistory,可实现对话的持久化。
如果你需要为每个用户维护独立记忆,只需在创建记忆实例时传入不同的 session_id:
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
def create_user_memory(user_id):
history = RedisChatMessageHistory(session_id=user_id, url="redis://localhost:6379")
return ConversationBufferMemory(chat_memory=history)
结合向量存储的长程记忆
当对话跨越多次会话且信息量巨大时,摘要和实体记忆还不够。此时可以将对话历史切块存入向量数据库,再用检索器根据当前问题提取相关记忆。这种“检索增强型记忆”让代理仿佛拥有了无限上下文。
LangChain 的 VectorStoreRetrieverMemory 就是此模式的现成实现:
from langchain.memory import VectorStoreRetrieverMemory
retriever = vectorstore.as_retriever(search_kwargs=dict(k=5))
memory = VectorStoreRetrieverMemory(retriever=retriever)
# 每次对话前,memory 会根据当前输入返回最相关的历史片段
综合实战:打造自主研究助手
将链条、代理与记忆融会贯通,我们可以构建一个自主研究助手:它会根据研究主题自动搜索资料、阅读文章、做笔记,并在多轮对话中记住用户偏好。
- 记忆层:使用
VectorStoreRetrieverMemory存储所有研究笔记,每当用户提出新问题,先检索相关旧笔记。 - 代理层:配置一个 OpenAI Functions 代理,挂载搜索工具、文章抓取工具、笔记保存工具和笔记检索工具。
- 链条编排:当代理判断需要深度阅读一篇长文时,内部调用一个
RetrievalQA链条,将长文分块后用嵌入检索回答问题,而不是让模型直接阅读整篇。 - 安全控制:为笔记保存工具添加人工审批,避免自动记录错误信息。
这个助手能跨多个会话持续学习,调用外部世界,并像真正的分析师一样工作。实现的核心在于将记忆、工具和链条背后的抽象组合起来,而非孤立的代码片段。
总结与资源
LangChain 的高级用法远不止接口调用,它提供了一套强大的抽象来编排 LLM 应用。掌握自定义链条、智能代理和结构化记忆,你就能从“写提示词”跃迁到“设计智能工作流”。
- 官方文档:LangChain 核心概念 是最权威的参考资料。
- 示例仓库:GitHub 上的
langchain-ai组织下有大量 cookbook 和模板项目。 - 社区生态:LangSmith 用于调试与观测,LangServe 帮助你快速部署链条为 API。
保持实验,生产环境中谨慎添加人工审核与速率限制,你会打造出稳健且强大的 LLM 应用。