1 快速开始
1.1 框架介绍
01.常用信息1
a.介绍
LangChain 是一个基于大型语言模型(LLM)开发应用程序的框架。
b.LangChain 简化了LLM应用程序生命周期的每个阶段:
开发:使用 LangChain 的开源构建模块和组件构建应用程序。使用第三方集成(opens in a new tab)和模板(opens in a new tab)快速上手。
生产化:使用LangSmith检查、监控和评估你的链条,以便你可以持续优化和自信地部署。
部署:使用LangServe(opens in a new tab)将任何链条转变为 API。
c.该框架由以下开源库组成
langchain-core:基本抽象和 LangChain 表达式语言
langchain-community:第三方集成,langchain-openai,langchain-anthropic
-----------------------------------------------------------------------------------------------------
langchain:构成应用程序认知架构的链条、代理和检索策略
langgraph(opens in a new tab):通过将步骤建模为图中的边缘和节点,使用LLMs构建强大且有状态的多角色应用程序
langserve(opens in a new tab):将 LangChain 链条部署为 REST API
-----------------------------------------------------------------------------------------------------
LangSmith:开发人员平台,可让你对LLM应用程序进行调试、测试、评估和监控,并与LangChain无缝集成
02.常用信息2
a.说明
LangChain 是一个用于开发由语言模型驱动的应用程序的框架。
我们相信,最强大和不同的应用程序不仅将通过 API 调用语言模型
-------------------------------------------------------------------------------------------------
数据感知:将语言模型与其他数据源连接在一起。
主动性:允许语言模型与其环境进行交互。
因此,LangChain 框架的设计目标是为了实现这些类型的应用程序。
c.两个主要的价值主张
组件:LangChain 为处理语言模型所需的组件提供模块化的抽象。LangChain 还为所有这些抽象提供了实现的集合。这些组件旨在易于使用,无论您是否使用 LangChain 框架的其余部分。
用例特定链:链可以被看作是以特定方式组装这些组件,以便最好地完成特定用例。这旨在成为一个更高级别的接口,使人们可以轻松地开始特定的用例。这些链也旨在可定制化。
因此,我们将以下文档分为这两个价值主张。在本文档中,我们以高层次和与语言无关的方式介绍组件和用例。有关使用这些组件和解决这些用例的语言特定方式,请参见页面顶部链接的语言特定部分。
03.框架定位
a.核心理念
a.功能说明
LangChain是专为开发基于大语言模型应用而设计的开源框架,通过模块化组件和链式组合方式,将LLM与外部数据源、工具和API无缝集成。框架采用声明式编程范式,提供统一的抽象层,屏蔽不同LLM提供商的接口差异,让开发者专注于业务逻辑而非底层实现细节。
b.代码示例
---
from langchain.llms import OpenAI
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
# 声明式定义提示模板
template = "请用{language}语言解释{concept}的概念"
prompt = PromptTemplate(
input_variables=["language", "concept"],
template=template
)
# 链式组合LLM和Prompt
llm = OpenAI(temperature=0.7)
chain = LLMChain(llm=llm, prompt=prompt)
# 简洁调用
result = chain.run(language="中文", concept="向量数据库")
print(result)
---
b.技术架构
a.功能说明
LangChain采用分层架构设计,底层为模型抽象层支持多种LLM提供商,中间层为组件层提供Prompts、Memory、Chains等核心组件,上层为应用层封装常见业务场景如问答、摘要、Agent。架构遵循高内聚低耦合原则,每个组件独立可测试,通过标准接口互相协作。
b.代码示例
---
# 分层架构示例:从底层到上层
# 1. 模型抽象层
from langchain.llms import OpenAI, HuggingFaceHub
llm = OpenAI(model_name="gpt-3.5-turbo")
# 2. 组件层
from langchain.prompts import PromptTemplate
from langchain.memory import ConversationBufferMemory
prompt = PromptTemplate.from_template("问题:{question}")
memory = ConversationBufferMemory()
# 3. 应用层
from langchain.chains import ConversationalRetrievalChain
qa_chain = ConversationalRetrievalChain.from_llm(
llm=llm,
memory=memory
)
---
04.生态系统
a.核心库
a.功能说明
LangChain生态包含langchain核心库提供基础组件,langchain-community社区集成包支持200多种第三方工具,langchain-experimental实验性功能库探索前沿技术,langgraph状态机编排库实现复杂Multi-Agent系统,langsmith调试平台提供可观测性支持,langserve部署工具实现一键API服务化。
b.代码示例
---
# 核心库协同使用
from langchain.chat_models import ChatOpenAI
from langchain_community.vectorstores import Milvus
from langchain_experimental.agents import create_pandas_dataframe_agent
from langgraph.graph import StateGraph
# 向量存储集成
vectorstore = Milvus(
embedding_function=embeddings,
connection_args={"host": "localhost", "port": "19530"}
)
# Agent创建
llm = ChatOpenAI(temperature=0)
agent = create_pandas_dataframe_agent(llm, df, verbose=True)
# 状态机编排
workflow = StateGraph(state_schema)
---
b.社区支持
a.功能说明
LangChain拥有活跃的开源社区,GitHub星标超过80K,贡献者超过2000人,集成超过700个第三方工具和服务。官方提供详细文档、教程视频、示例代码库,社区维护LangChain Hub共享提示模板和链配置,Discord频道和论坛提供实时技术支持。
b.代码示例
---
# 使用LangChain Hub中的提示模板
from langchain import hub
# 拉取社区精选模板
prompt = hub.pull("rlm/rag-prompt")
print(prompt)
# 推送自定义模板
custom_prompt = PromptTemplate(
template="根据{context}回答{question}",
input_variables=["context", "question"]
)
hub.push("myorg/custom-rag", custom_prompt)
# 浏览可用模板
templates = hub.list()
for t in templates:
print(f"{t.name}: {t.description}")
---
1.2 安装配置
01.社区生态
a.正式版
a.命令
pip install langchain
b.说明
这将安装LangChain的最低要求。集成LangChain与各种模型提供程序、数据存储等的价值很大。
默认情况下,安装这些集成所需的依赖项没有安装。您需要单独安装特定集成的依赖项。
b.合集1
a.从源代码安装
如果您想从源代码安装,请通过克隆存储库,并确保目录为PATH/TO/REPO/langchain/libs/langchain运行以下命令:
---
pip install -e .
---
b.LangChain核心
langchain-core包包含LangChain生态系统的基本抽象,以及LangChain表达语言。它会自动被langchain安装,但也可以单独使用。用以下命令安装:
---
pip install langchain-core
---
c.LangChain社区版
langchain-community包包含第三方集成。它会自动被langchain安装,但也可以单独使用。用以下命令安装:
---
pip install langchain-community
---
d.LangChain实验版
langchain-experimental包包含实验性的LangChain代码,用于研究和实验用途。使用以下命令安装:
---
pip install langchain-experimental
---
c.合集2
a.LangGraph
langgraph是一个用于构建具有LLMs、基于LangChain的有状态多参与者应用程序的库。使用以下命令安装:
---
pip install langgraph
---
b.LangServe
LangServe可帮助开发人员将LangChain可运行文件和链部署为RESTAPI。LangServe会自动被LangChainCLI安装。如果未使用LangChainCLI,请使用以下命令安装:
---
pip install "langserve[all]"
---
同时安装客户端和服务器依赖。或者使用pipinstall"langserve[client]"安装客户端代码,使用pipinstall"langserve[server]"安装服务器代码。
c.LangChainCLI
LangChainCLI对于使用LangChain模板和其他LangServe项目非常有用。使用以下命令安装:
---
pip install langchain-cli
---
d.LangSmithSDK
LangSmithSDK会根据LangChain自动安装。如果未使用LangChain,请使用以下命令安装:
---
pip install langsmith
02.环境准备
a.Python环境
a.功能说明
LangChain要求Python版本大于等于3.8.1,推荐使用3.10或3.11以获得最佳性能和兼容性。建议使用虚拟环境管理依赖,避免包冲突。支持pip、conda、poetry等多种包管理工具安装,可根据项目需求选择最小化安装或完整安装模式。
b.代码示例
---
# 创建虚拟环境
python3 -m venv langchain-env
source langchain-env/bin/activate # Linux/Mac
# langchain-env\Scripts\activate # Windows
# 验证Python版本
python --version # 应显示Python 3.8.1+
# 升级pip到最新版本
pip install --upgrade pip
# 安装核心库(最小化安装)
pip install langchain
# 完整安装(包含所有集成)
pip install langchain[all]
---
b.依赖管理
a.功能说明
LangChain采用模块化依赖设计,核心包langchain仅包含必需依赖,特定功能需安装额外包如langchain-openai支持OpenAI模型,langchain-community提供社区集成,langchain-experimental包含实验性功能。可通过extras机制按需安装,减少项目体积和依赖冲突风险。
b.代码示例
---
# 按需安装特定功能
pip install langchain-openai # OpenAI集成
pip install langchain-anthropic # Anthropic集成
pip install langchain-community # 社区工具集成
# 使用extras安装常见组合
pip install langchain[llms] # 常用LLM集成
pip install langchain[vectorstores] # 向量数据库集成
# requirements.txt管理
langchain==0.1.0
langchain-openai==0.0.5
langchain-community==0.0.13
pymilvus==2.3.0
# 使用poetry管理依赖
poetry add langchain langchain-openai
poetry add --group dev langchain-experimental
---
03.API密钥配置
a.环境变量设置
a.功能说明
LangChain通过环境变量管理API密钥,支持OpenAI、Anthropic、Cohere等多家LLM提供商。推荐使用dotenv库加载.env文件管理密钥,避免硬编码泄露风险。支持多密钥轮询、密钥优先级配置,可针对不同模型使用不同密钥实现成本控制和负载均衡。
b.代码示例
---
# .env文件配置
OPENAI_API_KEY=sk-proj-xxx
ANTHROPIC_API_KEY=sk-ant-xxx
OPENAI_API_BASE=https://api.openai.com/v1
OPENAI_ORGANIZATION=org-xxx
# Python代码加载环境变量
from dotenv import load_dotenv
import os
load_dotenv()
# 方式1:自动从环境变量读取
from langchain.llms import OpenAI
llm = OpenAI() # 自动读取OPENAI_API_KEY
# 方式2:显式传递密钥
llm = OpenAI(openai_api_key=os.getenv("OPENAI_API_KEY"))
# 方式3:多密钥轮询配置
from langchain.llms import OpenAI
llm = OpenAI(
openai_api_key=os.getenv("OPENAI_API_KEY"),
openai_organization=os.getenv("OPENAI_ORGANIZATION")
)
---
b.信创环境适配
a.功能说明
在信创环境下,LangChain支持对接国产大模型如通义千问、ChatGLM、文心一言等,通过自定义LLM包装器或使用langchain-community提供的集成。支持本地部署的Ollama、vLLM等推理引擎,可配置代理服务器、自定义证书、离线模式等适应内网环境限制。
b.代码示例
---
# 对接通义千问
from langchain_community.llms import Tongyi
llm = Tongyi(
model_name="qwen-turbo",
dashscope_api_key=os.getenv("DASHSCOPE_API_KEY")
)
# 对接本地Ollama
from langchain_community.llms import Ollama
llm = Ollama(
model="qwen2.5:7b",
base_url="http://localhost:11434"
)
# 配置代理服务器
import os
os.environ["HTTP_PROXY"] = "http://proxy.company.com:8080"
os.environ["HTTPS_PROXY"] = "http://proxy.company.com:8080"
# 自定义LLM包装器
from langchain.llms.base import LLM
from typing import Optional, List
class CustomLLM(LLM):
model_url: str = "http://internal-llm-api.com"
def _call(self, prompt: str, stop: Optional[List[str]] = None) -> str:
# 调用内部API
response = requests.post(self.model_url, json={"prompt": prompt})
return response.json()["text"]
@property
def _llm_type(self) -> str:
return "custom-internal-llm"
---
1.3 核心价值
01.开发效率提升
a.模块化组件
a.功能说明
LangChain提供开箱即用的模块化组件,涵盖模型调用、提示工程、记忆管理、数据检索等各个环节,开发者无需从零实现基础功能。组件间通过标准接口互操作,可像搭积木一样快速组装复杂应用。官方提供超过50种预置链模板,覆盖问答、摘要、翻译等常见场景,大幅缩短开发周期。
b.代码示例
---
# 使用预置组件快速构建RAG应用
from langchain.chains import RetrievalQA
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
from langchain.chat_models import ChatOpenAI
# 10行代码实现完整RAG系统
embeddings = OpenAIEmbeddings()
vectorstore = Milvus(
embedding_function=embeddings,
connection_args={"host": "localhost", "port": "19530"}
)
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-3.5-turbo"),
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)
result = qa_chain.run("什么是向量数据库?")
---
b.链式编排
a.功能说明
LangChain的链式编排机制支持将多个操作步骤组合成工作流,通过LCEL表达式语言实现声明式编排,支持顺序执行、并行执行、条件分支等复杂逻辑。链具备自动错误处理、重试机制、流式输出等生产级特性,开发者只需关注业务逻辑而非底层实现细节。
b.代码示例
---
# LCEL表达式语言实现链式编排
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 声明式定义处理流程
prompt = ChatPromptTemplate.from_template("将以下文本翻译成{language}:{text}")
model = ChatOpenAI()
output_parser = StrOutputParser()
# 链式组合:prompt | model | parser
chain = prompt | model | output_parser
# 简洁调用
result = chain.invoke({"language": "英文", "text": "你好世界"})
print(result) # "Hello World"
# 支持流式输出
for chunk in chain.stream({"language": "法语", "text": "再见"}):
print(chunk, end="", flush=True)
---
02.生产可用性
a.可观测性
a.功能说明
LangChain集成LangSmith调试平台,提供全链路追踪、性能分析、成本统计等可观测性能力。每次调用自动记录输入输出、耗时、Token消耗,支持链路可视化、错误定位、A/B测试。通过Callbacks机制可接入自定义日志系统、监控告警、审计合规等企业级需求。
b.代码示例
---
# 启用LangSmith追踪
import os
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-xxx"
os.environ["LANGCHAIN_PROJECT"] = "my-project"
# 自动追踪所有调用
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run(input="test") # 自动上报到LangSmith
# 自定义Callbacks
from langchain.callbacks import StdOutCallbackHandler
from langchain.callbacks.base import BaseCallbackHandler
class CustomCallbackHandler(BaseCallbackHandler):
def on_llm_start(self, serialized, prompts, **kwargs):
print(f"LLM开始调用: {prompts}")
def on_llm_end(self, response, **kwargs):
print(f"LLM调用结束: {response.generations[0][0].text}")
chain.run(input="test", callbacks=[CustomCallbackHandler()])
---
b.容错机制
a.功能说明
LangChain内置多层容错机制,包括自动重试、降级回退、超时控制、限流保护等。支持配置重试策略、指数退避、错误分类处理,可针对不同错误类型采取不同应对措施。通过Fallback机制实现模型降级,当主模型不可用时自动切换到备用模型,确保服务高可用性。
b.代码示例
---
# 配置重试策略
from langchain.llms import OpenAI
from langchain.callbacks import get_openai_callback
llm = OpenAI(
max_retries=3, # 最大重试次数
request_timeout=30, # 超时时间
max_tokens=1000 # Token限制
)
# Fallback降级机制
from langchain.chat_models import ChatOpenAI, ChatAnthropic
from langchain.prompts import ChatPromptTemplate
# 主模型
primary = ChatOpenAI(model="gpt-4")
# 备用模型
fallback = ChatAnthropic(model="claude-3-sonnet")
# 配置降级链
chain = (
ChatPromptTemplate.from_template("回答问题:{question}")
| primary.with_fallbacks([fallback])
)
# 主模型失败时自动切换
result = chain.invoke({"question": "什么是AI?"})
# 批量调用容错
from langchain.schema.runnable import RunnablePassthrough
chain_with_retry = chain.with_retry(
stop_after_attempt=3,
wait_exponential_multiplier=1,
wait_exponential_max=10
)
---
1.4 场景示例
01.应用程序编程接口:API
API(应用程序编程接口)非常强大,因为它们既允许您通过它们执行操作,也可以通过它们查询数据。本页面介绍了 LangChain 中用于处理 API 的所有资源。
---------------------------------------------------------------------------------------------------------
链式操作( Chains )
如果您刚刚开始,且拥有相对较小/简单的API,您应该从链式操作( Chains )开始。链式操作是一系列预定步骤,因此它们非常适合入门,因为它们能够提供更多的控制并让您更好地理解正在发生的事情。
---------------------------------------------------------------------------------------------------------
代理( Agents )
代理更复杂,需要多次查询语言模型以了解如何操作。代理的缺点是您拥有的控制权较少。优点是它们更强大,使您能够在更大或更复杂的API上使用它们。
02.聊天机器人 Chatbots
ChatGPT 以其提供全新的接口——聊天( chat )而席卷了全球。构建聊天机器人需要几个组成部分。
模型 - 您可以使用普通的语言模型或聊天模型构建聊天机器人。重要的是要记住,即使您使用的是聊天模型,API本身是无状态的,这意味着它不会记住先前的交互 - 您必须将它们传递进去。
PromptTemplate(提示模板)- 这将指导您的聊天机器人的行为方式。它们是率性的还是乐于助人的?这些可以用来赋予您的聊天机器人一些个性。
Memory(记忆)- 如上所述,模型本身是无状态的。记忆为聊天机器人带来了一定的状态概念,使其能够记住先前的交互。
将聊天机器人与其他数据源结合使用时,聊天机器人通常非常强大且更具差异化。与"在文档上进行问答"的基本技术相同,也可以在这里使用,使您的聊天机器人能够访问这些数据。
03.评估的方法,Evaluation
评估 LangChain 链式操作和代理可能非常困难。
---------------------------------------------------------------------------------------------------------
这主要有两个原因:
缺乏数据
在开始项目之前,通常很难获得用于评估链式操作和代理的大量数据。
这通常是因为大型语言模型(大多数链式操作和代理的核心)具有出色的小样本和零样本学习能力,
这意味着您几乎总是能够在特定任务(如文本到 SQL、问答等)上开始,而无需大量的示例数据集。
这与传统的机器学习形成鲜明对比,传统机器学习在使用模型之前必须先收集大量数据点。
---------------------------------------------------------------------------------------------------------
缺乏评估指标
大多数链式操作和代理执行的任务往往没有很好的评估指标来评估性能。例如,生成某种形式的文本比评估分类预测或数值预测要复杂得多。
04.提取,Extraction
语言模型实际上非常擅长从非结构化文本中提取结构化信息。这非常有用,因为很多信息都以文本形式存储,但为了在下游使用时更方便,通常需要将其转换为结构化格式。
在这里理解的最有用的概念是 OutputParser(输出解析器)。OutputParser 负责指定语言模型应该以何种模式响应,并将其原始文本输出解析为结构化格式。
在进行提取时,您可以使用 OutputParser 定义要提取的信息的模式。然后,您可以创建一个 PromptTemplate(提示模板),该模板接收原始文本块,并包含提取信息的指令,使其以指定的格式提取信息。
05.个人助理,Personal Assistants
个人助理是一个非常理想的应用场景,因为它结合了 LangChain 的两个核心价值主张(行动执行和个性化数据)。为了构建一个个人助理,您需要了解以下概念:
PromptTemplate(提示模板)- 这将指导您的个人助理的行为方式。它们是率性的还是乐于助人的?这些可以用来赋予您的个人助理一些个性。
Memory(记忆)- 您的个人助理可能需要记住一些事情。它应该能够进行对话(短期记忆),并且可能还需要一些长期记忆的概念。
Tools(工具)- 您的个人助理将通过您提供的工具来展现其特点。它应该知道如何做什么?
Agent(代理)- 您的个人助理将需要理解它应该采取的行动。构建尽可能好的代理将非常重要。
Agent Executor(代理执行器)- 在获得了工具和代理之后,为了将其付诸实践,您需要设置一个环境供代理使用这些工具。这就是代理执行器发挥作用的地方。
06.基于文档的问答,Question Answering Over Documents
虽然LLM非常强大,但它们对于它们未经训练的信息一无所知。如果您想使用LLM来回答它未经训练的文档相关问题,您需要向其提供这些文档的信息。最常用的方法是通过"检索增强生成"( retrieval augmented generation )。
检索增强生成的思想是,在给定一个问题时,首先进行检索步骤以获取任何相关文档。然后将这些文档与原始问题一起传递给语言模型,并让它生成一个回答。然而,为了做到这一点,首先需要将文档以适合进行此类查询的格式呈现。
本页介绍了这两个步骤的高级思想:
(1)将文档摄入到可查询格式中,然后
(2)进行检索增强生成链。
---------------------------------------------------------------------------------------------------------
摄入,Ingestion
为了使用语言模型与数据进行交互,您首先需要将其转换为适合的格式。这种格式可以是索引( Index )。通过将数据放入索引中,可以使后续的步骤更容易与之交互。
有几种类型的索引,但最常见的是向量存储( Vectorstore )。将文档摄入到向量存储中可以按照以下步骤完成:
1.加载文档(使用文档加载器)
2.分割文档(使用文本分割器)
3.为文档创建嵌入(使用文本嵌入模型)
4.将文档和嵌入存储到向量存储中
---------------------------------------------------------------------------------------------------------
生成,Generation
现在我们有了一个索引,如何使用它进行生成呢?可以将此过程分为以下步骤:
1.接收用户提问
2.在索引中查找与问题相关的文档
3.使用问题和任何相关文档构建一个 PromptValue(使用 PromptTemplate )
4.将PromptValue传递给模型
5.获取结果并返回给用户。
07.表格数据查询,Querying Tabular Data
大量的数据和信息存储在表格数据中,无论是 CSV 文件、 Excel 表格还是 SQL 表格。本页面介绍了 LangChain 中用于处理这种格式数据的所有资源。
---------------------------------------------------------------------------------------------------------
文档加载( Document Loading )
如果您的文本数据以表格格式存储,您可能希望将数据加载到文档中,然后像处理其他文本/非结构化数据一样对其进行索引。
为此,您应该使用像 CSVLoader 这样的文档加载器,然后在该数据上创建索引,并进行查询。
---------------------------------------------------------------------------------------------------------
查询( Querying )
如果您有更多的数值型表格数据,或者有大量数据且不想对其进行索引,您也可以直接使用语言模型进行交互。
---------------------------------------------------------------------------------------------------------
链式操作( Chains )
如果您刚刚开始,且拥有相对较小/简单的表格数据,您应该从链式操作( Chains )开始。
链式操作是一系列预定步骤,因此它们非常适合入门,因为它们能够提供更多的控制并让您更好地理解正在发生的事情。
---------------------------------------------------------------------------------------------------------
代理( Agents )
代理更复杂,需要多次查询语言模型以了解如何操作。代理的缺点是您拥有的控制权较少。优点是它们更强大,使您能够在更大的数据库和更复杂的模式上使用它们。
2 核心概念
2.1 代理:agents
00.汇总
有些应用程序不仅需要预定的LLM/其他工具的调用链,还可能需要根据用户的输入构建未知的链条。
在这些类型的链条中,有一个"代理"(agent)可以访问一套工具。根据用户的输入,代理可以决定是否调用这些工具中的任何一个。
-----------------------------------------------------------------------------------------------------
我们将文档拆分为以下几个部分:
工具(Tools):语言模型与其他资源的交互方式。
代理(Agents):驱动决策的语言模型。
工具包(Toolkits):一组工具,当它们一起使用时可以完成特定的任务。
代理执行器(Agent Executor):负责运行带有工具的代理的逻辑。
01.代理执行器,Agent Executor
代理执行器( Agent Executor )是一个代理( Agent )和一组工具( Tools )。
代理执行器负责调用代理,获取动作和动作输入,根据动作引用的工具以及相应的输入调用工具,获取工具的输出,
然后将所有这些信息传递回代理,以获取它应该采取的下一个动作。
代理执行器充当了协调和管理代理与工具之间交互的角色。
它通过调用代理获取动作和动作输入,然后根据动作引用的工具调用相应的工具进行处理。
最后,将工具的输出和其他相关信息传递回代理,以便代理决定下一步应该采取的动作。
代理执行器将代理与工具的使用组织起来,以实现更复杂的任务或问题的解决。
02.代理,Agent
代理( Agent )是围绕模型的封装,它接收用户输入并返回与 “动作 action ”相对应的响应,以及相应的 “动作输入 action input ”。
代理的作用是接收用户的输入,并根据模型的处理逻辑生成相应的响应。这个响应通常包括执行的动作和对应的动作输入,用于进一步指导系统的行为。
代理起到了将用户输入与模型的交互进行封装和管理的作用。
03.工具,Tool
工具(Tool)是围绕函数的具体抽象,使语言模型可以与之进行交互变得简单。具体而言,工具的接口具有单一的文本输入和单一的文本输出。
04.工具包,Toolkits
工具包( Toolkits )是一组工具的集合,可以用于解决特定问题或完成特定任务。
这些工具包是为了一起使用、相互协作以实现更复杂的目标而组织起来的。
使用工具包可以提供更全面、高效的解决方案,因为它们涵盖了解决特定问题所需的各个方面或阶段。
2.2 链:chains
00.汇总
链( Chains )是一个非常通用的概念,它指的是将一系列模块化组件(或其他链)以特定方式组合起来,以实现共同的用例。
最常用的链类型是LLMChain(LLM链),它结合了PromptTemplate(提示模板)、Model(模型)和Guardrails(守卫)来接收用户输入,进行相应的格式化,
将其传递给模型并获取响应,然后验证和修正(如果需要)模型的输出。
01.链,Chain
链(Chain)是对多个独立组件进行端到端封装的一种方式。
02.索引链,Index-related chains
a.说明
这类链用于与索引进行交互。这些链的目的是将自己的数据(存储在索引中)与LLM结合起来。其中最好的例子是对自己的文档进行问答。
其中一个重要部分是了解如何将多个文档传递给语言模型。有几种不同的方法或链来实现这一点。
LangChain 支持其中四种常见的方法,并且我们正在积极寻求包括更多方法,如果你有任何想法,请随时联系我们!
请注意,并没有一种最佳方法-选择使用哪种方法通常非常依赖上下文。按照从简单到复杂的顺序:
b.Stuffing(填充)
Stuffing 是最简单的方法,您只需将所有相关数据作为上下文直接添加到提示中,然后传递给语言模型。LangChain 中的 StuffDocumentsChain 便是采用了这种方法。
优点:只需向LLM发出一次调用。在生成文本时,LLM 可以一次性访问所有数据。
缺点:大多数LLM具有上下文长度限制,对于大型文档(或许多文档),这种方法将不起作用,因为提示的长度将超过上下文长度。
这种方法的主要缺点是它只适用于较小的数据片段。一旦您处理许多数据片段,这种方法就不再可行。接下来的两种方法旨在帮助处理这个问题。
c.Map Reduce(映射-归约)
该方法涉及对每个数据块运行初始提示(对于摘要任务,可以是该块的摘要;对于问答任务,可以是仅基于该块的答案)。然后运行不同的提示来组合所有初始输出。LangChain中实现了这种方法,称为 MapReduceDocumentsChain。
优点:可以扩展到比 StuffDocumentsChain 更大的文档(以及更多的文档)。对各个文档进行的LLM调用是独立的,因此可以并行化处理。
缺点:比 StuffDocumentsChain 需要更多对 LLM 的调用。在最终的组合调用中会丢失一些信息。
d.Refine(优化)
该方法涉及在第一个数据块上运行初始提示,生成一些输出。对于剩余的文档,将该输出与下一个文档一起传递给LLM,要求LLM基于新文档优化输出。
优点:可以获取更多相关的上下文,并且可能比 MapReduceDocumentsChain 更少损失信息。
缺点:比 StuffDocumentsChain 需要更多对LLM的调用。这些调用也不是独立的,这意味着无法像 MapReduceDocumentsChain 那样进行并行化处理。还可能对文档的顺序造成潜在的影响。
e.Map-Rerank(映射-重新排序)
这种方法涉及在每个数据块上运行初始提示,该提示不仅尝试完成任务,还为其答案的确定程度给出一个分数。然后根据这个分数对响应进行排序,返回最高分的响应。
优点:与MapReduceDocumentsChain类似的优点。与MapReduceDocumentsChain相比,需要更少的调用。
缺点:无法在文档之间合并信息。这意味着当您期望在单个文档中有一个简单的单一答案时,这种方法最为有用。
03.LLM链,LLMChain
LLMChain(LLM链)是最常见的链类型。
它由 PromptTemplate(提示模板)、模型(可以是 LLM 或 ChatModel )和可选的输出解析器组成。
该链接受多个输入变量,使用 PromptTemplate 将它们格式化为提示。然后将提示传递给模型。
最后,它使用 OutputParser(如果提供)将LLM的输出解析为最终格式。
04.提示选择器,Prompt Selector
在 LangChain 中,链的一个目标是让人们尽快开始使用特定的用例。这其中一个重要部分是拥有良好的提示。
问题是,对一个模型有效的提示对另一个模型可能效果不佳。我们希望链对所有类型的模型都能够良好工作。
因此,我们没有在链中硬编码默认提示的使用方式,而是引入了 PromptSelector 的概念。PromptSelector 负责根据传入的模型选择一个默认提示。
PromptSelectors 的最常见用例是为 LLM 和 Chat Model 设置不同的默认提示。然而,如果需要,也可以为不同的模型提供商设置不同的默认提示。
2.3 索引:indexes
01.加载文档,Document Loaders
Document Loaders负责加载文档对象列表的类。
02.检索数据,Retriever
一种存储数据的方式,使其可以被语言模型查询。该对象必须提供的唯一接口是get_relevant_texts方法,它接收一个字符串作为输入,并返回一个文档列表。
03.文档拆分,Text Splitters
通常您想将大型文本文档分成更小的块以更好地处理语言模型。TextSplitters 负责将文档拆分成较小的文档。
04.向量存储库,Vectorstore
最常见的索引类型是为每个文档创建数值嵌入(通过嵌入模型(Embedding Model)生成)。
向量存储库(Vector Store)存储文档和相关的嵌入,并提供通过嵌入快速查找相关文档的方法。
2.4 内存:memory
00.汇总
内存(Memory)是在对话过程中存储和检索数据的概念。主要有两种方法:
1.根据输入,获取任何相关的数据。
2.根据输入和输出,相应地更新状态。
---------------------------------------------------------------------------------------------------------
内存主要分为两种类型:短期内存和长期内存。
1.短期内存通常指的是如何在单个对话的上下文中传递数据(通常是先前的聊天消息或其摘要)。
2.长期内存处理的是如何在对话之间获取和更新信息的问题。
01.记录聊天历史,ChatMessageHistory
目前,与语言模型的主要接口是通过聊天界面进行的。ChatMessageHistory类负责记录所有先前的聊天互动。
然后,可以直接将它们传递回模型,以某种方式进行总结,或者进行某种组合。
---------------------------------------------------------------------------------------------------------
ChatMessageHistory提供了两个方法和一个属性。
它提供的两个方法是add_user_message和add_ai_message,用于分别存储来自用户的消息和AI的响应。
它提供的属性是messages属性,用于访问所有先前的消息。
2.5 模型:models
00.汇总
LLMs
首先介绍的是大型语言模型( LLMs )。这些模型以文本字符串作为输入,并返回文本字符串作为输出。
---------------------------------------------------------------------------------------------------------
聊天模型 Chat Models
聊天模型是第二种介绍的模型类型。这些模型通常由语言模型支持,但其 API 更加结构化。具体来说,这些模型以聊天消息列表作为输入,并返回聊天消息。
---------------------------------------------------------------------------------------------------------
文本嵌入模型 Text Embedding Models
第三种介绍的模型类型是文本嵌入模型。这些模型以文本作为输入,并返回浮点数列表。
01.聊天模型,Chat Models
聊天模型接受一个聊天消息列表作为输入,并返回一个聊天消息。
02.语言模型,LLMs
语言模型接受文本作为输入,并返回文本作为输出。
03.文本嵌入模型,Text Embedding Models
文本嵌入模型接受文本作为输入,并以浮点数列表的形式返回该文本的数字表示。
2.6 提示:prompt
00.汇总
PromptValue
表示模型输入的类。
---------------------------------------------------------------------------------------------------------
Prompt Templates
负责构建 PromptValue 的类。
---------------------------------------------------------------------------------------------------------
示例选择器 Example Selectors
在提示中包含示例通常是有用的。这些示例可以硬编码,但如果它们是动态选择的,则通常更有用。
---------------------------------------------------------------------------------------------------------
输出解析器 Output Parsers
语言模型(和聊天模型)输出文本。但是许多时候,您可能想获得比仅文本更有结构化的信息。这就是输出解析器发挥作用的地方。
输出解析器负责(1)指示模型如何格式化输出,(2)将输出解析为所需格式(包括必要时进行重试)。
01.示例选择器,Example Selectors
在提示中包括示例通常是很有用的。这些示例可以硬编码,但如果动态选择,则通常更强大。示例选择器是一种对象,接受用户输入,然后返回要使用的示例列表。
02.输出解析器,Output Parsers
Output parsers(输出解析器)是用于结构化语言模型响应的类。输出解析器必须实现两个主要方法:
get_format_instructions() -> str:一个返回包含语言模型输出格式化指令的字符串的方法。 parse(str) -> Any:一个接受字符串(假设为语言模型的响应)并将其解析为某种结构的方法。 还有一个可选的方法:
parse_with_prompt(str) -> Any:一个接受字符串(假设为语言模型的响应)和提示(假设为生成此响应的提示)的方法,并将其解析为某种结构。在输出解析器希望以某种方式重试或修复输出,并且需要来自提示的信息时,提供提示非常有用。
03.Prompt模板,Prompt Templates
“ PromptValue ”是最终传递给模型的内容。大多数情况下,该值不是硬编码的,而是根据用户输入、其他非静态信息(通常来自多个源)和固定的模板字符串动态创建的。
我们将负责创建“ PromptValue ”的对象称为 PromptTemplate。该对象公开了一种将输入变量作为输入并返回“ PromptValue ”的方法。
04.提示内容,PromptValue
“提示 Prompt”指的是传递给底层模型的内容。LangChain 提供了几种针对提示的抽象,以处理文本数据。对于其他数据类型(例如图像、音频),我们正在努力添加抽象,但还没有完成。
不同的模型可能期望不同的数据格式。在可能的情况下,我们希望允许在不同的模型类型中使用相同的提示。因此,我们有“ PromptValue ”的概念。这是一个类,公开了用于转换为每种模型类型所期望的确切输入类型(目前为文本或 ChatMessages )的方法。
2.7 模式:schema
01.聊天消息,ChatMessages
最终用户与之交互的主要接口是聊天接口。因此,一些模型提供商甚至开始以预期聊天消息的方式提供对底层 API 的访问。
这些消息具有内容字段(通常是文本),并与用户相关联。目前支持的用户为系统、人类和 AI。
---------------------------------------------------------------------------------------------------------
SystemChatMessage
表示应该将什么信息传达给 AI 系统的聊天消息。
---------------------------------------------------------------------------------------------------------
HumanChatMessage
表示来自与 AI 系统交互的人类的信息的聊天消息。
---------------------------------------------------------------------------------------------------------
AIChatMessage
表示来自 AI 系统的信息的聊天消息。
02.文档,Document
一份非结构化数据。由页面内容(数据的内容)和元数据(描述数据属性的辅助信息)组成。
03.例子,Examples
例子是输入/输出对,表示函数的输入和预期输出。它们可以在模型的训练和评估中使用。
这些可以是模型或链的输入/输出。两种类型的示例有不同的用途。模型的示例可用于微调模型。链式的示例可用于评估端到端链式过程,甚至可能训练替换整个链的模型。
04.文本,text
在处理语言模型时,您可以通过文本与其进行交互。作为一种过于简化的描述,很多模型是 “输入文本,输出文本”。因此,LangChain 中的很多接口都围绕文本展开。
3 核心组件
3.1 Models模型
01.LLM抽象
a.统一接口
a.功能说明
LangChain提供统一的LLM抽象层,屏蔽不同模型提供商的API差异,支持OpenAI、Anthropic、Cohere、HuggingFace等20多家主流厂商。通过标准接口实现模型切换无需修改业务代码,支持同步和异步调用、流式输出、批量处理等特性。模型参数如temperature、max_tokens等通过统一方式配置,简化多模型管理复杂度。
b.代码示例
---
# 统一接口调用不同模型
from langchain.llms import OpenAI, Anthropic, Cohere
# OpenAI模型
openai_llm = OpenAI(
model_name="gpt-3.5-turbo",
temperature=0.7,
max_tokens=500
)
# Anthropic模型
anthropic_llm = Anthropic(
model="claude-3-sonnet",
temperature=0.7,
max_tokens_to_sample=500
)
# 相同的调用方式
prompt = "解释什么是向量数据库"
result1 = openai_llm(prompt)
result2 = anthropic_llm(prompt)
# 异步调用
import asyncio
async def async_call():
result = await openai_llm.agenerate([prompt])
return result
---
b.本地模型集成
a.功能说明
LangChain支持集成本地部署的开源模型,通过Ollama、vLLM、LlamaCpp等推理引擎加载Llama、Qwen、ChatGLM等模型。支持量化模型加载、GPU加速、多卡并行等优化技术,可在信创环境或离线场景下运行。提供HuggingFacePipeline包装器直接加载Transformers模型,支持自定义模型适配。
b.代码示例
---
# 使用Ollama加载本地模型
from langchain_community.llms import Ollama
local_llm = Ollama(
model="qwen2.5:7b",
base_url="http://localhost:11434",
temperature=0.7
)
result = local_llm("你好")
# 使用HuggingFace Pipeline
from langchain_community.llms import HuggingFacePipeline
from transformers import AutoTokenizer, AutoModelForCausalLM, pipeline
model_id = "THUDM/chatglm3-6b"
tokenizer = AutoTokenizer.from_pretrained(model_id, trust_remote_code=True)
model = AutoModelForCausalLM.from_pretrained(
model_id,
trust_remote_code=True,
device_map="auto"
)
pipe = pipeline(
"text-generation",
model=model,
tokenizer=tokenizer,
max_new_tokens=512
)
hf_llm = HuggingFacePipeline(pipeline=pipe)
result = hf_llm("介绍一下LangChain")
---
02.ChatModel
a.对话模型
a.功能说明
ChatModel专门用于处理多轮对话场景,支持System、Human、AI等多种消息角色,自动管理对话历史和上下文。相比普通LLM,ChatModel更适合构建聊天机器人、客服系统等交互式应用。支持函数调用Function Calling、工具使用Tool Use等高级特性,可实现Agent决策和工具编排。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage, SystemMessage, AIMessage
chat = ChatOpenAI(model="gpt-3.5-turbo", temperature=0.7)
# 单轮对话
messages = [
SystemMessage(content="你是一个Python编程专家"),
HumanMessage(content="如何读取CSV文件?")
]
response = chat(messages)
print(response.content)
# 多轮对话
messages = [
SystemMessage(content="你是AI助手"),
HumanMessage(content="我叫张三"),
AIMessage(content="你好张三,很高兴认识你"),
HumanMessage(content="我叫什么名字?")
]
response = chat(messages) # 能记住之前的对话
# 流式输出
for chunk in chat.stream(messages):
print(chunk.content, end="", flush=True)
---
b.函数调用
a.功能说明
ChatModel支持函数调用特性,模型可自主决策何时调用哪个函数以及传递什么参数,实现从自然语言到结构化API的映射。通过定义函数schema描述函数功能和参数类型,模型根据用户意图生成函数调用指令。支持并行函数调用、嵌套调用、错误处理等复杂场景,是构建Agent系统的核心能力。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.schema import HumanMessage
import json
# 定义函数schema
functions = [
{
"name": "get_weather",
"description": "获取指定城市的天气信息",
"parameters": {
"type": "object",
"properties": {
"city": {
"type": "string",
"description": "城市名称,如北京、上海"
},
"unit": {
"type": "string",
"enum": ["celsius", "fahrenheit"],
"description": "温度单位"
}
},
"required": ["city"]
}
}
]
chat = ChatOpenAI(model="gpt-3.5-turbo")
messages = [HumanMessage(content="北京今天天气怎么样?")]
# 模型自主选择调用函数
response = chat.predict_messages(
messages,
functions=functions
)
# 解析函数调用
if response.additional_kwargs.get("function_call"):
function_call = response.additional_kwargs["function_call"]
function_name = function_call["name"]
arguments = json.loads(function_call["arguments"])
print(f"调用函数: {function_name}")
print(f"参数: {arguments}")
---
3.2 Prompts提示
01.提示模板
a.模板定义
a.功能说明
PromptTemplate提供变量替换、条件渲染、模板继承等能力,将提示词工程化管理。支持Jinja2风格语法定义复杂模板,通过input_variables声明动态变量,通过partial_variables设置默认值。模板可序列化存储、版本管理、跨项目复用,将提示词从代码中解耦便于迭代优化。
b.代码示例
---
from langchain.prompts import PromptTemplate
# 基础模板
template = "将以下{source_lang}翻译成{target_lang}:\n{text}"
prompt = PromptTemplate(
template=template,
input_variables=["source_lang", "target_lang", "text"]
)
# 格式化生成提示词
formatted = prompt.format(
source_lang="中文",
target_lang="英文",
text="你好世界"
)
print(formatted)
# 部分变量预设
prompt_with_defaults = PromptTemplate(
template="用{language}回答:{question}",
input_variables=["question"],
partial_variables={"language": "中文"}
)
# 从文件加载模板
prompt = PromptTemplate.from_file(
"templates/translation.txt",
input_variables=["text"]
)
---
b.Few-shot模板
a.功能说明
FewShotPromptTemplate支持Few-shot学习范式,通过提供示例引导模型输出格式和风格。可动态选择最相关的示例,使用示例选择器根据输入相似度自动筛选最佳示例。支持示例格式化、前缀后缀配置、示例数量限制等高级特性,有效提升模型在特定任务上的表现。
b.代码示例
---
from langchain.prompts import FewShotPromptTemplate, PromptTemplate
from langchain.prompts.example_selector import SemanticSimilarityExampleSelector
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
# 定义示例
examples = [
{"input": "happy", "output": "sad"},
{"input": "tall", "output": "short"},
{"input": "hot", "output": "cold"}
]
# 示例格式化模板
example_template = "输入:{input}\n输出:{output}"
example_prompt = PromptTemplate(
input_variables=["input", "output"],
template=example_template
)
# 基于相似度选择示例
example_selector = SemanticSimilarityExampleSelector.from_examples(
examples,
OpenAIEmbeddings(),
Milvus,
k=2 # 选择2个最相似示例
)
# Few-shot模板
few_shot_prompt = FewShotPromptTemplate(
example_selector=example_selector,
example_prompt=example_prompt,
prefix="请给出以下词的反义词:",
suffix="输入:{input}\n输出:",
input_variables=["input"]
)
# 自动选择相关示例
formatted = few_shot_prompt.format(input="big")
print(formatted)
---
02.ChatPrompt
a.对话模板
a.功能说明
ChatPromptTemplate专为对话模型设计,支持System、Human、AI等多种消息类型的结构化管理。可通过消息模板组合复杂对话流程,支持消息角色切换、历史对话注入、动态变量替换。提供MessagesPlaceholder占位符动态插入对话历史,实现记忆管理和上下文维护。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.prompts.chat import SystemMessagePromptTemplate, HumanMessagePromptTemplate
# 定义对话模板
prompt = ChatPromptTemplate.from_messages([
SystemMessagePromptTemplate.from_template(
"你是{role},擅长{skill}"
),
MessagesPlaceholder(variable_name="history"), # 历史对话占位符
HumanMessagePromptTemplate.from_template("{input}")
])
# 格式化生成消息
from langchain.schema import HumanMessage, AIMessage
messages = prompt.format_messages(
role="Python专家",
skill="数据分析",
history=[
HumanMessage(content="你好"),
AIMessage(content="你好,有什么可以帮助你的?")
],
input="如何使用pandas读取CSV?"
)
# 简化写法
prompt = ChatPromptTemplate.from_messages([
("system", "你是{role}"),
MessagesPlaceholder("history"),
("human", "{input}")
])
---
b.模板组合
a.功能说明
LangChain支持通过Pipeline机制组合多个模板,实现复杂提示词工程流程。可将数据预处理、示例选择、格式化输出等步骤串联,构建可复用的提示词处理管道。支持条件分支、循环嵌套、错误处理等控制流,通过PipelinePromptTemplate实现模板间的数据传递和转换。
b.代码示例
---
from langchain.prompts.pipeline import PipelinePromptTemplate
from langchain.prompts import PromptTemplate
# 定义子模板
intro_template = PromptTemplate.from_template(
"你是{role},专注于{domain}领域"
)
task_template = PromptTemplate.from_template(
"当前任务:{task}\n要求:{requirements}"
)
output_template = PromptTemplate.from_template(
"请以{format}格式输出结果"
)
# 组合成Pipeline
full_template = "{intro}\n\n{task}\n\n{output}"
pipeline = PipelinePromptTemplate(
final_prompt=PromptTemplate.from_template(full_template),
pipeline_prompts=[
("intro", intro_template),
("task", task_template),
("output", output_template)
]
)
# 统一传参
result = pipeline.format(
role="数据分析师",
domain="金融",
task="分析股票趋势",
requirements="准确率95%以上",
format="JSON"
)
print(result)
---
3.3 Chains链
01.链式编排
a.基础链
a.功能说明
LLMChain是最基础的链组件,将Prompt和LLM组合成可调用单元。支持输入变量验证、输出解析、错误处理等基础能力。通过链式调用可将多个处理步骤串联,实现从数据输入到结果输出的完整流程。链具备序列化、并行执行、内存共享等特性,是构建复杂应用的基石。
b.代码示例
---
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
# 创建基础链
prompt = PromptTemplate(
template="给{product}写一句广告语",
input_variables=["product"]
)
llm = OpenAI(temperature=0.7)
chain = LLMChain(llm=llm, prompt=prompt)
# 单次调用
result = chain.run(product="智能手表")
print(result)
# 批量调用
results = chain.apply([
{"product": "智能手表"},
{"product": "无线耳机"},
{"product": "平板电脑"}
])
# 异步调用
import asyncio
async def async_run():
result = await chain.arun(product="智能音箱")
return result
---
b.LCEL表达式
a.功能说明
LCEL是LangChain Expression Language的缩写,提供声明式链编排能力。通过管道运算符将组件连接,支持链式调用、流式处理、并行执行、条件分支等高级特性。LCEL链自动处理输入输出类型转换、错误传播、追踪日志等底层细节,让开发者专注于业务逻辑。相比传统链更简洁、类型安全、易于调试。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
# LCEL链式编排
prompt = ChatPromptTemplate.from_template("讲个关于{topic}的笑话")
model = ChatOpenAI()
output_parser = StrOutputParser()
# 使用 | 运算符连接组件
chain = prompt | model | output_parser
# 简洁调用
result = chain.invoke({"topic": "程序员"})
# 流式输出
for chunk in chain.stream({"topic": "产品经理"}):
print(chunk, end="", flush=True)
# 批量处理
results = chain.batch([
{"topic": "程序员"},
{"topic": "设计师"}
])
# 并行执行
from langchain.schema.runnable import RunnableParallel
parallel_chain = RunnableParallel({
"joke": prompt | model | output_parser,
"topic": RunnablePassthrough()
})
result = parallel_chain.invoke({"topic": "AI"})
---
02.预置链
a.检索链
a.功能说明
RetrievalQA是专门用于构建问答系统的预置链,整合向量检索和LLM生成两个环节。支持多种检索策略如相似度搜索、MMR多样性检索、上下文压缩等。可配置检索器参数、返回文档数量、相关性阈值,支持添加数据源引用、来源追溯等特性,适用于知识库问答、文档查询等场景。
b.代码示例
---
from langchain.chains import RetrievalQA
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
from langchain.chat_models import ChatOpenAI
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载文档
loader = TextLoader("data/knowledge.txt")
documents = loader.load()
# 文本分割
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000,
chunk_overlap=200
)
texts = text_splitter.split_documents(documents)
# 构建向量库
embeddings = OpenAIEmbeddings()
vectorstore = Milvus.from_documents(
texts,
embeddings,
connection_args={"host": "localhost", "port": "19530"}
)
# 创建检索链
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-3.5-turbo"),
chain_type="stuff",
retriever=vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
),
return_source_documents=True # 返回来源文档
)
# 问答
result = qa_chain({"query": "什么是向量数据库?"})
print(result["result"])
print("来源:", result["source_documents"])
---
b.对话链
a.功能说明
ConversationalRetrievalChain在检索链基础上增加对话记忆管理,支持多轮对话场景下的上下文理解和历史引用。自动将历史对话整合到检索查询中,支持对话压缩、摘要生成、关键信息提取等优化策略。适用于智能客服、知识助手等需要保持对话连贯性的交互式应用。
b.代码示例
---
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
# 创建记忆组件
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
output_key="answer"
)
# 创建对话检索链
qa = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(temperature=0),
retriever=vectorstore.as_retriever(),
memory=memory,
return_source_documents=True
)
# 多轮对话
result1 = qa({"question": "什么是Milvus?"})
print(result1["answer"])
# 基于上一轮对话继续提问
result2 = qa({"question": "它有什么优势?"}) # "它"会被理解为Milvus
print(result2["answer"])
# 查看对话历史
print(memory.load_memory_variables({}))
---
3.4 Memory记忆
01.记忆类型
a.缓冲记忆
a.功能说明
ConversationBufferMemory保存完整的对话历史,适用于短对话场景。每次交互都会将用户输入和AI响应追加到记忆中,检索时返回全部历史。优点是信息无损失,缺点是Token消耗随对话增长线性增加。支持配置返回格式为字符串或消息列表,可通过memory_key自定义变量名注入到提示词中。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.llms import OpenAI
# 创建缓冲记忆
memory = ConversationBufferMemory()
# 集成到对话链
conversation = ConversationChain(
llm=OpenAI(temperature=0),
memory=memory,
verbose=True
)
# 多轮对话
conversation.predict(input="我叫张三")
conversation.predict(input="我喜欢编程")
response = conversation.predict(input="我叫什么名字?")
# 查看记忆内容
print(memory.load_memory_variables({}))
# {'history': 'Human: 我叫张三\nAI: ...\nHuman: 我喜欢编程\nAI: ...'}
# 手动管理记忆
memory.save_context(
{"input": "你好"},
{"output": "你好,很高兴见到你"}
)
memory.clear() # 清空记忆
---
b.摘要记忆
a.功能说明
ConversationSummaryMemory通过LLM定期总结对话历史,压缩长对话内容。当对话超过一定长度时自动触发摘要,保留关键信息丢弃冗余细节,有效控制Token消耗。适用于长对话、客服场景等需要保持上下文但又要控制成本的应用。可配置摘要触发阈值、摘要提示词模板、摘要LLM模型。
b.代码示例
---
from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI
# 创建摘要记忆
memory = ConversationSummaryMemory(
llm=OpenAI(temperature=0),
max_token_limit=100 # 超过100 Token触发摘要
)
conversation = ConversationChain(
llm=OpenAI(temperature=0),
memory=memory
)
# 长对话测试
conversation.predict(input="我在北京工作,是一名软件工程师")
conversation.predict(input="我主要使用Python和Go语言")
conversation.predict(input="我最近在学习AI和大模型技术")
# 查看摘要
print(memory.load_memory_variables({}))
# {'history': '用户是北京的软件工程师,使用Python和Go,正在学习AI技术'}
---
02.持久化存储
a.向量记忆
a.功能说明
VectorStoreRetrieverMemory基于向量相似度检索历史对话,不是按时间顺序返回而是返回最相关的历史片段。将每轮对话转换为向量存储,查询时根据当前输入检索相似对话。适用于需要引用长期历史、跨会话记忆的场景,如个性化推荐、长期用户画像维护。
b.代码示例
---
from langchain.memory import VectorStoreRetrieverMemory
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Milvus
# 创建向量记忆
vectorstore = Milvus(
embedding_function=OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"},
collection_name="conversation_memory"
)
memory = VectorStoreRetrieverMemory(
retriever=vectorstore.as_retriever(search_kwargs={"k": 2})
)
# 保存对话
memory.save_context(
{"input": "我喜欢Python编程"},
{"output": "Python是很好的选择"}
)
memory.save_context(
{"input": "推荐一本机器学习的书"},
{"output": "推荐《机器学习实战》"}
)
# 基于相似度检索
relevant_history = memory.load_memory_variables(
{"input": "有什么编程语言推荐?"}
)
print(relevant_history) # 会返回Python相关的历史
---
b.数据库持久化
a.功能说明
LangChain支持将记忆持久化到Redis、PostgreSQL、MongoDB等数据库,实现跨会话、跨进程的记忆共享。通过session_id区分不同用户或会话,支持记忆过期、容量限制、并发访问等企业级特性。可结合向量数据库实现混合存储,结构化数据存关系数据库,对话内容存向量数据库。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
# 使用Redis持久化
message_history = RedisChatMessageHistory(
url="redis://localhost:6379",
session_id="user_12345"
)
memory = ConversationBufferMemory(
chat_memory=message_history,
return_messages=True
)
# 多进程/多机器共享记忆
conversation = ConversationChain(
llm=OpenAI(),
memory=memory
)
# PostgreSQL持久化
from langchain.memory.chat_message_histories import PostgresChatMessageHistory
message_history = PostgresChatMessageHistory(
connection_string="postgresql://user:pass@localhost/dbname",
session_id="user_12345"
)
# 达梦数据库适配
from sqlalchemy import create_engine
engine = create_engine("dm://user:pass@localhost:5236/dbname")
# 自定义SQLChatMessageHistory子类适配达梦数据库
---
3.5 Agents代理
01.Agent架构
a.ReAct范式
a.功能说明
Agent基于ReAct推理框架,循环执行Reasoning推理、Action行动、Observation观察三个步骤。LLM根据任务目标和当前状态决策下一步行动,可选择调用工具、返回答案或请求更多信息。通过思维链Thought记录推理过程,支持复杂任务分解和多步骤规划,具备自主决策和错误纠正能力。
b.代码示例
---
from langchain.agents import initialize_agent, AgentType
from langchain.agents import Tool
from langchain.llms import OpenAI
# 定义工具
def search_tool(query: str) -> str:
return f"搜索结果:{query}的相关信息"
def calculator_tool(expression: str) -> str:
try:
result = eval(expression)
return f"计算结果:{result}"
except:
return "计算错误"
tools = [
Tool(
name="Search",
func=search_tool,
description="搜索工具,用于查找信息"
),
Tool(
name="Calculator",
func=calculator_tool,
description="计算器,用于数学计算"
)
]
# 创建Agent
llm = OpenAI(temperature=0)
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# Agent自主决策
result = agent.run("2023年世界杯冠军是谁?他们夺冠多少次了?")
# Agent会先用Search查询,再用Calculator计算
---
b.工具使用
a.功能说明
LangChain提供丰富的预置工具库,包括搜索引擎、API调用、数据库查询、文件操作、代码执行等。支持自定义工具通过Tool类封装任意Python函数,定义工具名称、描述、参数schema等元信息。Agent根据工具描述自主选择合适工具,可并行调用多个工具、嵌套使用工具、处理工具错误。
b.代码示例
---
from langchain.agents import Tool
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from typing import Optional, Type
# 方式1:简单函数封装
def get_weather(city: str) -> str:
# 实际调用天气API
return f"{city}今天晴天,25度"
weather_tool = Tool(
name="Weather",
func=get_weather,
description="获取城市天气,输入城市名称"
)
# 方式2:带参数验证的工具
class WeatherInput(BaseModel):
city: str = Field(description="城市名称")
unit: str = Field(default="celsius", description="温度单位")
class WeatherTool(BaseTool):
name = "weather"
description = "获取指定城市的天气信息"
args_schema: Type[BaseModel] = WeatherInput
def _run(self, city: str, unit: str = "celsius") -> str:
return f"{city}天气:25{unit}"
async def _arun(self, city: str, unit: str = "celsius") -> str:
return self._run(city, unit)
# 集成到Agent
tools = [WeatherTool()]
agent = initialize_agent(
tools,
llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
)
---
02.Agent类型
a.对话Agent
a.功能说明
ConversationalAgent专为多轮对话场景设计,整合记忆管理和工具使用能力。支持上下文理解、指代消解、对话历史引用,可在对话过程中动态调用工具获取信息。适用于智能客服、个人助理等需要保持对话连贯性的交互式应用,可配置记忆类型、工具列表、决策策略。
b.代码示例
---
from langchain.agents import initialize_agent, AgentType
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
# 创建记忆
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
# 创建对话Agent
agent = initialize_agent(
tools,
ChatOpenAI(temperature=0),
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,
verbose=True
)
# 多轮对话
agent.run("我想去北京旅游")
agent.run("那里的天气怎么样?") # "那里"会被理解为北京
agent.run("推荐几个景点")
# 查看对话历史
print(memory.chat_memory.messages)
---
b.结构化Agent
a.功能说明
StructuredChatAgent支持调用需要复杂参数的工具,可处理嵌套对象、数组、可选参数等结构化数据。通过JSON Schema定义工具参数格式,Agent自动解析和验证参数有效性。适用于API调用、数据库操作、文件处理等需要精确参数控制的场景,相比普通Agent更适合企业级集成。
b.代码示例
---
from langchain.agents import AgentType
from langchain.tools import StructuredTool
from pydantic import BaseModel, Field
# 定义复杂参数结构
class SearchParams(BaseModel):
query: str = Field(description="搜索关键词")
filters: dict = Field(description="过滤条件")
limit: int = Field(default=10, description="返回结果数量")
def advanced_search(query: str, filters: dict, limit: int = 10) -> str:
return f"搜索'{query}',过滤条件{filters},返回{limit}条结果"
# 创建结构化工具
search_tool = StructuredTool.from_function(
func=advanced_search,
name="AdvancedSearch",
description="高级搜索工具,支持复杂过滤",
args_schema=SearchParams
)
# 使用结构化Agent
agent = initialize_agent(
[search_tool],
ChatOpenAI(temperature=0),
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
result = agent.run(
"搜索Python相关内容,只要2023年之后的,返回5条"
)
---
3.6 Callbacks回调
01.回调机制
a.生命周期钩子
a.功能说明
Callbacks提供组件生命周期监控能力,可在LLM调用开始、结束、错误等关键节点插入自定义逻辑。支持on_llm_start、on_llm_end、on_chain_start等20多种钩子函数,涵盖LLM、Chain、Tool、Agent等所有组件类型。通过Callbacks实现日志记录、性能监控、成本统计、审计合规等横切关注点,无需侵入业务代码。
b.代码示例
---
from langchain.callbacks.base import BaseCallbackHandler
from langchain.schema import LLMResult
from typing import Dict, List, Any
class CustomCallbackHandler(BaseCallbackHandler):
"""自定义回调处理器"""
def on_llm_start(
self, serialized: Dict[str, Any], prompts: List[str], **kwargs
) -> None:
print(f"[LLM开始] 提示词数量: {len(prompts)}")
def on_llm_end(self, response: LLMResult, **kwargs) -> None:
total_tokens = response.llm_output.get("token_usage", {}).get("total_tokens", 0)
print(f"[LLM结束] Token消耗: {total_tokens}")
def on_llm_error(self, error: Exception, **kwargs) -> None:
print(f"[LLM错误] {str(error)}")
def on_chain_start(
self, serialized: Dict[str, Any], inputs: Dict[str, Any], **kwargs
) -> None:
print(f"[Chain开始] 输入: {inputs}")
def on_agent_action(self, action, **kwargs) -> None:
print(f"[Agent行动] 工具: {action.tool}, 输入: {action.tool_input}")
# 使用回调
from langchain.llms import OpenAI
llm = OpenAI(callbacks=[CustomCallbackHandler()])
result = llm("你好")
---
b.流式回调
a.功能说明
StreamingCallbackHandler支持流式输出场景,在LLM生成Token时实时触发回调。可用于构建打字机效果的UI、实时内容审查、渐进式处理等场景。支持on_llm_new_token钩子接收每个新生成的Token,可累积处理、条件中断、动态反馈。配合异步调用实现高性能流式处理。
b.代码示例
---
from langchain.callbacks.streaming_stdout import StreamingStdOutCallbackHandler
from langchain.callbacks.base import BaseCallbackHandler
from langchain.chat_models import ChatOpenAI
# 方式1:使用预置流式处理器
llm = ChatOpenAI(
streaming=True,
callbacks=[StreamingStdOutCallbackHandler()]
)
response = llm.predict("写一首诗")
# Token会逐个打印到控制台
# 方式2:自定义流式处理
class CustomStreamHandler(BaseCallbackHandler):
def __init__(self):
self.tokens = []
def on_llm_new_token(self, token: str, **kwargs) -> None:
self.tokens.append(token)
print(token, end="", flush=True)
# 实时内容审查
if any(word in token for word in ["敏感词1", "敏感词2"]):
raise ValueError("检测到敏感内容")
handler = CustomStreamHandler()
llm = ChatOpenAI(streaming=True, callbacks=[handler])
try:
response = llm.predict("讲个故事")
except ValueError as e:
print(f"\n中断:{e}")
---
02.可观测性
a.追踪集成
a.功能说明
LangChain通过Callbacks集成LangSmith追踪平台,自动记录所有组件调用的输入输出、耗时、Token消耗、错误信息。支持链路可视化、性能分析、成本统计、版本对比等功能。可配置采样率、过滤规则、数据脱敏策略,满足生产环境的可观测性需求。
b.代码示例
---
import os
# 启用LangSmith追踪
os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = "ls-xxx"
os.environ["LANGCHAIN_PROJECT"] = "production"
# 所有调用自动追踪
from langchain.chains import LLMChain
chain = LLMChain(llm=llm, prompt=prompt)
result = chain.run(input="test")
# 自动上报到LangSmith平台
# 自定义追踪标签
from langchain.callbacks import tracing_v2_enabled
with tracing_v2_enabled(project_name="experiment-v2") as cb:
result = chain.run(
input="test",
tags=["version:v2", "user:12345"],
metadata={"session_id": "abc"}
)
---
b.监控告警
a.功能说明
通过自定义Callbacks实现业务监控和告警能力,可统计调用次数、成功率、平均耗时等指标,集成Prometheus、Grafana等监控系统。支持设置阈值触发告警,如Token消耗超限、响应时间过长、错误率过高等。可接入企业现有的运维监控体系,实现统一的可观测性管理。
b.代码示例
---
from langchain.callbacks.base import BaseCallbackHandler
from prometheus_client import Counter, Histogram, Gauge
import time
# Prometheus指标
llm_calls = Counter("llm_calls_total", "LLM调用总数", ["model", "status"])
llm_duration = Histogram("llm_duration_seconds", "LLM调用耗时", ["model"])
llm_tokens = Gauge("llm_tokens_current", "当前Token消耗", ["model"])
class MonitoringCallbackHandler(BaseCallbackHandler):
def __init__(self):
self.start_time = None
def on_llm_start(self, serialized, prompts, **kwargs):
self.start_time = time.time()
model = serialized.get("name", "unknown")
llm_calls.labels(model=model, status="started").inc()
def on_llm_end(self, response, **kwargs):
duration = time.time() - self.start_time
model = response.llm_output.get("model_name", "unknown")
tokens = response.llm_output.get("token_usage", {}).get("total_tokens", 0)
llm_duration.labels(model=model).observe(duration)
llm_tokens.labels(model=model).set(tokens)
llm_calls.labels(model=model, status="success").inc()
# 告警检查
if tokens > 3000:
self._send_alert(f"Token消耗过高: {tokens}")
if duration > 10:
self._send_alert(f"响应时间过长: {duration}s")
def _send_alert(self, message):
# 发送钉钉/企业微信告警
print(f"[告警] {message}")
# 使用监控
llm = OpenAI(callbacks=[MonitoringCallbackHandler()])
---
4 数据连接
4.1 Document Loaders
01.文档加载器
a.通用加载器
a.功能说明
LangChain提供100多种文档加载器,支持PDF、Word、Excel、HTML、Markdown、JSON等常见格式。UnstructuredFileLoader可自动识别文件类型并提取文本内容,处理表格、图片、公式等复杂结构。支持批量加载、目录遍历、文件过滤、编码检测等实用功能,简化文档预处理流程。
b.代码示例
---
from langchain.document_loaders import (
PyPDFLoader, UnstructuredWordDocumentLoader,
CSVLoader, UnstructuredHTMLLoader, TextLoader
)
# PDF加载
pdf_loader = PyPDFLoader("document.pdf")
pdf_docs = pdf_loader.load()
# Word加载
word_loader = UnstructuredWordDocumentLoader("document.docx")
word_docs = word_loader.load()
# CSV加载
csv_loader = CSVLoader(
file_path="data.csv",
encoding="utf-8",
csv_args={"delimiter": ","}
)
csv_docs = csv_loader.load()
# 目录批量加载
from langchain.document_loaders import DirectoryLoader
loader = DirectoryLoader(
"data/",
glob="**/*.txt",
loader_cls=TextLoader,
loader_kwargs={"encoding": "utf-8"}
)
docs = loader.load()
print(f"加载{len(docs)}个文档")
---
b.信创适配
a.功能说明
在信创环境下,LangChain支持集成国产OCR引擎如PaddleOCR、RapidOCR处理扫描文档和图片。可对接达梦数据库、金仓数据库等国产数据库加载结构化数据。支持国密算法处理加密文档,适配国产操作系统的文件系统特性。提供自定义加载器扩展点,便于集成企业内部文档系统。
b.代码示例
---
# 集成PaddleOCR
from paddleocr import PaddleOCR
from langchain.document_loaders.base import BaseLoader
from langchain.schema import Document
class PaddleOCRLoader(BaseLoader):
def __init__(self, image_path: str):
self.image_path = image_path
self.ocr = PaddleOCR(use_angle_cls=True, lang='ch')
def load(self):
result = self.ocr.ocr(self.image_path, cls=True)
text = "\n".join([line[1][0] for line in result[0]])
return [Document(page_content=text, metadata={"source": self.image_path})]
# 加载图片文本
loader = PaddleOCRLoader("scan.jpg")
docs = loader.load()
# 达梦数据库加载
from langchain.document_loaders import SQLDatabaseLoader
loader = SQLDatabaseLoader(
"SELECT content FROM documents",
db_uri="dm://user:pass@localhost:5236/dbname"
)
docs = loader.load()
---
02.Web加载器
a.网页抓取
a.功能说明
WebBaseLoader支持从URL加载网页内容,自动处理HTML标签、提取正文、过滤广告导航等噪音。支持配置请求头、代理、超时、重试等参数,适应各种网站的反爬策略。可使用BeautifulSoup、Playwright等后端引擎,支持JavaScript渲染、动态内容加载、表单提交等复杂场景。
b.代码示例
---
from langchain.document_loaders import WebBaseLoader
# 单页加载
loader = WebBaseLoader("https://python.langchain.com/docs/")
docs = loader.load()
# 批量加载
urls = [
"https://example.com/page1",
"https://example.com/page2"
]
loader = WebBaseLoader(urls)
docs = loader.load()
# 使用Playwright渲染JavaScript
from langchain.document_loaders import PlaywrightURLLoader
loader = PlaywrightURLLoader(
urls=["https://spa-website.com"],
remove_selectors=["header", "footer", ".ads"]
)
docs = loader.load()
# 配置代理和请求头
loader = WebBaseLoader(
web_path="https://example.com",
header_template={
"User-Agent": "Mozilla/5.0",
"Authorization": "Bearer token"
},
requests_kwargs={"proxies": {"http": "http://proxy:8080"}}
)
---
b.API数据源
a.功能说明
LangChain支持从RESTful API、GraphQL、数据库等数据源加载结构化数据。提供专用加载器对接Notion、Confluence、Jira等知识管理工具,支持增量同步、权限控制、版本管理。可自定义加载器封装企业内部API,实现统一的数据接入层,便于构建企业级知识库。
b.代码示例
---
# Notion加载
from langchain.document_loaders import NotionDirectoryLoader
loader = NotionDirectoryLoader("notion_export/")
docs = loader.load()
# 自定义API加载器
import requests
from langchain.docstore.document import Document
class CustomAPILoader(BaseLoader):
def __init__(self, api_url: str, api_key: str):
self.api_url = api_url
self.api_key = api_key
def load(self):
response = requests.get(
self.api_url,
headers={"Authorization": f"Bearer {self.api_key}"}
)
data = response.json()
documents = []
for item in data["items"]:
doc = Document(
page_content=item["content"],
metadata={
"id": item["id"],
"title": item["title"],
"created_at": item["created_at"]
}
)
documents.append(doc)
return documents
# 使用自定义加载器
loader = CustomAPILoader(
api_url="https://api.company.com/knowledge",
api_key="your-api-key"
)
docs = loader.load()
---
4.2 Text Splitters
01.分割策略
a.字符分割
a.功能说明
RecursiveCharacterTextSplitter按字符长度递归分割文本,优先使用段落、句子、单词等自然边界,避免切断语义完整的内容。支持配置chunk_size控制分块大小、chunk_overlap设置块间重叠以保持上下文连贯性。适用于长文档分块、向量化存储前处理,是最常用的分割策略。
b.代码示例
---
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 创建分割器
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每块最大字符数
chunk_overlap=200, # 块间重叠字符数
length_function=len, # 长度计算函数
separators=["\n\n", "\n", "。", ",", " ", ""] # 分割优先级
)
# 分割文本
text = "这是一篇很长的文章..." * 100
chunks = text_splitter.split_text(text)
print(f"分割成{len(chunks)}个块")
# 分割文档并保留元数据
from langchain.schema import Document
docs = [Document(page_content=text, metadata={"source": "doc.txt"})]
split_docs = text_splitter.split_documents(docs)
# 每个分块保留原始元数据
for doc in split_docs:
print(doc.metadata) # {'source': 'doc.txt'}
---
b.Token分割
a.功能说明
TokenTextSplitter按Token数量分割,确保每块不超过模型上下文窗口限制。使用tiktoken库精确计算Token数,支持不同模型的tokenizer如GPT-3.5、GPT-4、Claude等。相比字符分割更精准,避免Token超限导致调用失败,适用于需要严格控制Token消耗的场景。
b.代码示例
---
from langchain.text_splitter import TokenTextSplitter
# 按Token分割
text_splitter = TokenTextSplitter(
chunk_size=500, # 每块最大Token数
chunk_overlap=50, # 块间重叠Token数
encoding_name="cl100k_base" # GPT-4的tokenizer
)
chunks = text_splitter.split_text(text)
# 使用特定模型的tokenizer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm3-6b")
text_splitter = TokenTextSplitter(
chunk_size=500,
chunk_overlap=50,
tokenizer=tokenizer.encode
)
# 验证Token数
for chunk in chunks:
token_count = len(tokenizer.encode(chunk))
print(f"Token数: {token_count}")
---
02.语义分割
a.句子分割
a.功能说明
SentenceTransformersTokenTextSplitter基于句子边界分割,保证每个分块都是完整的句子集合,避免破坏语义完整性。使用spaCy或nltk进行句子识别,支持多语言场景。适用于需要保持语义完整性的场景如摘要生成、问答系统,生成的分块更适合向量化和检索。
b.代码示例
---
from langchain.text_splitter import SpacyTextSplitter
# 基于句子分割
text_splitter = SpacyTextSplitter(
chunk_size=1000,
pipeline="zh_core_web_sm" # 中文模型
)
chunks = text_splitter.split_text(text)
# 使用nltk
import nltk
nltk.download('punkt')
from langchain.text_splitter import NLTKTextSplitter
text_splitter = NLTKTextSplitter(chunk_size=1000)
chunks = text_splitter.split_text(text)
# 每个chunk都是完整句子
for chunk in chunks:
sentences = nltk.sent_tokenize(chunk)
print(f"包含{len(sentences)}个句子")
---
b.语义相似度分割
a.功能说明
SemanticChunker基于语义相似度动态分割文本,将语义相关的内容聚合在一起,而不是机械按长度切分。使用Embedding模型计算句子间相似度,当相似度低于阈值时进行分割。生成的分块语义更连贯,检索效果更好,但计算开销较大,适用于高价值文档处理。
b.代码示例
---
from langchain_experimental.text_splitter import SemanticChunker
from langchain.embeddings import OpenAIEmbeddings
# 创建语义分割器
text_splitter = SemanticChunker(
embeddings=OpenAIEmbeddings(),
breakpoint_threshold_type="percentile", # 分割阈值类型
breakpoint_threshold_amount=90 # 阈值参数
)
# 语义分割
chunks = text_splitter.split_text(text)
# 自定义相似度计算
from langchain.text_splitter import TextSplitter
import numpy as np
class CustomSemanticSplitter(TextSplitter):
def __init__(self, embeddings, threshold=0.7):
self.embeddings = embeddings
self.threshold = threshold
def split_text(self, text: str):
sentences = text.split("。")
vectors = self.embeddings.embed_documents(sentences)
chunks = []
current_chunk = [sentences[0]]
for i in range(1, len(sentences)):
similarity = np.dot(vectors[i-1], vectors[i])
if similarity < self.threshold:
chunks.append("。".join(current_chunk))
current_chunk = [sentences[i]]
else:
current_chunk.append(sentences[i])
chunks.append("。".join(current_chunk))
return chunks
---
4.3 Embeddings嵌入
01.Embedding模型
a.OpenAI Embeddings
a.功能说明
OpenAIEmbeddings是LangChain默认的向量化方案,使用text-embedding-ada-002模型生成1536维向量。支持批量向量化、速率限制、自动重试等特性,适用于英文和多语言场景。性能稳定、质量高,但需要调用外部API,存在网络延迟和成本问题,在信创环境下可能受限。
b.代码示例
---
from langchain.embeddings import OpenAIEmbeddings
# 创建Embeddings
embeddings = OpenAIEmbeddings(
model="text-embedding-ada-002",
openai_api_key="sk-xxx"
)
# 单文本向量化
text = "向量数据库是存储和检索高维向量的专用数据库"
vector = embeddings.embed_query(text)
print(f"向量维度: {len(vector)}") # 1536
# 批量向量化
texts = [
"LangChain是AI应用开发框架",
"Milvus是开源向量数据库",
"RAG是检索增强生成技术"
]
vectors = embeddings.embed_documents(texts)
print(f"生成{len(vectors)}个向量")
# 配置参数
embeddings = OpenAIEmbeddings(
model="text-embedding-3-large",
dimensions=3072, # 自定义维度
chunk_size=1000, # 批量大小
max_retries=3 # 重试次数
)
---
b.本地Embeddings
a.功能说明
HuggingFaceEmbeddings支持加载开源模型如BERT、Sentence-Transformers等,可在本地或内网环境运行,无需依赖外部API。支持中文模型如text2vec-large-chinese、m3e-base等,适合信创环境和隐私敏感场景。可配置模型路径、设备类型、批处理大小等参数,实现灵活部署。
b.代码示例
---
from langchain.embeddings import HuggingFaceEmbeddings
# 使用本地模型
embeddings = HuggingFaceEmbeddings(
model_name="shibing624/text2vec-base-chinese",
model_kwargs={"device": "cuda"}, # 使用GPU
encode_kwargs={"normalize_embeddings": True}
)
# 向量化
text = "这是一段中文文本"
vector = embeddings.embed_query(text)
print(f"向量维度: {len(vector)}") # 768
# 昇腾NPU适配
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-large-zh",
model_kwargs={"device": "npu:0"} # 使用昇腾NPU
)
# 自定义模型路径
embeddings = HuggingFaceEmbeddings(
model_name="/path/to/local/model",
cache_folder="/path/to/cache"
)
# Ollama集成
from langchain_community.embeddings import OllamaEmbeddings
embeddings = OllamaEmbeddings(
model="nomic-embed-text",
base_url="http://localhost:11434"
)
---
02.向量缓存
a.缓存策略
a.功能说明
CacheBackedEmbeddings提供向量缓存机制,避免重复向量化相同文本,显著降低API调用成本和计算开销。支持多种存储后端如Redis、文件系统、SQLite等,可配置过期时间、容量限制、缓存策略。在处理大规模文档或频繁更新场景下,缓存可将性能提升10倍以上。
b.代码示例
---
from langchain.embeddings import CacheBackedEmbeddings
from langchain.storage import LocalFileStore
from langchain.embeddings import OpenAIEmbeddings
# 文件系统缓存
store = LocalFileStore("./cache/embeddings")
cached_embeddings = CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings=OpenAIEmbeddings(),
document_embedding_cache=store,
namespace="openai-ada-002"
)
# 首次调用会向量化并缓存
vectors1 = cached_embeddings.embed_documents(texts)
# 再次调用直接从缓存读取
vectors2 = cached_embeddings.embed_documents(texts)
# Redis缓存
from langchain.storage import RedisStore
import redis
redis_client = redis.Redis(host="localhost", port=6379, db=0)
store = RedisStore(client=redis_client)
cached_embeddings = CacheBackedEmbeddings.from_bytes_store(
underlying_embeddings=OpenAIEmbeddings(),
document_embedding_cache=store,
namespace="embeddings",
query_embedding_cache=store # 查询也缓存
)
---
b.批量优化
a.功能说明
LangChain支持批量向量化优化,通过合并多个请求减少网络往返和API调用次数。可配置批处理大小、并发数、超时时间等参数,平衡吞吐量和延迟。支持异步批处理,在等待API响应期间继续处理其他任务,提升整体处理效率。
b.代码示例
---
from langchain.embeddings import OpenAIEmbeddings
import asyncio
embeddings = OpenAIEmbeddings(
chunk_size=100, # 每批最多100个文本
max_retries=3,
request_timeout=30
)
# 大规模文本向量化
texts = ["文本" + str(i) for i in range(1000)]
# 同步批处理
vectors = embeddings.embed_documents(texts)
# 异步批处理
async def async_embed():
vectors = await embeddings.aembed_documents(texts)
return vectors
vectors = asyncio.run(async_embed())
# 自定义批处理逻辑
def batch_embed(texts, batch_size=100):
all_vectors = []
for i in range(0, len(texts), batch_size):
batch = texts[i:i+batch_size]
vectors = embeddings.embed_documents(batch)
all_vectors.extend(vectors)
print(f"处理进度: {i+len(batch)}/{len(texts)}")
return all_vectors
vectors = batch_embed(texts, batch_size=50)
---
4.4 Vector Stores
01.向量存储集成
a.Milvus集成
a.功能说明
Milvus是LangChain推荐的生产级向量数据库,支持百万到十亿级向量规模,提供毫秒级检索性能。LangChain提供原生Milvus集成,支持Collection管理、索引配置、混合检索、标量过滤等高级特性。可配置连接池、分区策略、副本数量,适用于大规模RAG应用、推荐系统等企业场景。
b.代码示例
---
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
from langchain.document_loaders import TextLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 加载并分割文档
loader = TextLoader("data/knowledge.txt")
documents = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000)
docs = text_splitter.split_documents(documents)
# 创建向量库
embeddings = OpenAIEmbeddings()
vectorstore = Milvus.from_documents(
documents=docs,
embedding=embeddings,
connection_args={
"host": "localhost",
"port": "19530"
},
collection_name="langchain_collection",
drop_old=True # 删除旧Collection
)
# 相似度搜索
query = "什么是向量数据库?"
results = vectorstore.similarity_search(query, k=3)
# 带分数搜索
results_with_scores = vectorstore.similarity_search_with_score(query, k=3)
for doc, score in results_with_scores:
print(f"Score: {score}, Content: {doc.page_content[:100]}")
# 配置索引参数
vectorstore = Milvus.from_documents(
documents=docs,
embedding=embeddings,
connection_args={"uri": "http://localhost:19530"},
index_params={
"metric_type": "IP",
"index_type": "IVF_FLAT",
"params": {"nlist": 1024}
}
)
---
b.轻量级方案
a.功能说明
Chroma是轻量级向量数据库,支持嵌入式运行无需独立部署,适合开发测试和小规模应用。FAISS是Meta开源的向量检索库,提供高性能CPU/GPU加速,适合离线批处理场景。两者都提供LangChain集成,可快速构建原型,但功能和扩展性不如Milvus等专业向量数据库。
b.代码示例
---
# Chroma - 嵌入式向量库
from langchain.vectorstores import Chroma
vectorstore = Chroma.from_documents(
documents=docs,
embedding=embeddings,
persist_directory="./chroma_db" # 持久化目录
)
# 查询
results = vectorstore.similarity_search("AI应用", k=5)
# FAISS - 高性能检索
from langchain.vectorstores import FAISS
vectorstore = FAISS.from_documents(docs, embeddings)
# 保存和加载
vectorstore.save_local("faiss_index")
new_vectorstore = FAISS.load_local("faiss_index", embeddings)
# GPU加速
import faiss
res = faiss.StandardGpuResources()
gpu_index = faiss.index_cpu_to_gpu(res, 0, vectorstore.index)
# pgvector - PostgreSQL扩展
from langchain.vectorstores import PGVector
vectorstore = PGVector.from_documents(
documents=docs,
embedding=embeddings,
connection_string="postgresql://user:pass@localhost/vectordb"
)
---
02.检索优化
a.混合检索
a.功能说明
混合检索结合向量相似度检索和关键词搜索,提升检索精度和召回率。向量检索擅长语义相似性匹配,关键词检索擅长精确匹配,两者互补可覆盖更多场景。LangChain支持配置不同检索策略的权重,可根据业务需求调整平衡点,适用于专业领域、多语言场景。
b.代码示例
---
from langchain.retrievers import BM25Retriever, EnsembleRetriever
# 向量检索器
vectorstore = Milvus.from_documents(docs, embeddings)
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# BM25关键词检索器
bm25_retriever = BM25Retriever.from_documents(docs)
bm25_retriever.k = 5
# 混合检索器
ensemble_retriever = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.7, 0.3] # 向量检索70%权重,关键词30%
)
# 检索
results = ensemble_retriever.get_relevant_documents("向量数据库原理")
# 自定义混合策略
from langchain.schema import Document
from typing import List
def custom_hybrid_search(query: str, vector_weight=0.7) -> List[Document]:
# 向量检索
vector_results = vectorstore.similarity_search_with_score(query, k=10)
# 关键词检索
keyword_results = bm25_retriever.get_relevant_documents(query)
# 融合结果
combined = {}
for doc, score in vector_results:
combined[doc.page_content] = score * vector_weight
for doc in keyword_results:
if doc.page_content in combined:
combined[doc.page_content] += (1 - vector_weight)
else:
combined[doc.page_content] = (1 - vector_weight)
# 排序返回
sorted_docs = sorted(combined.items(), key=lambda x: x[1], reverse=True)
return [Document(page_content=content) for content, score in sorted_docs[:5]]
---
b.元数据过滤
a.功能说明
LangChain支持基于元数据的过滤检索,可在向量检索基础上添加时间、类别、作者等结构化条件。通过filter参数传递过滤条件,向量数据库会先过滤再检索,减少无效结果提升精度。支持复杂过滤表达式如范围查询、正则匹配、逻辑组合,适用于多租户、权限控制、时效性要求等场景。
b.代码示例
---
from datetime import datetime
# 添加元数据
docs_with_metadata = [
Document(
page_content="LangChain教程",
metadata={
"source": "doc1.pdf",
"page": 1,
"category": "tutorial",
"date": "2024-01-01",
"author": "张三"
}
),
Document(
page_content="Milvus实战",
metadata={
"source": "doc2.pdf",
"page": 5,
"category": "practice",
"date": "2024-02-01",
"author": "李四"
}
)
]
vectorstore = Milvus.from_documents(
docs_with_metadata,
embeddings,
connection_args={"host": "localhost", "port": "19530"}
)
# 基础元数据过滤
results = vectorstore.similarity_search(
"教程",
k=5,
expr='category == "tutorial"' # Milvus表达式
)
# 复杂过滤条件
results = vectorstore.similarity_search(
"实战",
k=5,
expr='date >= "2024-01-01" and author in ["张三", "李四"]'
)
# 其他向量库的过滤语法
# Chroma
results = chroma_vectorstore.similarity_search(
"查询",
k=5,
filter={"category": "tutorial"}
)
# Qdrant
from qdrant_client.models import Filter, FieldCondition, MatchValue
results = qdrant_vectorstore.similarity_search(
"查询",
k=5,
filter=Filter(
must=[
FieldCondition(
key="category",
match=MatchValue(value="tutorial")
)
]
)
)
---
4.5 Retrievers检索器
01.检索器类型
a.向量检索器
a.功能说明
VectorStoreRetriever是最基础的检索器,封装向量数据库的相似度检索能力。支持配置检索数量k、相似度阈值score_threshold、检索类型search_type等参数。提供统一的检索接口,屏蔽底层向量数据库差异,便于切换存储后端。可作为其他高级检索器的基础组件。
b.代码示例
---
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
vectorstore = Milvus.from_documents(
docs,
OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"}
)
# 创建检索器
retriever = vectorstore.as_retriever(
search_type="similarity", # 检索类型
search_kwargs={
"k": 5, # 返回5个结果
"score_threshold": 0.7 # 相似度阈值
}
)
# 检索
results = retriever.get_relevant_documents("什么是RAG?")
# MMR多样性检索
retriever = vectorstore.as_retriever(
search_type="mmr", # 最大边际相关性
search_kwargs={
"k": 5,
"fetch_k": 20, # 先检索20个候选
"lambda_mult": 0.5 # 多样性参数
}
)
# 相似度阈值检索
retriever = vectorstore.as_retriever(
search_type="similarity_score_threshold",
search_kwargs={
"score_threshold": 0.8, # 只返回分数>0.8的
"k": 10
}
)
---
b.上下文压缩检索器
a.功能说明
ContextualCompressionRetriever在检索基础上添加内容压缩,只保留与查询相关的片段。使用LLM或其他模型提取关键信息,过滤冗余内容,减少后续处理的Token消耗。适用于检索长文档、多文档场景,可显著提升检索质量和降低成本,是生产环境的重要优化手段。
b.代码示例
---
from langchain.retrievers import ContextualCompressionRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
from langchain.llms import OpenAI
# 创建压缩器
llm = OpenAI(temperature=0)
compressor = LLMChainExtractor.from_llm(llm)
# 包装基础检索器
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 10})
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=base_retriever
)
# 检索并压缩
query = "LangChain如何集成Milvus?"
compressed_docs = compression_retriever.get_relevant_documents(query)
# 对比压缩前后
original_docs = base_retriever.get_relevant_documents(query)
print(f"原始文档数: {len(original_docs)}")
print(f"压缩后文档数: {len(compressed_docs)}")
# 使用Embedding过滤器压缩
from langchain.retrievers.document_compressors import EmbeddingsFilter
embeddings_filter = EmbeddingsFilter(
embeddings=OpenAIEmbeddings(),
similarity_threshold=0.76 # 相似度阈值
)
compression_retriever = ContextualCompressionRetriever(
base_compressor=embeddings_filter,
base_retriever=base_retriever
)
---
02.高级检索策略
a.父文档检索器
a.功能说明
ParentDocumentRetriever采用两阶段检索策略,先检索小块提升精度,再返回完整父文档保证上下文完整。将文档分为小块存储到向量库,建立子块到父文档的映射关系,检索时根据子块匹配返回父文档。解决了细粒度检索和完整上下文的矛盾,适用于需要完整文档上下文的问答场景。
b.代码示例
---
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
from langchain.text_splitter import RecursiveCharacterTextSplitter
# 父文档存储
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
# 子块分割(用于向量检索)
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 父文档仓库
store = InMemoryStore()
# 创建检索器
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
# 添加文档
retriever.add_documents(docs)
# 检索 - 返回完整父文档
results = retriever.get_relevant_documents("RAG原理")
for doc in results:
print(f"文档长度: {len(doc.page_content)}") # 完整父文档
# 使用Redis存储父文档
from langchain.storage import RedisStore
store = RedisStore(
redis_url="redis://localhost:6379",
namespace="parent_docs"
)
retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter
)
---
b.多查询检索器
a.功能说明
MultiQueryRetriever通过LLM生成原始查询的多个变体,从不同角度检索文档后合并去重。解决了单一查询表达不准确的问题,通过查询扩展提升召回率。可配置查询生成数量、合并策略、去重算法,适用于用户查询意图模糊、需要全面检索的场景。
b.代码示例
---
from langchain.retrievers.multi_query import MultiQueryRetriever
from langchain.chat_models import ChatOpenAI
# 创建多查询检索器
llm = ChatOpenAI(temperature=0)
base_retriever = vectorstore.as_retriever(search_kwargs={"k": 3})
retriever = MultiQueryRetriever.from_llm(
retriever=base_retriever,
llm=llm
)
# 检索 - 自动生成多个查询变体
query = "向量数据库"
results = retriever.get_relevant_documents(query)
# 查看生成的查询变体
import logging
logging.basicConfig()
logging.getLogger("langchain.retrievers.multi_query").setLevel(logging.INFO)
# 自定义查询生成提示词
from langchain.prompts import PromptTemplate
QUERY_PROMPT = PromptTemplate(
input_variables=["question"],
template="""你是AI助手,任务是生成5个不同版本的查询语句。
原始查询: {question}
生成的查询变体:"""
)
retriever = MultiQueryRetriever.from_llm(
retriever=base_retriever,
llm=llm,
prompt=QUERY_PROMPT
)
# 自定义结果合并
from langchain.schema import Document
from typing import List
def custom_unique_union(docs_list: List[List[Document]]) -> List[Document]:
seen = set()
unique_docs = []
for docs in docs_list:
for doc in docs:
content = doc.page_content
if content not in seen:
seen.add(content)
unique_docs.append(doc)
return unique_docs
---
5 链式操作
5.1 LLMChain
01.基础用法
a.链的创建
a.功能说明
LLMChain是LangChain最基础的链组件,将Prompt和LLM组合成可复用单元。通过链式调用简化代码结构,支持参数验证、输出解析、错误处理等基础能力。可配置输出键、回调函数、内存组件,是构建复杂应用的基础模块,几乎所有高级链都基于LLMChain实现。
b.代码示例
---
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
# 创建提示模板
prompt = PromptTemplate(
input_variables=["product"],
template="为{product}写一句吸引人的广告语"
)
# 创建LLM
llm = OpenAI(temperature=0.9)
# 创建链
chain = LLMChain(llm=llm, prompt=prompt)
# 调用方式1: run
result = chain.run(product="智能手表")
print(result)
# 调用方式2: predict
result = chain.predict(product="无线耳机")
print(result)
# 调用方式3: __call__
result = chain({"product": "平板电脑"})
print(result["text"])
# 配置输出键
chain = LLMChain(
llm=llm,
prompt=prompt,
output_key="slogan" # 自定义输出键名
)
result = chain({"product": "智能音箱"})
print(result["slogan"])
---
b.批量处理
a.功能说明
LLMChain支持批量处理多个输入,提升处理效率和资源利用率。通过apply方法批量调用,自动管理并发和错误处理。支持配置批处理大小、超时时间、重试策略,适用于大规模数据处理场景如批量摘要、翻译、分类等任务。
b.代码示例
---
# 批量调用
inputs = [
{"product": "智能手表"},
{"product": "无线耳机"},
{"product": "平板电脑"},
{"product": "智能音箱"}
]
results = chain.apply(inputs)
for result in results:
print(result["text"])
# 异步批量调用
import asyncio
async def async_batch():
results = await chain.aapply(inputs)
return results
results = asyncio.run(async_batch())
# 生成多个输出
results = chain.generate(inputs)
for generation in results.generations:
print(generation[0].text)
print(f"Token使用: {generation[0].generation_info}")
---
02.链的组合
a.顺序链
a.功能说明
SequentialChain将多个链顺序连接,前一个链的输出作为后一个链的输入。通过input_variables和output_variables管理数据流,支持中间变量传递和结果聚合。适用于多步骤处理流程如先翻译再摘要、先分类再生成,SimpleSequentialChain适用于单输入单输出场景。
b.代码示例
---
from langchain.chains import SequentialChain
# 第一个链:翻译
translate_prompt = PromptTemplate(
input_variables=["text"],
template="将以下中文翻译成英文:{text}"
)
translate_chain = LLMChain(
llm=llm,
prompt=translate_prompt,
output_key="english_text"
)
# 第二个链:摘要
summary_prompt = PromptTemplate(
input_variables=["english_text"],
template="为以下英文文本写一个简短摘要:{english_text}"
)
summary_chain = LLMChain(
llm=llm,
prompt=summary_prompt,
output_key="summary"
)
# 组合成顺序链
overall_chain = SequentialChain(
chains=[translate_chain, summary_chain],
input_variables=["text"],
output_variables=["english_text", "summary"],
verbose=True
)
# 执行
result = overall_chain({
"text": "LangChain是一个强大的AI应用开发框架"
})
print(result["summary"])
# 简单顺序链
from langchain.chains import SimpleSequentialChain
simple_chain = SimpleSequentialChain(
chains=[translate_chain, summary_chain],
verbose=True
)
result = simple_chain.run("这是一段中文文本")
---
b.并行执行
a.功能说明
LangChain支持链的并行执行,多个独立任务同时处理提升效率。使用RunnableParallel或LCEL的字典语法定义并行结构,自动管理并发控制和结果汇总。适用于多角度分析、多模型对比、多数据源查询等需要并行处理的场景。
b.代码示例
---
from langchain.schema.runnable import RunnableParallel, RunnablePassthrough
# 定义多个处理链
summary_chain = LLMChain(llm=llm, prompt=summary_prompt, output_key="summary")
keywords_chain = LLMChain(llm=llm, prompt=keywords_prompt, output_key="keywords")
sentiment_chain = LLMChain(llm=llm, prompt=sentiment_prompt, output_key="sentiment")
# 并行执行
parallel_chain = RunnableParallel(
summary=summary_chain,
keywords=keywords_chain,
sentiment=sentiment_chain
)
result = parallel_chain.invoke({"text": "这是一篇文章..."})
print(result)
# {
# 'summary': '文章摘要...',
# 'keywords': ['关键词1', '关键词2'],
# 'sentiment': '积极'
# }
# LCEL字典语法
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
chain = {
"summary": ChatPromptTemplate.from_template("摘要:{text}") | llm,
"keywords": ChatPromptTemplate.from_template("关键词:{text}") | llm,
"original": RunnablePassthrough() # 原始输入
}
result = parallel_chain.invoke({"text": "文章内容"})
---
5.2 SequentialChain
01.顺序链组合
a.基本用法
a.功能说明
SequentialChain将多个LLMChain按顺序连接,前一个链的输出自动作为后一个链的输入。通过input_variables定义初始输入变量,output_variables指定最终输出变量,支持中间变量传递和多输出聚合。适用于需要多步骤处理的复杂任务,如数据清洗后分析、翻译后摘要、提取后分类等流程。
b.代码示例
---
from langchain.chains import LLMChain, SequentialChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
llm = OpenAI(temperature=0.7)
# 链1:提取关键信息
extract_template = """从以下文本中提取关键信息:
文本:{text}
关键信息:"""
extract_prompt = PromptTemplate(
input_variables=["text"],
template=extract_template
)
extract_chain = LLMChain(
llm=llm,
prompt=extract_prompt,
output_key="key_info"
)
# 链2:生成摘要
summary_template = """基于以下关键信息生成摘要:
关键信息:{key_info}
摘要:"""
summary_prompt = PromptTemplate(
input_variables=["key_info"],
template=summary_template
)
summary_chain = LLMChain(
llm=llm,
prompt=summary_prompt,
output_key="summary"
)
# 组合成顺序链
overall_chain = SequentialChain(
chains=[extract_chain, summary_chain],
input_variables=["text"],
output_variables=["key_info", "summary"],
verbose=True
)
# 执行
result = overall_chain({
"text": "LangChain是一个强大的AI应用开发框架,提供模块化组件..."
})
print("关键信息:", result["key_info"])
print("摘要:", result["summary"])
---
b.简单顺序链
a.功能说明
SimpleSequentialChain是顺序链的简化版本,专门用于单输入单输出场景。每个链只接收一个字符串输入并返回一个字符串输出,无需显式声明输入输出变量名。代码更简洁,适用于简单的流水线处理任务,如连续的文本转换、多级翻译、逐步精炼等场景。
b.代码示例
---
from langchain.chains import LLMChain, SimpleSequentialChain
# 链1:中文翻译成英文
translate_prompt = PromptTemplate(
input_variables=["text"],
template="将以下中文翻译成英文:\n{text}\n英文:"
)
translate_chain = LLMChain(llm=llm, prompt=translate_prompt)
# 链2:润色英文
polish_prompt = PromptTemplate(
input_variables=["text"],
template="润色以下英文,使其更加地道:\n{text}\n润色后:"
)
polish_chain = LLMChain(llm=llm, prompt=polish_prompt)
# 组合成简单顺序链
overall_chain = SimpleSequentialChain(
chains=[translate_chain, polish_chain],
verbose=True
)
# 执行
result = overall_chain.run("你好世界,欢迎使用LangChain")
print(result)
# 多步骤处理
chains = [
LLMChain(llm=llm, prompt=PromptTemplate.from_template("步骤1:{input}")),
LLMChain(llm=llm, prompt=PromptTemplate.from_template("步骤2:{input}")),
LLMChain(llm=llm, prompt=PromptTemplate.from_template("步骤3:{input}"))
]
pipeline = SimpleSequentialChain(chains=chains)
result = pipeline.run("原始输入")
---
02.高级组合
a.条件路由
a.功能说明
RouterChain根据输入内容动态选择执行路径,实现条件分支逻辑。通过LLM或自定义规则判断输入类型,路由到对应的专用链处理。支持多分支路由、默认链兜底、路由键映射等功能,适用于多意图识别、分类处理、动态工作流等场景。
b.代码示例
---
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.prompts import PromptTemplate
# 定义不同领域的专用链
physics_template = """你是物理学专家,请回答:{input}"""
physics_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(physics_template)
)
math_template = """你是数学专家,请回答:{input}"""
math_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template(math_template)
)
# 定义路由链信息
prompt_infos = [
{
"name": "physics",
"description": "适合回答物理学问题",
"prompt_template": physics_template
},
{
"name": "math",
"description": "适合回答数学问题",
"prompt_template": math_template
}
]
# 创建路由链
destination_chains = {
"physics": physics_chain,
"math": math_chain
}
default_chain = LLMChain(
llm=llm,
prompt=PromptTemplate.from_template("我不确定如何回答:{input}")
)
# 构建多提示链
chain = MultiPromptChain(
router_chain=LLMRouterChain.from_llms(llm, prompt_infos),
destination_chains=destination_chains,
default_chain=default_chain,
verbose=True
)
# 自动路由
result1 = chain.run("什么是牛顿第一定律?") # 路由到physics
result2 = chain.run("如何计算圆的面积?") # 路由到math
---
b.变量传递
a.功能说明
SequentialChain支持在链之间传递多个变量,实现复杂的数据流控制。可以保留中间结果供后续链使用,也可以从前面的链中提取部分变量传递给后续链。通过output_variables控制哪些变量暴露给外部,实现封装和抽象,适用于需要多个中间状态的复杂流程。
b.代码示例
---
from langchain.chains import SequentialChain, LLMChain
llm = OpenAI(temperature=0)
# 链1:生成产品描述
description_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=["product_name"],
template="为{product_name}写一段产品描述"
),
output_key="description"
)
# 链2:提取关键卖点
features_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=["description"],
template="从以下描述中提取3个关键卖点:\n{description}"
),
output_key="features"
)
# 链3:生成广告语
slogan_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=["product_name", "features"],
template="基于产品{product_name}和卖点{features},创作一句广告语"
),
output_key="slogan"
)
# 组合链,传递多个变量
overall_chain = SequentialChain(
chains=[description_chain, features_chain, slogan_chain],
input_variables=["product_name"],
output_variables=["description", "features", "slogan"],
verbose=True
)
# 执行,获取所有中间结果
result = overall_chain({"product_name": "智能手表"})
print("产品描述:", result["description"])
print("关键卖点:", result["features"])
print("广告语:", result["slogan"])
# 部分变量传递
partial_chain = SequentialChain(
chains=[description_chain, features_chain, slogan_chain],
input_variables=["product_name"],
output_variables=["slogan"], # 只输出最终结果
verbose=False
)
result = partial_chain({"product_name": "无线耳机"})
print(result["slogan"])
---
01.数据转换
a.自定义转换
a.功能说明
TransformChain用于在链之间插入自定义数据处理逻辑,执行纯函数式转换而不调用LLM。可以进行数据清洗、格式转换、特征提取、验证校验等预处理或后处理操作。通过Python函数定义转换逻辑,支持多输入多输出,成本为零且速度极快,适用于不需要AI能力的数据处理环节。
b.代码示例
---
from langchain.chains import TransformChain, LLMChain, SequentialChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
# 定义转换函数
def transform_func(inputs: dict) -> dict:
text = inputs["text"]
# 数据清洗:移除多余空格、特殊字符
cleaned_text = " ".join(text.split())
cleaned_text = cleaned_text.replace("\n", " ").replace("\t", " ")
# 添加元数据
return {
"cleaned_text": cleaned_text,
"word_count": len(cleaned_text.split()),
"char_count": len(cleaned_text)
}
# 创建转换链
transform_chain = TransformChain(
input_variables=["text"],
output_variables=["cleaned_text", "word_count", "char_count"],
transform=transform_func
)
# 测试转换
result = transform_chain({
"text": "这是一段 \n\n 包含多余空格\t的文本"
})
print(result)
# {'cleaned_text': '这是一段 包含多余空格 的文本',
# 'word_count': 5,
# 'char_count': 14}
# 结合LLM使用
llm = OpenAI()
analysis_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
input_variables=["cleaned_text", "word_count"],
template="分析以下文本({word_count}个词):\n{cleaned_text}"
),
output_key="analysis"
)
# 组合:先转换,后分析
pipeline = SequentialChain(
chains=[transform_chain, analysis_chain],
input_variables=["text"],
output_variables=["analysis", "word_count"],
verbose=True
)
result = pipeline({"text": "待分析的文本..."})
---
b.数据预处理
a.功能说明
TransformChain常用于LLM调用前的数据预处理,如文本截断、格式标准化、特征工程等。可以根据业务规则过滤无效数据、补全缺失字段、格式化日期时间等。预处理可以显著提升LLM输出质量,减少幻觉和格式错误,同时通过截断控制Token消耗降低成本。
b.代码示例
---
# 文本截断预处理
def truncate_text(inputs: dict) -> dict:
text = inputs["text"]
max_length = inputs.get("max_length", 1000)
if len(text) > max_length:
# 智能截断:保留完整句子
truncated = text[:max_length]
last_period = truncated.rfind("。")
if last_period > 0:
truncated = truncated[:last_period + 1]
return {"text": truncated, "truncated": True}
return {"text": text, "truncated": False}
truncate_chain = TransformChain(
input_variables=["text", "max_length"],
output_variables=["text", "truncated"],
transform=truncate_text
)
# 日期格式化
from datetime import datetime
def format_date(inputs: dict) -> dict:
date_str = inputs.get("date", "")
try:
dt = datetime.strptime(date_str, "%Y-%m-%d")
formatted = dt.strftime("%Y年%m月%d日")
return {"formatted_date": formatted, "valid": True}
except:
return {"formatted_date": date_str, "valid": False}
date_chain = TransformChain(
input_variables=["date"],
output_variables=["formatted_date", "valid"],
transform=format_date
)
# 数据验证
def validate_input(inputs: dict) -> dict:
email = inputs.get("email", "")
phone = inputs.get("phone", "")
import re
email_valid = bool(re.match(r"^[\w\.-]+@[\w\.-]+\.\w+$", email))
phone_valid = bool(re.match(r"^1[3-9]\d{9}$", phone))
return {
"email": email,
"phone": phone,
"email_valid": email_valid,
"phone_valid": phone_valid,
"all_valid": email_valid and phone_valid
}
validate_chain = TransformChain(
input_variables=["email", "phone"],
output_variables=["email", "phone", "all_valid"],
transform=validate_input
)
---
02.后处理
a.结果格式化
a.功能说明
TransformChain可用于LLM输出的后处理,如解析结构化数据、格式转换、结果校验等。可以将LLM的自然语言输出转换为JSON、XML等结构化格式,或者提取特定字段、清理格式错误。后处理确保输出符合业务系统的接口要求,提高系统集成的可靠性。
b.代码示例
---
import json
import re
# 提取JSON
def extract_json(inputs: dict) -> dict:
text = inputs["text"]
# 尝试提取JSON
json_match = re.search(r'\{.*\}', text, re.DOTALL)
if json_match:
try:
data = json.loads(json_match.group())
return {"parsed_data": data, "success": True}
except:
pass
return {"parsed_data": None, "success": False, "error": "无法解析JSON"}
json_extract_chain = TransformChain(
input_variables=["text"],
output_variables=["parsed_data", "success"],
transform=extract_json
)
# 结果聚合
def aggregate_results(inputs: dict) -> dict:
results = inputs["results"] # List[str]
# 统计分析
total = len(results)
avg_length = sum(len(r) for r in results) / total if total > 0 else 0
# 合并结果
combined = "\n\n".join(f"{i+1}. {r}" for i, r in enumerate(results))
return {
"combined_results": combined,
"total_count": total,
"avg_length": avg_length
}
aggregate_chain = TransformChain(
input_variables=["results"],
output_variables=["combined_results", "total_count"],
transform=aggregate_results
)
# 完整处理流程
from langchain.chains import SequentialChain
# 预处理 -> LLM -> 后处理
full_pipeline = SequentialChain(
chains=[
truncate_chain, # 截断文本
llm_chain, # LLM处理
json_extract_chain # 提取JSON
],
input_variables=["text", "max_length"],
output_variables=["parsed_data", "success"],
verbose=True
)
result = full_pipeline({
"text": "很长的文本...",
"max_length": 500
})
if result["success"]:
print("解析成功:", result["parsed_data"])
else:
print("解析失败")
---
5.4 RouterChain
01.动态路由
a.LLM路由
a.功能说明
LLMRouterChain使用LLM判断输入内容的类型或意图,动态选择最合适的处理链。根据预定义的目标链描述,LLM分析输入并输出路由决策。支持多分支路由、默认兜底链、路由置信度评分等特性,适用于多意图识别、智能分流、个性化处理等场景。
b.代码示例
---
from langchain.chains.router import LLMRouterChain, RouterOutputParser
from langchain.chains import ConversationChain
from langchain.prompts import PromptTemplate
from langchain.llms import OpenAI
# 定义路由提示词
router_template = """给定用户输入,选择最合适的专家来回答。
可选专家:
- physics: 物理学专家,擅长力学、光学、电磁学等
- chemistry: 化学专家,擅长有机化学、无机化学等
- biology: 生物学专家,擅长遗传学、生态学等
用户输入:{input}
请返回JSON格式:{{"destination": "专家名称", "next_inputs": {{"input": "修正后的问题"}}}}
"""
# 创建LLM路由链
llm = OpenAI()
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"]
)
router_chain = LLMRouterChain.from_llm(
llm,
router_prompt,
verbose=True
)
# 定义目标链
physics_chain = ConversationChain(
llm=llm,
prompt=PromptTemplate.from_template(
"你是物理学专家。问题:{input}\n回答:"
)
)
chemistry_chain = ConversationChain(
llm=llm,
prompt=PromptTemplate.from_template(
"你是化学专家。问题:{input}\n回答:"
)
)
# 使用路由链
from langchain.chains.router import MultiPromptChain
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains={
"physics": physics_chain,
"chemistry": chemistry_chain
},
default_chain=ConversationChain(llm=llm),
verbose=True
)
# 自动路由到合适的专家
result1 = chain.run("什么是牛顿第二定律?") # 路由到physics
result2 = chain.run("氢氧化钠的化学式是什么?") # 路由到chemistry
result3 = chain.run("今天天气怎么样?") # 路由到default_chain
---
b.规则路由
a.功能说明
EmbeddingRouterChain基于语义相似度进行路由,避免LLM调用成本。预先计算目标链描述的向量,通过相似度匹配选择最佳路由。速度快、成本低、结果稳定,适用于路由规则明确、分类数量较多的场景。也可以自定义规则函数实现基于关键词、正则表达式等确定性路由。
b.代码示例
---
from langchain.chains.router.embedding_router import EmbeddingRouterChain
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
# 定义路由规则
names_and_descriptions = [
("physics", ["物理", "力学", "运动", "能量"]),
("chemistry", ["化学", "反应", "元素", "分子"]),
("biology", ["生物", "细胞", "遗传", "进化"])
]
# 创建Embedding路由链
router_chain = EmbeddingRouterChain.from_names_and_descriptions(
names_and_descriptions,
FAISS,
OpenAIEmbeddings(),
routing_keys=["input"]
)
# 自定义规则路由
from langchain.chains.router.base import RouterChain
import re
class CustomRouterChain(RouterChain):
"""基于关键词的规则路由"""
def route(self, inputs):
text = inputs["input"].lower()
# 关键词匹配
if any(kw in text for kw in ["物理", "力", "运动"]):
return {"destination": "physics", "next_inputs": inputs}
elif any(kw in text for kw in ["化学", "反应", "元素"]):
return {"destination": "chemistry", "next_inputs": inputs}
elif any(kw in text for kw in ["生物", "细胞", "基因"]):
return {"destination": "biology", "next_inputs": inputs}
else:
return {"destination": "default", "next_inputs": inputs}
# 使用自定义路由
custom_router = CustomRouterChain()
# 正则表达式路由
def regex_router(inputs):
text = inputs["input"]
if re.search(r'\d+[+\-*/]\d+', text):
return {"destination": "calculator", "next_inputs": inputs}
elif re.search(r'http[s]?://', text):
return {"destination": "web_search", "next_inputs": inputs}
else:
return {"destination": "default", "next_inputs": inputs}
---
02.多级路由
a.嵌套路由
a.功能说明
支持多级路由实现复杂的决策树,先粗分类再细分类。第一级路由确定大类,每个大类下再有子路由选择具体处理链。可以构建层次化的专家系统,提升路由精度和处理效率。适用于领域复杂、分支较多的场景,如多层级客服、专业咨询系统。
b.代码示例
---
from langchain.chains.router import MultiPromptChain
# 第一级:大类路由
category_router = LLMRouterChain.from_llm(
llm,
PromptTemplate(
template="判断问题类型:技术问题、业务问题、其他\n问题:{input}",
input_variables=["input"]
)
)
# 第二级:技术子路由
tech_chains = {
"backend": LLMChain(llm=llm, prompt=PromptTemplate.from_template("后端技术问题:{input}")),
"frontend": LLMChain(llm=llm, prompt=PromptTemplate.from_template("前端技术问题:{input}")),
"database": LLMChain(llm=llm, prompt=PromptTemplate.from_template("数据库问题:{input}"))
}
tech_router = EmbeddingRouterChain.from_names_and_descriptions(
[
("backend", ["Python", "Java", "API", "服务器"]),
("frontend", ["React", "Vue", "HTML", "CSS"]),
("database", ["SQL", "MySQL", "MongoDB", "Redis"])
],
FAISS,
OpenAIEmbeddings()
)
tech_chain = MultiPromptChain(
router_chain=tech_router,
destination_chains=tech_chains,
default_chain=LLMChain(llm=llm, prompt=PromptTemplate.from_template("技术问题:{input}"))
)
# 业务链
business_chain = LLMChain(llm=llm, prompt=PromptTemplate.from_template("业务问题:{input}"))
# 顶层路由
main_chain = MultiPromptChain(
router_chain=category_router,
destination_chains={
"tech": tech_chain, # 嵌套的技术路由链
"business": business_chain
},
default_chain=LLMChain(llm=llm, prompt=PromptTemplate.from_template("通用问题:{input}"))
)
# 自动多级路由
result = main_chain.run("React组件如何优化性能?") # 顶层→tech→frontend
---
b.路由监控
a.功能说明
在生产环境中需要监控路由决策的准确性和分布,优化路由策略。通过Callbacks记录路由路径、统计各分支使用频率、分析路由失败原因。可以基于监控数据调整路由规则、增加新分支、优化默认链,持续提升系统效果。
b.代码示例
---
from langchain.callbacks.base import BaseCallbackHandler
from collections import defaultdict
import json
class RouterMonitorCallback(BaseCallbackHandler):
"""路由监控回调"""
def __init__(self):
self.route_stats = defaultdict(int)
self.route_history = []
def on_chain_start(self, serialized, inputs, **kwargs):
if "router" in serialized.get("name", "").lower():
self.current_input = inputs.get("input", "")
def on_chain_end(self, outputs, **kwargs):
if "destination" in outputs:
destination = outputs["destination"]
self.route_stats[destination] += 1
self.route_history.append({
"input": self.current_input,
"destination": destination,
"timestamp": str(datetime.now())
})
def get_stats(self):
total = sum(self.route_stats.values())
return {
dest: {
"count": count,
"percentage": f"{count/total*100:.2f}%"
}
for dest, count in self.route_stats.items()
}
def export_history(self, filename="route_history.json"):
with open(filename, 'w') as f:
json.dump(self.route_history, f, indent=2, ensure_ascii=False)
# 使用监控
monitor = RouterMonitorCallback()
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain,
callbacks=[monitor]
)
# 运行多次
for question in questions:
chain.run(question)
# 查看统计
print("路由统计:", monitor.get_stats())
monitor.export_history()
# A/B测试路由策略
from langchain.callbacks import get_openai_callback
with get_openai_callback() as cb:
result = chain.run("测试问题")
print(f"Token消耗: {cb.total_tokens}, 成本: ${cb.total_cost}")
---
6 代理系统
6.1 Agent类型
01.Zero-shot Agent
a.基础Agent
a.功能说明
Zero-shot ReAct Agent是最基础的Agent类型,无需示例即可执行任务。基于ReAct框架循环执行推理Reasoning、行动Action、观察Observation三步骤,LLM根据工具描述自主决策何时调用哪个工具。适用于工具描述清晰、任务简单直接的场景,是入门Agent开发的首选类型。
b.代码示例
---
from langchain.agents import initialize_agent, AgentType, Tool
from langchain.llms import OpenAI
# 定义工具
def search(query: str) -> str:
return f"搜索结果:{query}相关信息..."
def calculator(expression: str) -> str:
try:
result = eval(expression)
return f"计算结果:{result}"
except:
return "计算错误"
tools = [
Tool(
name="Search",
func=search,
description="用于搜索信息,输入搜索关键词"
),
Tool(
name="Calculator",
func=calculator,
description="用于数学计算,输入算术表达式"
)
]
# 创建Zero-shot Agent
llm = OpenAI(temperature=0)
agent = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
verbose=True,
max_iterations=5, # 最大迭代次数
early_stopping_method="generate" # 提前停止策略
)
# 执行
result = agent.run("搜索2023年世界杯冠军,然后计算他们获得了多少次冠军")
# Agent会自动分解任务:
# 1. 使用Search工具搜索
# 2. 根据结果使用Calculator计算
# 3. 返回最终答案
---
b.结构化Agent
a.功能说明
Structured Tool Chat Agent支持使用需要复杂参数的工具,可处理嵌套对象、数组、可选参数等结构化输入。通过JSON Schema定义工具参数,Agent自动构造正确的参数结构。适用于API调用、数据库操作、文件处理等需要精确参数控制的场景,是企业级应用的常用选择。
b.代码示例
---
from langchain.agents import AgentType
from langchain.tools import StructuredTool
from langchain.chat_models import ChatOpenAI
from pydantic import BaseModel, Field
from typing import Optional
# 定义复杂参数结构
class SearchParams(BaseModel):
query: str = Field(description="搜索关键词")
category: str = Field(description="分类:news/blog/forum")
limit: int = Field(default=10, description="返回结果数量")
date_from: Optional[str] = Field(None, description="开始日期YYYY-MM-DD")
def advanced_search(
query: str,
category: str,
limit: int = 10,
date_from: Optional[str] = None
) -> str:
return f"在{category}中搜索'{query}',返回{limit}条结果"
# 创建结构化工具
search_tool = StructuredTool.from_function(
func=advanced_search,
name="AdvancedSearch",
description="高级搜索工具,支持分类和日期过滤",
args_schema=SearchParams
)
# 创建结构化Agent
agent = initialize_agent(
[search_tool],
ChatOpenAI(temperature=0),
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# Agent会正确构造复杂参数
result = agent.run(
"在新闻中搜索AI相关内容,只要2024年1月以后的,返回5条"
)
---
02.对话Agent
a.会话记忆Agent
a.功能说明
Conversational Agent整合记忆管理和工具使用,支持多轮对话场景下的上下文理解。可以引用历史对话、理解指代关系、保持对话连贯性。适用于需要交互式问答的场景如智能客服、个人助理,可以在对话过程中动态调用工具获取信息。
b.代码示例
---
from langchain.agents import AgentType
from langchain.memory import ConversationBufferMemory
from langchain.chat_models import ChatOpenAI
# 创建记忆
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
# 创建对话Agent
agent = initialize_agent(
tools,
ChatOpenAI(temperature=0),
agent=AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION,
memory=memory,
verbose=True
)
# 多轮对话
agent.run("帮我搜索北京的天气")
agent.run("那里现在几点?") # "那里"会被理解为北京
agent.run("推荐几个旅游景点")
# 查看对话历史
print(memory.chat_memory.messages)
# 带上下文的工具调用
agent.run("计算刚才搜索结果中的数字之和") # 引用之前的搜索结果
---
b.OpenAI Functions Agent
a.功能说明
OpenAI Functions Agent专门利用OpenAI的函数调用能力,更准确地选择工具和构造参数。支持并行函数调用、多步骤规划、复杂参数处理,准确率和成功率显著高于普通Agent。要求使用支持函数调用的模型如gpt-3.5-turbo、gpt-4,是生产环境推荐的Agent类型。
b.代码示例
---
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
# 使用OpenAI Functions Agent
agent = initialize_agent(
tools,
ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True
)
# 更准确的工具选择
result = agent.run(
"搜索LangChain的GitHub仓库,统计星标数量,然后计算平均每天增长多少星"
)
# 并行函数调用
result = agent.run(
"同时搜索LangChain和LlamaIndex的信息,对比它们的特点"
)
# 多步骤Agent
from langchain.agents import AgentType
from langchain.agents.openai_functions_multi_action.base import OpenAIMultiFunctionsAgent
agent = initialize_agent(
tools,
ChatOpenAI(model="gpt-4"),
agent=AgentType.OPENAI_MULTI_FUNCTIONS,
verbose=True
)
# Agent可以规划多个步骤并行执行
result = agent.run(
"调研向量数据库市场:搜索Milvus、Pinecone、Weaviate的信息,对比它们的功能和价格"
)
---
01.预置工具
a.搜索工具
a.功能说明
LangChain提供丰富的预置搜索工具,包括Google搜索、Bing搜索、DuckDuckGo、Wikipedia等。搜索工具让Agent能够获取实时信息、验证事实、查找参考资料。支持配置搜索结果数量、语言偏好、安全搜索等参数,可根据业务需求选择合适的搜索引擎。
b.代码示例
---
from langchain.tools import DuckDuckGoSearchRun
from langchain.utilities import GoogleSearchAPIWrapper
from langchain.agents import Tool
# DuckDuckGo搜索(无需API密钥)
ddg_search = DuckDuckGoSearchRun()
result = ddg_search.run("LangChain最新版本")
# Google搜索
search = GoogleSearchAPIWrapper(
google_api_key="your-key",
google_cse_id="your-cse-id"
)
google_tool = Tool(
name="Google Search",
description="搜索实时信息,输入搜索关键词",
func=search.run
)
# Wikipedia工具
from langchain.tools import WikipediaQueryRun
from langchain.utilities import WikipediaAPIWrapper
wikipedia = WikipediaQueryRun(api_wrapper=WikipediaAPIWrapper())
result = wikipedia.run("Python programming language")
# 集成到Agent
tools = [
Tool(
name="DuckDuckGo",
func=ddg_search.run,
description="搜索实时信息,免费无需API密钥"
),
Tool(
name="Wikipedia",
func=wikipedia.run,
description="搜索百科知识,适合查询定义和背景"
)
]
agent = initialize_agent(tools, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION)
---
b.工具包
a.功能说明
LangChain提供工具包Toolkits集成多个相关工具,如SQLDatabaseToolkit数据库操作、PythonREPLToolkit代码执行、FileManagementToolkit文件管理等。工具包简化复杂系统集成,提供开箱即用的专业能力。支持自定义工具包扩展,便于封装企业内部系统和API。
b.代码示例
---
# SQL数据库工具包
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.sql_database import SQLDatabase
from langchain.agents import create_sql_agent
db = SQLDatabase.from_uri("sqlite:///example.db")
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent = create_sql_agent(
llm=llm,
toolkit=toolkit,
verbose=True
)
# Agent可以自动编写和执行SQL
result = agent.run("统计用户表中有多少条记录")
result = agent.run("查询销售额最高的前5个产品")
# Python代码执行工具
from langchain.agents.agent_toolkits import create_python_agent
from langchain.tools import PythonREPLTool
python_agent = create_python_agent(
llm=llm,
tool=PythonREPLTool(),
verbose=True
)
# Agent可以编写和执行Python代码
result = python_agent.run("计算斐波那契数列的第20项")
result = python_agent.run("用matplotlib画一个正弦曲线")
# CSV数据分析工具
from langchain.agents import create_pandas_dataframe_agent
import pandas as pd
df = pd.read_csv("sales_data.csv")
agent = create_pandas_dataframe_agent(
llm,
df,
verbose=True,
allow_dangerous_code=True # 允许执行代码
)
result = agent.run("数据集有多少行?")
result = agent.run("销售额最高的月份是哪个?")
result = agent.run("画出销售趋势图")
---
02.自定义工具
a.函数工具
a.功能说明
通过Tool类封装任意Python函数为Agent工具,提供工具名称、描述、参数说明等元信息。工具描述决定Agent何时调用该工具,需要清晰准确地说明工具功能和使用场景。支持同步和异步函数、带参数验证的Pydantic模型、错误处理等高级特性。
b.代码示例
---
from langchain.agents import Tool
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
from typing import Optional, Type
# 方式1:简单函数包装
def get_word_length(word: str) -> int:
"""返回单词长度"""
return len(word)
length_tool = Tool(
name="WordLength",
func=get_word_length,
description="返回单词的字符长度,输入一个单词"
)
# 方式2:带参数验证的工具
class WeatherInput(BaseModel):
city: str = Field(description="城市名称,如北京、上海")
date: Optional[str] = Field(None, description="日期YYYY-MM-DD")
class WeatherTool(BaseTool):
name = "weather"
description = "获取指定城市的天气信息"
args_schema: Type[BaseModel] = WeatherInput
def _run(self, city: str, date: Optional[str] = None) -> str:
# 实际调用天气API
import requests
response = requests.get(
f"https://api.weather.com/v1/city/{city}",
params={"date": date}
)
return response.json()["weather"]
async def _arun(self, city: str, date: Optional[str] = None) -> str:
# 异步实现
return self._run(city, date)
# 方式3:StructuredTool
from langchain.tools import StructuredTool
def multiply(a: int, b: int) -> int:
"""两数相乘"""
return a * b
multiply_tool = StructuredTool.from_function(
func=multiply,
name="Multiply",
description="计算两个整数的乘积"
)
# 使用自定义工具
tools = [length_tool, WeatherTool(), multiply_tool]
agent = initialize_agent(tools, llm, agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION)
---
b.API工具
a.功能说明
将外部API封装为Agent工具,实现系统集成和数据互通。可以调用内部微服务、第三方API、数据库接口等,让Agent能够执行实际的业务操作。支持REST、GraphQL、gRPC等协议,可配置认证、重试、超时等参数。适用于工单创建、数据查询、订单处理等企业场景。
b.代码示例
---
import requests
from langchain.tools import BaseTool
from typing import Optional
class CRMTool(BaseTool):
"""CRM系统集成工具"""
name = "crm_query"
description = "查询CRM系统中的客户信息,输入客户ID"
def __init__(self, api_url: str, api_key: str):
super().__init__()
self.api_url = api_url
self.api_key = api_key
def _run(self, customer_id: str) -> str:
try:
response = requests.get(
f"{self.api_url}/customers/{customer_id}",
headers={"Authorization": f"Bearer {self.api_key}"},
timeout=10
)
response.raise_for_status()
data = response.json()
return f"客户信息:{data['name']}, {data['email']}, {data['phone']}"
except Exception as e:
return f"查询失败:{str(e)}"
async def _arun(self, customer_id: str) -> str:
# 异步HTTP请求
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(
f"{self.api_url}/customers/{customer_id}",
headers={"Authorization": f"Bearer {self.api_key}"}
) as response:
data = await response.json()
return f"客户信息:{data['name']}"
# 数据库查询工具
class DatabaseTool(BaseTool):
"""达梦数据库查询工具"""
name = "database_query"
description = "执行SQL查询,输入SQL语句"
def __init__(self, connection_string: str):
super().__init__()
self.conn_str = connection_string
def _run(self, sql: str) -> str:
import dmPython # 达梦数据库Python驱动
try:
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute(sql)
results = cursor.fetchall()
cursor.close()
conn.close()
return f"查询结果:{results}"
except Exception as e:
return f"查询失败:{str(e)}"
# 使用企业工具
crm_tool = CRMTool(
api_url="http://internal-crm.company.com/api",
api_key="your-api-key"
)
db_tool = DatabaseTool(
connection_string="dm://user:pass@localhost:5236/dbname"
)
tools = [crm_tool, db_tool]
agent = initialize_agent(tools, llm, agent=AgentType.OPENAI_FUNCTIONS)
# Agent可以调用内部系统
result = agent.run("查询客户ID为12345的信息")
result = agent.run("统计本月新增客户数量")
---
01.数据库工具包
a.SQL操作
a.功能说明
SQLDatabaseToolkit提供完整的数据库操作能力,包括查询表结构、执行SELECT、检查查询语法等。Agent可以根据自然语言问题自动生成SQL、执行查询、解释结果。支持MySQL、PostgreSQL、SQLite、达梦等主流数据库,通过SQLAlchemy实现统一接口。适用于数据分析、报表生成、智能查询等场景。
b.代码示例
---
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.agents import create_sql_agent
from langchain.sql_database import SQLDatabase
from langchain.chat_models import ChatOpenAI
# 连接数据库
db = SQLDatabase.from_uri("mysql://user:pass@localhost/mydb")
# 创建SQL工具包
toolkit = SQLDatabaseToolkit(db=db, llm=ChatOpenAI())
# 查看工具包包含的工具
for tool in toolkit.get_tools():
print(f"{tool.name}: {tool.description}")
# 创建SQL Agent
agent = create_sql_agent(
llm=ChatOpenAI(temperature=0),
toolkit=toolkit,
verbose=True,
agent_type=AgentType.OPENAI_FUNCTIONS
)
# 自然语言查询
result = agent.run("有多少个用户?")
result = agent.run("找出销售额最高的前10个产品")
result = agent.run("计算每个月的平均订单金额")
# 达梦数据库适配
from sqlalchemy import create_engine
engine = create_engine("dm://SYSDBA:SYSDBA@localhost:5236/TEST")
db = SQLDatabase(engine)
toolkit = SQLDatabaseToolkit(db=db, llm=llm)
agent = create_sql_agent(llm, toolkit, verbose=True)
# 复杂查询
result = agent.run("""
查询2024年每个季度的销售趋势,
包括订单数量、总金额、平均客单价
""")
---
b.安全控制
a.功能说明
SQL Agent需要严格的安全控制,防止SQL注入、数据泄露、误删除等风险。可以配置只读模式、表白名单、查询审批等安全策略。支持查询预览、执行确认、结果脱敏等保护机制。在生产环境应使用受限数据库用户、启用审计日志、设置资源限制。
b.代码示例
---
from langchain.agents import create_sql_agent
from langchain.sql_database import SQLDatabase
# 只读模式
db = SQLDatabase.from_uri(
"mysql://readonly_user:pass@localhost/mydb",
sample_rows_in_table_info=2 # 限制示例行数
)
# 表白名单
db = SQLDatabase.from_uri(
"mysql://user:pass@localhost/mydb",
include_tables=["users", "orders", "products"] # 只能访问这些表
)
# 自定义安全Agent
from langchain.agents.agent_toolkits import SQLDatabaseToolkit
from langchain.tools import BaseTool
class SafeSQLTool(BaseTool):
"""安全SQL执行工具"""
name = "safe_sql_query"
description = "执行只读SQL查询"
def __init__(self, db: SQLDatabase):
super().__init__()
self.db = db
def _run(self, query: str) -> str:
# 检查危险操作
dangerous_keywords = ["DROP", "DELETE", "UPDATE", "INSERT", "TRUNCATE"]
if any(kw in query.upper() for kw in dangerous_keywords):
return "错误:不允许执行修改操作"
# 查询长度限制
if len(query) > 500:
return "错误:查询语句过长"
# 结果行数限制
try:
result = self.db.run(query + " LIMIT 100")
return result
except Exception as e:
return f"查询失败:{str(e)}"
# 查询审批流程
class ApprovalSQLTool(BaseTool):
"""需要审批的SQL工具"""
name = "sql_with_approval"
description = "执行需要审批的SQL查询"
def _run(self, query: str) -> str:
print(f"待审批查询:\n{query}\n")
approval = input("是否批准执行?(y/n): ")
if approval.lower() == 'y':
return db.run(query)
else:
return "查询已拒绝"
# 敏感数据脱敏
import re
def mask_sensitive_data(result: str) -> str:
# 手机号脱敏
result = re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', result)
# 邮箱脱敏
result = re.sub(r'([\w\.-]{2})[\w\.-]*@', r'\1***@', result)
return result
---
02.文件管理工具包
a.文件操作
a.功能说明
FileManagementToolkit提供文件和目录管理能力,包括读写文件、创建删除目录、搜索文件等。Agent可以自动化文件处理任务如日志分析、配置管理、报告生成等。支持文本文件、JSON、CSV等常见格式,可配置文件系统根目录、访问权限、操作白名单等安全策略。
b.代码示例
---
from langchain.agents.agent_toolkits import FileManagementToolkit
from langchain.tools.file_management import (
ReadFileTool,
WriteFileTool,
ListDirectoryTool
)
from tempfile import TemporaryDirectory
# 创建临时工作目录
working_directory = TemporaryDirectory()
# 创建文件工具包
toolkit = FileManagementToolkit(
root_dir=str(working_directory.name)
)
tools = toolkit.get_tools()
agent = initialize_agent(
tools,
llm,
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION,
verbose=True
)
# Agent可以执行文件操作
agent.run("创建一个名为notes.txt的文件,写入今天的工作总结")
agent.run("列出当前目录下的所有文件")
agent.run("读取notes.txt的内容")
# 自定义安全文件工具
class SafeReadTool(BaseTool):
"""安全的文件读取工具"""
name = "safe_read"
description = "安全读取文件内容"
def __init__(self, allowed_dirs: list):
super().__init__()
self.allowed_dirs = allowed_dirs
def _run(self, file_path: str) -> str:
import os
# 检查路径是否在允许的目录内
abs_path = os.path.abspath(file_path)
if not any(abs_path.startswith(d) for d in self.allowed_dirs):
return "错误:无权访问该路径"
# 文件大小限制
if os.path.getsize(file_path) > 10 * 1024 * 1024: # 10MB
return "错误:文件过大"
try:
with open(file_path, 'r', encoding='utf-8') as f:
return f.read()
except Exception as e:
return f"读取失败:{str(e)}"
---
b.信创适配
a.功能说明
在信创环境下,文件工具需要适配国产操作系统如麒麟、统信UOS,处理中文路径、特殊字符、文件系统差异等问题。可以集成国产文档格式如WPS、永中Office,对接企业内部文档管理系统。支持文件加密、数字签名、审计日志等企业级安全特性。
b.代码示例
---
import os
from pathlib import Path
from langchain.tools import BaseTool
class KylinFileTool(BaseTool):
"""适配麒麟操作系统的文件工具"""
name = "kylin_file"
description = "在麒麟系统上操作文件"
def _run(self, operation: str, path: str) -> str:
# 处理中文路径
path = Path(path).expanduser()
# 检查系统类型
if not self._is_kylin_os():
return "警告:非麒麟系统"
if operation == "read":
return self._read_file(path)
elif operation == "write":
return self._write_file(path)
else:
return "不支持的操作"
def _is_kylin_os(self) -> bool:
try:
with open('/etc/os-release', 'r') as f:
content = f.read()
return 'Kylin' in content
except:
return False
def _read_file(self, path: Path) -> str:
# 使用国密算法解密
try:
with open(path, 'rb') as f:
encrypted_data = f.read()
# 调用国密库解密
# from gmssl import sm4
# decrypted = sm4.decrypt(encrypted_data, key)
return encrypted_data.decode('utf-8')
except Exception as e:
return f"读取失败:{str(e)}"
# WPS文档处理
class WPSTool(BaseTool):
"""WPS文档处理工具"""
name = "wps_document"
description = "读取和编辑WPS文档"
def _run(self, file_path: str) -> str:
# 使用WPS Python接口
import subprocess
# 转换为PDF
result = subprocess.run([
'wps',
'--export-pdf',
file_path
], capture_output=True, text=True)
if result.returncode == 0:
return "转换成功"
else:
return f"转换失败:{result.stderr}"
# 集成企业文档系统
class EnterpriseDocTool(BaseTool):
"""企业文档管理系统工具"""
name = "doc_system"
description = "访问企业文档管理系统"
def __init__(self, api_url: str, token: str):
super().__init__()
self.api_url = api_url
self.token = token
def _run(self, doc_id: str) -> str:
import requests
response = requests.get(
f"{self.api_url}/documents/{doc_id}",
headers={"Authorization": f"Bearer {self.token}"}
)
if response.status_code == 200:
return response.json()["content"]
else:
return f"获取文档失败:{response.status_code}"
---
6.4 Agent Executor
01.执行控制
a.迭代限制
a.功能说明
AgentExecutor负责管理Agent的执行流程,包括迭代次数控制、超时管理、错误处理等。通过max_iterations限制最大执行步数防止无限循环,max_execution_time设置超时时间避免长时间运行。支持early_stopping_method配置提前停止策略,可选择force强制停止或generate生成最终答案。
b.代码示例
---
from langchain.agents import initialize_agent, AgentType, AgentExecutor
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
# 配置执行限制
agent_executor = initialize_agent(
tools,
llm,
agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
max_iterations=10, # 最多10次迭代
max_execution_time=60, # 最多运行60秒
early_stopping_method="generate", # 超限时生成答案
handle_parsing_errors=True, # 处理解析错误
verbose=True
)
# 自定义停止条件
from langchain.agents import AgentExecutor
from langchain.callbacks import StdOutCallbackHandler
class CustomStoppingCallback(StdOutCallbackHandler):
def __init__(self, max_cost: float):
self.total_cost = 0
self.max_cost = max_cost
def on_llm_end(self, response, **kwargs):
# 累计成本
tokens = response.llm_output.get("token_usage", {}).get("total_tokens", 0)
self.total_cost += tokens * 0.00002 # 假设单价
if self.total_cost > self.max_cost:
raise ValueError(f"成本超限:${self.total_cost:.4f}")
# 使用自定义回调
cost_callback = CustomStoppingCallback(max_cost=1.0)
try:
result = agent_executor.run(
"复杂任务...",
callbacks=[cost_callback]
)
except ValueError as e:
print(f"执行中断:{e}")
# 手动控制执行
for step in agent_executor.iter({"input": "任务描述"}):
print(f"步骤 {step['step']}: {step['action']}")
# 自定义停止逻辑
if should_stop(step):
break
---
b.错误处理
a.功能说明
AgentExecutor提供多层错误处理机制,包括工具执行错误、LLM输出解析错误、超时错误等。可以配置重试策略、错误回退、人工介入等恢复机制。支持自定义错误处理器,实现业务特定的容错逻辑。错误信息会反馈给Agent,帮助其调整策略继续执行。
b.代码示例
---
from langchain.agents import AgentExecutor
# 处理解析错误
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
handle_parsing_errors=True, # 自动处理解析错误
verbose=True
)
# 自定义错误处理
def handle_tool_error(error: Exception) -> str:
"""自定义工具错误处理"""
if isinstance(error, requests.Timeout):
return "工具调用超时,请重试"
elif isinstance(error, requests.HTTPError):
return f"API调用失败:{error.response.status_code}"
else:
return f"发生错误:{str(error)}"
from langchain.tools import Tool
tool = Tool(
name="API Call",
func=api_function,
description="调用外部API",
handle_tool_error=handle_tool_error # 工具级错误处理
)
# Agent级错误处理
class RobustAgentExecutor(AgentExecutor):
"""增强的Agent执行器"""
def _call(self, inputs, run_manager=None):
try:
return super()._call(inputs, run_manager)
except Exception as e:
# 记录错误
self._log_error(e, inputs)
# 尝试恢复
if self._can_recover(e):
return self._recover(inputs)
else:
return {"output": f"执行失败:{str(e)}"}
def _log_error(self, error, inputs):
import logging
logging.error(f"Agent错误:{error}, 输入:{inputs}")
def _can_recover(self, error):
# 判断是否可恢复
return isinstance(error, (TimeoutError, ConnectionError))
def _recover(self, inputs):
# 重试逻辑
time.sleep(5)
return self._call(inputs)
# 人工介入
from langchain.callbacks import HumanApprovalCallbackHandler
approval_callback = HumanApprovalCallbackHandler(
approve=lambda x: input(f"执行操作:{x}\n批准?(y/n): ") == "y"
)
agent_executor = AgentExecutor(
agent=agent,
tools=tools,
callbacks=[approval_callback]
)
---
02.优化策略
a.并行执行
a.功能说明
Agent可以并行调用多个独立工具提升效率,减少总执行时间。使用Multi-Action Agent支持一次规划多个动作同时执行,适用于可并行的查询、计算、检索等任务。需要确保工具线程安全、处理并发错误、合理控制并发数。
b.代码示例
---
from langchain.agents import AgentType
from langchain.chat_models import ChatOpenAI
# 支持并行执行的Agent
agent = initialize_agent(
tools,
ChatOpenAI(model="gpt-4"),
agent=AgentType.OPENAI_MULTI_FUNCTIONS, # 支持多动作
verbose=True
)
# Agent会并行执行独立任务
result = agent.run("""
同时完成以下任务:
1. 搜索LangChain的GitHub星标数
2. 搜索LlamaIndex的GitHub星标数
3. 搜索Milvus的GitHub星标数
然后对比它们的受欢迎程度
""")
# 自定义并行工具
import asyncio
from langchain.tools import BaseTool
class AsyncTool(BaseTool):
"""支持异步的工具"""
name = "async_search"
description = "异步搜索工具"
async def _arun(self, query: str) -> str:
# 异步HTTP请求
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(f"https://api.example.com/search?q={query}") as resp:
return await resp.text()
def _run(self, query: str) -> str:
# 同步版本
return asyncio.run(self._arun(query))
# 批量并行处理
async def batch_process(queries: list):
tasks = [AsyncTool()._arun(q) for q in queries]
results = await asyncio.gather(*tasks)
return results
results = asyncio.run(batch_process([
"query1", "query2", "query3"
]))
---
b.缓存优化
a.功能说明
通过缓存工具执行结果避免重复调用,显著降低成本和延迟。可以缓存搜索结果、API响应、计算结果等幂等操作的输出。支持内存缓存、Redis缓存、文件缓存等多种后端,可配置过期时间、缓存键策略、容量限制。适用于频繁调用、变化较慢的数据源。
b.代码示例
---
from langchain.cache import InMemoryCache, RedisCache
from langchain.globals import set_llm_cache
import langchain
# LLM结果缓存
langchain.llm_cache = InMemoryCache()
# Redis缓存
from redis import Redis
redis_client = Redis(host='localhost', port=6379)
langchain.llm_cache = RedisCache(redis_client)
# 工具结果缓存
from functools import lru_cache
class CachedTool(BaseTool):
"""带缓存的工具"""
name = "cached_search"
description = "带缓存的搜索"
@lru_cache(maxsize=100)
def _cached_search(self, query: str) -> str:
# 实际搜索逻辑
return self._do_search(query)
def _run(self, query: str) -> str:
return self._cached_search(query)
# Redis工具缓存
import json
import hashlib
class RedisCachedTool(BaseTool):
name = "redis_cached"
description = "Redis缓存工具"
def __init__(self, redis_client, ttl=3600):
super().__init__()
self.redis = redis_client
self.ttl = ttl
def _run(self, query: str) -> str:
# 生成缓存键
cache_key = f"tool:{self.name}:{hashlib.md5(query.encode()).hexdigest()}"
# 尝试从缓存读取
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)
# 执行工具
result = self._execute(query)
# 写入缓存
self.redis.setex(
cache_key,
self.ttl,
json.dumps(result)
)
return result
# 智能缓存失效
class SmartCachedTool(BaseTool):
"""智能缓存工具"""
def _should_cache(self, query: str) -> bool:
# 根据查询类型决定是否缓存
if "实时" in query or "最新" in query:
return False
return True
def _run(self, query: str) -> str:
if not self._should_cache(query):
return self._execute(query)
# 使用缓存
return self._cached_execute(query)
---
7 记忆管理
7.1 ChatMessageHistory
01.消息存储
a.内存存储
a.功能说明
ChatMessageHistory是记忆系统的底层存储接口,负责消息的增删改查。InMemoryChatMessageHistory将消息存储在内存中,进程重启后数据丢失,适用于临时会话、开发测试场景。提供add_user_message、add_ai_message等方法管理消息,支持clear清空历史、messages属性访问所有消息。
b.代码示例
---
from langchain.memory.chat_message_histories import ChatMessageHistory
from langchain.schema import HumanMessage, AIMessage
# 创建消息历史
history = ChatMessageHistory()
# 添加消息
history.add_user_message("你好")
history.add_ai_message("你好,有什么可以帮助你?")
history.add_user_message("介绍一下LangChain")
history.add_ai_message("LangChain是一个AI应用开发框架...")
# 访问消息
messages = history.messages
for msg in messages:
if isinstance(msg, HumanMessage):
print(f"用户:{msg.content}")
else:
print(f"AI:{msg.content}")
# 清空历史
history.clear()
# 直接操作消息列表
history.messages.append(HumanMessage(content="新消息"))
history.messages.pop(0) # 删除第一条消息
# 消息数量限制
from collections import deque
class LimitedHistory(ChatMessageHistory):
def __init__(self, max_messages: int = 10):
super().__init__()
self.max_messages = max_messages
self._messages = deque(maxlen=max_messages)
def add_message(self, message):
self._messages.append(message)
@property
def messages(self):
return list(self._messages)
history = LimitedHistory(max_messages=20)
---
b.持久化存储
a.功能说明
RedisChatMessageHistory、FileChatMessageHistory等实现将消息持久化到外部存储,支持跨会话、跨进程访问。通过session_id区分不同用户或会话,实现多租户隔离。支持消息过期、容量限制、并发访问控制等企业级特性,适用于生产环境的长期记忆存储。
b.代码示例
---
from langchain.memory.chat_message_histories import RedisChatMessageHistory
# Redis持久化
history = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id="user_12345",
ttl=3600 # 1小时过期
)
history.add_user_message("你好")
history.add_ai_message("你好")
# 另一个进程/机器可以访问相同历史
history2 = RedisChatMessageHistory(
url="redis://localhost:6379/0",
session_id="user_12345" # 相同session_id
)
print(history2.messages) # 能读取到之前的消息
# PostgreSQL持久化
from langchain.memory.chat_message_histories import PostgresChatMessageHistory
history = PostgresChatMessageHistory(
connection_string="postgresql://user:pass@localhost/chatdb",
session_id="session_001"
)
# 文件持久化
from langchain.memory.chat_message_histories import FileChatMessageHistory
history = FileChatMessageHistory(
file_path="./chat_history/user_12345.json"
)
# 达梦数据库持久化
from langchain.schema import BaseChatMessageHistory, BaseMessage
import dmPython
import json
class DmChatMessageHistory(BaseChatMessageHistory):
"""达梦数据库消息历史"""
def __init__(self, connection_string: str, session_id: str):
self.conn_str = connection_string
self.session_id = session_id
self._ensure_table()
def _ensure_table(self):
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS chat_history (
session_id VARCHAR(100),
message TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
conn.commit()
conn.close()
def add_message(self, message: BaseMessage):
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute(
"INSERT INTO chat_history (session_id, message) VALUES (?, ?)",
(self.session_id, json.dumps(message.dict()))
)
conn.commit()
conn.close()
@property
def messages(self):
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute(
"SELECT message FROM chat_history WHERE session_id = ? ORDER BY created_at",
(self.session_id,)
)
rows = cursor.fetchall()
conn.close()
from langchain.schema import messages_from_dict
return [messages_from_dict([json.loads(row[0])])[0] for row in rows]
def clear(self):
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute("DELETE FROM chat_history WHERE session_id = ?", (self.session_id,))
conn.commit()
conn.close()
---
02.消息管理
a.消息过滤
a.功能说明
在长对话场景下,需要过滤和筛选历史消息,只保留相关内容。可以按时间范围、消息类型、关键词等条件过滤,或使用LLM提取关键对话。支持消息窗口滑动、优先级保留、智能压缩等策略,平衡记忆完整性和Token消耗。
b.代码示例
---
from langchain.memory.chat_message_histories import ChatMessageHistory
from langchain.schema import HumanMessage, AIMessage, SystemMessage
from datetime import datetime, timedelta
history = ChatMessageHistory()
# 添加带时间戳的消息
class TimestampedMessage:
def __init__(self, message, timestamp=None):
self.message = message
self.timestamp = timestamp or datetime.now()
# 时间过滤
def filter_by_time(history, hours=24):
cutoff = datetime.now() - timedelta(hours=hours)
return [
msg for msg in history.messages
if getattr(msg, 'timestamp', datetime.now()) > cutoff
]
# 类型过滤
def filter_by_type(history, message_type=HumanMessage):
return [
msg for msg in history.messages
if isinstance(msg, message_type)
]
# 关键词过滤
def filter_by_keyword(history, keywords: list):
return [
msg for msg in history.messages
if any(kw in msg.content for kw in keywords)
]
# 滑动窗口
def sliding_window(history, window_size=10):
"""保留最近N条消息"""
return history.messages[-window_size:]
# 重要消息保留
def keep_important(history, llm):
"""使用LLM判断消息重要性"""
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
importance_chain = LLMChain(
llm=llm,
prompt=PromptTemplate(
template="判断以下消息是否重要(是/否):{message}",
input_variables=["message"]
)
)
important_messages = []
for msg in history.messages:
result = importance_chain.run(message=msg.content)
if "是" in result:
important_messages.append(msg)
return important_messages
# 应用过滤策略
from langchain.memory import ConversationBufferMemory
class FilteredMemory(ConversationBufferMemory):
def load_memory_variables(self, inputs):
# 加载时自动过滤
all_messages = self.chat_memory.messages
filtered = sliding_window(all_messages, 20)
# 临时替换消息
original = self.chat_memory.messages
self.chat_memory.messages = filtered
result = super().load_memory_variables(inputs)
self.chat_memory.messages = original
return result
---
7.2 ConversationBufferMemory
01.缓冲记忆
a.完整历史
a.功能说明
ConversationBufferMemory保存完整的对话历史,适用于短对话或需要完整上下文的场景。每次交互都追加消息到历史列表,检索时返回全部内容。优点是信息无损失上下文完整,缺点是Token消耗随对话增长线性增加。支持返回字符串或消息列表格式,可配置memory_key自定义变量名。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationChain
from langchain.llms import OpenAI
# 创建缓冲记忆
memory = ConversationBufferMemory()
# 集成到对话链
conversation = ConversationChain(
llm=OpenAI(temperature=0),
memory=memory,
verbose=True
)
# 多轮对话
conversation.predict(input="我叫张三")
conversation.predict(input="我在北京工作")
response = conversation.predict(input="我叫什么名字,在哪工作?")
print(response) # 能记住所有信息
# 查看记忆内容
memory_vars = memory.load_memory_variables({})
print(memory_vars["history"])
# 返回消息列表格式
memory = ConversationBufferMemory(return_messages=True)
conversation = ConversationChain(llm=llm, memory=memory)
# 手动管理记忆
memory.save_context(
{"input": "用户问题"},
{"output": "AI回答"}
)
# 清空记忆
memory.clear()
# 自定义memory_key
memory = ConversationBufferMemory(
memory_key="chat_history",
input_key="question",
output_key="answer"
)
---
b.窗口限制
a.功能说明
ConversationBufferWindowMemory在缓冲记忆基础上增加窗口限制,只保留最近k轮对话。通过k参数控制记忆窗口大小,自动丢弃旧对话,有效控制Token消耗。适用于对话轮次较多但只需要近期上下文的场景,如客服系统、长时间交互应用。
b.代码示例
---
from langchain.memory import ConversationBufferWindowMemory
# 只保留最近5轮对话
memory = ConversationBufferWindowMemory(k=5)
conversation = ConversationChain(
llm=OpenAI(temperature=0),
memory=memory,
verbose=True
)
# 进行10轮对话
for i in range(10):
conversation.predict(input=f"第{i+1}轮对话")
# 只能记住最近5轮
memory_vars = memory.load_memory_variables({})
print(f"记忆窗口大小:{len(memory.buffer)}") # 5
# 动态调整窗口大小
class DynamicWindowMemory(ConversationBufferWindowMemory):
"""动态窗口记忆"""
def __init__(self, min_k=3, max_k=10, token_limit=1000):
super().__init__(k=max_k)
self.min_k = min_k
self.max_k = max_k
self.token_limit = token_limit
def load_memory_variables(self, inputs):
# 计算当前Token数
history_str = self.buffer_as_str
token_count = len(history_str.split())
# 动态调整窗口
if token_count > self.token_limit:
self.k = max(self.min_k, self.k - 1)
else:
self.k = min(self.max_k, self.k + 1)
return super().load_memory_variables(inputs)
memory = DynamicWindowMemory(min_k=3, max_k=10, token_limit=500)
---
02.Token管理
a.Token限制
a.功能说明
ConversationTokenBufferMemory基于Token数量而非对话轮次管理记忆,更精确控制成本。使用tiktoken计算Token数,当超过限制时自动删除最早的消息。适用于对成本敏感、对话长度不固定的场景,确保记忆永远不会超过模型上下文窗口限制。
b.代码示例
---
from langchain.memory import ConversationTokenBufferMemory
from langchain.llms import OpenAI
# 基于Token限制
memory = ConversationTokenBufferMemory(
llm=OpenAI(),
max_token_limit=500 # 最多500个Token
)
conversation = ConversationChain(
llm=OpenAI(temperature=0),
memory=memory,
verbose=True
)
# 长对话测试
for i in range(20):
conversation.predict(input=f"这是第{i+1}轮对话的问题...")
# 记忆始终不超过500 Token
memory_vars = memory.load_memory_variables({})
token_count = memory.llm.get_num_tokens(memory_vars["history"])
print(f"当前Token数:{token_count}") # <= 500
# 使用特定模型的tokenizer
from transformers import AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("THUDM/chatglm3-6b")
class CustomTokenMemory(ConversationTokenBufferMemory):
"""自定义tokenizer的记忆"""
def __init__(self, tokenizer, max_token_limit=1000):
super().__init__(llm=llm, max_token_limit=max_token_limit)
self.tokenizer = tokenizer
def get_num_tokens_from_messages(self, messages):
text = self.get_buffer_string(messages)
return len(self.tokenizer.encode(text))
memory = CustomTokenMemory(tokenizer, max_token_limit=500)
---
b.优先级保留
a.功能说明
在Token受限情况下,可以优先保留重要消息,丢弃次要信息。通过LLM判断消息重要性、根据业务规则标记关键消息、使用相似度保留相关对话等策略。实现智能记忆管理,在有限Token内保留最有价值的上下文信息。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.schema import BaseMessage
from typing import List
class PriorityMemory(ConversationBufferMemory):
"""优先级记忆"""
def __init__(self, max_tokens=1000, llm=None):
super().__init__(return_messages=True)
self.max_tokens = max_tokens
self.llm = llm
self.important_keywords = ["重要", "关键", "必须", "核心"]
def _is_important(self, message: BaseMessage) -> bool:
"""判断消息是否重要"""
content = message.content
# 基于关键词
if any(kw in content for kw in self.important_keywords):
return True
# 基于长度(较长的消息可能更重要)
if len(content) > 100:
return True
return False
def load_memory_variables(self, inputs):
messages = self.chat_memory.messages
# 分类消息
important = [msg for msg in messages if self._is_important(msg)]
others = [msg for msg in messages if not self._is_important(msg)]
# 优先保留重要消息
selected = important.copy()
current_tokens = sum(len(msg.content.split()) for msg in selected)
# 填充其他消息直到达到限制
for msg in reversed(others):
msg_tokens = len(msg.content.split())
if current_tokens + msg_tokens <= self.max_tokens:
selected.append(msg)
current_tokens += msg_tokens
else:
break
# 按时间排序
selected.sort(key=lambda m: messages.index(m))
self.chat_memory.messages = selected
return super().load_memory_variables(inputs)
memory = PriorityMemory(max_tokens=500, llm=llm)
# 使用相似度保留相关对话
from langchain.embeddings import OpenAIEmbeddings
import numpy as np
class SemanticMemory(ConversationBufferMemory):
"""基于语义相似度的记忆"""
def __init__(self, embeddings, max_messages=10):
super().__init__(return_messages=True)
self.embeddings = embeddings
self.max_messages = max_messages
def load_memory_variables(self, inputs):
current_input = inputs.get("input", "")
messages = self.chat_memory.messages
if len(messages) <= self.max_messages:
return super().load_memory_variables(inputs)
# 计算相似度
input_vector = self.embeddings.embed_query(current_input)
message_vectors = self.embeddings.embed_documents(
[msg.content for msg in messages]
)
# 计算相似度分数
similarities = [
np.dot(input_vector, msg_vec)
for msg_vec in message_vectors
]
# 选择最相关的消息
top_indices = np.argsort(similarities)[-self.max_messages:]
selected = [messages[i] for i in sorted(top_indices)]
self.chat_memory.messages = selected
return super().load_memory_variables(inputs)
---
7.3 ConversationSummaryMemory
01.摘要记忆
a.自动摘要
a.功能说明
ConversationSummaryMemory通过LLM定期总结对话历史,压缩长对话内容为简洁摘要。保留关键信息如用户偏好、重要决策、事实陈述等,丢弃寒暄、重复内容。自动触发摘要无需人工干预,显著降低长对话的Token消耗。适用于客服系统、长期顾问、个性化助理等需要长期记忆的场景。
b.代码示例
---
from langchain.memory import ConversationSummaryMemory
from langchain.llms import OpenAI
# 创建摘要记忆
llm = OpenAI(temperature=0)
memory = ConversationSummaryMemory(llm=llm)
# 保存对话
memory.save_context(
{"input": "你好,我叫张三,是一名软件工程师"},
{"output": "你好张三,很高兴认识你"}
)
memory.save_context(
{"input": "我主要使用Python和Java开发"},
{"output": "很好,这两种语言都很流行"}
)
memory.save_context(
{"input": "我最近在学习AI和机器学习"},
{"output": "AI是个很有前景的方向"}
)
# 查看摘要
print(memory.load_memory_variables({}))
# {'history': '张三是软件工程师,使用Python和Java,正在学习AI'}
# 配置摘要提示词
from langchain.prompts import PromptTemplate
summary_prompt = PromptTemplate(
input_variables=["summary", "new_lines"],
template="""当前摘要:{summary}
新对话:
{new_lines}
请更新摘要,保留关键信息:"""
)
memory = ConversationSummaryMemory(
llm=llm,
prompt=summary_prompt
)
# 集成到Chain
from langchain.chains import ConversationChain
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
result = conversation.predict(input="我的问题...")
---
b.增量摘要
a.功能说明
ConversationSummaryBufferMemory结合摘要和缓冲两种策略,保留最近k条原始消息,更早的消息进行摘要。平衡了上下文完整性和Token控制,既能引用近期细节又能保留长期记忆。可配置max_token_limit触发摘要阈值,是长对话场景的最佳选择。
b.代码示例
---
from langchain.memory import ConversationSummaryBufferMemory
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
# 创建混合记忆
memory = ConversationSummaryBufferMemory(
llm=llm,
max_token_limit=200, # 超过200 Token触发摘要
return_messages=True
)
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 长对话
conversation.predict(input="我叫张三,在北京的一家AI公司工作")
conversation.predict(input="我们公司主要做智能客服和文档分析")
conversation.predict(input="我负责RAG系统的开发,使用LangChain和Milvus")
conversation.predict(input="最近在研究Multi-Agent系统")
conversation.predict(input="今天的任务是优化检索性能")
# 查看记忆结构
memory_vars = memory.load_memory_variables({})
print("摘要:", memory_vars.get("history", ""))
# 会包含早期对话的摘要 + 最近几轮的原始消息
# 自定义摘要策略
class AdaptiveSummaryMemory(ConversationSummaryBufferMemory):
"""自适应摘要记忆"""
def predict_new_summary(self, messages, existing_summary):
# 分析对话主题
topics = self._extract_topics(messages)
# 根据主题决定摘要策略
if "技术" in topics:
# 技术对话保留更多细节
prompt = """详细总结技术讨论的关键点:
当前摘要:{summary}
新对话:{new_lines}
更新摘要:"""
else:
# 其他对话简要概括
prompt = """简要概括对话要点:
当前摘要:{summary}
新对话:{new_lines}
更新摘要:"""
# 执行摘要
return self.llm.predict(
prompt.format(
summary=existing_summary,
new_lines=self._get_buffer_string(messages)
)
)
def _extract_topics(self, messages):
# 提取对话主题
text = " ".join([m.content for m in messages])
keywords = ["技术", "业务", "产品", "管理"]
return [kw for kw in keywords if kw in text]
---
02.知识提取
a.实体记忆
a.功能说明
ConversationEntityMemory专门提取和记忆对话中的实体信息,如人名、地点、组织、日期等。使用NER模型或LLM识别实体,构建结构化知识库。适用于需要记住用户偏好、历史事件、关系网络的场景,如CRM系统、个性化推荐。
b.代码示例
---
from langchain.memory import ConversationEntityMemory
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
# 创建实体记忆
memory = ConversationEntityMemory(llm=llm)
# 对话
memory.save_context(
{"input": "我叫张三,在阿里巴巴工作"},
{"output": "很高兴认识你"}
)
memory.save_context(
{"input": "我们团队在杭州,负责AI项目"},
{"output": "杭州是个好地方"}
)
# 查看提取的实体
print(memory.entity_store.store)
# {
# '张三': '用户名字',
# '阿里巴巴': '用户工作的公司',
# '杭州': '团队所在城市',
# 'AI项目': '团队负责的项目'
# }
# 基于实体加载记忆
memory_vars = memory.load_memory_variables({"input": "我在哪里工作?"})
# 会自动关联"工作"相关的实体:阿里巴巴、杭州
# 自定义实体提取
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
entity_extraction_prompt = PromptTemplate(
input_variables=["history", "input"],
template="""从对话中提取实体信息:
历史:{history}
新输入:{input}
提取以下实体:
- 人名:
- 公司:
- 地点:
- 产品:
- 技术:
"""
)
memory = ConversationEntityMemory(
llm=llm,
entity_extraction_prompt=entity_extraction_prompt
)
# Neo4j知识图谱存储
from langchain.graphs import Neo4jGraph
from langchain.memory import ConversationKGMemory
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="password"
)
memory = ConversationKGMemory(
llm=llm,
kg=graph
)
# 自动构建知识图谱
memory.save_context(
{"input": "张三在阿里巴巴工作"},
{"output": "明白了"}
)
# 会在Neo4j中创建:(张三)-[工作于]->(阿里巴巴)
---
7.4 VectorStoreMemory
01.向量记忆
a.相似度检索记忆
a.功能说明
VectorStoreRetrieverMemory基于语义相似度检索历史对话,而非按时间顺序返回。将每轮对话向量化存储,查询时根据当前输入检索最相关的历史片段。适用于长期记忆、跨会话引用、个性化推荐等场景。不受Token限制,可存储无限历史,但需要向量数据库支持。
b.代码示例
---
from langchain.memory import VectorStoreRetrieverMemory
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Milvus
# 创建向量库
vectorstore = Milvus(
embedding_function=OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"},
collection_name="conversation_memory"
)
# 创建向量记忆
memory = VectorStoreRetrieverMemory(
retriever=vectorstore.as_retriever(search_kwargs={"k": 3})
)
# 保存对话
memory.save_context(
{"input": "我喜欢Python编程"},
{"output": "Python是很好的选择"}
)
memory.save_context(
{"input": "推荐一本机器学习的书"},
{"output": "推荐《机器学习实战》"}
)
memory.save_context(
{"input": "我在学习深度学习"},
{"output": "可以从PyTorch开始"}
)
# 基于相似度检索
relevant = memory.load_memory_variables(
{"input": "有什么编程语言推荐?"}
)
print(relevant)
# 会返回Python相关的历史,而不是按时间顺序
# 集成到Chain
from langchain.chains import ConversationChain
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
result = conversation.predict(input="什么书适合入门?")
# 会自动关联之前推荐的《机器学习实战》
---
b.混合记忆
a.功能说明
结合多种记忆类型实现混合记忆策略,如短期缓冲+长期向量记忆。近期对话使用缓冲记忆保证细节完整,历史对话使用向量记忆按需检索。通过CombinedMemory组合多个记忆组件,实现分层记忆架构,平衡性能、成本和效果。
b.代码示例
---
from langchain.memory import CombinedMemory, ConversationBufferMemory
from langchain.memory import VectorStoreRetrieverMemory
# 短期记忆:最近5轮对话
short_term = ConversationBufferWindowMemory(
k=5,
memory_key="recent_history",
input_key="input"
)
# 长期记忆:向量检索
long_term = VectorStoreRetrieverMemory(
retriever=vectorstore.as_retriever(search_kwargs={"k": 2}),
memory_key="long_term_context",
input_key="input"
)
# 组合记忆
memory = CombinedMemory(memories=[short_term, long_term])
conversation = ConversationChain(
llm=llm,
memory=memory,
verbose=True
)
# 同时使用两种记忆
result = conversation.predict(input="我们之前讨论过什么?")
# 会同时返回:
# 1. recent_history: 最近5轮的完整对话
# 2. long_term_context: 语义相关的历史对话
# 自定义混合策略
class HybridMemory(BaseMemory):
"""自定义混合记忆"""
def __init__(self, buffer_memory, vector_memory, summary_memory):
self.buffer = buffer_memory
self.vector = vector_memory
self.summary = summary_memory
def load_memory_variables(self, inputs):
# 加载各类记忆
recent = self.buffer.load_memory_variables(inputs)
relevant = self.vector.load_memory_variables(inputs)
summary = self.summary.load_memory_variables(inputs)
# 组合成统一上下文
context = f"""
摘要:{summary.get('history', '')}
相关历史:{relevant.get('history', '')}
最近对话:{recent.get('history', '')}
"""
return {"history": context.strip()}
def save_context(self, inputs, outputs):
# 同时保存到所有记忆
self.buffer.save_context(inputs, outputs)
self.vector.save_context(inputs, outputs)
self.summary.save_context(inputs, outputs)
---
02.个性化记忆
a.用户画像
a.功能说明
通过记忆系统构建用户画像,提取用户偏好、兴趣、背景等特征。可以分析对话历史生成用户标签、计算兴趣权重、更新画像属性。支持将画像存储到数据库,实现跨会话的个性化服务。适用于推荐系统、精准营销、个性化客服等场景。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
import json
class UserProfileMemory(ConversationBufferMemory):
"""用户画像记忆"""
def __init__(self, llm, user_id: str):
super().__init__(return_messages=True)
self.llm = llm
self.user_id = user_id
self.profile = self._load_profile()
def _load_profile(self):
"""从数据库加载用户画像"""
# 实际从数据库读取
return {
"user_id": self.user_id,
"name": "",
"interests": [],
"preferences": {},
"interaction_count": 0
}
def save_context(self, inputs, outputs):
super().save_context(inputs, outputs)
# 更新画像
self._update_profile(inputs["input"], outputs["output"])
def _update_profile(self, user_input: str, ai_output: str):
"""基于对话更新画像"""
# 提取用户信息
extraction_chain = LLMChain(
llm=self.llm,
prompt=PromptTemplate(
template="""从对话中提取用户信息:
用户:{input}
AI:{output}
提取JSON格式:
{{"name": "", "interests": [], "preferences": {{}}}}
""",
input_variables=["input", "output"]
)
)
result = extraction_chain.run(input=user_input, output=ai_output)
try:
extracted = json.loads(result)
# 更新画像
if extracted.get("name"):
self.profile["name"] = extracted["name"]
for interest in extracted.get("interests", []):
if interest not in self.profile["interests"]:
self.profile["interests"].append(interest)
self.profile["preferences"].update(extracted.get("preferences", {}))
self.profile["interaction_count"] += 1
# 持久化
self._save_profile()
except:
pass
def _save_profile(self):
"""保存画像到数据库"""
# 实际保存到Redis/MySQL
import redis
r = redis.Redis(host='localhost', port=6379)
r.set(
f"profile:{self.user_id}",
json.dumps(self.profile),
ex=86400 * 30 # 30天过期
)
def get_personalized_context(self):
"""获取个性化上下文"""
return f"""
用户:{self.profile['name']}
兴趣:{', '.join(self.profile['interests'])}
偏好:{self.profile['preferences']}
交互次数:{self.profile['interaction_count']}
"""
# 使用个性化记忆
memory = UserProfileMemory(llm=llm, user_id="user_12345")
# 第一次对话
memory.save_context(
{"input": "我叫张三,喜欢编程和旅游"},
{"output": "很高兴认识你张三"}
)
# 查看画像
print(memory.get_personalized_context())
# 第二次对话(可能是几天后)
memory2 = UserProfileMemory(llm=llm, user_id="user_12345")
# 自动加载之前的画像
print(f"欢迎回来,{memory2.profile['name']}")
---
b.知识图谱记忆
a.功能说明
ConversationKGMemory使用知识图谱存储对话中的实体关系,构建结构化知识网络。自动提取主谓宾三元组,如张三-工作于-阿里巴巴、LangChain-是-框架等。支持关系推理、路径查询、子图检索,适用于需要复杂关系理解的场景如智能问答、决策支持。
b.代码示例
---
from langchain.memory import ConversationKGMemory
from langchain.llms import OpenAI
llm = OpenAI(temperature=0)
# 创建知识图谱记忆
memory = ConversationKGMemory(llm=llm)
# 对话会自动构建知识图谱
memory.save_context(
{"input": "张三在阿里巴巴工作"},
{"output": "明白了"}
)
memory.save_context(
{"input": "阿里巴巴在杭州"},
{"output": "是的"}
)
memory.save_context(
{"input": "张三使用Python开发"},
{"output": "Python很流行"}
)
# 查看知识图谱
print(memory.kg.get_triples())
# [
# ('张三', '工作于', '阿里巴巴'),
# ('阿里巴巴', '位于', '杭州'),
# ('张三', '使用', 'Python')
# ]
# 基于实体检索
context = memory.load_memory_variables({"input": "张三在哪里?"})
# 会通过图谱推理:张三→工作于→阿里巴巴→位于→杭州
# 集成Neo4j
from langchain.graphs import Neo4jGraph
graph = Neo4jGraph(
url="bolt://localhost:7687",
username="neo4j",
password="password"
)
memory = ConversationKGMemory(llm=llm, kg=graph)
# 使用Cypher查询
result = graph.query("""
MATCH (p:Person {name: '张三'})-[r]->(c:Company)
RETURN p, r, c
""")
# 自定义关系提取
from langchain.chains import create_extraction_chain
schema = {
"properties": {
"subject": {"type": "string"},
"predicate": {"type": "string"},
"object": {"type": "string"}
},
"required": ["subject", "predicate", "object"]
}
extraction_chain = create_extraction_chain(schema, llm)
text = "张三在阿里巴巴工作,负责AI项目开发"
triples = extraction_chain.run(text)
print(triples)
# [
# {'subject': '张三', 'predicate': '工作于', 'object': '阿里巴巴'},
# {'subject': '张三', 'predicate': '负责', 'object': 'AI项目'}
# ]
---
b.多模态记忆
a.功能说明
支持存储和检索多模态信息,包括文本、图片、音频、视频等。将多模态内容向量化后统一存储,支持跨模态检索如文本查询图片、图片匹配对话。适用于多模态对话系统、内容管理、智能相册等场景。需要多模态Embedding模型支持。
b.代码示例
---
from langchain.schema import Document
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
# 多模态向量化
class MultiModalEmbedding:
"""多模态向量化"""
def __init__(self):
self.text_embeddings = OpenAIEmbeddings()
# 使用多模态模型如CLIP
self.image_model = self._load_clip_model()
def embed_text(self, text: str):
return self.text_embeddings.embed_query(text)
def embed_image(self, image_path: str):
# 使用CLIP编码图片
from PIL import Image
import torch
image = Image.open(image_path)
with torch.no_grad():
features = self.image_model.encode_image(image)
return features.tolist()
def _load_clip_model(self):
# 加载CLIP模型
import clip
model, _ = clip.load("ViT-B/32")
return model
# 存储多模态记忆
class MultiModalMemory:
"""多模态记忆"""
def __init__(self, vectorstore):
self.vectorstore = vectorstore
self.embeddings = MultiModalEmbedding()
def save_text(self, text: str, metadata: dict):
vector = self.embeddings.embed_text(text)
self.vectorstore.add_texts(
[text],
metadatas=[{**metadata, "type": "text"}],
embeddings=[vector]
)
def save_image(self, image_path: str, description: str, metadata: dict):
vector = self.embeddings.embed_image(image_path)
self.vectorstore.add_texts(
[description],
metadatas=[{**metadata, "type": "image", "path": image_path}],
embeddings=[vector]
)
def search(self, query: str, modality: str = "all"):
# 跨模态检索
results = self.vectorstore.similarity_search(
query,
k=5,
filter={"type": modality} if modality != "all" else None
)
return results
# 使用多模态记忆
vectorstore = Milvus(
embedding_function=OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"}
)
memory = MultiModalMemory(vectorstore)
# 保存对话
memory.save_text(
"用户询问了产品价格",
metadata={"session_id": "123", "timestamp": "2024-01-01"}
)
# 保存图片
memory.save_image(
"product_screenshot.jpg",
"产品界面截图",
metadata={"session_id": "123", "timestamp": "2024-01-01"}
)
# 跨模态检索
results = memory.search("产品相关的内容") # 同时检索文本和图片
---
02.分布式记忆
a.多租户记忆
a.功能说明
在多租户SaaS系统中,需要隔离不同用户的记忆数据。通过session_id、user_id、tenant_id等标识符实现记忆隔离。支持记忆共享、继承、迁移等高级特性,如团队共享记忆、用户切换账号保留记忆。需要考虑权限控制、数据隔离、性能优化等企业级需求。
b.代码示例
---
from langchain.memory import ConversationBufferMemory
from langchain.memory.chat_message_histories import RedisChatMessageHistory
class MultiTenantMemory:
"""多租户记忆管理"""
def __init__(self, redis_url: str):
self.redis_url = redis_url
def get_memory(self, tenant_id: str, user_id: str, session_id: str):
"""获取租户隔离的记忆"""
# 构造唯一键
full_session_id = f"{tenant_id}:{user_id}:{session_id}"
history = RedisChatMessageHistory(
url=self.redis_url,
session_id=full_session_id,
key_prefix="chat:" # 命名空间
)
memory = ConversationBufferMemory(
chat_memory=history,
return_messages=True
)
return memory
def list_user_sessions(self, tenant_id: str, user_id: str):
"""列出用户的所有会话"""
import redis
r = redis.Redis.from_url(self.redis_url)
pattern = f"chat:{tenant_id}:{user_id}:*"
keys = r.keys(pattern)
return [key.decode().split(":")[-1] for key in keys]
def share_memory(self, from_user: str, to_user: str, session_id: str):
"""共享记忆给其他用户"""
import redis
r = redis.Redis.from_url(self.redis_url)
source_key = f"chat:{from_user}:{session_id}"
target_key = f"chat:{to_user}:{session_id}"
# 复制记忆
r.copy(source_key, target_key)
# 使用多租户记忆
memory_manager = MultiTenantMemory("redis://localhost:6379")
# 租户A的用户1
memory_a1 = memory_manager.get_memory("tenant_a", "user_1", "session_001")
# 租户B的用户1(完全隔离)
memory_b1 = memory_manager.get_memory("tenant_b", "user_1", "session_001")
# 达梦数据库多租户
class DmMultiTenantMemory:
"""基于达梦数据库的多租户记忆"""
def __init__(self, connection_string: str):
self.conn_str = connection_string
self._ensure_schema()
def _ensure_schema(self):
import dmPython
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS chat_memory (
tenant_id VARCHAR(50),
user_id VARCHAR(50),
session_id VARCHAR(50),
message_type VARCHAR(20),
content TEXT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
PRIMARY KEY (tenant_id, user_id, session_id, created_at)
)
""")
# 创建索引
cursor.execute("""
CREATE INDEX IF NOT EXISTS idx_session
ON chat_memory(tenant_id, user_id, session_id)
""")
conn.commit()
conn.close()
def get_memory(self, tenant_id, user_id, session_id):
# 返回隔离的记忆实例
from langchain.memory.chat_message_histories import SQLChatMessageHistory
return SQLChatMessageHistory(
connection_string=self.conn_str,
session_id=f"{tenant_id}:{user_id}:{session_id}",
table_name="chat_memory"
)
---
8 输出解析
8.1 Structured Output
01.结构化输出
a.JSON输出
a.功能说明
LangChain支持将LLM输出解析为结构化JSON格式,便于程序处理和系统集成。通过在提示词中指定输出格式、使用函数调用功能、应用输出解析器等方式实现。结构化输出提升数据可靠性,避免自然语言解析的歧义和错误,是生产环境的标准做法。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.output_parsers import ResponseSchema, StructuredOutputParser
# 定义输出结构
response_schemas = [
ResponseSchema(
name="product_name",
description="产品名称"
),
ResponseSchema(
name="price",
description="价格,数字格式"
),
ResponseSchema(
name="features",
description="特性列表"
)
]
# 创建解析器
output_parser = StructuredOutputParser.from_response_schemas(response_schemas)
# 获取格式说明
format_instructions = output_parser.get_format_instructions()
# 构造提示词
prompt = ChatPromptTemplate.from_template(
"""提取以下产品信息:
{text}
{format_instructions}
"""
)
# 执行
llm = ChatOpenAI(temperature=0)
messages = prompt.format_messages(
text="iPhone 15 Pro售价7999元,支持5G、120Hz屏幕、A17芯片",
format_instructions=format_instructions
)
response = llm(messages)
# 解析为结构化数据
parsed = output_parser.parse(response.content)
print(parsed)
# {
# 'product_name': 'iPhone 15 Pro',
# 'price': 7999,
# 'features': ['5G', '120Hz屏幕', 'A17芯片']
# }
# 使用LCEL
chain = prompt | llm | output_parser
result = chain.invoke({"text": "产品描述..."})
---
b.函数调用输出
a.功能说明
OpenAI的函数调用功能提供原生的结构化输出支持,准确率和可靠性更高。定义函数schema描述输出格式,模型自动生成符合schema的JSON。支持复杂嵌套结构、数组、可选字段、类型验证等,是生产环境推荐的结构化输出方案。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.output_parsers.openai_functions import JsonOutputFunctionsParser
# 定义输出schema
extraction_function = {
"name": "extract_product_info",
"description": "提取产品信息",
"parameters": {
"type": "object",
"properties": {
"name": {
"type": "string",
"description": "产品名称"
},
"price": {
"type": "number",
"description": "价格"
},
"category": {
"type": "string",
"enum": ["电子产品", "服装", "食品", "其他"],
"description": "产品类别"
},
"features": {
"type": "array",
"items": {"type": "string"},
"description": "特性列表"
},
"in_stock": {
"type": "boolean",
"description": "是否有货"
}
},
"required": ["name", "price", "category"]
}
}
# 创建链
prompt = ChatPromptTemplate.from_template("提取产品信息:{text}")
llm = ChatOpenAI(model="gpt-3.5-turbo")
chain = (
prompt
| llm.bind(functions=[extraction_function], function_call={"name": "extract_product_info"})
| JsonOutputFunctionsParser()
)
# 执行
result = chain.invoke({"text": "iPhone 15 Pro 7999元,有货,支持5G和120Hz屏幕"})
print(result)
# {
# 'name': 'iPhone 15 Pro',
# 'price': 7999,
# 'category': '电子产品',
# 'features': ['5G', '120Hz屏幕'],
# 'in_stock': True
# }
---
02.输出验证
a.格式验证
a.功能说明
输出解析器提供自动格式验证,检查LLM输出是否符合预期schema。当解析失败时可以自动重试、修复错误、请求补充信息。通过Pydantic模型定义严格的类型约束,确保数据质量和类型安全。适用于对数据准确性要求高的场景如订单处理、数据录入。
b.代码示例
---
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from pydantic import BaseModel, Field, validator
from typing import List
# 定义数据模型
class ProductInfo(BaseModel):
name: str = Field(description="产品名称")
price: float = Field(description="价格,必须大于0")
category: str = Field(description="类别")
features: List[str] = Field(description="特性列表")
@validator("price")
def price_must_be_positive(cls, v):
if v <= 0:
raise ValueError("价格必须大于0")
return v
@validator("features")
def features_not_empty(cls, v):
if len(v) == 0:
raise ValueError("特性列表不能为空")
return v
# 创建解析器
parser = PydanticOutputParser(pydantic_object=ProductInfo)
# 获取格式说明
format_instructions = parser.get_format_instructions()
prompt = PromptTemplate(
template="提取产品信息:\n{text}\n\n{format_instructions}",
input_variables=["text"],
partial_variables={"format_instructions": format_instructions}
)
# 执行和解析
chain = prompt | llm | parser
try:
result = chain.invoke({"text": "iPhone 15 Pro 7999元,5G/120Hz"})
print(result)
print(f"类型:{type(result)}") # <class 'ProductInfo'>
print(f"价格:{result.price}") # 类型安全访问
except Exception as e:
print(f"解析失败:{e}")
# 嵌套模型
class Address(BaseModel):
city: str
district: str
street: str
class Customer(BaseModel):
name: str
age: int
address: Address # 嵌套对象
orders: List[ProductInfo] # 嵌套数组
parser = PydanticOutputParser(pydantic_object=Customer)
chain = prompt | llm | parser
result = chain.invoke({"text": "客户信息..."})
print(result.address.city) # 访问嵌套属性
---
8.2 PydanticOutputParser
01.Pydantic模型
a.模型定义
a.功能说明
PydanticOutputParser基于Pydantic模型定义输出结构,提供类型安全、数据验证、默认值、字段约束等强大功能。Pydantic是Python标准的数据验证库,支持复杂类型、嵌套模型、自定义验证器、序列化配置等。通过类型注解和Field描述符清晰定义数据结构,自动生成JSON Schema供LLM参考。
b.代码示例
---
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field, validator
from typing import List, Optional
from enum import Enum
from datetime import datetime
# 枚举类型
class OrderStatus(str, Enum):
PENDING = "待处理"
PROCESSING = "处理中"
COMPLETED = "已完成"
CANCELLED = "已取消"
# 嵌套模型
class OrderItem(BaseModel):
product_id: str = Field(description="产品ID")
name: str = Field(description="产品名称")
quantity: int = Field(ge=1, description="数量,至少为1")
price: float = Field(gt=0, description="单价,必须大于0")
@property
def total(self) -> float:
"""计算小计"""
return self.quantity * self.price
# 主模型
class Order(BaseModel):
order_id: str = Field(description="订单编号")
customer_name: str = Field(description="客户姓名")
phone: str = Field(regex=r"^1[3-9]\d{9}$", description="手机号")
items: List[OrderItem] = Field(description="订单项列表")
status: OrderStatus = Field(default=OrderStatus.PENDING)
created_at: Optional[datetime] = Field(default_factory=datetime.now)
notes: Optional[str] = Field(None, description="备注信息")
@validator("items")
def items_not_empty(cls, v):
if len(v) == 0:
raise ValueError("订单至少包含一个商品")
return v
@property
def total_amount(self) -> float:
"""计算订单总金额"""
return sum(item.total for item in self.items)
# 创建解析器
parser = PydanticOutputParser(pydantic_object=Order)
# 生成格式说明
format_instructions = parser.get_format_instructions()
print(format_instructions)
# 使用
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
prompt = PromptTemplate(
template="从文本中提取订单信息:\n{text}\n\n{format_instructions}",
input_variables=["text"],
partial_variables={"format_instructions": format_instructions}
)
chain = prompt | ChatOpenAI(temperature=0) | parser
result = chain.invoke({
"text": "客户张三,电话13812345678,订购2个iPhone(7999元)和1个iPad(5999元)"
})
print(f"订单号:{result.order_id}")
print(f"总金额:{result.total_amount}元") # 自动计算
print(f"状态:{result.status.value}")
---
b.类型转换
a.功能说明
Pydantic自动处理类型转换,如字符串转数字、日期解析、枚举映射等。支持自定义转换器处理特殊格式,如金额单位转换、日期格式统一、编码转换等。提供pre和root_validator实现复杂验证逻辑,确保数据一致性和完整性。
b.代码示例
---
from pydantic import BaseModel, Field, validator, root_validator
from datetime import datetime
from typing import Union
class Transaction(BaseModel):
"""交易记录"""
amount: float = Field(description="金额")
currency: str = Field(default="CNY", description="货币单位")
date: Union[str, datetime] = Field(description="交易日期")
category: str = Field(description="类别")
@validator("amount", pre=True)
def parse_amount(cls, v):
"""解析金额字符串"""
if isinstance(v, str):
# 移除货币符号和逗号
v = v.replace("¥", "").replace("$", "").replace(",", "")
return float(v)
return v
@validator("date", pre=True)
def parse_date(cls, v):
"""解析日期"""
if isinstance(v, str):
# 支持多种日期格式
for fmt in ["%Y-%m-%d", "%Y/%m/%d", "%Y年%m月%d日"]:
try:
return datetime.strptime(v, fmt)
except:
continue
raise ValueError(f"无法解析日期:{v}")
return v
@root_validator
def check_consistency(cls, values):
"""跨字段验证"""
amount = values.get("amount")
category = values.get("category")
# 业务规则验证
if category == "大额交易" and amount < 10000:
raise ValueError("大额交易金额必须大于10000")
return values
# 使用
parser = PydanticOutputParser(pydantic_object=Transaction)
# LLM输出会自动转换
result = parser.parse('''
{
"amount": "¥12,888.50",
"currency": "CNY",
"date": "2024年1月15日",
"category": "大额交易"
}
''')
print(f"金额:{result.amount}") # 12888.5 (float类型)
print(f"日期:{result.date}") # datetime对象
---
02.高级解析
a.JsonOutputParser
a.功能说明
JsonOutputParser是轻量级的JSON解析器,无需定义Pydantic模型,直接解析LLM输出的JSON字符串。支持提取代码块中的JSON、处理格式错误、自动修复等。适用于输出格式简单、快速原型开发的场景,但缺少类型验证和数据校验能力。
b.代码示例
---
from langchain.output_parsers import JsonOutputParser
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
# 创建JSON解析器
parser = JsonOutputParser()
# 提示词
prompt = PromptTemplate(
template="""提取信息并返回JSON格式:
{text}
返回格式:{{"name": "", "age": 0, "city": ""}}
""",
input_variables=["text"]
)
chain = prompt | ChatOpenAI() | parser
result = chain.invoke({"text": "张三,28岁,住在北京"})
print(result) # {'name': '张三', 'age': 28, 'city': '北京'}
# 处理代码块包裹的JSON
text_with_code = """
```json
{
"name": "李四",
"age": 30
}
```
"""
result = parser.parse(text_with_code)
print(result) # 自动提取JSON
# 处理格式错误
malformed_json = """
{
name: "王五", // 缺少引号
age: 25
}
"""
try:
result = parser.parse(malformed_json)
except Exception as e:
print(f"解析失败:{e}")
# 可以重试或使用OutputFixingParser修复
# 列表输出
from langchain.output_parsers import JsonOutputParser
list_parser = JsonOutputParser()
prompt = PromptTemplate.from_template(
"列出{topic}的前5名:返回JSON数组"
)
chain = prompt | llm | list_parser
result = chain.invoke({"topic": "编程语言"})
print(result) # ['Python', 'JavaScript', 'Java', 'C++', 'Go']
---
8.3 OutputFixingParser
01.错误修复
a.自动修复
a.功能说明
OutputFixingParser在解析失败时自动调用LLM修复错误输出。将原始输出和解析错误信息发送给LLM,让其修正格式错误、补全缺失字段、调整类型等。相比直接重试更智能,可以理解错误原因并针对性修复。适用于LLM输出不稳定、格式要求严格的场景。
b.代码示例
---
from langchain.output_parsers import PydanticOutputParser, OutputFixingParser
from pydantic import BaseModel, Field
from langchain.chat_models import ChatOpenAI
# 定义严格的输出模型
class StrictProduct(BaseModel):
name: str = Field(description="产品名称")
price: float = Field(gt=0, description="价格必须大于0")
stock: int = Field(ge=0, description="库存不能为负")
# 基础解析器
base_parser = PydanticOutputParser(pydantic_object=StrictProduct)
# 错误的LLM输出
bad_output = """
{
"name": "iPhone",
"price": "七千九百九十九", // 错误:应该是数字
"stock": -5 // 错误:负数库存
}
"""
# 直接解析会失败
try:
result = base_parser.parse(bad_output)
except Exception as e:
print(f"解析失败:{e}")
# 使用修复解析器
fixing_parser = OutputFixingParser.from_llm(
parser=base_parser,
llm=ChatOpenAI(temperature=0)
)
# 自动修复并解析
result = fixing_parser.parse(bad_output)
print(result)
# StrictProduct(name='iPhone', price=7999.0, stock=0)
# 自动修正:价格转数字、负数库存改为0
# 查看修复过程
fixing_parser = OutputFixingParser.from_llm(
parser=base_parser,
llm=ChatOpenAI(temperature=0),
verbose=True # 打印修复过程
)
result = fixing_parser.parse(bad_output)
# 自定义修复策略
from langchain.prompts import PromptTemplate
fix_prompt = PromptTemplate(
template="""原始输出有错误,请修正:
原始输出:
{completion}
错误信息:
{error}
要求输出格式:
{instructions}
请修正输出:""",
input_variables=["completion", "error", "instructions"]
)
fixing_parser = OutputFixingParser(
parser=base_parser,
retry_chain=LLMChain(llm=llm, prompt=fix_prompt)
)
---
b.多次重试
a.功能说明
当首次修复仍然失败时,可以配置多次重试策略。每次重试会在提示词中包含之前的失败信息,帮助LLM理解问题所在。可以配置最大重试次数、退避策略、修复提示词模板等。建议结合RetryOutputParser使用,实现更robust的解析能力。
b.代码示例
---
from langchain.output_parsers import RetryWithErrorOutputParser
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
# 基础解析器
base_parser = PydanticOutputParser(pydantic_object=StrictProduct)
# 重试解析器
llm = ChatOpenAI(temperature=0)
retry_parser = RetryWithErrorOutputParser.from_llm(
parser=base_parser,
llm=llm,
max_retries=3 # 最多重试3次
)
# 需要原始提示词
prompt = PromptTemplate.from_template("提取产品信息:{text}")
# 解析
result = retry_parser.parse_with_prompt(
bad_output,
prompt.format(text="产品描述...")
)
# 自定义重试策略
class SmartRetryParser(RetryWithErrorOutputParser):
"""智能重试解析器"""
def __init__(self, parser, llm, max_retries=3):
super().__init__(parser=parser, llm=llm)
self.max_retries = max_retries
self.retry_count = 0
def parse_with_prompt(self, completion, prompt_value):
self.retry_count = 0
while self.retry_count < self.max_retries:
try:
return self.parser.parse(completion)
except Exception as e:
self.retry_count += 1
# 记录失败
print(f"第{self.retry_count}次尝试失败:{e}")
# 调整temperature提升多样性
self.llm.temperature = min(0.9, self.llm.temperature + 0.2)
# 重新生成
completion = self._get_retry_completion(
completion, e, prompt_value
)
raise ValueError(f"重试{self.max_retries}次后仍然失败")
parser = SmartRetryParser(base_parser, llm, max_retries=5)
# 链式重试
from langchain.schema.runnable import RunnablePassthrough
chain = (
prompt
| llm
| {"completion": RunnablePassthrough(), "prompt": lambda _: prompt}
| retry_parser
)
---
02.降级策略
a.宽松解析
a.功能说明
当严格解析多次失败后,可以降级到宽松解析模式,牺牲部分准确性换取可用性。使用更宽松的数据模型、提供默认值、跳过可选字段、容忍格式差异等。可以配置降级触发条件、降级策略优先级、最终兜底方案,确保系统在各种情况下都能返回结果。
b.代码示例
---
from pydantic import BaseModel, Field
from typing import Optional
# 严格模型
class StrictModel(BaseModel):
name: str = Field(description="必需字段")
age: int = Field(gt=0, le=150, description="年龄1-150")
email: str = Field(regex=r"^[\w\.-]+@[\w\.-]+\.\w+$")
# 宽松模型
class RelaxedModel(BaseModel):
name: Optional[str] = Field(default="未知")
age: Optional[int] = Field(default=0)
email: Optional[str] = Field(default="")
# 降级解析器
class FallbackParser:
"""降级解析器"""
def __init__(self, strict_parser, relaxed_parser, llm):
self.strict_parser = strict_parser
self.relaxed_parser = relaxed_parser
self.llm = llm
self.fixing_parser = OutputFixingParser.from_llm(
parser=strict_parser,
llm=llm
)
def parse(self, text: str):
# 1. 尝试严格解析
try:
return self.strict_parser.parse(text), "strict"
except Exception as e1:
print(f"严格解析失败:{e1}")
# 2. 尝试自动修复
try:
return self.fixing_parser.parse(text), "fixed"
except Exception as e2:
print(f"修复失败:{e2}")
# 3. 降级到宽松解析
try:
return self.relaxed_parser.parse(text), "relaxed"
except Exception as e3:
print(f"宽松解析失败:{e3}")
# 4. 返回空对象
return RelaxedModel(), "empty"
# 使用降级解析
strict = PydanticOutputParser(pydantic_object=StrictModel)
relaxed = PydanticOutputParser(pydantic_object=RelaxedModel)
parser = FallbackParser(strict, relaxed, llm)
bad_data = '{"name": "张三", "age": "unknown", "email": "invalid"}'
result, mode = parser.parse(bad_data)
print(f"解析模式:{mode}")
print(f"结果:{result}")
# 自定义默认值策略
class SmartDefaultModel(BaseModel):
"""智能默认值模型"""
name: str = Field(default="匿名用户")
age: int = Field(default=0)
@validator("age", pre=True, always=True)
def set_default_age(cls, v):
if v is None or v == "":
return 0
if isinstance(v, str):
# 尝试提取数字
import re
match = re.search(r'\d+', v)
if match:
return int(match.group())
return v
---
8.4 RetryOutputParser
01.重试机制
a.基础重试
a.功能说明
RetryOutputParser在解析失败时重新调用LLM生成输出。将原始提示词、错误输出和解析错误信息一起发送给LLM,让其基于错误反馈重新生成。相比OutputFixingParser直接修复输出,RetryParser从源头重新生成,适用于输出严重错误、修复成本高的场景。
b.代码示例
---
from langchain.output_parsers import PydanticOutputParser, RetryWithErrorOutputParser
from pydantic import BaseModel, Field
from langchain.chat_models import ChatOpenAI
from langchain.prompts import PromptTemplate
# 定义输出模型
class Analysis(BaseModel):
sentiment: str = Field(description="情感:正面/负面/中性")
score: float = Field(ge=0, le=1, description="置信度0-1")
keywords: list[str] = Field(description="关键词列表")
# 基础解析器
base_parser = PydanticOutputParser(pydantic_object=Analysis)
# 重试解析器
llm = ChatOpenAI(temperature=0)
retry_parser = RetryWithErrorOutputParser.from_llm(
parser=base_parser,
llm=llm
)
# 原始提示词
prompt = PromptTemplate(
template="分析以下文本的情感:\n{text}\n\n{format_instructions}",
input_variables=["text"],
partial_variables={"format_instructions": base_parser.get_format_instructions()}
)
# 首次调用(可能失败)
chain = prompt | llm
completion = chain.invoke({"text": "这个产品很不错"})
# 重试解析
try:
result = retry_parser.parse_with_prompt(
completion.content,
prompt.format(text="这个产品很不错")
)
print(result)
except Exception as e:
print(f"重试后仍失败:{e}")
# 配置重试参数
from langchain.output_parsers import RetryOutputParser
retry_parser = RetryOutputParser(
parser=base_parser,
retry_chain=LLMChain(
llm=llm,
prompt=PromptTemplate(
template="""之前的输出有错误,请重新生成:
原始提示词:{prompt}
错误输出:{completion}
错误信息:{error}
请严格按照格式要求重新生成:""",
input_variables=["prompt", "completion", "error"]
)
),
max_retries=3
)
---
b.渐进式重试
a.功能说明
渐进式重试策略在每次重试时逐步放宽要求,从严格验证到宽松验证。首次尝试最严格的格式和类型约束,失败后放宽部分限制,最后降级到最低可接受标准。通过多级降级确保在各种情况下都能返回可用结果,同时尽可能保证数据质量。
b.代码示例
---
from pydantic import BaseModel, Field
from typing import Optional
# 第1级:严格模型
class Level1Product(BaseModel):
name: str = Field(min_length=2, description="名称至少2字符")
price: float = Field(gt=0, le=100000, description="价格0-100000")
category: str = Field(regex="^(电子|服装|食品)$")
stock: int = Field(ge=0, le=10000)
# 第2级:中等严格
class Level2Product(BaseModel):
name: str = Field(min_length=1)
price: float = Field(gt=0)
category: str
stock: Optional[int] = Field(default=0)
# 第3级:最宽松
class Level3Product(BaseModel):
name: Optional[str] = Field(default="未知产品")
price: Optional[float] = Field(default=0.0)
category: Optional[str] = Field(default="其他")
stock: Optional[int] = Field(default=0)
# 渐进式解析器
class ProgressiveRetryParser:
"""渐进式重试解析器"""
def __init__(self, llm):
self.llm = llm
self.parsers = [
PydanticOutputParser(pydantic_object=Level1Product),
PydanticOutputParser(pydantic_object=Level2Product),
PydanticOutputParser(pydantic_object=Level3Product)
]
self.fixing_parsers = [
OutputFixingParser.from_llm(parser=p, llm=llm)
for p in self.parsers
]
def parse(self, text: str, prompt: str):
for i, parser in enumerate(self.fixing_parsers):
try:
result = parser.parse_with_prompt(text, prompt)
print(f"使用Level {i+1}解析成功")
return result, i+1
except Exception as e:
print(f"Level {i+1}解析失败:{e}")
if i < len(self.parsers) - 1:
print(f"降级到Level {i+2}...")
continue
raise ValueError("所有级别的解析都失败了")
# 使用
parser = ProgressiveRetryParser(llm)
bad_data = '{"name": "A", "price": 999999, "category": "未知"}'
result, level = parser.parse(bad_data, "提取产品信息")
print(f"使用Level {level}解析:{result}")
---
02.智能恢复
a.上下文补充
a.功能说明
当输出信息不完整时,可以基于上下文自动补充缺失字段。分析提示词中的信息、参考历史记录、使用默认值等策略填补空缺。通过ContextualRetryParser实现上下文感知的重试,提升解析成功率和数据完整性。
b.代码示例
---
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
from langchain.chains import LLMChain
class ContextualRetryParser:
"""上下文感知重试解析器"""
def __init__(self, parser, llm, context_sources: dict):
self.parser = parser
self.llm = llm
self.context_sources = context_sources # 上下文数据源
def parse(self, text: str, original_input: dict):
try:
return self.parser.parse(text)
except Exception as e:
# 从上下文补充信息
return self._补充_and_retry(text, original_input, e)
def _补充_and_retry(self, text, original_input, error):
# 分析缺失字段
missing_fields = self._extract_missing_fields(str(error))
# 从上下文查找
补充_data = {}
for field in missing_fields:
if field in self.context_sources:
补充_data[field] = self.context_sources[field]
elif field in original_input:
补充_data[field] = original_input[field]
# 重新生成
retry_prompt = f"""之前的输出缺少字段,请补充:
原始输出:{text}
缺失字段及参考值:{补充_data}
请生成完整输出:"""
completion = self.llm.predict(retry_prompt)
return self.parser.parse(completion)
def _extract_missing_fields(self, error_msg):
# 从错误信息提取缺失字段
import re
matches = re.findall(r"field '(\w+)' required", error_msg)
return matches
# 使用
context = {
"default_category": "电子产品",
"default_currency": "CNY",
"default_stock": 100
}
parser = ContextualRetryParser(base_parser, llm, context)
incomplete_output = '{"name": "iPhone"}' # 缺少price和stock
result = parser.parse(incomplete_output, {"category": "电子产品"})
# 历史记忆补充
from langchain.memory import ConversationBufferMemory
class MemoryAugmentedParser:
"""记忆增强解析器"""
def __init__(self, parser, llm, memory):
self.parser = parser
self.llm = llm
self.memory = memory
def parse(self, text: str):
try:
return self.parser.parse(text)
except:
# 从记忆中查找相关信息
memory_vars = self.memory.load_memory_variables({})
history = memory_vars.get("history", "")
# 使用历史补充
retry_prompt = f"""基于历史对话补充缺失信息:
历史:{history}
当前输出:{text}
请生成完整输出:"""
completion = self.llm.predict(retry_prompt)
return self.parser.parse(completion)
memory = ConversationBufferMemory()
parser = MemoryAugmentedParser(base_parser, llm, memory)
---
8.5 回退机制
01.RunnableWithFallbacks
a.异常回退
a.功能说明
RunnableWithFallbacks在主链执行失败时自动切换到备用链,提升系统鲁棒性。可以配置多级回退链、回退条件、异常类型匹配等。支持降级策略如使用更简单的模型、缓存结果、默认回复等。适用于需要高可用性的生产环境,确保服务始终可用。
b.代码示例
---
from langchain.schema.runnable import RunnableWithFallbacks
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI, ChatAnthropic
from langchain.schema.output_parser import StrOutputParser
# 主链(使用GPT-4)
primary_chain = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatOpenAI(model="gpt-4", temperature=0)
| StrOutputParser()
)
# 备用链1(使用GPT-3.5)
fallback_chain_1 = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
| StrOutputParser()
)
# 备用链2(使用Claude)
fallback_chain_2 = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatAnthropic(model="claude-2")
| StrOutputParser()
)
# 配置回退
chain_with_fallbacks = primary_chain.with_fallbacks(
[fallback_chain_1, fallback_chain_2]
)
# 执行(主链失败自动切换)
result = chain_with_fallbacks.invoke({"question": "什么是AI?"})
# 使用RunnableWithFallbacks类
chain = RunnableWithFallbacks(
runnable=primary_chain,
fallbacks=[fallback_chain_1, fallback_chain_2]
)
# 配置回退参数
chain_with_fallbacks = primary_chain.with_fallbacks(
fallbacks=[fallback_chain_1, fallback_chain_2],
exceptions_to_handle=(TimeoutError, ConnectionError), # 只处理特定异常
exception_key="error" # 将异常信息传递给下一个链
)
# 条件回退
def should_fallback(exception):
"""判断是否应该回退"""
if isinstance(exception, TimeoutError):
return True
if isinstance(exception, APIError) and exception.status_code == 429:
return True # API限流时回退
return False
# 信创环境回退链
# 主链:使用GPT
primary = (
prompt
| ChatOpenAI(api_base="https://api.openai.com")
| parser
)
# 回退链:使用国产模型
fallback_qwen = (
prompt
| ChatOpenAI(
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-turbo"
)
| parser
)
fallback_local = (
prompt
| Ollama(model="qwen2.5:7b")
| parser
)
chain = primary.with_fallbacks([fallback_qwen, fallback_local])
---
b.降级策略
a.功能说明
当服务质量下降但未完全失败时,可以主动降级到更快更便宜的方案。包括模型降级(GPT-4→GPT-3.5→本地模型)、功能降级(完整分析→简要总结→关键词提取)、数据降级(实时查询→缓存结果→默认回复)等策略。降级策略平衡质量和可用性,确保用户体验。
b.代码示例
---
import time
from langchain.schema.runnable import Runnable
# 响应时间监控
class PerformanceMonitor(Runnable):
"""性能监控和自动降级"""
def __init__(self, primary, fallback, threshold_ms=3000):
self.primary = primary
self.fallback = fallback
self.threshold_ms = threshold_ms
self.recent_times = []
def invoke(self, input, config=None):
start = time.time()
try:
result = self.primary.invoke(input, config)
elapsed = (time.time() - start) * 1000
self.recent_times.append(elapsed)
if len(self.recent_times) > 10:
self.recent_times.pop(0)
# 平均响应时间过长,降级
avg_time = sum(self.recent_times) / len(self.recent_times)
if avg_time > self.threshold_ms:
print(f"平均响应{avg_time}ms,超过阈值,降级")
return self.fallback.invoke(input, config)
return result
except Exception as e:
print(f"主链失败,降级:{e}")
return self.fallback.invoke(input, config)
monitor = PerformanceMonitor(
primary=gpt4_chain,
fallback=gpt35_chain,
threshold_ms=2000
)
# 成本控制降级
class CostControlledChain(Runnable):
"""成本控制链"""
def __init__(self, chains, daily_budget=10.0):
self.chains = chains # 按成本从高到低排序
self.daily_budget = daily_budget
self.daily_cost = 0
self.last_reset = datetime.now().date()
def invoke(self, input, config=None):
# 每天重置
today = datetime.now().date()
if today > self.last_reset:
self.daily_cost = 0
self.last_reset = today
# 选择合适的链
for chain, cost_per_call in self.chains:
if self.daily_cost + cost_per_call <= self.daily_budget:
result = chain.invoke(input, config)
self.daily_cost += cost_per_call
return result
# 预算耗尽,使用最便宜的
return self.chains[-1][0].invoke(input, config)
cost_chain = CostControlledChain(
chains=[
(gpt4_chain, 0.03), # GPT-4: $0.03/call
(gpt35_chain, 0.002), # GPT-3.5: $0.002/call
(local_chain, 0.0) # 本地模型: 免费
],
daily_budget=5.0
)
# 功能降级
def degraded_analysis(text):
"""降级的分析功能"""
if len(text) > 5000:
# 长文本只返回关键词
return extract_keywords_chain.invoke({"text": text})
elif len(text) > 1000:
# 中等文本返回摘要
return summarize_chain.invoke({"text": text})
else:
# 短文本完整分析
return full_analysis_chain.invoke({"text": text})
# 缓存回退
from functools import lru_cache
import redis
class CachedFallbackChain(Runnable):
"""带缓存回退的链"""
def __init__(self, primary, redis_client):
self.primary = primary
self.redis = redis_client
def invoke(self, input, config=None):
# 生成缓存键
import hashlib
cache_key = f"cache:{hashlib.md5(str(input).encode()).hexdigest()}"
try:
# 尝试主链
result = self.primary.invoke(input, config)
# 缓存结果
self.redis.setex(cache_key, 3600, json.dumps(result))
return result
except:
# 主链失败,尝试缓存
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)
# 缓存也没有,返回默认回复
return "抱歉,服务暂时不可用,请稍后重试。"
---
02.错误恢复
a.重试机制
a.功能说明
在暂时性错误时自动重试,如网络超时、API限流、临时故障等。配置重试次数、退避策略、重试条件等参数。支持指数退避、固定延迟、随机抖动等退避算法。重试机制提升系统稳定性,减少偶发错误影响。
b.代码示例
---
import time
import random
from langchain.schema.runnable import Runnable
class RetryableChain(Runnable):
"""支持重试的链"""
def __init__(self, chain, max_retries=3, backoff_factor=2):
self.chain = chain
self.max_retries = max_retries
self.backoff_factor = backoff_factor
def invoke(self, input, config=None):
last_exception = None
for attempt in range(self.max_retries + 1):
try:
return self.chain.invoke(input, config)
except Exception as e:
last_exception = e
if attempt < self.max_retries:
# 计算退避时间
wait_time = self.backoff_factor ** attempt
# 添加随机抖动
wait_time += random.uniform(0, 1)
print(f"第{attempt + 1}次尝试失败,{wait_time}秒后重试")
time.sleep(wait_time)
else:
print(f"重试{self.max_retries}次后仍失败")
raise last_exception
retry_chain = RetryableChain(
chain=primary_chain,
max_retries=3,
backoff_factor=2
)
# 条件重试
class ConditionalRetry(Runnable):
"""条件重试"""
def __init__(self, chain, max_retries=3):
self.chain = chain
self.max_retries = max_retries
def should_retry(self, exception):
"""判断是否应该重试"""
# 网络错误重试
if isinstance(exception, (TimeoutError, ConnectionError)):
return True
# API限流重试
if isinstance(exception, APIError) and exception.status_code == 429:
return True
# 其他错误不重试
return False
def invoke(self, input, config=None):
for attempt in range(self.max_retries + 1):
try:
return self.chain.invoke(input, config)
except Exception as e:
if attempt < self.max_retries and self.should_retry(e):
wait = 2 ** attempt
time.sleep(wait)
else:
raise
# 结合重试和回退
retry_then_fallback = (
RetryableChain(primary_chain, max_retries=2)
.with_fallbacks([fallback_chain])
)
# 使用tenacity库
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def reliable_invoke(chain, input_data):
return chain.invoke(input_data)
result = reliable_invoke(chain, {"question": "..."})
---
b.熔断保护
a.功能说明
当服务持续失败时启动熔断保护,暂时停止调用避免雪崩效应。熔断器有关闭、打开、半开三种状态,根据失败率和失败次数自动切换。半开状态定期尝试恢复,成功后关闭熔断器。熔断保护确保系统整体稳定性,避免级联故障。
b.代码示例
---
import time
from enum import Enum
from datetime import datetime, timedelta
class CircuitState(Enum):
CLOSED = "closed" # 正常状态
OPEN = "open" # 熔断打开
HALF_OPEN = "half_open" # 半开状态
class CircuitBreaker(Runnable):
"""熔断器"""
def __init__(
self,
chain,
failure_threshold=5, # 失败阈值
success_threshold=2, # 恢复阈值
timeout=60, # 熔断超时
half_open_timeout=10 # 半开超时
):
self.chain = chain
self.failure_threshold = failure_threshold
self.success_threshold = success_threshold
self.timeout = timeout
self.half_open_timeout = half_open_timeout
self.state = CircuitState.CLOSED
self.failure_count = 0
self.success_count = 0
self.last_failure_time = None
self.last_attempt_time = None
def invoke(self, input, config=None):
# 检查是否应该从打开状态转为半开
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.timeout:
print("熔断器进入半开状态")
self.state = CircuitState.HALF_OPEN
self.success_count = 0
else:
raise Exception("熔断器打开,拒绝调用")
# 记录尝试时间
self.last_attempt_time = time.time()
try:
result = self.chain.invoke(input, config)
# 调用成功
self.failure_count = 0
if self.state == CircuitState.HALF_OPEN:
self.success_count += 1
# 达到成功阈值,关闭熔断器
if self.success_count >= self.success_threshold:
print("熔断器关闭")
self.state = CircuitState.CLOSED
return result
except Exception as e:
# 调用失败
self.failure_count += 1
self.last_failure_time = time.time()
# 达到失败阈值,打开熔断器
if self.failure_count >= self.failure_threshold:
print(f"连续失败{self.failure_count}次,打开熔断器")
self.state = CircuitState.OPEN
# 半开状态失败,立即打开
if self.state == CircuitState.HALF_OPEN:
print("半开状态失败,重新打开熔断器")
self.state = CircuitState.OPEN
raise e
# 使用熔断器
protected_chain = CircuitBreaker(
chain=primary_chain,
failure_threshold=5,
timeout=60
)
# 熔断器 + 降级
class CircuitBreakerWithFallback(CircuitBreaker):
"""带降级的熔断器"""
def __init__(self, chain, fallback, **kwargs):
super().__init__(chain, **kwargs)
self.fallback = fallback
def invoke(self, input, config=None):
try:
return super().invoke(input, config)
except Exception as e:
if self.state == CircuitState.OPEN:
# 熔断打开时使用降级
print("使用降级服务")
return self.fallback.invoke(input, config)
raise e
protected_with_fallback = CircuitBreakerWithFallback(
chain=primary_chain,
fallback=fallback_chain,
failure_threshold=5,
timeout=60
)
# 监控熔断状态
def monitor_circuit_breaker(breaker: CircuitBreaker):
"""监控熔断器状态"""
while True:
status = {
"state": breaker.state.value,
"failure_count": breaker.failure_count,
"success_count": breaker.success_count
}
print(f"熔断器状态:{status}")
time.sleep(10)
---
9 LCEL表达式
9.1 基础语法
01.管道符组合
a.链式调用
a.功能说明
LCEL(LangChain Expression Language)使用管道符|串联组件,构建声明式的执行链。每个组件都实现Runnable接口,支持invoke、batch、stream等统一方法。管道符自动处理数据流转,前一个组件的输出成为后一个组件的输入。相比传统嵌套调用,LCEL语法更简洁直观,便于理解和维护。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 基础链式组合
prompt = ChatPromptTemplate.from_template("将以下内容翻译成{language}:{text}")
model = ChatOpenAI(temperature=0)
parser = StrOutputParser()
# 使用管道符串联
chain = prompt | model | parser
# 执行
result = chain.invoke({
"language": "英文",
"text": "你好,世界"
})
print(result) # "Hello, World"
# 传统写法对比
# 不使用LCEL(繁琐)
messages = prompt.format_messages(language="英文", text="你好,世界")
response = model(messages)
result = parser.parse(response.content)
# 使用LCEL(简洁)
result = chain.invoke({"language": "英文", "text": "你好,世界"})
# 多阶段处理链
summarize = ChatPromptTemplate.from_template("总结以下内容:{text}")
translate = ChatPromptTemplate.from_template("翻译成英文:{summary}")
full_chain = (
summarize
| model
| StrOutputParser()
| (lambda summary: {"summary": summary})
| translate
| model
| StrOutputParser()
)
result = full_chain.invoke({"text": "很长的中文文档..."})
---
b.数据转换
a.功能说明
在链中可以插入Python函数进行数据转换,如字段提取、格式转换、过滤处理等。使用lambda或自定义函数包装转换逻辑,实现灵活的数据流控制。支持RunnablePassthrough透传数据、RunnableLambda包装函数、RunnableMap字典映射等高级转换模式。
b.代码示例
---
from langchain.schema.runnable import RunnablePassthrough, RunnableLambda
# Lambda转换
chain = (
{"text": lambda x: x["content"].upper()} # 转大写
| ChatPromptTemplate.from_template("处理:{text}")
| model
| StrOutputParser()
| (lambda x: x.strip()) # 去空格
)
# 自定义转换函数
def preprocess(data: dict) -> dict:
"""预处理数据"""
return {
"text": data["raw_text"].strip(),
"lang": data.get("language", "中文")
}
def postprocess(text: str) -> dict:
"""后处理结果"""
return {
"result": text,
"length": len(text),
"timestamp": datetime.now()
}
chain = (
RunnableLambda(preprocess)
| prompt
| model
| StrOutputParser()
| RunnableLambda(postprocess)
)
# RunnablePassthrough透传
from langchain.schema.runnable import RunnablePassthrough
chain = (
RunnablePassthrough.assign(
summary=summarize | model | StrOutputParser()
)
| (lambda x: {
"original": x["text"],
"summary": x["summary"]
})
)
result = chain.invoke({"text": "原文..."})
# {'original': '原文...', 'summary': '摘要...'}
# 条件转换
def route_by_length(data):
if len(data["text"]) > 1000:
return {"text": data["text"][:1000] + "..."} # 截断
return data
chain = (
RunnableLambda(route_by_length)
| prompt
| model
| StrOutputParser()
)
---
02.Runnable组件
a.RunnablePassthrough
a.功能说明
RunnablePassthrough用于透传输入数据,常用于需要保留原始输入的场景。支持assign方法添加新字段,pick方法选择字段。可以在链中插入Passthrough保持数据结构,便于后续组件访问原始信息。
b.代码示例
---
from langchain.schema.runnable import RunnablePassthrough
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 基础透传
chain = (
RunnablePassthrough()
| ChatPromptTemplate.from_template("问题:{question}")
| ChatOpenAI()
| StrOutputParser()
)
result = chain.invoke({"question": "什么是AI?"})
# assign添加字段
chain = (
RunnablePassthrough.assign(
answer=ChatPromptTemplate.from_template("{question}") | model | StrOutputParser()
)
| (lambda x: f"问:{x['question']}\n答:{x['answer']}")
)
result = chain.invoke({"question": "什么是LangChain?"})
# 输出包含原始问题和生成答案
# pick选择字段
chain = (
{
"question": lambda x: x["query"],
"context": lambda x: x["documents"]
}
| ChatPromptTemplate.from_template("基于{context}回答:{question}")
| model
| StrOutputParser()
)
# 复杂数据流
chain = (
RunnablePassthrough.assign(
summary=summarize_chain,
keywords=extract_keywords_chain
)
| RunnablePassthrough.assign(
combined=lambda x: f"{x['summary']}\n关键词:{', '.join(x['keywords'])}"
)
| {"result": lambda x: x["combined"], "metadata": lambda x: x}
)
---
b.RunnableParallel
a.功能说明
RunnableParallel实现并行执行多个链,提升处理效率。将输入同时发送给多个分支,收集所有分支的输出并合并。可以用字典语法或RunnableParallel类创建并行链,自动处理并发和结果聚合。
b.代码示例
---
from langchain.schema.runnable import RunnableParallel
# 字典语法创建并行链
parallel = {
"summary": summarize_prompt | model | StrOutputParser(),
"keywords": keywords_prompt | model | StrOutputParser(),
"sentiment": sentiment_prompt | model | StrOutputParser()
}
results = parallel.invoke({"text": "产品评论文本..."})
# {
# 'summary': '产品总结...',
# 'keywords': ['质量', '价格', '服务'],
# 'sentiment': '正面'
# }
# RunnableParallel类
parallel_chain = RunnableParallel(
translation=translate_chain,
sentiment=sentiment_chain,
entities=entity_extraction_chain
)
results = parallel_chain.invoke({"text": "..."})
# 嵌套并行
chain = (
{"text": RunnablePassthrough()}
| RunnableParallel(
chinese=chinese_chain,
english=english_chain
)
| RunnableParallel(
comparison=compare_chain,
summary=summary_chain
)
)
# 并行检索
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
vectorstore1 = Milvus(...)
vectorstore2 = Milvus(...)
retrieval_chain = RunnableParallel(
docs1=vectorstore1.as_retriever(),
docs2=vectorstore2.as_retriever()
)
results = retrieval_chain.invoke("查询文本")
# {'docs1': [doc1, doc2, ...], 'docs2': [doc3, doc4, ...]}
# 合并并行结果
def merge_results(parallel_output):
all_docs = parallel_output["docs1"] + parallel_output["docs2"]
# 去重、排序
return sorted(set(all_docs), key=lambda d: d.metadata["score"])
chain = retrieval_chain | RunnableLambda(merge_results)
---
9.2 流式处理
01.流式输出
a.stream方法
a.功能说明
LCEL链原生支持流式处理,通过stream方法逐块返回结果。每个组件都可以流式传递数据,无需等待完整输出。流式处理显著改善用户体验,提供实时反馈,降低首字响应延迟。适用于长文本生成、实时翻译、交互式对话等场景。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 基础流式链
prompt = ChatPromptTemplate.from_template("写一篇关于{topic}的文章")
model = ChatOpenAI(streaming=True) # 启用流式
parser = StrOutputParser()
chain = prompt | model | parser
# 流式调用
for chunk in chain.stream({"topic": "人工智能"}):
print(chunk, end="", flush=True)
# 流式批量处理
inputs = [
{"topic": "机器学习"},
{"topic": "深度学习"},
{"topic": "自然语言处理"}
]
for i, input_data in enumerate(inputs):
print(f"\n=== 主题{i+1} ===")
for chunk in chain.stream(input_data):
print(chunk, end="", flush=True)
# 异步流式
async def async_stream():
async for chunk in chain.astream({"topic": "AI"}):
print(chunk, end="", flush=True)
import asyncio
asyncio.run(async_stream())
# 流式事件
from langchain.callbacks import StreamingStdOutCallbackHandler
chain_with_callback = (
prompt
| model.bind(callbacks=[StreamingStdOutCallbackHandler()])
| parser
)
result = chain_with_callback.invoke({"topic": "..."})
---
b.astream_events
a.功能说明
astream_events提供更细粒度的流式事件,可以监听链中每个组件的执行状态。支持on_chain_start、on_llm_new_token、on_chain_end等事件,实现实时进度追踪和日志记录。适用于需要详细监控、调试、性能分析的场景。
b.代码示例
---
from langchain.schema.runnable import RunnableConfig
chain = prompt | model | parser
# 流式事件监听
async def stream_events():
async for event in chain.astream_events(
{"topic": "AI"},
version="v1"
):
kind = event["event"]
if kind == "on_chain_start":
print(f"\n开始执行:{event['name']}")
elif kind == "on_chain_stream":
chunk = event.get("data", {}).get("chunk")
if chunk:
print(chunk, end="", flush=True)
elif kind == "on_llm_start":
print(f"\nLLM调用开始")
elif kind == "on_llm_new_token":
token = event["data"]["chunk"]
print(token, end="", flush=True)
elif kind == "on_chain_end":
print(f"\n执行完成:{event['name']}")
asyncio.run(stream_events())
# 过滤特定事件
async def filter_events():
async for event in chain.astream_events(
{"topic": "AI"},
version="v1"
):
# 只处理LLM token事件
if event["event"] == "on_llm_new_token":
print(event["data"]["chunk"], end="")
# 自定义事件处理
class EventLogger:
def __init__(self):
self.events = []
async def log_events(self, chain_input):
async for event in chain.astream_events(chain_input, version="v1"):
self.events.append({
"type": event["event"],
"name": event.get("name"),
"timestamp": event["run_id"]
})
yield event
logger = EventLogger()
async for event in logger.log_events({"topic": "AI"}):
# 处理事件
pass
print(f"记录了{len(logger.events)}个事件")
---
02.流式聚合
a.累积输出
a.功能说明
在流式处理中,某些组件需要累积所有chunk才能处理,如输出解析器、后处理函数等。LCEL自动处理流式聚合,组件可以选择逐块处理或等待完整输出。可以通过自定义Runnable实现特定的累积逻辑,如滑动窗口聚合、部分解析等。
b.代码示例
---
from langchain.schema.runnable import Runnable
from typing import Iterator, Any
# 自定义流式累积器
class StreamingAccumulator(Runnable):
"""累积流式输出"""
def __init__(self, separator=""):
self.separator = separator
def invoke(self, input: Any, config=None) -> str:
if isinstance(input, str):
return input
# 累积迭代器
return self.separator.join(str(chunk) for chunk in input)
def stream(self, input: Any, config=None) -> Iterator[str]:
"""逐块传递"""
accumulated = ""
for chunk in input:
accumulated += str(chunk)
yield accumulated # 返回累积结果
# 使用累积器
accumulator = StreamingAccumulator()
chain = (
prompt
| model # 流式输出
| accumulator # 累积处理
| parser
)
for partial_result in chain.stream({"topic": "AI"}):
print(f"当前长度:{len(partial_result)}")
# 滑动窗口累积
class SlidingWindowAccumulator(Runnable):
"""滑动窗口累积"""
def __init__(self, window_size=10):
self.window_size = window_size
def stream(self, input: Iterator, config=None) -> Iterator[str]:
window = []
for chunk in input:
window.append(chunk)
# 保持窗口大小
if len(window) > self.window_size:
window.pop(0)
# 返回窗口内容
yield "".join(window)
# 部分解析
class PartialJSONParser(Runnable):
"""部分JSON解析"""
def stream(self, input: Iterator[str], config=None) -> Iterator[dict]:
buffer = ""
for chunk in input:
buffer += chunk
# 尝试解析部分JSON
try:
import json
partial = json.loads(buffer + "}") # 尝试闭合
yield partial
except:
pass # 继续累积
chain = (
prompt
| model
| PartialJSONParser()
)
for partial in chain.stream({"input": "..."}):
print(f"部分结果:{partial}")
---
b.流式转换
a.功能说明
在流式处理过程中对数据进行转换,如格式化、过滤、映射等。支持逐块转换和批量转换两种模式,可以根据需求选择处理策略。流式转换不阻塞数据流,保持低延迟的同时完成数据处理。
b.代码示例
---
from typing import Iterator
from langchain.schema.runnable import Runnable
# 流式格式化
class StreamingFormatter(Runnable):
"""流式格式化输出"""
def __init__(self, prefix="", suffix=""):
self.prefix = prefix
self.suffix = suffix
self.started = False
def stream(self, input: Iterator[str], config=None) -> Iterator[str]:
for i, chunk in enumerate(input):
if i == 0 and self.prefix:
yield self.prefix
yield chunk
if i == len(list(input)) - 1 and self.suffix:
yield self.suffix
formatter = StreamingFormatter(prefix="【开始】\n", suffix="\n【结束】")
chain = prompt | model | formatter | parser
# 流式过滤
class StreamingFilter(Runnable):
"""过滤不需要的内容"""
def __init__(self, filter_words: list):
self.filter_words = filter_words
def stream(self, input: Iterator[str], config=None) -> Iterator[str]:
for chunk in input:
# 过滤敏感词
filtered = chunk
for word in self.filter_words:
filtered = filtered.replace(word, "***")
yield filtered
filter_chain = StreamingFilter(["敏感词1", "敏感词2"])
chain = (
prompt
| model
| filter_chain
| parser
)
# 流式映射
class StreamingMapper(Runnable):
"""映射转换"""
def __init__(self, mapping_fn):
self.mapping_fn = mapping_fn
def stream(self, input: Iterator[str], config=None) -> Iterator[str]:
for chunk in input:
yield self.mapping_fn(chunk)
# 转小写
lowercase_mapper = StreamingMapper(lambda x: x.lower())
chain = prompt | model | lowercase_mapper | parser
# WebSocket流式推送
import asyncio
import websockets
async def stream_to_websocket(websocket, chain_input):
"""将流式输出推送到WebSocket"""
try:
async for chunk in chain.astream(chain_input):
await websocket.send(json.dumps({
"type": "chunk",
"data": chunk
}))
await websocket.send(json.dumps({
"type": "done"
}))
except websockets.exceptions.ConnectionClosed:
print("WebSocket连接已关闭")
# SSE流式推送
from fastapi import FastAPI
from fastapi.responses import StreamingResponse
app = FastAPI()
@app.get("/stream")
async def stream_endpoint(topic: str):
async def event_stream():
async for chunk in chain.astream({"topic": topic}):
yield f"data: {chunk}\n\n"
yield "data: [DONE]\n\n"
return StreamingResponse(
event_stream(),
media_type="text/event-stream"
)
---
9.3 并行执行
01.RunnableParallel
a.并行分支
a.功能说明
RunnableParallel将输入同时发送给多个独立的处理分支,并行执行后合并结果。每个分支可以是完整的处理链,分支间互不影响。并行执行显著降低总延迟,充分利用I/O等待时间。适用于独立任务如多语言翻译、多角度分析、多模型对比等场景。
b.代码示例
---
from langchain.schema.runnable import RunnableParallel
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 创建多个独立链
summary_chain = (
ChatPromptTemplate.from_template("总结:{text}")
| ChatOpenAI()
| StrOutputParser()
)
keywords_chain = (
ChatPromptTemplate.from_template("提取关键词:{text}")
| ChatOpenAI()
| StrOutputParser()
)
sentiment_chain = (
ChatPromptTemplate.from_template("分析情感:{text}")
| ChatOpenAI()
| StrOutputParser()
)
# 并行执行
parallel_chain = RunnableParallel(
summary=summary_chain,
keywords=keywords_chain,
sentiment=sentiment_chain
)
result = parallel_chain.invoke({"text": "产品评论内容..."})
# {
# 'summary': '产品总体不错...',
# 'keywords': '质量,价格,服务',
# 'sentiment': '正面'
# }
# 字典语法(更简洁)
parallel = {
"summary": summary_chain,
"keywords": keywords_chain,
"sentiment": sentiment_chain
}
result = parallel | (lambda x: f"摘要:{x['summary']}\n关键词:{x['keywords']}")
# 嵌套并行
analysis_parallel = RunnableParallel(
chinese=chinese_analysis_chain,
english=english_analysis_chain
)
comparison_parallel = RunnableParallel(
similarity=similarity_chain,
difference=difference_chain
)
full_chain = (
{"text": RunnablePassthrough()}
| analysis_parallel
| comparison_parallel
)
# 并行检索多个向量库
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
vectorstore_tech = Milvus(
collection_name="tech_docs",
embedding_function=OpenAIEmbeddings()
)
vectorstore_business = Milvus(
collection_name="business_docs",
embedding_function=OpenAIEmbeddings()
)
retrieval_parallel = RunnableParallel(
tech_docs=vectorstore_tech.as_retriever(search_kwargs={"k": 3}),
business_docs=vectorstore_business.as_retriever(search_kwargs={"k": 3})
)
results = retrieval_parallel.invoke("查询文本")
all_docs = results["tech_docs"] + results["business_docs"]
---
b.性能优化
a.功能说明
并行执行需要考虑并发控制、资源限制、错误处理等问题。通过配置max_concurrency限制并发数,避免资源耗尽。使用return_exceptions处理部分分支失败,实现容错并行。可以设置超时、重试、熔断等机制,确保并行执行的稳定性。
b.代码示例
---
from langchain.schema.runnable import RunnableConfig
import asyncio
# 配置并发限制
parallel = RunnableParallel(
task1=chain1,
task2=chain2,
task3=chain3,
task4=chain4,
task5=chain5
)
result = parallel.invoke(
{"input": "..."},
config=RunnableConfig(
max_concurrency=3, # 最多3个并发
return_exceptions=True # 返回异常
)
)
# 处理部分失败
for key, value in result.items():
if isinstance(value, Exception):
print(f"任务{key}失败:{value}")
else:
print(f"任务{key}成功:{value}")
# 超时控制
from asyncio import TimeoutError
async def parallel_with_timeout():
try:
result = await asyncio.wait_for(
parallel.ainvoke({"input": "..."}),
timeout=30.0 # 30秒超时
)
return result
except TimeoutError:
print("并行执行超时")
return None
# 自定义并发控制
class ConcurrencyLimiter:
"""并发限制器"""
def __init__(self, max_concurrent=5):
self.semaphore = asyncio.Semaphore(max_concurrent)
async def run_with_limit(self, chain, input_data):
async with self.semaphore:
return await chain.ainvoke(input_data)
limiter = ConcurrencyLimiter(max_concurrent=3)
async def controlled_parallel(inputs):
tasks = [
limiter.run_with_limit(chain, inp)
for inp in inputs
]
return await asyncio.gather(*tasks)
# 熔断机制
class CircuitBreaker:
"""熔断器"""
def __init__(self, failure_threshold=5, timeout=60):
self.failure_count = 0
self.failure_threshold = failure_threshold
self.timeout = timeout
self.last_failure_time = None
self.is_open = False
async def call(self, chain, input_data):
if self.is_open:
if time.time() - self.last_failure_time > self.timeout:
self.is_open = False
self.failure_count = 0
else:
raise Exception("熔断器打开,拒绝调用")
try:
result = await chain.ainvoke(input_data)
self.failure_count = 0
return result
except Exception as e:
self.failure_count += 1
self.last_failure_time = time.time()
if self.failure_count >= self.failure_threshold:
self.is_open = True
raise e
breaker = CircuitBreaker()
# 批量并行处理
async def batch_parallel(inputs, batch_size=10):
results = []
for i in range(0, len(inputs), batch_size):
batch = inputs[i:i+batch_size]
batch_tasks = [
parallel.ainvoke(inp) for inp in batch
]
batch_results = await asyncio.gather(*batch_tasks)
results.extend(batch_results)
# 进度
print(f"完成:{len(results)}/{len(inputs)}")
return results
---
02.数据聚合
a.结果合并
a.功能说明
并行执行后需要合并多个分支的结果,支持字典合并、列表拼接、自定义聚合等方式。可以实现简单的键值合并,也可以编写复杂的聚合逻辑如排序、去重、加权平均等。聚合策略影响最终输出的质量和格式。
b.代码示例
---
from langchain.schema.runnable import RunnableParallel, RunnableLambda
# 基础字典合并
parallel = RunnableParallel(
summary=summary_chain,
keywords=keywords_chain
)
# 自动合并为字典
result = parallel.invoke({"text": "..."})
# {'summary': '...', 'keywords': '...'}
# 自定义合并逻辑
def merge_results(parallel_output):
"""合并并行结果"""
summary = parallel_output["summary"]
keywords = parallel_output["keywords"]
sentiment = parallel_output["sentiment"]
return {
"content": f"{summary}\n\n关键词:{keywords}",
"metadata": {
"sentiment": sentiment,
"timestamp": datetime.now()
}
}
chain = (
parallel
| RunnableLambda(merge_results)
)
# 列表聚合
parallel = RunnableParallel(
results1=chain1,
results2=chain2,
results3=chain3
)
def aggregate_lists(parallel_output):
"""聚合多个列表"""
all_results = []
for key, results in parallel_output.items():
all_results.extend(results)
# 去重
unique_results = list(set(all_results))
# 排序
return sorted(unique_results, key=lambda x: x.score, reverse=True)
chain = parallel | RunnableLambda(aggregate_lists)
# 加权聚合
def weighted_aggregation(parallel_output):
"""加权聚合评分"""
weights = {
"model1": 0.5,
"model2": 0.3,
"model3": 0.2
}
weighted_score = sum(
parallel_output[model] * weight
for model, weight in weights.items()
)
return weighted_score
# 投票聚合
def voting_aggregation(parallel_output):
"""多数投票"""
from collections import Counter
votes = [
parallel_output["classifier1"],
parallel_output["classifier2"],
parallel_output["classifier3"]
]
# 统计投票
counter = Counter(votes)
most_common = counter.most_common(1)[0][0]
return {
"result": most_common,
"confidence": counter[most_common] / len(votes)
}
parallel = RunnableParallel(
classifier1=model1_chain,
classifier2=model2_chain,
classifier3=model3_chain
)
chain = parallel | RunnableLambda(voting_aggregation)
# 检索结果融合
from langchain.retrievers import EnsembleRetriever
def rerank_documents(parallel_output):
"""重排序文档"""
all_docs = []
for docs in parallel_output.values():
all_docs.extend(docs)
# 使用相关性分数重排序
sorted_docs = sorted(
all_docs,
key=lambda d: d.metadata.get("score", 0),
reverse=True
)
# 去重
seen_ids = set()
unique_docs = []
for doc in sorted_docs:
doc_id = doc.metadata.get("id")
if doc_id not in seen_ids:
seen_ids.add(doc_id)
unique_docs.append(doc)
return unique_docs[:10] # 返回top 10
retrieval_parallel = RunnableParallel(
vector_search=vector_retriever,
keyword_search=keyword_retriever,
hybrid_search=hybrid_retriever
)
chain = retrieval_parallel | RunnableLambda(rerank_documents)
---
9.4 条件路由
01.RunnableBranch
a.条件分支
a.功能说明
RunnableBranch根据输入条件动态选择执行分支,实现if-elif-else逻辑。每个分支是一个(condition, runnable)元组,按顺序检查条件,执行第一个满足的分支。可以设置默认分支处理未匹配情况。条件路由实现动态workflow,根据数据特征选择不同处理策略。
b.代码示例
---
from langchain.schema.runnable import RunnableBranch
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import StrOutputParser
# 创建不同的处理链
tech_chain = (
ChatPromptTemplate.from_template("技术角度分析:{text}")
| ChatOpenAI()
| StrOutputParser()
)
business_chain = (
ChatPromptTemplate.from_template("商业角度分析:{text}")
| ChatOpenAI()
| StrOutputParser()
)
general_chain = (
ChatPromptTemplate.from_template("一般性分析:{text}")
| ChatOpenAI()
| StrOutputParser()
)
# 条件路由
branch = RunnableBranch(
(lambda x: "技术" in x["category"], tech_chain),
(lambda x: "商业" in x["category"], business_chain),
general_chain # 默认分支
)
# 技术类别走tech_chain
result = branch.invoke({"category": "技术", "text": "..."})
# 商业类别走business_chain
result = branch.invoke({"category": "商业", "text": "..."})
# 其他类别走general_chain
result = branch.invoke({"category": "生活", "text": "..."})
# 复杂条件
def is_long_text(x):
return len(x["text"]) > 1000
def is_technical(x):
technical_keywords = ["API", "算法", "架构", "代码"]
return any(kw in x["text"] for kw in technical_keywords)
branch = RunnableBranch(
(lambda x: is_long_text(x) and is_technical(x), detailed_tech_chain),
(lambda x: is_long_text(x), summary_chain),
(lambda x: is_technical(x), quick_tech_chain),
quick_general_chain
)
# 多级路由
language_branch = RunnableBranch(
(lambda x: x["lang"] == "zh", chinese_chain),
(lambda x: x["lang"] == "en", english_chain),
auto_detect_chain
)
complexity_branch = RunnableBranch(
(lambda x: x["difficulty"] == "high", expert_chain),
(lambda x: x["difficulty"] == "medium", intermediate_chain),
beginner_chain
)
full_chain = (
language_branch
| complexity_branch
)
---
b.动态路由
a.功能说明
使用LLM或规则引擎动态判断路由目标,实现智能分流。LLM Router通过自然语言理解意图选择路由,Embedding Router使用语义相似度匹配。支持多级路由、回退路由、循环检测等高级特性。适用于意图识别、任务分类、智能客服等场景。
b.代码示例
---
from langchain.chains.router import MultiPromptChain
from langchain.chains.router.llm_router import LLMRouterChain, RouterOutputParser
from langchain.prompts import PromptTemplate
# 定义目标链
tech_prompt = PromptTemplate(
template="作为技术专家,回答:{input}",
input_variables=["input"]
)
business_prompt = PromptTemplate(
template="作为商业顾问,回答:{input}",
input_variables=["input"]
)
legal_prompt = PromptTemplate(
template="作为法律顾问,回答:{input}",
input_variables=["input"]
)
# 目标链字典
destination_chains = {
"tech": LLMChain(llm=llm, prompt=tech_prompt),
"business": LLMChain(llm=llm, prompt=business_prompt),
"legal": LLMChain(llm=llm, prompt=legal_prompt)
}
# 路由提示词
destinations = [
{
"name": "tech",
"description": "回答技术相关问题,如编程、算法、架构"
},
{
"name": "business",
"description": "回答商业问题,如市场、战略、管理"
},
{
"name": "legal",
"description": "回答法律问题,如合同、知识产权、合规"
}
]
# 创建路由链
router_template = """给定用户输入,选择最合适的专家:
可选专家:
{destinations}
用户输入:{input}
选择格式:{{"destination": "专家名称", "next_inputs": {{"input": "用户输入"}}}}
"""
router_prompt = PromptTemplate(
template=router_template,
input_variables=["input"],
partial_variables={
"destinations": "\n".join([f"{d['name']}: {d['description']}" for d in destinations])
}
)
router_chain = LLMRouterChain.from_llm(llm, router_prompt)
# 创建多路由链
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=LLMChain(llm=llm, prompt=general_prompt),
verbose=True
)
# 自动路由
result = chain.run("如何优化数据库查询性能?") # 路由到tech
result = chain.run("如何制定市场拓展策略?") # 路由到business
result = chain.run("商标注册需要哪些材料?") # 路由到legal
# Embedding路由
from langchain.chains.router.embedding_router import EmbeddingRouterChain
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import FAISS
names_and_descriptions = [
("tech", ["技术", "编程", "算法", "代码", "系统"]),
("business", ["商业", "市场", "战略", "管理", "销售"]),
("legal", ["法律", "合同", "知识产权", "合规", "诉讼"])
]
router_chain = EmbeddingRouterChain.from_names_and_descriptions(
names_and_descriptions,
FAISS,
OpenAIEmbeddings(),
routing_keys=["input"]
)
chain = MultiPromptChain(
router_chain=router_chain,
destination_chains=destination_chains,
default_chain=default_chain
)
---
02.路由策略
a.意图分类
a.功能说明
基于意图分类的路由策略,先识别用户意图,再根据意图选择处理流程。可以使用分类模型、关键词匹配、规则引擎等方法判断意图。支持多意图、意图置信度、意图澄清等高级特性。适用于智能客服、任务型对话、命令分发等场景。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.schema.output_parser import JsonOutputParser
from pydantic import BaseModel, Field
# 定义意图模型
class Intent(BaseModel):
intent: str = Field(description="意图类型")
confidence: float = Field(description="置信度0-1")
entities: dict = Field(description="提取的实体")
# 意图分类链
intent_prompt = ChatPromptTemplate.from_template("""分析用户意图:
用户输入:{input}
可能的意图:
- query: 查询信息
- order: 下单购买
- complaint: 投诉反馈
- faq: 常见问题
返回JSON格式:{{"intent": "", "confidence": 0.0, "entities": {{}}}}
""")
intent_chain = (
intent_prompt
| ChatOpenAI()
| JsonOutputParser()
)
# 根据意图路由
def route_by_intent(user_input):
# 识别意图
intent_result = intent_chain.invoke({"input": user_input})
intent = intent_result["intent"]
confidence = intent_result["confidence"]
# 低置信度澄清
if confidence < 0.7:
return clarify_chain.invoke({"input": user_input})
# 高置信度路由
if intent == "query":
return query_chain.invoke(intent_result)
elif intent == "order":
return order_chain.invoke(intent_result)
elif intent == "complaint":
return complaint_chain.invoke(intent_result)
elif intent == "faq":
return faq_chain.invoke(intent_result)
else:
return default_chain.invoke({"input": user_input})
# 多意图处理
class MultiIntent(BaseModel):
intents: list[Intent] = Field(description="识别的多个意图")
multi_intent_chain = (
multi_intent_prompt
| ChatOpenAI()
| PydanticOutputParser(pydantic_object=MultiIntent)
)
def handle_multi_intent(user_input):
result = multi_intent_chain.invoke({"input": user_input})
responses = []
for intent_obj in result.intents:
if intent_obj.confidence > 0.6:
chain = get_chain_for_intent(intent_obj.intent)
response = chain.invoke({"input": user_input, "entities": intent_obj.entities})
responses.append(response)
return "\n\n".join(responses)
# 意图澄清
clarify_prompt = ChatPromptTemplate.from_template("""用户意图不明确,请澄清:
用户:{input}
可能的理解:
{possible_intents}
请选择:
1. {option1}
2. {option2}
3. 其他(请详细说明)
""")
---
9.5 回退机制
01.RunnableWithFallbacks
a.异常回退
a.功能说明
RunnableWithFallbacks在主链执行失败时自动切换到备用链,提升系统鲁棒性。可以配置多级回退链、回退条件、异常类型匹配等。支持降级策略如使用更简单的模型、缓存结果、默认回复等。适用于需要高可用性的生产环境,确保服务始终可用。
b.代码示例
---
from langchain.schema.runnable import RunnableWithFallbacks
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI, ChatAnthropic
from langchain.schema.output_parser import StrOutputParser
# 主链(使用GPT-4)
primary_chain = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatOpenAI(model="gpt-4", temperature=0)
| StrOutputParser()
)
# 备用链1(使用GPT-3.5)
fallback_chain_1 = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
| StrOutputParser()
)
# 备用链2(使用Claude)
fallback_chain_2 = (
ChatPromptTemplate.from_template("回答:{question}")
| ChatAnthropic(model="claude-2")
| StrOutputParser()
)
# 配置回退
chain_with_fallbacks = primary_chain.with_fallbacks(
[fallback_chain_1, fallback_chain_2]
)
# 执行(主链失败自动切换)
result = chain_with_fallbacks.invoke({"question": "什么是AI?"})
# 使用RunnableWithFallbacks类
chain = RunnableWithFallbacks(
runnable=primary_chain,
fallbacks=[fallback_chain_1, fallback_chain_2]
)
# 配置回退参数
chain_with_fallbacks = primary_chain.with_fallbacks(
fallbacks=[fallback_chain_1, fallback_chain_2],
exceptions_to_handle=(TimeoutError, ConnectionError), # 只处理特定异常
exception_key="error" # 将异常信息传递给下一个链
)
# 条件回退
def should_fallback(exception):
"""判断是否应该回退"""
if isinstance(exception, TimeoutError):
return True
if isinstance(exception, APIError) and exception.status_code == 429:
return True # API限流时回退
return False
# 信创环境回退链
# 主链:使用GPT
primary = (
prompt
| ChatOpenAI(api_base="https://api.openai.com")
| parser
)
# 回退链:使用国产模型
fallback_qwen = (
prompt
| ChatOpenAI(
api_base="https://dashscope.aliyuncs.com/compatible-mode/v1",
model="qwen-turbo"
)
| parser
)
fallback_local = (
prompt
| Ollama(model="qwen2.5:7b")
| parser
)
chain = primary.with_fallbacks([fallback_qwen, fallback_local])
---
b.降级策略
a.功能说明
当服务质量下降但未完全失败时,可以主动降级到更快更便宜的方案。包括模型降级(GPT-4→GPT-3.5→本地模型)、功能降级(完整分析→简要总结→关键词提取)、数据降级(实时查询→缓存结果→默认回复)等策略。降级策略平衡质量和可用性,确保用户体验。
b.代码示例
---
import time
from langchain.schema.runnable import Runnable
# 响应时间监控
class PerformanceMonitor(Runnable):
"""性能监控和自动降级"""
def __init__(self, primary, fallback, threshold_ms=3000):
self.primary = primary
self.fallback = fallback
self.threshold_ms = threshold_ms
self.recent_times = []
def invoke(self, input, config=None):
start = time.time()
try:
result = self.primary.invoke(input, config)
elapsed = (time.time() - start) * 1000
self.recent_times.append(elapsed)
if len(self.recent_times) > 10:
self.recent_times.pop(0)
# 平均响应时间过长,降级
avg_time = sum(self.recent_times) / len(self.recent_times)
if avg_time > self.threshold_ms:
print(f"平均响应{avg_time}ms,超过阈值,降级")
return self.fallback.invoke(input, config)
return result
except Exception as e:
print(f"主链失败,降级:{e}")
return self.fallback.invoke(input, config)
monitor = PerformanceMonitor(
primary=gpt4_chain,
fallback=gpt35_chain,
threshold_ms=2000
)
# 成本控制降级
class CostControlledChain(Runnable):
"""成本控制链"""
def __init__(self, chains, daily_budget=10.0):
self.chains = chains # 按成本从高到低排序
self.daily_budget = daily_budget
self.daily_cost = 0
self.last_reset = datetime.now().date()
def invoke(self, input, config=None):
# 每天重置
today = datetime.now().date()
if today > self.last_reset:
self.daily_cost = 0
self.last_reset = today
# 选择合适的链
for chain, cost_per_call in self.chains:
if self.daily_cost + cost_per_call <= self.daily_budget:
result = chain.invoke(input, config)
self.daily_cost += cost_per_call
return result
# 预算耗尽,使用最便宜的
return self.chains[-1][0].invoke(input, config)
cost_chain = CostControlledChain(
chains=[
(gpt4_chain, 0.03), # GPT-4: $0.03/call
(gpt35_chain, 0.002), # GPT-3.5: $0.002/call
(local_chain, 0.0) # 本地模型: 免费
],
daily_budget=5.0
)
# 功能降级
def degraded_analysis(text):
"""降级的分析功能"""
if len(text) > 5000:
# 长文本只返回关键词
return extract_keywords_chain.invoke({"text": text})
elif len(text) > 1000:
# 中等文本返回摘要
return summarize_chain.invoke({"text": text})
else:
# 短文本完整分析
return full_analysis_chain.invoke({"text": text})
# 缓存回退
from functools import lru_cache
import redis
class CachedFallbackChain(Runnable):
"""带缓存回退的链"""
def __init__(self, primary, redis_client):
self.primary = primary
self.redis = redis_client
def invoke(self, input, config=None):
# 生成缓存键
import hashlib
cache_key = f"cache:{hashlib.md5(str(input).encode()).hexdigest()}"
try:
# 尝试主链
result = self.primary.invoke(input, config)
# 缓存结果
self.redis.setex(cache_key, 3600, json.dumps(result))
return result
except:
# 主链失败,尝试缓存
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)
# 缓存也没有,返回默认回复
return "抱歉,服务暂时不可用,请稍后重试。"
---
02.错误恢复
a.重试机制
a.功能说明
在暂时性错误时自动重试,如网络超时、API限流、临时故障等。配置重试次数、退避策略、重试条件等参数。支持指数退避、固定延迟、随机抖动等退避算法。重试机制提升系统稳定性,减少偶发错误影响。
b.代码示例
---
import time
import random
from langchain.schema.runnable import Runnable
class RetryableChain(Runnable):
"""支持重试的链"""
def __init__(self, chain, max_retries=3, backoff_factor=2):
self.chain = chain
self.max_retries = max_retries
self.backoff_factor = backoff_factor
def invoke(self, input, config=None):
last_exception = None
for attempt in range(self.max_retries + 1):
try:
return self.chain.invoke(input, config)
except Exception as e:
last_exception = e
if attempt < self.max_retries:
# 计算退避时间
wait_time = self.backoff_factor ** attempt
# 添加随机抖动
wait_time += random.uniform(0, 1)
print(f"第{attempt + 1}次尝试失败,{wait_time}秒后重试")
time.sleep(wait_time)
else:
print(f"重试{self.max_retries}次后仍失败")
raise last_exception
retry_chain = RetryableChain(
chain=primary_chain,
max_retries=3,
backoff_factor=2
)
# 条件重试
class ConditionalRetry(Runnable):
"""条件重试"""
def __init__(self, chain, max_retries=3):
self.chain = chain
self.max_retries = max_retries
def should_retry(self, exception):
"""判断是否应该重试"""
# 网络错误重试
if isinstance(exception, (TimeoutError, ConnectionError)):
return True
# API限流重试
if isinstance(exception, APIError) and exception.status_code == 429:
return True
# 其他错误不重试
return False
def invoke(self, input, config=None):
for attempt in range(self.max_retries + 1):
try:
return self.chain.invoke(input, config)
except Exception as e:
if attempt < self.max_retries and self.should_retry(e):
wait = 2 ** attempt
time.sleep(wait)
else:
raise
# 结合重试和回退
retry_then_fallback = (
RetryableChain(primary_chain, max_retries=2)
.with_fallbacks([fallback_chain])
)
# 使用tenacity库
from tenacity import retry, stop_after_attempt, wait_exponential
@retry(
stop=stop_after_attempt(3),
wait=wait_exponential(multiplier=1, min=2, max=10)
)
def reliable_invoke(chain, input_data):
return chain.invoke(input_data)
result = reliable_invoke(chain, {"question": "..."})
---
b.熔断保护
a.功能说明
当服务持续失败时启动熔断保护,暂时停止调用避免雪崩效应。熔断器有关闭、打开、半开三种状态,根据失败率和失败次数自动切换。半开状态定期尝试恢复,成功后关闭熔断器。熔断保护确保系统整体稳定性,避免级联故障。
b.代码示例
---
import time
from enum import Enum
from datetime import datetime, timedelta
class CircuitState(Enum):
CLOSED = "closed" # 正常状态
OPEN = "open" # 熔断打开
HALF_OPEN = "half_open" # 半开状态
class CircuitBreaker(Runnable):
"""熔断器"""
def __init__(
self,
chain,
failure_threshold=5, # 失败阈值
success_threshold=2, # 恢复阈值
timeout=60, # 熔断超时
half_open_timeout=10 # 半开超时
):
self.chain = chain
self.failure_threshold = failure_threshold
self.success_threshold = success_threshold
self.timeout = timeout
self.half_open_timeout = half_open_timeout
self.state = CircuitState.CLOSED
self.failure_count = 0
self.success_count = 0
self.last_failure_time = None
self.last_attempt_time = None
def invoke(self, input, config=None):
# 检查是否应该从打开状态转为半开
if self.state == CircuitState.OPEN:
if time.time() - self.last_failure_time > self.timeout:
print("熔断器进入半开状态")
self.state = CircuitState.HALF_OPEN
self.success_count = 0
else:
raise Exception("熔断器打开,拒绝调用")
# 记录尝试时间
self.last_attempt_time = time.time()
try:
result = self.chain.invoke(input, config)
# 调用成功
self.failure_count = 0
if self.state == CircuitState.HALF_OPEN:
self.success_count += 1
# 达到成功阈值,关闭熔断器
if self.success_count >= self.success_threshold:
print("熔断器关闭")
self.state = CircuitState.CLOSED
return result
except Exception as e:
# 调用失败
self.failure_count += 1
self.last_failure_time = time.time()
# 达到失败阈值,打开熔断器
if self.failure_count >= self.failure_threshold:
print(f"连续失败{self.failure_count}次,打开熔断器")
self.state = CircuitState.OPEN
# 半开状态失败,立即打开
if self.state == CircuitState.HALF_OPEN:
print("半开状态失败,重新打开熔断器")
self.state = CircuitState.OPEN
raise e
# 使用熔断器
protected_chain = CircuitBreaker(
chain=primary_chain,
failure_threshold=5,
timeout=60
)
# 熔断器 + 降级
class CircuitBreakerWithFallback(CircuitBreaker):
"""带降级的熔断器"""
def __init__(self, chain, fallback, **kwargs):
super().__init__(chain, **kwargs)
self.fallback = fallback
def invoke(self, input, config=None):
try:
return super().invoke(input, config)
except Exception as e:
if self.state == CircuitState.OPEN:
# 熔断打开时使用降级
print("使用降级服务")
return self.fallback.invoke(input, config)
raise e
protected_with_fallback = CircuitBreakerWithFallback(
chain=primary_chain,
fallback=fallback_chain,
failure_threshold=5,
timeout=60
)
# 监控熔断状态
def monitor_circuit_breaker(breaker: CircuitBreaker):
"""监控熔断器状态"""
while True:
status = {
"state": breaker.state.value,
"failure_count": breaker.failure_count,
"success_count": breaker.success_count
}
print(f"熔断器状态:{status}")
time.sleep(10)
---
10 实战案例
10.1 RAG问答系统
01.基础RAG
a.文档加载与切分
a.功能说明
RAG(Retrieval-Augmented Generation)系统首先需要加载文档并切分成合适的chunk。支持PDF、Word、Markdown、HTML等多种格式,使用RecursiveCharacterTextSplitter按语义切分。切分策略影响检索质量,需要平衡chunk大小和语义完整性。可以配置chunk_size、chunk_overlap、separators等参数优化切分效果。
b.代码示例
---
from langchain.document_loaders import DirectoryLoader, PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.embeddings import OpenAIEmbeddings
from langchain.vectorstores import Milvus
from langchain.chat_models import ChatOpenAI
from langchain.chains import RetrievalQA
# 1. 加载文档
loader = DirectoryLoader(
"./documents",
glob="**/*.pdf",
loader_cls=PyPDFLoader
)
documents = loader.load()
print(f"加载了{len(documents)}个文档")
# 2. 切分文档
text_splitter = RecursiveCharacterTextSplitter(
chunk_size=1000, # 每个chunk 1000字符
chunk_overlap=200, # 重叠200字符
separators=["\n\n", "\n", "。", "!", "?", ";", " ", ""]
)
chunks = text_splitter.split_documents(documents)
print(f"切分成{len(chunks)}个chunk")
# 添加元数据
for i, chunk in enumerate(chunks):
chunk.metadata["chunk_id"] = i
chunk.metadata["source_file"] = chunk.metadata.get("source", "")
# 3. 向量化存储
embeddings = OpenAIEmbeddings()
vectorstore = Milvus.from_documents(
documents=chunks,
embedding=embeddings,
connection_args={"host": "localhost", "port": "19530"},
collection_name="knowledge_base"
)
# 4. 创建检索链
retriever = vectorstore.as_retriever(
search_type="similarity",
search_kwargs={"k": 3}
)
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(model="gpt-3.5-turbo", temperature=0),
chain_type="stuff",
retriever=retriever,
return_source_documents=True
)
# 5. 问答
result = qa_chain({"query": "什么是LangChain?"})
print(f"回答:{result['result']}")
print(f"\n来源文档:")
for doc in result["source_documents"]:
print(f"- {doc.metadata['source']}")
---
b.检索优化
a.功能说明
优化检索质量是RAG系统的关键,包括混合检索(向量+关键词)、重排序、多查询、上下文压缩等技术。混合检索结合语义相似度和关键词匹配,提升召回率。重排序使用LLM对检索结果重新打分,提升精确度。多查询生成多个改写问题扩大检索范围。
b.代码示例
---
from langchain.retrievers import EnsembleRetriever, ContextualCompressionRetriever
from langchain.retrievers import BM25Retriever, MultiQueryRetriever
from langchain.retrievers.document_compressors import LLMChainExtractor
# 1. 混合检索
# 向量检索
vector_retriever = vectorstore.as_retriever(search_kwargs={"k": 5})
# 关键词检索
bm25_retriever = BM25Retriever.from_documents(chunks)
bm25_retriever.k = 5
# 混合检索
ensemble_retriever = EnsembleRetriever(
retrievers=[vector_retriever, bm25_retriever],
weights=[0.7, 0.3] # 70%向量 + 30%关键词
)
# 2. 多查询检索
multi_query_retriever = MultiQueryRetriever.from_llm(
retriever=vector_retriever,
llm=ChatOpenAI(temperature=0)
)
# 自动生成3-5个相关查询
docs = multi_query_retriever.get_relevant_documents("LangChain是什么?")
# 3. 上下文压缩
compressor = LLMChainExtractor.from_llm(ChatOpenAI(temperature=0))
compression_retriever = ContextualCompressionRetriever(
base_compressor=compressor,
base_retriever=ensemble_retriever
)
# 压缩检索结果,只保留相关部分
compressed_docs = compression_retriever.get_relevant_documents(
"LangChain的核心功能"
)
# 4. 父文档检索
from langchain.retrievers import ParentDocumentRetriever
from langchain.storage import InMemoryStore
# 小chunk用于检索
child_splitter = RecursiveCharacterTextSplitter(chunk_size=400)
# 大chunk用于生成
parent_splitter = RecursiveCharacterTextSplitter(chunk_size=2000)
store = InMemoryStore()
parent_retriever = ParentDocumentRetriever(
vectorstore=vectorstore,
docstore=store,
child_splitter=child_splitter,
parent_splitter=parent_splitter
)
parent_retriever.add_documents(documents)
# 检索时用小chunk,返回时用大chunk
docs = parent_retriever.get_relevant_documents("...")
# 5. 集成到QA链
qa_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(temperature=0),
chain_type="stuff",
retriever=compression_retriever, # 使用优化后的检索器
return_source_documents=True,
verbose=True
)
---
02.高级RAG
a.对话式RAG
a.功能说明
ConversationalRetrievalChain支持多轮对话RAG,自动处理对话历史和上下文。能够理解代词指代、省略信息、追问等对话现象。使用对话记忆管理历史,使用问题改写处理上下文依赖。适用于需要持续交互的知识问答、技术支持等场景。
b.代码示例
---
from langchain.chains import ConversationalRetrievalChain
from langchain.memory import ConversationBufferMemory
# 创建对话记忆
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True,
output_key="answer"
)
# 创建对话式RAG链
qa = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(temperature=0),
retriever=vectorstore.as_retriever(),
memory=memory,
return_source_documents=True,
verbose=True
)
# 多轮对话
# 第一轮
result1 = qa({"question": "LangChain是什么?"})
print(f"回答:{result1['answer']}")
# 第二轮(代词指代)
result2 = qa({"question": "它有哪些核心组件?"}) # "它"指代LangChain
print(f"回答:{result2['answer']}")
# 第三轮(追问)
result3 = qa({"question": "第二个组件怎么用?"})
print(f"回答:{result3['answer']}")
# 自定义问题改写
from langchain.chains import LLMChain
from langchain.prompts import PromptTemplate
condense_prompt = PromptTemplate(
template="""基于对话历史,将追问改写为独立问题:
对话历史:
{chat_history}
追问:{question}
独立问题:""",
input_variables=["chat_history", "question"]
)
qa = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(temperature=0),
retriever=retriever,
condense_question_prompt=condense_prompt,
memory=memory
)
# 流式对话RAG
from langchain.callbacks import StreamingStdOutCallbackHandler
qa = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(temperature=0, streaming=True),
retriever=retriever,
memory=memory,
callbacks=[StreamingStdOutCallbackHandler()]
)
result = qa({"question": "介绍一下RAG技术"})
---
b.信创RAG系统
a.功能说明
在信创环境部署RAG系统,使用国产模型(Qwen、ChatGLM)、国产数据库(达梦、Milvus)、国产OS(麒麟、统信)。需要处理中文分词、编码兼容、性能优化等问题。可以使用本地模型降低成本,使用GPU/NPU加速推理,使用Redis缓存提升响应速度。
b.代码示例
---
from langchain.embeddings import HuggingFaceEmbeddings
from langchain.vectorstores import Milvus
from langchain_community.llms import Tongyi, Ollama
from langchain.text_splitter import SpacyTextSplitter
# 1. 使用国产Embedding模型
embeddings = HuggingFaceEmbeddings(
model_name="BAAI/bge-large-zh-v1.5", # 中文Embedding
model_kwargs={"device": "cuda"}, # 使用GPU
encode_kwargs={"normalize_embeddings": True}
)
# 2. Milvus向量库(国产)
vectorstore = Milvus(
embedding_function=embeddings,
connection_args={
"host": "localhost",
"port": "19530",
"user": "root",
"password": "Milvus"
},
collection_name="xinchuang_kb"
)
# 3. 使用国产LLM
# 方案1:通义千问API
llm = Tongyi(
model_name="qwen-turbo",
dashscope_api_key=os.getenv("DASHSCOPE_API_KEY"),
temperature=0.1
)
# 方案2:本地Qwen模型
llm = Ollama(
model="qwen2.5:14b",
base_url="http://localhost:11434",
temperature=0.1
)
# 4. 中文分词优化
import jieba
# 加载自定义词典
jieba.load_userdict("custom_dict.txt")
text_splitter = SpacyTextSplitter(
chunk_size=1000,
chunk_overlap=200,
pipeline="zh_core_web_sm" # 中文模型
)
# 5. 达梦数据库存储元数据
import dmPython
conn = dmPython.connect(
"dm://SYSDBA:SYSDBA@localhost:5236/TEST"
)
cursor = conn.cursor()
cursor.execute("""
CREATE TABLE IF NOT EXISTS document_metadata (
doc_id VARCHAR(100) PRIMARY KEY,
title VARCHAR(200),
source VARCHAR(500),
chunk_count INT,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
""")
# 6. Redis缓存
import redis
redis_client = redis.Redis(
host='localhost',
port=6379,
decode_responses=True
)
def cached_qa(query: str):
# 查询缓存
cache_key = f"qa:{hashlib.md5(query.encode()).hexdigest()}"
cached = redis_client.get(cache_key)
if cached:
return json.loads(cached)
# 执行RAG
result = qa_chain({"query": query})
# 缓存结果
redis_client.setex(
cache_key,
3600, # 1小时
json.dumps(result, ensure_ascii=False)
)
return result
# 7. 完整RAG系统
from langchain.chains import RetrievalQA
qa_chain = RetrievalQA.from_chain_type(
llm=llm,
chain_type="stuff",
retriever=vectorstore.as_retriever(search_kwargs={"k": 3}),
return_source_documents=True
)
# FastAPI服务
from fastapi import FastAPI
from pydantic import BaseModel
app = FastAPI()
class Query(BaseModel):
question: str
@app.post("/qa")
async def qa_endpoint(query: Query):
result = cached_qa(query.question)
return {
"answer": result["result"],
"sources": [doc.metadata["source"] for doc in result["source_documents"]]
}
---
10.2 智能客服
01.意图识别
a.分类路由
a.功能说明
智能客服系统首先需要识别用户意图,将问题路由到不同的处理流程。意图分类包括常见问题、订单查询、投诉建议、人工转接等。可以使用LLM分类、规则匹配、机器学习模型等方法。准确的意图识别是提升客服效率的关键。
b.代码示例
---
from langchain.prompts import ChatPromptTemplate
from langchain.chat_models import ChatOpenAI
from langchain.output_parsers import PydanticOutputParser
from pydantic import BaseModel, Field
from typing import List
# 定义意图模型
class Intent(BaseModel):
intent: str = Field(description="意图类别")
confidence: float = Field(description="置信度0-1")
entities: dict = Field(default_factory=dict, description="提取的实体")
# 意图识别链
parser = PydanticOutputParser(pydantic_object=Intent)
intent_prompt = ChatPromptTemplate.from_template("""识别用户意图:
用户:{input}
可选意图:
- faq: 常见问题(产品使用、功能说明等)
- order_query: 订单查询(物流、状态、退换货)
- complaint: 投诉建议(质量问题、服务不满)
- account: 账户相关(登录、密码、会员)
- transfer: 转人工客服
{format_instructions}
""")
intent_chain = (
intent_prompt.partial(format_instructions=parser.get_format_instructions())
| ChatOpenAI(temperature=0)
| parser
)
# 识别意图
result = intent_chain.invoke({"input": "我的订单什么时候到?"})
print(f"意图:{result.intent}") # order_query
print(f"置信度:{result.confidence}") # 0.95
# 路由处理
from langchain.schema.runnable import RunnableBranch
# 不同意图的处理链
faq_chain = ... # 常见问题链
order_chain = ... # 订单查询链
complaint_chain = ... # 投诉处理链
transfer_chain = ... # 转人工链
# 基于意图路由
def route_by_intent(x):
intent_result = intent_chain.invoke(x)
x["intent"] = intent_result.intent
x["entities"] = intent_result.entities
return x
router = RunnableBranch(
(lambda x: x["intent"] == "faq", faq_chain),
(lambda x: x["intent"] == "order_query", order_chain),
(lambda x: x["intent"] == "complaint", complaint_chain),
(lambda x: x["intent"] == "transfer", transfer_chain),
faq_chain # 默认
)
customer_service = route_by_intent | router
result = customer_service.invoke({"input": "怎么退货?"})
---
b.实体提取
a.功能说明
从用户输入中提取关键实体信息,如订单号、产品名称、日期、金额等。实体信息用于后续的数据库查询、业务处理。可以使用NER模型、正则表达式、LLM提取等方法。准确的实体提取减少多轮对话,提升用户体验。
b.代码示例
---
from langchain.chains import create_extraction_chain
from langchain.chat_models import ChatOpenAI
# 定义实体schema
schema = {
"properties": {
"order_id": {
"type": "string",
"description": "订单号"
},
"product_name": {
"type": "string",
"description": "产品名称"
},
"issue_type": {
"type": "string",
"enum": ["质量问题", "物流延迟", "产品损坏", "其他"],
"description": "问题类型"
},
"contact": {
"type": "string",
"description": "联系方式"
}
}
}
# 创建提取链
extraction_chain = create_extraction_chain(
schema,
ChatOpenAI(temperature=0)
)
# 提取实体
text = "我的订单ORD123456还没到,是iPhone 15,已经延迟3天了,我的手机是13812345678"
entities = extraction_chain.run(text)
print(entities)
# [
# {
# 'order_id': 'ORD123456',
# 'product_name': 'iPhone 15',
# 'issue_type': '物流延迟',
# 'contact': '13812345678'
# }
# ]
# 使用正则表达式辅助
import re
def extract_order_id(text: str):
"""提取订单号"""
patterns = [
r'ORD\d{6,}', # ORD123456
r'\d{10,}', # 1234567890
r'[A-Z]{2}\d{8}' # AB12345678
]
for pattern in patterns:
match = re.search(pattern, text)
if match:
return match.group()
return None
def extract_phone(text: str):
"""提取手机号"""
match = re.search(r'1[3-9]\d{9}', text)
return match.group() if match else None
# 组合提取
def extract_all_entities(text: str):
# LLM提取
llm_entities = extraction_chain.run(text)[0] if extraction_chain.run(text) else {}
# 规则提取
rule_entities = {
"order_id": extract_order_id(text),
"phone": extract_phone(text)
}
# 合并结果
return {**llm_entities, **rule_entities}
---
02.知识库集成
a.FAQ问答
a.功能说明
集成FAQ知识库实现快速问答,使用向量检索匹配相似问题,返回预设答案。可以设置相似度阈值过滤不相关问题,低于阈值转人工处理。支持FAQ动态更新、多轮澄清、答案个性化等功能。FAQ问答覆盖常见问题,减轻人工客服压力。
b.代码示例
---
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
from langchain.chains import RetrievalQA
from langchain.prompts import PromptTemplate
# 1. 构建FAQ知识库
faqs = [
{"question": "如何注册账户?", "answer": "点击首页右上角注册按钮..."},
{"question": "忘记密码怎么办?", "answer": "点击登录页面的忘记密码..."},
{"question": "如何退货?", "answer": "在订单详情页点击申请退货..."},
{"question": "支持哪些支付方式?", "answer": "支持微信、支付宝、银行卡..."},
# ... 更多FAQ
]
from langchain.schema import Document
faq_docs = [
Document(
page_content=f"问题:{faq['question']}\n答案:{faq['answer']}",
metadata={"question": faq["question"], "answer": faq["answer"]}
)
for faq in faqs
]
# 向量化存储
embeddings = OpenAIEmbeddings()
faq_vectorstore = Milvus.from_documents(
documents=faq_docs,
embedding=embeddings,
collection_name="faq"
)
# 2. FAQ检索链
faq_retriever = faq_vectorstore.as_retriever(
search_kwargs={"k": 1, "score_threshold": 0.8} # 相似度阈值
)
faq_prompt = PromptTemplate(
template="""基于以下FAQ回答用户问题:
FAQ:
{context}
用户问题:{question}
如果FAQ中有答案,直接返回。如果没有相关FAQ,回复"抱歉,我无法回答这个问题,为您转接人工客服。"
""",
input_variables=["context", "question"]
)
faq_chain = RetrievalQA.from_chain_type(
llm=ChatOpenAI(temperature=0),
chain_type="stuff",
retriever=faq_retriever,
chain_type_kwargs={"prompt": faq_prompt}
)
# 3. 带相似度过滤的FAQ
def faq_with_threshold(question: str, threshold=0.8):
# 检索相似问题
docs_and_scores = faq_vectorstore.similarity_search_with_score(
question, k=1
)
if not docs_and_scores:
return {"answer": "转人工", "matched": False}
doc, score = docs_and_scores[0]
# 相似度过滤
if score < threshold:
return {"answer": "转人工", "matched": False}
return {
"answer": doc.metadata["answer"],
"matched_question": doc.metadata["question"],
"similarity": score,
"matched": True
}
# 4. 多轮FAQ
from langchain.memory import ConversationBufferMemory
from langchain.chains import ConversationalRetrievalChain
memory = ConversationBufferMemory(
memory_key="chat_history",
return_messages=True
)
conversational_faq = ConversationalRetrievalChain.from_llm(
llm=ChatOpenAI(temperature=0),
retriever=faq_retriever,
memory=memory
)
# 对话
result1 = conversational_faq({"question": "怎么退货?"})
result2 = conversational_faq({"question": "需要多久?"}) # 理解"需要多久"指退货时间
---
b.工单系统集成
a.功能说明
当FAQ无法解决问题时,自动创建工单转人工处理。工单包含用户信息、问题描述、对话历史、优先级等。可以集成CRM系统、工单系统、数据库等。支持工单状态追踪、自动分配、SLA监控等功能。工单系统确保问题闭环处理。
b.代码示例
---
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
import requests
# 1. 工单创建工具
class CreateTicketInput(BaseModel):
user_id: str = Field(description="用户ID")
question: str = Field(description="问题描述")
priority: str = Field(description="优先级:低/中/高")
category: str = Field(description="问题类别")
class CreateTicketTool(BaseTool):
"""创建工单工具"""
name = "create_ticket"
description = "创建客服工单,转人工处理"
args_schema = CreateTicketInput
def _run(
self,
user_id: str,
question: str,
priority: str,
category: str
) -> str:
# 调用工单系统API
response = requests.post(
"http://ticket-system/api/tickets",
json={
"user_id": user_id,
"question": question,
"priority": priority,
"category": category,
"source": "chatbot",
"created_at": datetime.now().isoformat()
}
)
if response.status_code == 200:
ticket_id = response.json()["ticket_id"]
return f"已为您创建工单,工单号:{ticket_id},客服将尽快处理。"
else:
return "创建工单失败,请稍后重试或拨打客服热线。"
# 2. 订单查询工具
class QueryOrderTool(BaseTool):
"""订单查询工具"""
name = "query_order"
description = "查询订单状态、物流信息"
def _run(self, order_id: str) -> str:
# 查询数据库
import dmPython
conn = dmPython.connect("dm://...")
cursor = conn.cursor()
cursor.execute("""
SELECT order_id, status, logistics_company, tracking_number
FROM orders
WHERE order_id = ?
""", (order_id,))
result = cursor.fetchone()
conn.close()
if result:
return f"""订单信息:
订单号:{result[0]}
状态:{result[1]}
物流公司:{result[2]}
快递单号:{result[3]}
"""
else:
return "未找到该订单,请检查订单号是否正确。"
# 3. Agent集成工具
from langchain.agents import initialize_agent, AgentType
from langchain.agents import Tool
tools = [
CreateTicketTool(),
QueryOrderTool(),
Tool(
name="faq",
func=lambda q: faq_with_threshold(q)["answer"],
description="查询常见问题"
)
]
customer_service_agent = initialize_agent(
tools,
ChatOpenAI(temperature=0),
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True
)
# 4. 完整客服流程
class CustomerServiceBot:
"""智能客服机器人"""
def __init__(self):
self.intent_chain = intent_chain
self.faq_chain = faq_chain
self.agent = customer_service_agent
self.memory = ConversationBufferMemory()
def process(self, user_input: str, user_id: str):
# 1. 意图识别
intent = self.intent_chain.invoke({"input": user_input})
# 2. FAQ处理
if intent.intent == "faq":
faq_result = faq_with_threshold(user_input)
if faq_result["matched"]:
return faq_result["answer"]
# 3. Agent处理(订单查询、工单创建等)
if intent.intent in ["order_query", "complaint"]:
result = self.agent.run(
f"用户ID:{user_id}\n问题:{user_input}\n意图:{intent.intent}"
)
return result
# 4. 转人工
ticket_tool = CreateTicketTool()
return ticket_tool._run(
user_id=user_id,
question=user_input,
priority="中",
category=intent.intent
)
# 使用客服机器人
bot = CustomerServiceBot()
response = bot.process("我的订单ORD123456什么时候到?", "user_001")
print(response)
---
10.3 文档分析
01.文档提取
a.多格式支持
a.功能说明
文档分析系统需要支持PDF、Word、Excel、PPT、图片等多种格式。使用对应的Loader加载不同格式,统一转换为Document对象。对于扫描件和图片,使用OCR技术提取文字。支持表格识别、图表提取、版面分析等高级功能。
b.代码示例
---
from langchain.document_loaders import (
PyPDFLoader,
UnstructuredWordDocumentLoader,
UnstructuredExcelLoader,
UnstructuredPowerPointLoader,
UnstructuredImageLoader
)
from paddleocr import PaddleOCR
import os
# 1. PDF文档
pdf_loader = PyPDFLoader("document.pdf")
pdf_docs = pdf_loader.load()
# 2. Word文档
word_loader = UnstructuredWordDocumentLoader("document.docx")
word_docs = word_loader.load()
# 3. Excel表格
excel_loader = UnstructuredExcelLoader("data.xlsx")
excel_docs = excel_loader.load()
# 4. PPT文档
ppt_loader = UnstructuredPowerPointLoader("presentation.pptx")
ppt_docs = ppt_loader.load()
# 5. 图片OCR(PaddleOCR for 信创)
ocr = PaddleOCR(use_angle_cls=True, lang="ch")
def extract_text_from_image(image_path: str) -> str:
"""从图片提取文字"""
result = ocr.ocr(image_path, cls=True)
texts = []
for line in result[0]:
text = line[1][0]
confidence = line[1][1]
if confidence > 0.8: # 置信度过滤
texts.append(text)
return "\n".join(texts)
# 批量处理
from langchain.schema import Document
def load_documents_from_directory(directory: str) -> list[Document]:
"""加载目录中的所有文档"""
docs = []
for filename in os.listdir(directory):
filepath = os.path.join(directory, filename)
if filename.endswith('.pdf'):
loader = PyPDFLoader(filepath)
elif filename.endswith('.docx'):
loader = UnstructuredWordDocumentLoader(filepath)
elif filename.endswith('.xlsx'):
loader = UnstructuredExcelLoader(filepath)
elif filename.endswith(('.jpg', '.png')):
# OCR处理
text = extract_text_from_image(filepath)
docs.append(Document(
page_content=text,
metadata={"source": filepath, "type": "image"}
))
continue
else:
continue
docs.extend(loader.load())
return docs
all_docs = load_documents_from_directory("./documents")
print(f"加载了{len(all_docs)}个文档")
---
b.结构化提取
a.功能说明
从文档中提取结构化信息,如合同要素、发票字段、简历信息等。定义提取schema,使用LLM或NER模型识别实体和关系。支持表格提取、多页关联、字段验证等功能。结构化提取将非结构化文档转换为可处理的数据。
b.代码示例
---
from langchain.chains import create_extraction_chain
from langchain.chat_models import ChatOpenAI
from pydantic import BaseModel, Field
from typing import List, Optional
# 1. 合同信息提取
class ContractInfo(BaseModel):
"""合同信息"""
contract_id: str = Field(description="合同编号")
party_a: str = Field(description="甲方")
party_b: str = Field(description="乙方")
amount: float = Field(description="合同金额")
start_date: str = Field(description="生效日期")
end_date: str = Field(description="截止日期")
terms: List[str] = Field(description="主要条款")
from langchain.output_parsers import PydanticOutputParser
from langchain.prompts import PromptTemplate
parser = PydanticOutputParser(pydantic_object=ContractInfo)
extraction_prompt = PromptTemplate(
template="""从合同文本中提取信息:
合同内容:
{text}
{format_instructions}
""",
input_variables=["text"],
partial_variables={"format_instructions": parser.get_format_instructions()}
)
extraction_chain = extraction_prompt | ChatOpenAI(temperature=0) | parser
# 提取
contract_text = pdf_docs[0].page_content
contract_info = extraction_chain.invoke({"text": contract_text})
print(f"合同号:{contract_info.contract_id}")
print(f"金额:{contract_info.amount}")
# 2. 发票信息提取
class InvoiceInfo(BaseModel):
"""发票信息"""
invoice_number: str = Field(description="发票号码")
invoice_date: str = Field(description="开票日期")
seller: str = Field(description="销售方")
buyer: str = Field(description="购买方")
items: List[dict] = Field(description="商品列表")
total_amount: float = Field(description="总金额")
tax: float = Field(description="税额")
# 3. 简历信息提取
class ResumeInfo(BaseModel):
"""简历信息"""
name: str = Field(description="姓名")
phone: str = Field(description="电话")
email: str = Field(description="邮箱")
education: List[dict] = Field(description="教育经历")
work_experience: List[dict] = Field(description="工作经历")
skills: List[str] = Field(description="技能列表")
# 4. 批量提取
def batch_extract_contracts(documents: List[Document]) -> List[ContractInfo]:
"""批量提取合同信息"""
results = []
for doc in documents:
try:
info = extraction_chain.invoke({"text": doc.page_content})
results.append(info)
except Exception as e:
print(f"提取失败:{doc.metadata['source']} - {e}")
return results
contract_infos = batch_extract_contracts(pdf_docs)
# 5. 保存到数据库
import dmPython
def save_to_database(contract_info: ContractInfo):
"""保存到达梦数据库"""
conn = dmPython.connect("dm://...")
cursor = conn.cursor()
cursor.execute("""
INSERT INTO contracts (
contract_id, party_a, party_b, amount,
start_date, end_date, terms
) VALUES (?, ?, ?, ?, ?, ?, ?)
""", (
contract_info.contract_id,
contract_info.party_a,
contract_info.party_b,
contract_info.amount,
contract_info.start_date,
contract_info.end_date,
json.dumps(contract_info.terms, ensure_ascii=False)
))
conn.commit()
conn.close()
for info in contract_infos:
save_to_database(info)
---
02.文档摘要
a.单文档摘要
a.功能说明
对单个文档生成摘要,提取关键信息和核心要点。支持不同摘要长度、不同摘要风格(概括式、问答式、关键词式)。可以使用MapReduce处理长文档,分段摘要后合并。摘要帮助快速了解文档内容,提升信息获取效率。
b.代码示例
---
from langchain.chains.summarize import load_summarize_chain
from langchain.prompts import PromptTemplate
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(temperature=0)
# 1. Stuff方法(短文档)
stuff_chain = load_summarize_chain(
llm,
chain_type="stuff"
)
summary = stuff_chain.run(pdf_docs)
print(summary)
# 2. MapReduce方法(长文档)
map_prompt = PromptTemplate(
template="总结以下内容:\n{text}\n\n简要总结:",
input_variables=["text"]
)
combine_prompt = PromptTemplate(
template="将以下摘要合并为完整总结:\n{text}\n\n完整总结:",
input_variables=["text"]
)
map_reduce_chain = load_summarize_chain(
llm,
chain_type="map_reduce",
map_prompt=map_prompt,
combine_prompt=combine_prompt
)
summary = map_reduce_chain.run(pdf_docs)
# 3. Refine方法(渐进式)
refine_chain = load_summarize_chain(
llm,
chain_type="refine"
)
summary = refine_chain.run(pdf_docs)
# 4. 自定义摘要风格
custom_prompt = PromptTemplate(
template="""请用以下格式总结文档:
文档内容:
{text}
摘要格式:
1. 核心主题:
2. 关键要点:
3. 结论建议:
""",
input_variables=["text"]
)
custom_chain = load_summarize_chain(
llm,
chain_type="stuff",
prompt=custom_prompt
)
# 5. 多语言摘要
def multilingual_summary(docs: List[Document], target_lang="en"):
"""生成多语言摘要"""
# 先生成中文摘要
summary_zh = stuff_chain.run(docs)
# 翻译为目标语言
translation_prompt = PromptTemplate.from_template(
f"将以下文本翻译为{target_lang}:\n{{text}}"
)
translation_chain = translation_prompt | llm | StrOutputParser()
summary_target = translation_chain.invoke({"text": summary_zh})
return {"zh": summary_zh, target_lang: summary_target}
summaries = multilingual_summary(pdf_docs, "en")
print(f"中文:{summaries['zh']}")
print(f"英文:{summaries['en']}")
---
b.多文档摘要
a.功能说明
对多个相关文档生成统一摘要,提取共同主题和差异点。适用于研究综述、新闻聚合、比较分析等场景。可以使用向量聚类分组文档,使用Map-Reduce分层摘要。支持时间线摘要、主题聚合、观点对比等高级功能。
b.代码示例
---
from langchain.chains import MapReduceDocumentsChain, StuffDocumentsChain
from langchain.chains import LLMChain, ReduceDocumentsChain
# 1. 多文档MapReduce摘要
# Map阶段:每个文档生成摘要
map_template = """总结以下文档的核心内容:
{docs}
简要总结:"""
map_prompt = PromptTemplate(
template=map_template,
input_variables=["docs"]
)
map_chain = LLMChain(llm=llm, prompt=map_prompt)
# Reduce阶段:合并所有摘要
reduce_template = """将以下各个文档的摘要合并为综合总结:
{doc_summaries}
综合总结:
1. 共同主题:
2. 关键观点:
3. 差异对比:
"""
reduce_prompt = PromptTemplate(
template=reduce_template,
input_variables=["doc_summaries"]
)
reduce_chain = LLMChain(llm=llm, prompt=reduce_prompt)
# 组合链
combine_documents_chain = StuffDocumentsChain(
llm_chain=reduce_chain,
document_variable_name="doc_summaries"
)
reduce_documents_chain = ReduceDocumentsChain(
combine_documents_chain=combine_documents_chain,
collapse_documents_chain=combine_documents_chain,
token_max=4000
)
map_reduce_chain = MapReduceDocumentsChain(
llm_chain=map_chain,
reduce_documents_chain=reduce_documents_chain,
document_variable_name="docs"
)
# 执行多文档摘要
all_docs = [
*load_documents_from_directory("./research_papers"),
*load_documents_from_directory("./reports")
]
综合摘要 = map_reduce_chain.run(all_docs)
# 2. 主题聚类摘要
from sklearn.cluster import KMeans
from langchain.embeddings import OpenAIEmbeddings
def cluster_and_summarize(docs: List[Document], n_clusters=3):
"""聚类并分组摘要"""
# 向量化
embeddings = OpenAIEmbeddings()
vectors = embeddings.embed_documents([doc.page_content for doc in docs])
# 聚类
kmeans = KMeans(n_clusters=n_clusters)
labels = kmeans.fit_predict(vectors)
# 分组
clusters = {i: [] for i in range(n_clusters)}
for doc, label in zip(docs, labels):
clusters[label].append(doc)
# 每组生成摘要
summaries = {}
for cluster_id, cluster_docs in clusters.items():
summary = stuff_chain.run(cluster_docs)
summaries[f"主题{cluster_id+1}"] = summary
return summaries
topic_summaries = cluster_and_summarize(all_docs, n_clusters=3)
for topic, summary in topic_summaries.items():
print(f"\n{topic}:\n{summary}")
---
10.4 API调用代理
01.工具定义
a.API工具封装
a.功能说明
将REST API封装为LangChain工具,供Agent调用执行实际操作。定义API参数、认证方式、错误处理等。支持GET、POST、PUT、DELETE等HTTP方法,支持JSON、表单等数据格式。API工具让Agent能够与外部系统交互,执行业务操作。
b.代码示例
---
from langchain.tools import BaseTool
from pydantic import BaseModel, Field
import requests
from typing import Optional
# 1. 天气查询API
class WeatherInput(BaseModel):
city: str = Field(description="城市名称")
date: Optional[str] = Field(None, description="日期YYYY-MM-DD")
class WeatherTool(BaseTool):
"""天气查询工具"""
name = "weather"
description = "查询指定城市的天气信息"
args_schema = WeatherInput
def _run(self, city: str, date: Optional[str] = None) -> str:
try:
response = requests.get(
"https://api.weather.com/v1/current",
params={"city": city, "date": date},
headers={"Authorization": f"Bearer {os.getenv('WEATHER_API_KEY')}"},
timeout=10
)
response.raise_for_status()
data = response.json()
return f"""天气信息:
城市:{data['city']}
温度:{data['temperature']}°C
天气:{data['condition']}
湿度:{data['humidity']}%
"""
except requests.Timeout:
return "查询超时,请稍后重试"
except requests.HTTPError as e:
return f"查询失败:{e.response.status_code}"
except Exception as e:
return f"发生错误:{str(e)}"
async def _arun(self, city: str, date: Optional[str] = None) -> str:
# 异步实现
import aiohttp
async with aiohttp.ClientSession() as session:
async with session.get(
"https://api.weather.com/v1/current",
params={"city": city, "date": date}
) as response:
data = await response.json()
return f"天气:{data['condition']}"
# 2. 数据库查询API
class DatabaseQueryTool(BaseTool):
"""数据库查询工具"""
name = "db_query"
description = "查询业务数据库"
def __init__(self, connection_string: str):
super().__init__()
self.conn_str = connection_string
def _run(self, query: str) -> str:
import dmPython
try:
conn = dmPython.connect(self.conn_str)
cursor = conn.cursor()
cursor.execute(query)
results = cursor.fetchall()
conn.close()
if not results:
return "未找到数据"
# 格式化结果
return "\n".join([str(row) for row in results])
except Exception as e:
return f"查询失败:{str(e)}"
# 3. 文件操作API
class FileOperationTool(BaseTool):
"""文件操作工具"""
name = "file_operation"
description = "读取或写入文件"
def _run(self, operation: str, filepath: str, content: str = "") -> str:
try:
if operation == "read":
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
elif operation == "write":
with open(filepath, 'w', encoding='utf-8') as f:
f.write(content)
return f"已写入文件:{filepath}"
else:
return "不支持的操作"
except Exception as e:
return f"操作失败:{str(e)}"
# 4. 发送邮件API
class SendEmailTool(BaseTool):
"""发送邮件工具"""
name = "send_email"
description = "发送电子邮件"
def _run(self, to: str, subject: str, body: str) -> str:
import smtplib
from email.mime.text import MIMEText
try:
msg = MIMEText(body, 'plain', 'utf-8')
msg['Subject'] = subject
msg['From'] = os.getenv('EMAIL_FROM')
msg['To'] = to
with smtplib.SMTP(os.getenv('SMTP_SERVER'), 587) as server:
server.starttls()
server.login(os.getenv('EMAIL_USER'), os.getenv('EMAIL_PASS'))
server.send_message(msg)
return f"邮件已发送到{to}"
except Exception as e:
return f"发送失败:{str(e)}"
---
b.工具组合
a.功能说明
将多个API工具组合成工具集,供Agent灵活调用。Agent根据任务需求自动选择和组合工具,实现复杂的业务流程。可以配置工具优先级、依赖关系、调用限制等。工具组合实现Agent的自动化能力。
b.代码示例
---
from langchain.agents import initialize_agent, AgentType, Tool
from langchain.chat_models import ChatOpenAI
# 1. 创建工具集
tools = [
WeatherTool(),
DatabaseQueryTool("dm://..."),
FileOperationTool(),
SendEmailTool(),
Tool(
name="calculator",
func=lambda x: eval(x),
description="执行数学计算"
),
Tool(
name="search",
func=lambda x: search_api(x),
description="搜索互联网信息"
)
]
# 2. 创建Agent
agent = initialize_agent(
tools,
ChatOpenAI(model="gpt-4", temperature=0),
agent=AgentType.OPENAI_FUNCTIONS,
verbose=True,
max_iterations=10,
early_stopping_method="generate"
)
# 3. 复杂任务执行
result = agent.run("""
查询北京的天气,然后从数据库查询今天的销售数据,
计算平均销售额,最后将结果发送邮件给[email protected]
""")
# 4. 工具链路追踪
from langchain.callbacks import StdOutCallbackHandler
class ToolTrackerCallback(StdOutCallbackHandler):
def __init__(self):
super().__init__()
self.tool_calls = []
def on_tool_start(self, tool, input_str, **kwargs):
print(f"\n调用工具:{tool} - 输入:{input_str}")
self.tool_calls.append({
"tool": tool,
"input": input_str,
"timestamp": datetime.now()
})
def on_tool_end(self, output, **kwargs):
print(f"工具输出:{output}")
tracker = ToolTrackerCallback()
agent = initialize_agent(
tools,
llm,
agent=AgentType.OPENAI_FUNCTIONS,
callbacks=[tracker],
verbose=True
)
result = agent.run("执行任务...")
# 查看工具调用历史
for call in tracker.tool_calls:
print(f"{call['timestamp']}: {call['tool']} - {call['input']}")
---
02.Agent应用
a.自动化工作流
a.功能说明
使用Agent执行自动化工作流,如数据处理、报告生成、任务调度等。Agent理解任务目标,自动规划步骤,调用工具完成执行。支持条件判断、循环处理、异常恢复等控制流。自动化工作流提升效率,减少人工干预。
b.代码示例
---
from langchain.agents import AgentExecutor
from langchain.prompts import ChatPromptTemplate
# 1. 数据分析工作流
data_analysis_tools = [
Tool(name="load_data", func=load_csv, description="加载CSV数据"),
Tool(name="clean_data", func=clean_dataframe, description="清洗数据"),
Tool(name="analyze", func=statistical_analysis, description="统计分析"),
Tool(name="visualize", func=create_chart, description="生成图表"),
Tool(name="save_report", func=save_to_file, description="保存报告")
]
data_agent = initialize_agent(
data_analysis_tools,
ChatOpenAI(temperature=0),
agent=AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION
)
result = data_agent.run("""
分析sales_data.csv文件:
1. 加载数据
2. 清洗缺失值和异常值
3. 计算月度销售统计
4. 生成趋势图表
5. 保存分析报告
""")
# 2. 内容生成工作流
content_tools = [
Tool(name="research", func=search_web, description="搜索资料"),
Tool(name="outline", func=create_outline, description="生成大纲"),
Tool(name="write_section", func=write_content, description="撰写章节"),
Tool(name="review", func=review_quality, description="质量审核"),
Tool(name="publish", func=publish_article, description="发布文章")
]
content_agent = initialize_agent(
content_tools,
ChatOpenAI(model="gpt-4"),
agent=AgentType.OPENAI_FUNCTIONS
)
result = content_agent.run("""
写一篇关于"AI在医疗领域的应用"的文章:
1. 搜索相关资料
2. 制定文章大纲
3. 逐章节撰写内容
4. 审核文章质量
5. 发布到博客平台
""")
# 3. 定时任务Agent
import schedule
def scheduled_task():
"""定时执行的任务"""
agent.run("""
每日任务:
1. 查询今日销售数据
2. 生成日报
3. 发送邮件给管理层
""")
# 每天早上8点执行
schedule.every().day.at("08:00").do(scheduled_task)
while True:
schedule.run_pending()
time.sleep(60)
# 4. 错误恢复工作流
class RobustAgent:
"""支持错误恢复的Agent"""
def __init__(self, agent, max_retries=3):
self.agent = agent
self.max_retries = max_retries
def run_with_retry(self, task: str):
"""带重试的执行"""
for attempt in range(self.max_retries):
try:
result = self.agent.run(task)
return {"success": True, "result": result}
except Exception as e:
print(f"第{attempt+1}次尝试失败:{e}")
if attempt < self.max_retries - 1:
# 等待后重试
time.sleep(2 ** attempt)
else:
# 最终失败,返回错误
return {"success": False, "error": str(e)}
robust_agent = RobustAgent(agent, max_retries=3)
result = robust_agent.run_with_retry("执行复杂任务...")
---
11 最佳实践
11.1 性能优化
01.LLM调用优化
a.缓存策略
a.功能说明
缓存LLM调用结果避免重复请求,显著降低成本和延迟。支持内存缓存、Redis缓存、SQLite缓存等多种后端。可以配置缓存键策略、过期时间、容量限制。缓存对幂等查询效果最好,如FAQ问答、文档摘要、数据分析等。
b.代码示例
---
from langchain.cache import InMemoryCache, RedisCache, SQLiteCache
from langchain.globals import set_llm_cache
import langchain
# 1. 内存缓存(简单但重启丢失)
langchain.llm_cache = InMemoryCache()
# 2. Redis缓存(持久化、分布式)
from redis import Redis
redis_client = Redis(
host='localhost',
port=6379,
db=0,
decode_responses=True
)
langchain.llm_cache = RedisCache(redis_client)
# 3. SQLite缓存(本地持久化)
langchain.llm_cache = SQLiteCache(database_path=".langchain.db")
# 4. 语义缓存(相似问题共享缓存)
from langchain.cache import RedisSemanticCache
from langchain.embeddings import OpenAIEmbeddings
langchain.llm_cache = RedisSemanticCache(
redis_url="redis://localhost:6379",
embedding=OpenAIEmbeddings(),
score_threshold=0.9 # 相似度阈值
)
# 使用缓存
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(temperature=0)
# 第一次调用(会缓存)
result1 = llm.predict("什么是LangChain?")
# 第二次调用(命中缓存,瞬间返回)
result2 = llm.predict("什么是LangChain?")
# 5. 自定义缓存键
from langchain.cache import BaseCache
import hashlib
class CustomCache(BaseCache):
"""自定义缓存策略"""
def __init__(self):
self.cache = {}
def lookup(self, prompt: str, llm_string: str):
# 忽略temperature等参数,只基于prompt缓存
key = hashlib.md5(prompt.encode()).hexdigest()
return self.cache.get(key)
def update(self, prompt: str, llm_string: str, return_val):
key = hashlib.md5(prompt.encode()).hexdigest()
self.cache[key] = return_val
langchain.llm_cache = CustomCache()
# 6. 缓存监控
class MonitoredCache(RedisCache):
"""带监控的缓存"""
def __init__(self, redis_client):
super().__init__(redis_client)
self.hits = 0
self.misses = 0
def lookup(self, prompt, llm_string):
result = super().lookup(prompt, llm_string)
if result is not None:
self.hits += 1
print(f"缓存命中 (命中率:{self.hit_rate():.2%})")
else:
self.misses += 1
return result
def hit_rate(self):
total = self.hits + self.misses
return self.hits / total if total > 0 else 0
cache = MonitoredCache(redis_client)
langchain.llm_cache = cache
---
b.批量处理
a.功能说明
批量调用LLM提升吞吐量,减少网络往返次数。使用batch方法并行处理多个输入,合理配置并发数避免限流。批量处理特别适合大规模数据处理如文档分类、情感分析、内容生成等场景。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.prompts import ChatPromptTemplate
from langchain.schema.output_parser import StrOutputParser
# 1. 基础批量处理
llm = ChatOpenAI(temperature=0)
inputs = [
{"text": "这个产品很棒"},
{"text": "质量太差了"},
{"text": "价格合理"},
{"text": "物流很快"}
]
# 批量调用
results = llm.batch(inputs)
# 2. Chain批量处理
chain = (
ChatPromptTemplate.from_template("分类情感:{text}")
| llm
| StrOutputParser()
)
results = chain.batch(inputs)
for inp, result in zip(inputs, results):
print(f"{inp['text']} -> {result}")
# 3. 配置并发参数
from langchain.schema.runnable import RunnableConfig
results = chain.batch(
inputs,
config=RunnableConfig(
max_concurrency=10 # 最多10个并发
)
)
# 4. 异步批量处理
import asyncio
async def async_batch():
results = await chain.abatch(inputs)
return results
results = asyncio.run(async_batch())
# 5. 分批处理大数据集
def process_large_dataset(data: list, batch_size=100):
"""分批处理避免内存溢出"""
all_results = []
for i in range(0, len(data), batch_size):
batch = data[i:i+batch_size]
batch_results = chain.batch(batch)
all_results.extend(batch_results)
# 进度显示
print(f"进度:{len(all_results)}/{len(data)} ({len(all_results)/len(data)*100:.1f}%)")
return all_results
large_data = [{"text": f"评论{i}"} for i in range(10000)]
results = process_large_dataset(large_data, batch_size=50)
# 6. 失败重试批量处理
def batch_with_retry(chain, inputs, max_retries=3):
"""带重试的批量处理"""
results = [None] * len(inputs)
failed_indices = list(range(len(inputs)))
for attempt in range(max_retries):
if not failed_indices:
break
# 只处理失败的项
retry_inputs = [inputs[i] for i in failed_indices]
try:
retry_results = chain.batch(retry_inputs)
# 更新结果
for idx, result in zip(failed_indices, retry_results):
results[idx] = result
failed_indices = []
except Exception as e:
print(f"第{attempt+1}次批量处理部分失败")
time.sleep(2 ** attempt)
return results
---
02.检索优化
a.向量索引
a.功能说明
优化向量数据库索引提升检索速度,包括HNSW、IVF、PQ等索引算法。合理配置索引参数平衡精度和性能,根据数据规模选择合适的索引类型。定期重建索引保持最佳性能,使用索引预热加速首次查询。
b.代码示例
---
from langchain.vectorstores import Milvus
from langchain.embeddings import OpenAIEmbeddings
# 1. HNSW索引(高精度、高性能)
vectorstore = Milvus(
embedding_function=OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"},
collection_name="documents",
index_params={
"metric_type": "L2", # 距离度量
"index_type": "HNSW", # 索引类型
"params": {
"M": 16, # 邻居数量
"efConstruction": 200 # 构建参数
}
},
search_params={
"metric_type": "L2",
"params": {"ef": 100} # 搜索参数
}
)
# 2. IVF索引(大规模数据)
vectorstore_ivf = Milvus(
embedding_function=OpenAIEmbeddings(),
connection_args={"host": "localhost", "port": "19530"},
collection_name="large_docs",
index_params={
"metric_type": "IP", # 内积距离
"index_type": "IVF_FLAT",
"params": {"nlist": 1024} # 聚类中心数
},
search_params={
"metric_type": "IP",
"params": {"nprobe": 16} # 搜索的聚类数
}
)
# 3. 混合索引(标量+向量)
from pymilvus import Collection, FieldSchema, CollectionSchema, DataType
fields = [
FieldSchema(name="id", dtype=DataType.INT64, is_primary=True, auto_id=True),
FieldSchema(name="embedding", dtype=DataType.FLOAT_VECTOR, dim=1536),
FieldSchema(name="category", dtype=DataType.VARCHAR, max_length=100),
FieldSchema(name="timestamp", dtype=DataType.INT64)
]
schema = CollectionSchema(fields, description="文档集合")
collection = Collection("documents", schema)
# 向量索引
collection.create_index(
field_name="embedding",
index_params={
"metric_type": "L2",
"index_type": "HNSW",
"params": {"M": 16, "efConstruction": 200}
}
)
# 标量索引(加速过滤)
collection.create_index(
field_name="category",
index_params={"index_type": "TRIE"}
)
# 4. 索引预热
collection.load() # 加载索引到内存
# 预热查询
dummy_vector = [0.0] * 1536
collection.search(
data=[dummy_vector],
anns_field="embedding",
param={"metric_type": "L2", "params": {"ef": 100}},
limit=10
)
# 5. 分区优化
# 按时间分区
collection.create_partition("2024_01")
collection.create_partition("2024_02")
# 只搜索特定分区
results = collection.search(
data=[query_vector],
anns_field="embedding",
param={"metric_type": "L2"},
limit=10,
partition_names=["2024_02"] # 只搜索2月数据
)
---
b.检索缓存
a.功能说明
缓存常见查询的检索结果,避免重复的向量搜索。可以缓存完整的Document列表,也可以缓存向量相似度计算结果。适用于FAQ系统、热门查询等场景。需要考虑缓存更新策略,当知识库更新时清理相关缓存。
b.代码示例
---
from functools import lru_cache
import redis
import hashlib
import json
# 1. 内存缓存检索
@lru_cache(maxsize=1000)
def cached_retrieval(query: str, k: int = 3):
"""缓存检索结果"""
retriever = vectorstore.as_retriever(search_kwargs={"k": k})
docs = retriever.get_relevant_documents(query)
return docs
# 2. Redis缓存检索
redis_client = Redis(host='localhost', port=6379)
def redis_cached_retrieval(query: str, k: int = 3):
"""Redis缓存检索"""
# 生成缓存键
cache_key = f"retrieval:{hashlib.md5(query.encode()).hexdigest()}:{k}"
# 查询缓存
cached = redis_client.get(cache_key)
if cached:
# 反序列化
docs_data = json.loads(cached)
from langchain.schema import Document
return [Document(**doc) for doc in docs_data]
# 执行检索
retriever = vectorstore.as_retriever(search_kwargs={"k": k})
docs = retriever.get_relevant_documents(query)
# 缓存结果
docs_data = [{"page_content": doc.page_content, "metadata": doc.metadata} for doc in docs]
redis_client.setex(
cache_key,
3600, # 1小时过期
json.dumps(docs_data, ensure_ascii=False)
)
return docs
# 3. 语义缓存检索
from langchain.embeddings import OpenAIEmbeddings
import numpy as np
class SemanticRetrievalCache:
"""语义缓存检索"""
def __init__(self, similarity_threshold=0.95):
self.cache = [] # [(query_vector, docs), ...]
self.embeddings = OpenAIEmbeddings()
self.threshold = similarity_threshold
def get(self, query: str):
"""查询缓存"""
query_vector = self.embeddings.embed_query(query)
for cached_vector, cached_docs in self.cache:
# 计算相似度
similarity = np.dot(query_vector, cached_vector)
if similarity > self.threshold:
print(f"命中语义缓存 (相似度:{similarity:.3f})")
return cached_docs
return None
def put(self, query: str, docs):
"""添加到缓存"""
query_vector = self.embeddings.embed_query(query)
self.cache.append((query_vector, docs))
# 限制缓存大小
if len(self.cache) > 100:
self.cache.pop(0)
semantic_cache = SemanticRetrievalCache()
def cached_semantic_retrieval(query: str):
# 查询缓存
cached = semantic_cache.get(query)
if cached:
return cached
# 执行检索
docs = retriever.get_relevant_documents(query)
# 添加到缓存
semantic_cache.put(query, docs)
return docs
# 4. 缓存失效策略
class SmartRetrievalCache:
"""智能缓存"""
def __init__(self, redis_client):
self.redis = redis_client
self.version_key = "cache:version"
def invalidate_all(self):
"""清空所有缓存"""
# 增加版本号
self.redis.incr(self.version_key)
def get_cached(self, query: str):
version = self.redis.get(self.version_key) or "0"
cache_key = f"retrieval:{version}:{hashlib.md5(query.encode()).hexdigest()}"
cached = self.redis.get(cache_key)
if cached:
return json.loads(cached)
return None
# 知识库更新时清空缓存
def update_knowledge_base(new_docs):
vectorstore.add_documents(new_docs)
cache.invalidate_all()
---
11.2 错误处理
01.异常捕获
a.常见错误类型
a.功能说明
LangChain应用中常见的错误包括API限流、超时、解析失败、资源不足等。需要识别不同错误类型并采取针对性的处理策略。可恢复错误应该重试,不可恢复错误应该降级或返回友好提示。完善的错误处理确保系统稳定性和用户体验。
b.代码示例
---
from langchain.chat_models import ChatOpenAI
from langchain.schema import OutputParserException
import openai
# 1. API错误处理
llm = ChatOpenAI(temperature=0)
try:
result = llm.predict("你的问题")
except openai.error.RateLimitError as e:
# API限流
print("API调用频率过高,请稍后重试")
time.sleep(60)
result = llm.predict("你的问题")
except openai.error.Timeout as e:
# 超时
print("请求超时,重试中...")
llm.request_timeout = 60 # 增加超时时间
result = llm.predict("你的问题")
except openai.error.APIError as e:
# API错误
print(f"API错误:{e}")
# 使用备用模型
fallback_llm = ChatOpenAI(model="gpt-3.5-turbo")
result = fallback_llm.predict("你的问题")
except Exception as e:
# 其他错误
print(f"发生错误:{e}")
result = "抱歉,服务暂时不可用"
# 2. 解析错误处理
from langchain.output_parsers import PydanticOutputParser
from langchain.output_parsers import OutputFixingParser
parser = PydanticOutputParser(pydantic_object=MyModel)
try:
result = parser.parse(llm_output)
except OutputParserException as e:
print(f"解析失败:{e}")
# 使用修复解析器
fixing_parser = OutputFixingParser.from_llm(
parser=parser,
llm=ChatOpenAI(temperature=0)
)
result = fixing_parser.parse(llm_output)
# 3. 向量库错误处理
from pymilvus import exceptions as milvus_exceptions
try:
vectorstore.similarity_search("query")
except milvus_exceptions.ConnectionConfigException:
print("Milvus连接配置错误")
# 重新初始化连接
vectorstore._init_connection()
except milvus_exceptions.CollectionNotExistException:
print("集合不存在,创建集合")
vectorstore.create_collection()
except Exception as e:
print(f"向量搜索失败:{e}")
# 降级到关键词搜索
results = keyword_search("query")
# 4. 内存错误处理
from langchain.memory import ConversationBufferMemory
memory = ConversationBufferMemory()
try:
memory.load_memory_variables({})
except MemoryError:
print("内存不足,清理历史记录")
memory.clear()
except Exception as e:
print(f"记忆加载失败:{e}")
# 使用新的记忆实例
memory = ConversationBufferMemory()
# 5. Agent执行错误
from langchain.agents import AgentExecutor
try:
result = agent.run("执行任务")
except ValueError as e:
if "Could not parse" in str(e):
print("Agent输出解析失败")
# 重新执行with更清晰的指令
result = agent.run("请按照指定格式输出:...")
except Exception as e:
print(f"Agent执行失败:{e}")
# 人工介入
result = "任务执行失败,已转人工处理"
---
b.重试策略
a.功能说明
实现自动重试机制处理暂时性错误,配置重试次数、退避策略、重试条件等。使用指数退避避免过度请求,使用随机抖动防止同时重试。可以集成tenacity等重试库简化实现。合理的重试策略提升系统可靠性。
b.代码示例
---
from tenacity import (
retry,
stop_after_attempt,
wait_exponential,
retry_if_exception_type,
before_sleep_log
)
import logging
logging.basicConfig(level=logging.INFO)
logger = logging.getLogger(__name__)
# 1. 基础重试
@retry(
stop=stop_after_attempt(3), # 最多3次
wait=wait_exponential(multiplier=1, min=2, max=10), # 指数退避
before_sleep=before_sleep_log(logger, logging.INFO)
)
def call_llm_with_retry(prompt: str):
"""带重试的LLM调用"""
llm = ChatOpenAI(temperature=0)
return llm.predict(prompt)
# 2. 条件重试
@retry(
stop=stop_after_attempt(5),
wait=wait_exponential(min=1, max=60),
retry=retry_if_exception_type((
openai.error.RateLimitError,
openai.error.Timeout,
openai.error.APIConnectionError
))
)
def resilient_llm_call(prompt: str):
"""只重试特定错误"""
llm = ChatOpenAI(temperature=0)
return llm.predict(prompt)
# 3. 自定义重试逻辑
from tenacity import retry_if_result
def is_empty_response(result):
"""检查是否是空响应"""
return result is None or result.strip() == ""
@retry(
stop=stop_after_attempt(3),
retry=retry_if_result(is_empty_response),
wait=wait_exponential(min=2, max=10)
)
def call_until_valid_response(prompt: str):
"""重试直到获得有效响应"""
llm = ChatOpenAI(temperature=0.7) # 增加温度提升多样性
return llm.predict(prompt)
# 4. 带回退的重试
from langchain.schema.runnable import Runnable
class RetryWithFallback(Runnable):
"""重试+回退策略"""
def __init__(self, primary, fallback, max_retries=3):
self.primary = primary
self.fallback = fallback
self.max_retries = max_retries
def invoke(self, input, config=None):
# 重试主服务
for attempt in range(self.max_retries):
try:
return self.primary.invoke(input, config)
except Exception as e:
logger.warning(f"第{attempt+1}次尝试失败:{e}")
if attempt < self.max_retries - 1:
time.sleep(2 ** attempt)
# 降级到备用服务
logger.info("降级到备用服务")
return self.fallback.invoke(input, config)
# 使用
primary_chain = prompt | ChatOpenAI(model="gpt-4")
fallback_chain = prompt | ChatOpenAI(model="gpt-3.5-turbo")
resilient_chain = RetryWithFallback(
primary=primary_chain,
fallback=fallback_chain,
max_retries=3
)
# 5. 全局重试配置
from langchain.chat_models import ChatOpenAI
llm = ChatOpenAI(
temperature=0,
max_retries=3, # 最大重试次数
request_timeout=30, # 超时时间
retry_on_failures=True # 启用重试
)
---
02.日志记录
a.调试日志
a.功能说明
记录详细的执行日志用于调试和问题排查,包括输入输出、中间步骤、错误堆栈等。可以配置日志级别、日志格式、输出目标。使用结构化日志便于后续分析。完善的日志是生产环境必备。
b.代码示例
---
import logging
from datetime import datetime
# 1. 配置日志
logging.basicConfig(
level=logging.INFO,
format='%(asctime)s - %(name)s - %(levelname)s - %(message)s',
handlers=[
logging.FileHandler('langchain.log'),
logging.StreamHandler()
]
)
logger = logging.getLogger(__name__)
# 2. LangChain详细日志
from langchain.globals import set_verbose, set_debug
set_verbose(True) # 启用详细日志
set_debug(True) # 启用调试模式
# 3. 自定义回调记录
from langchain.callbacks import BaseCallbackHandler
class DetailedLogger(BaseCallbackHandler):
"""详细日志回调"""
def on_chain_start(self, serialized, inputs, **kwargs):
logger.info(f"Chain开始:{serialized.get('name')}")
logger.debug(f"输入:{inputs}")
def on_chain_end(self, outputs, **kwargs):
logger.info("Chain完成")
logger.debug(f"输出:{outputs}")
def on_llm_start(self, serialized, prompts, **kwargs):
logger.info(f"LLM调用:{serialized.get('name')}")
for i, prompt in enumerate(prompts):
logger.debug(f"提示词{i+1}:{prompt}")
def on_llm_end(self, response, **kwargs):
logger.info("LLM响应完成")
logger.debug(f"Token使用:{response.llm_output.get('token_usage')}")
def on_llm_error(self, error, **kwargs):
logger.error(f"LLM错误:{error}")
def on_tool_start(self, tool, input_str, **kwargs):
logger.info(f"工具调用:{tool}")
logger.debug(f"输入:{input_str}")
def on_tool_end(self, output, **kwargs):
logger.info("工具执行完成")
logger.debug(f"输出:{output}")
# 使用回调
chain = prompt | llm | parser
result = chain.invoke(
{"input": "..."},
config={"callbacks": [DetailedLogger()]}
)
# 4. 结构化日志
import json
class StructuredLogger:
"""结构化日志"""
def log_event(self, event_type: str, **kwargs):
log_entry = {
"timestamp": datetime.now().isoformat(),
"event_type": event_type,
**kwargs
}
logger.info(json.dumps(log_entry, ensure_ascii=False))
def log_llm_call(self, prompt, response, duration, tokens):
self.log_event(
"llm_call",
prompt_length=len(prompt),
response_length=len(response),
duration_ms=duration,
tokens=tokens
)
def log_error(self, error_type, error_message, context):
self.log_event(
"error",
error_type=error_type,
error_message=str(error_message),
context=context
)
structured_logger = StructuredLogger()
# 5. 性能监控日志
import time
class PerformanceLogger(BaseCallbackHandler):
"""性能监控日志"""
def __init__(self):
self.start_times = {}
def on_chain_start(self, serialized, inputs, run_id, **kwargs):
self.start_times[str(run_id)] = time.time()
def on_chain_end(self, outputs, run_id, **kwargs):
duration = time.time() - self.start_times.get(str(run_id), 0)
logger.info(f"Chain执行时间:{duration:.2f}秒")
def on_llm_start(self, serialized, prompts, run_id, **kwargs):
self.start_times[f"llm_{run_id}"] = time.time()
def on_llm_end(self, response, run_id, **kwargs):
duration = time.time() - self.start_times.get(f"llm_{run_id}", 0)
tokens = response.llm_output.get("token_usage", {})
logger.info(f"""LLM性能:
执行时间:{duration:.2f}秒
Token数量:{tokens.get('total_tokens', 0)}
TPS:{tokens.get('total_tokens', 0) / duration:.1f}
""")
# 使用性能监控
chain = prompt | llm | parser
result = chain.invoke(
{"input": "..."},
config={"callbacks": [PerformanceLogger()]}
)
---
11.3 安全防护
01.输入验证
a.注入攻击防护
a.功能说明
防止提示词注入、SQL注入、命令注入等攻击。验证用户输入长度、格式、内容,过滤危险字符和关键词。使用参数化查询避免SQL注入,使用沙箱环境执行代码避免命令注入。输入验证是安全防护的第一道防线。
b.代码示例
---
import re
from typing import Optional
# 1. 提示词注入防护
class PromptInjectionFilter:
"""提示词注入过滤器"""
def __init__(self):
self.dangerous_patterns = [
r"ignore\s+previous\s+instructions",
r"forget\s+everything",
r"new\s+instructions",
r"system\s+prompt",
r"<script>",
r"javascript:",
]
def is_safe(self, user_input: str) -> bool:
"""检查输入是否安全"""
user_input_lower = user_input.lower()
for pattern in self.dangerous_patterns:
if re.search(pattern, user_input_lower):
return False
return True
def sanitize(self, user_input: str) -> str:
"""清理输入"""
# 移除特殊字符
sanitized = re.sub(r'[<>{}\\]', '', user_input)
# 限制长度
if len(sanitized) > 1000:
sanitized = sanitized[:1000]
return sanitized
filter = PromptInjectionFilter()
def safe_llm_call(user_input: str):
"""安全的LLM调用"""
if not filter.is_safe(user_input):
raise ValueError("检测到不安全的输入")
cleaned_input = filter.sanitize(user_input)
# 使用清理后的输入
prompt = ChatPromptTemplate.from_template(
"用户问题:{input}\n请回答:"
)
chain = prompt | llm | parser
return chain.invoke({"input": cleaned_input})
# 2. SQL注入防护
from langchain.sql_database import SQLDatabase
class SafeSQLDatabase(SQLDatabase):
"""安全的SQL数据库"""
def run(self, command: str, fetch: str = "all"):
"""安全的SQL执行"""
# 检查危险操作
dangerous_keywords = ["DROP", "DELETE", "UPDATE", "INSERT", "TRUNCATE", "ALTER"]
command_upper = command.upper()
for keyword in dangerous_keywords:
if keyword in command_upper:
raise ValueError(f"不允许执行{keyword}操作")
# 限制返回行数
if "LIMIT" not in command_upper:
command += " LIMIT 100"
return super().run(command, fetch)
# 3. 命令注入防护
import subprocess
import shlex
def safe_command_execution(command: str):
"""安全的命令执行"""
# 白名单命令
allowed_commands = ["ls", "pwd", "date", "whoami"]
parts = shlex.split(command)
if not parts or parts[0] not in allowed_commands:
raise ValueError("不允许执行该命令")
# 使用参数列表避免注入
result = subprocess.run(
parts,
capture_output=True,
text=True,
timeout=10
)
return result.stdout
# 4. 文件访问防护
import os
class SafeFileAccess:
"""安全的文件访问"""
def __init__(self, base_dir: str):
self.base_dir = os.path.abspath(base_dir)
def is_safe_path(self, filepath: str) -> bool:
"""检查路径是否安全"""
abs_path = os.path.abspath(filepath)
# 必须在基础目录内
if not abs_path.startswith(self.base_dir):
return False
# 不能是特殊文件
dangerous_files = [".env", "config.yaml", "secrets.json"]
if os.path.basename(abs_path) in dangerous_files:
return False
return True
def read_file(self, filepath: str) -> str:
"""安全读取文件"""
if not self.is_safe_path(filepath):
raise ValueError("不允许访问该文件")
# 文件大小限制
if os.path.getsize(filepath) > 10 * 1024 * 1024: # 10MB
raise ValueError("文件过大")
with open(filepath, 'r', encoding='utf-8') as f:
return f.read()
file_access = SafeFileAccess("/allowed/directory")
---
b.输出过滤
a.功能说明
过滤LLM输出中的敏感信息、有害内容、隐私数据等。检测并脱敏个人信息如手机号、身份证、银行卡等。过滤违规内容如暴力、色情、政治敏感等。使用内容审核API或本地模型进行检测。输出过滤确保内容合规安全。
b.代码示例
---
import re
from typing import List
# 1. 敏感信息脱敏
class SensitiveDataMasker:
"""敏感数据脱敏"""
def mask_phone(self, text: str) -> str:
"""脱敏手机号"""
return re.sub(r'(\d{3})\d{4}(\d{4})', r'\1****\2', text)
def mask_id_card(self, text: str) -> str:
"""脱敏身份证"""
return re.sub(r'(\d{6})\d{8}(\d{4})', r'\1********\2', text)
def mask_email(self, text: str) -> str:
"""脱敏邮箱"""
return re.sub(r'([\w\.-]{2})[\w\.-]*@', r'\1***@', text)
def mask_bank_card(self, text: str) -> str:
"""脱敏银行卡"""
return re.sub(r'(\d{4})\d{8,12}(\d{4})', r'\1********\2', text)
def mask_all(self, text: str) -> str:
"""脱敏所有敏感信息"""
text = self.mask_phone(text)
text = self.mask_id_card(text)
text = self.mask_email(text)
text = self.mask_bank_card(text)
return text
masker = SensitiveDataMasker()
def safe_output(llm_response: str) -> str:
"""安全输出"""
return masker.mask_all(llm_response)
# 2. 有害内容过滤
class ContentModerator:
"""内容审核"""
def __init__(self):
self.sensitive_keywords = [
"暴力", "色情", "赌博", "毒品",
# ... 更多敏感词
]
def contains_sensitive(self, text: str) -> bool:
"""检查是否包含敏感内容"""
text_lower = text.lower()
for keyword in self.sensitive_keywords:
if keyword in text_lower:
return True
return False
def moderate(self, text: str) -> dict:
"""审核内容"""
if self.contains_sensitive(text):
return {
"safe": False,
"reason": "包含敏感内容",
"filtered_text": "[内容已过滤]"
}
return {
"safe": True,
"text": text
}
moderator = ContentModerator()
# 3. 集成到Chain
from langchain.schema.runnable import Runnable
class SafeOutputChain(Runnable):
"""安全输出链"""
def __init__(self, chain, masker, moderator):
self.chain = chain
self.masker = masker
self.moderator = moderator
def invoke(self, input, config=None):
# 执行Chain
result = self.chain.invoke(input, config)
# 脱敏
result = self.masker.mask_all(result)
# 审核
moderation_result = self.moderator.moderate(result)
if not moderation_result["safe"]:
return moderation_result["filtered_text"]
return result
# 使用安全输出
base_chain = prompt | llm | parser
safe_chain = SafeOutputChain(base_chain, masker, moderator)
result = safe_chain.invoke({"input": "..."})
# 4. 调用外部审核API
import requests
def moderate_with_api(text: str) -> bool:
"""使用审核API"""
response = requests.post(
"https://moderation-api.com/v1/check",
json={"text": text},
headers={"Authorization": f"Bearer {os.getenv('MODERATION_API_KEY')}"}
)
data = response.json()
return data.get("safe", False)
---
02.访问控制
a.认证授权
a.功能说明
实现用户认证和权限控制,确保只有授权用户能访问系统。支持多种认证方式如API密钥、JWT令牌、OAuth等。基于角色的访问控制(RBAC)限制不同用户的操作权限。记录访问日志便于审计和追踪。
b.代码示例
---
from functools import wraps
import jwt
from datetime import datetime, timedelta
# 1. API密钥认证
class APIKeyAuth:
"""API密钥认证"""
def __init__(self):
self.valid_keys = {
"key_123": {"user_id": "user1", "role": "admin"},
"key_456": {"user_id": "user2", "role": "user"}
}
def authenticate(self, api_key: str) -> Optional[dict]:
"""验证API密钥"""
return self.valid_keys.get(api_key)
def require_auth(self, func):
"""认证装饰器"""
@wraps(func)
def wrapper(api_key: str, *args, **kwargs):
user = self.authenticate(api_key)
if not user:
raise ValueError("无效的API密钥")
return func(user=user, *args, **kwargs)
return wrapper
auth = APIKeyAuth()
@auth.require_auth
def protected_endpoint(user: dict, query: str):
"""受保护的端点"""
print(f"用户{user['user_id']}执行查询")
return qa_chain.run(query)
# 2. JWT令牌认证
class JWTAuth:
"""JWT认证"""
def __init__(self, secret_key: str):
self.secret_key = secret_key
def create_token(self, user_id: str, role: str) -> str:
"""创建JWT令牌"""
payload = {
"user_id": user_id,
"role": role,
"exp": datetime.utcnow() + timedelta(hours=24)
}
return jwt.encode(payload, self.secret_key, algorithm="HS256")
def verify_token(self, token: str) -> Optional[dict]:
"""验证JWT令牌"""
try:
payload = jwt.decode(token, self.secret_key, algorithms=["HS256"])
return payload
except jwt.ExpiredSignatureError:
raise ValueError("令牌已过期")
except jwt.InvalidTokenError:
raise ValueError("无效的令牌")
jwt_auth = JWTAuth(secret_key="your-secret-key")
# 生成令牌
token = jwt_auth.create_token("user1", "admin")
# 验证令牌
user = jwt_auth.verify_token(token)
# 3. 基于角色的访问控制
class RBACManager:
"""角色权限管理"""
def __init__(self):
self.permissions = {
"admin": ["read", "write", "delete", "admin"],
"user": ["read", "write"],
"guest": ["read"]
}
def has_permission(self, role: str, action: str) -> bool:
"""检查权限"""
return action in self.permissions.get(role, [])
def require_permission(self, action: str):
"""权限装饰器"""
def decorator(func):
@wraps(func)
def wrapper(user: dict, *args, **kwargs):
role = user.get("role", "guest")
if not self.has_permission(role, action):
raise ValueError(f"用户无权限执行{action}操作")
return func(user=user, *args, **kwargs)
return wrapper
return decorator
rbac = RBACManager()
@rbac.require_permission("admin")
def admin_only_operation(user: dict):
"""仅管理员操作"""
print(f"管理员{user['user_id']}执行操作")
# 4. 速率限制
from collections import defaultdict
import time
class RateLimiter:
"""速率限制器"""
def __init__(self, max_requests: int, window_seconds: int):
self.max_requests = max_requests
self.window_seconds = window_seconds
self.requests = defaultdict(list)
def is_allowed(self, user_id: str) -> bool:
"""检查是否允许请求"""
now = time.time()
# 清理过期记录
self.requests[user_id] = [
t for t in self.requests[user_id]
if now - t < self.window_seconds
]
# 检查是否超限
if len(self.requests[user_id]) >= self.max_requests:
return False
# 记录请求
self.requests[user_id].append(now)
return True
limiter = RateLimiter(max_requests=10, window_seconds=60)
def rate_limited_endpoint(user_id: str, query: str):
"""限流端点"""
if not limiter.is_allowed(user_id):
raise ValueError("请求过于频繁,请稍后再试")
return qa_chain.run(query)
# 5. FastAPI集成
from fastapi import FastAPI, Depends, HTTPException, Header
from fastapi.security import HTTPBearer, HTTPAuthorizationCredentials
app = FastAPI()
security = HTTPBearer()
async def get_current_user(
credentials: HTTPAuthorizationCredentials = Depends(security)
):
"""获取当前用户"""
try:
user = jwt_auth.verify_token(credentials.credentials)
return user
except ValueError as e:
raise HTTPException(status_code=401, detail=str(e))
@app.post("/qa")
async def qa_endpoint(
query: str,
user: dict = Depends(get_current_user)
):
"""问答端点"""
# 检查速率限制
if not limiter.is_allowed(user["user_id"]):
raise HTTPException(status_code=429, detail="请求过于频繁")
# 执行查询
result = qa_chain.run(query)
return {"answer": result}
---
11.4 生产部署
01.容器化部署
a.Docker打包
a.功能说明
使用Docker容器化LangChain应用,实现环境一致性和快速部署。编写Dockerfile定义镜像构建步骤,使用多阶段构建减小镜像体积。配置docker-compose管理多容器应用,包括应用服务、数据库、缓存等。容器化简化部署流程,提升可移植性。
b.代码示例
---
# 1. Dockerfile
# Dockerfile
FROM python:3.10-slim
# 设置工作目录
WORKDIR /app
# 复制依赖文件
COPY requirements.txt .
# 安装依赖
RUN pip install --no-cache-dir -r requirements.txt
# 复制应用代码
COPY . .
# 暴露端口
EXPOSE 8000
# 启动命令
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# 2. 多阶段构建
# Dockerfile.multistage
# 构建阶段
FROM python:3.10 AS builder
WORKDIR /app
COPY requirements.txt .
RUN pip install --user --no-cache-dir -r requirements.txt
# 运行阶段
FROM python:3.10-slim
WORKDIR /app
COPY --from=builder /root/.local /root/.local
COPY . .
ENV PATH=/root/.local/bin:$PATH
EXPOSE 8000
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
# 3. docker-compose.yml
---
version: '3.8'
services:
app:
build: .
ports:
- "8000:8000"
environment:
- OPENAI_API_KEY=${OPENAI_API_KEY}
- REDIS_URL=redis://redis:6379
- MILVUS_HOST=milvus
- MILVUS_PORT=19530
depends_on:
- redis
- milvus
volumes:
- ./data:/app/data
networks:
- langchain-network
redis:
image: redis:7-alpine
ports:
- "6379:6379"
volumes:
- redis-data:/data
networks:
- langchain-network
milvus:
image: milvusdb/milvus:latest
ports:
- "19530:19530"
environment:
- ETCD_ENDPOINTS=etcd:2379
- MINIO_ADDRESS=minio:9000
depends_on:
- etcd
- minio
volumes:
- milvus-data:/var/lib/milvus
networks:
- langchain-network
etcd:
image: quay.io/coreos/etcd:latest
environment:
- ETCD_AUTO_COMPACTION_MODE=revision
- ETCD_AUTO_COMPACTION_RETENTION=1000
volumes:
- etcd-data:/etcd
networks:
- langchain-network
minio:
image: minio/minio:latest
environment:
- MINIO_ROOT_USER=minioadmin
- MINIO_ROOT_PASSWORD=minioadmin
command: server /data
volumes:
- minio-data:/data
networks:
- langchain-network
volumes:
redis-data:
milvus-data:
etcd-data:
minio-data:
networks:
langchain-network:
driver: bridge
---
# 4. 构建和运行
# 构建镜像
docker build -t langchain-app:latest .
# 运行容器
docker run -d \
-p 8000:8000 \
-e OPENAI_API_KEY=your-key \
--name langchain-app \
langchain-app:latest
# 使用docker-compose
docker-compose up -d
# 查看日志
docker-compose logs -f app
# 停止服务
docker-compose down
# 5. 健康检查
# Dockerfile with health check
FROM python:3.10-slim
WORKDIR /app
COPY requirements.txt .
RUN pip install --no-cache-dir -r requirements.txt
COPY . .
EXPOSE 8000
# 健康检查
HEALTHCHECK --interval=30s --timeout=10s --start-period=40s --retries=3 \
CMD curl -f http://localhost:8000/health || exit 1
CMD ["uvicorn", "main:app", "--host", "0.0.0.0", "--port", "8000"]
---
b.Kubernetes部署
a.功能说明
使用Kubernetes编排容器化应用,实现自动扩缩容、滚动更新、服务发现等。定义Deployment、Service、ConfigMap、Secret等资源。配置HPA自动水平扩展,配置Ingress对外暴露服务。Kubernetes提供生产级的容器编排能力。
b.代码示例
---
# 1. deployment.yaml
---
apiVersion: apps/v1
kind: Deployment
metadata:
name: langchain-app
labels:
app: langchain
spec:
replicas: 3
selector:
matchLabels:
app: langchain
template:
metadata:
labels:
app: langchain
spec:
containers:
- name: app
image: langchain-app:latest
ports:
- containerPort: 8000
env:
- name: OPENAI_API_KEY
valueFrom:
secretKeyRef:
name: langchain-secrets
key: openai-api-key
- name: REDIS_URL
value: "redis://redis-service:6379"
- name: MILVUS_HOST
value: "milvus-service"
resources:
requests:
memory: "512Mi"
cpu: "500m"
limits:
memory: "1Gi"
cpu: "1000m"
livenessProbe:
httpGet:
path: /health
port: 8000
initialDelaySeconds: 30
periodSeconds: 10
readinessProbe:
httpGet:
path: /ready
port: 8000
initialDelaySeconds: 10
periodSeconds: 5
---
# 2. service.yaml
---
apiVersion: v1
kind: Service
metadata:
name: langchain-service
spec:
selector:
app: langchain
ports:
- protocol: TCP
port: 80
targetPort: 8000
type: LoadBalancer
---
# 3. configmap.yaml
---
apiVersion: v1
kind: ConfigMap
metadata:
name: langchain-config
data:
app_config.yaml: |
llm:
temperature: 0.7
max_tokens: 2000
retrieval:
top_k: 3
score_threshold: 0.8
---
# 4. secret.yaml
---
apiVersion: v1
kind: Secret
metadata:
name: langchain-secrets
type: Opaque
data:
openai-api-key: eW91ci1iYXNlNjQtZW5jb2RlZC1rZXk=
---
# 5. hpa.yaml (自动扩缩容)
---
apiVersion: autoscaling/v2
kind: HorizontalPodAutoscaler
metadata:
name: langchain-hpa
spec:
scaleTargetRef:
apiVersion: apps/v1
kind: Deployment
name: langchain-app
minReplicas: 3
maxReplicas: 10
metrics:
- type: Resource
resource:
name: cpu
target:
type: Utilization
averageUtilization: 70
- type: Resource
resource:
name: memory
target:
type: Utilization
averageUtilization: 80
---
# 6. ingress.yaml
---
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
name: langchain-ingress
annotations:
nginx.ingress.kubernetes.io/rewrite-target: /
spec:
rules:
- host: langchain.example.com
http:
paths:
- path: /
pathType: Prefix
backend:
service:
name: langchain-service
port:
number: 80
---
# 部署命令
# kubectl apply -f deployment.yaml
# kubectl apply -f service.yaml
# kubectl apply -f configmap.yaml
# kubectl apply -f secret.yaml
# kubectl apply -f hpa.yaml
# kubectl apply -f ingress.yaml
# 查看状态
# kubectl get pods
# kubectl get svc
# kubectl logs -f deployment/langchain-app
---
02.监控告警
a.性能监控
a.功能说明
监控应用性能指标,包括响应时间、吞吐量、错误率、资源使用等。使用Prometheus采集指标,使用Grafana可视化展示。配置告警规则,异常时发送通知。性能监控帮助发现瓶颈,优化系统性能。
b.代码示例
---
# 1. Prometheus指标暴露
from prometheus_client import Counter, Histogram, Gauge, generate_latest
from fastapi import FastAPI, Response
app = FastAPI()
# 定义指标
request_count = Counter(
'langchain_requests_total',
'总请求数',
['endpoint', 'status']
)
request_duration = Histogram(
'langchain_request_duration_seconds',
'请求耗时',
['endpoint']
)
llm_token_usage = Counter(
'langchain_llm_tokens_total',
'LLM Token使用量',
['model']
)
active_requests = Gauge(
'langchain_active_requests',
'当前活跃请求数'
)
# 2. 中间件记录指标
import time
from starlette.middleware.base import BaseHTTPMiddleware
class MetricsMiddleware(BaseHTTPMiddleware):
async def dispatch(self, request, call_next):
active_requests.inc()
start_time = time.time()
try:
response = await call_next(request)
# 记录指标
duration = time.time() - start_time
request_duration.labels(endpoint=request.url.path).observe(duration)
request_count.labels(
endpoint=request.url.path,
status=response.status_code
).inc()
return response
finally:
active_requests.dec()
app.add_middleware(MetricsMiddleware)
# 3. 指标端点
@app.get("/metrics")
def metrics():
return Response(
content=generate_latest(),
media_type="text/plain"
)
# 4. Prometheus配置
# prometheus.yml
---
global:
scrape_interval: 15s
scrape_configs:
- job_name: 'langchain-app'
static_configs:
- targets: ['langchain-app:8000']
---
# 5. Grafana Dashboard配置
# grafana-dashboard.json
---
{
"dashboard": {
"title": "LangChain监控",
"panels": [
{
"title": "请求QPS",
"targets": [
{
"expr": "rate(langchain_requests_total[1m])"
}
]
},
{
"title": "平均响应时间",
"targets": [
{
"expr": "rate(langchain_request_duration_seconds_sum[1m]) / rate(langchain_request_duration_seconds_count[1m])"
}
]
},
{
"title": "Token使用量",
"targets": [
{
"expr": "rate(langchain_llm_tokens_total[5m])"
}
]
}
]
}
}
---
# 6. 告警规则
# alert-rules.yml
---
groups:
- name: langchain-alerts
rules:
- alert: HighErrorRate
expr: rate(langchain_requests_total{status=~"5.."}[5m]) > 0.05
for: 5m
labels:
severity: critical
annotations:
summary: "错误率过高"
description: "5分钟内错误率超过5%"
- alert: HighResponseTime
expr: histogram_quantile(0.95, rate(langchain_request_duration_seconds_bucket[5m])) > 3
for: 10m
labels:
severity: warning
annotations:
summary: "响应时间过长"
description: "P95响应时间超过3秒"
- alert: HighTokenUsage
expr: rate(langchain_llm_tokens_total[1h]) > 1000000
for: 5m
labels:
severity: warning
annotations:
summary: "Token使用量过高"
description: "每小时Token使用量超过100万"
---
---
b.日志收集
a.功能说明
集中收集和管理应用日志,使用ELK(Elasticsearch + Logstash + Kibana)或EFK(Elasticsearch + Fluentd + Kibana)栈。结构化日志便于搜索和分析,配置日志等级和轮转策略。日志是问题排查和审计的重要依据。
b.代码示例
---
# 1. 结构化日志
import logging
import json
from datetime import datetime
class StructuredLogger:
"""结构化日志"""
def __init__(self, name: str):
self.logger = logging.getLogger(name)
self.logger.setLevel(logging.INFO)
# JSON格式处理器
handler = logging.StreamHandler()
handler.setFormatter(logging.Formatter('%(message)s'))
self.logger.addHandler(handler)
def _log(self, level: str, message: str, **kwargs):
log_entry = {
"timestamp": datetime.now().isoformat(),
"level": level,
"message": message,
**kwargs
}
self.logger.info(json.dumps(log_entry, ensure_ascii=False))
def info(self, message: str, **kwargs):
self._log("INFO", message, **kwargs)
def error(self, message: str, **kwargs):
self._log("ERROR", message, **kwargs)
def warning(self, message: str, **kwargs):
self._log("WARNING", message, **kwargs)
logger = StructuredLogger("langchain-app")
# 使用
logger.info(
"LLM调用",
user_id="user123",
prompt_length=100,
response_length=500,
duration_ms=1234,
tokens=600
)
# 2. Fluentd配置
# fluentd.conf
---
<source>
@type forward
port 24224
</source>
<match langchain.**>
@type elasticsearch
host elasticsearch
port 9200
logstash_format true
logstash_prefix langchain
include_tag_key true
type_name fluentd
</match>
---
# 3. Docker日志驱动
# docker-compose.yml
---
services:
app:
build: .
logging:
driver: fluentd
options:
fluentd-address: localhost:24224
tag: langchain.app
---
# 4. Kubernetes日志收集
# fluentd-daemonset.yaml
---
apiVersion: apps/v1
kind: DaemonSet
metadata:
name: fluentd
namespace: kube-system
spec:
selector:
matchLabels:
app: fluentd
template:
metadata:
labels:
app: fluentd
spec:
containers:
- name: fluentd
image: fluent/fluentd-kubernetes-daemonset:v1-debian-elasticsearch
env:
- name: FLUENT_ELASTICSEARCH_HOST
value: "elasticsearch.logging.svc.cluster.local"
- name: FLUENT_ELASTICSEARCH_PORT
value: "9200"
volumeMounts:
- name: varlog
mountPath: /var/log
volumes:
- name: varlog
hostPath:
path: /var/log
---
# 5. 日志轮转
# logrotate配置
---
/app/logs/*.log {
daily
rotate 7
compress
delaycompress
missingok
notifempty
create 0644 app app
postrotate
kill -USR1 $(cat /var/run/app.pid)
endscript
}
---
---