【Langchian】Langchian的核心概念串联

122 阅读18分钟

🟡 LangChain 高频用法

🔘语言模型调用(LLM):让 AI 开口说话

假设你要做一个问答助手,用户问“你好”,AI 得回答“こんにちは”。这需要调用外部 LLM,比如 OpenAI 的 GPT。LangChain 的 ChatOpenAI 是最常用的类,封装了模型交互。

from langchain_openai import ChatOpenAI

# 实例化 LLM
llm = ChatOpenAI(openai_api_key="your-api-key",model_name="gpt-3.5-turbo",temperature=0.)

# 调用生成回答
response = llm.invoke("你好") # invoke 是 ChatOpenAI 类里预定义的实例方法,返回对象(内容+元数据)
print(response.content) # invoke 返回的是对象,我们通常只关心文本。提取纯文本内容

🔘 提示词管理(Prompts):告诉 AI 干什么

from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# 基础提示词
prompt = ChatPromptTemplate.from_messages([
    ("system", "你是编程专家,回答要准确"),
    ("human", "{input}")
])

# 带上下文的提示词
prompt_with_history = ChatPromptTemplate.from_messages([
    ("system", "你是编程专家,基于上下文回答"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}")
])

# 测试
formatted = prompt.invoke({"input": "Python 谁发明的?"})
print(formatted)
  • ChatPromptTemplate(类):LangChain 自带的类,用于创建消息模板。把多角色消息组织成标准格式,供 LLM 处理。
  • ChatPromptTemplate.from_messages(静态方法):接收消息列表(元组列表),参数:每个元组是 (角色, 内容),角色可选 "system"、"human"、"ai"。,返回模板对象。把散乱的指令整合成结构化提示。
  • MessagesPlaceholder(类):LangChain 自带的类,创建占位符。 参数:variable_name 指定变量名(这里是 "chat_history")。动态插入历史消息,实现上下文感知。
  • prompt.invoke(方法):模板对象的方法,接收字典(键匹配占位符),返回 ChatMessage 列表。生成最终提示,供 LLM 使用。

🔘 上下文记忆(Memory):让 AI 有记忆

from langchain.memory import ConversationBufferMemory

# 实例化内存
memory = ConversationBufferMemory(return_messages=True)

# 保存对话
memory.save_context({"input": "我叫小明"}, {"output": "好的,小明"})

# 加载历史
history = memory.load_memory_variables({})
print(history["chat_history"])
  • ConversationBufferMemory(类):LangChain 自带的内存管理工具类,管理对话历史。参数:return_messages=True 确保返回 List[BaseMessage],匹配提示词。
  • memory.save_context(实例方法)参数是两个字典,键是 "input" 和 "output"。保存输入和输出。
  • memory.load_memory_variables(实例方法)参数是个空字典,加载历史。

🔘 输出解析(Output Parsers):让结果更干净

为什么需要?

代码实践:

from langchain_core.output_parsers import StrOutputParser

parser = StrOutputParser()
raw_output = llm.invoke("Python 谁发明的?")
text = parser.invoke(raw_output)
print(text)

代码拆解:

  • StrOutputParser(类):LLM 输出是 AIMessage 对象,我们只想要文本。StrOutputParser 是LangChain 自带的用于解析输出。提取纯文本的类
  • parser.invoke(实例方法)接收 AIMessage,返回字符串。

🔘 检索增强(Retrieval / RAG):给 AI 补知识

from langchain.document_loaders import PyPDFLoader
from langchain.text_splitter import RecursiveCharacterTextSplitter
from langchain.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings

loader = PyPDFLoader("langchain_docs.pdf")
docs = loader.load()

splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
split_docs = splitter.split_documents(docs)

embeddings = OpenAIEmbeddings(openai_api_key="your-api-key")
vectorstore = FAISS.from_documents(split_docs, embeddings)
retriever = vectorstore.as_retriever()
results = retriever.invoke("最新版本")

代码拆解:

  • PyPDFLoader(类):加载 PDF。
    • 方法:load() 返回文档列表。
    • 为什么用?把文件转为可处理的数据。
  • RecursiveCharacterTextSplitter(类):切分文本。
    • 参数:chunk_size(块大小)、chunk_overlap(重叠)。
    • 方法:split_documents 返回切分后的文档。
    • 为什么用?大文档需分块,便于检索。
  • OpenAIEmbeddings(类):生成嵌入。
    • 为什么用?把文本转为向量,供 FAISS 使用。
  • FAISS(类):向量数据库。
    • 方法:from_documents(建索引)、as_retriever(转检索器)。
    • 为什么用?高效存储和查询。
  • retriever.invoke(方法):检索相关文档。
    • 为什么用?返回匹配输入的文档片段。

场景: 知识库问答。

🔘 工具调用(Tools):让 AI 用外挂

为什么需要?
用户问“北京天气如何?”,模型得查 API。Toolcreate_openai_tools_agent 实现工具调用。

代码实践:

from langchain.tools import Tool
from langchain.agents import create_openai_tools_agent

def get_weather(city):
    return f"{city} 今天 20°C"

weather_tool = Tool(name="Weather", func=get_weather, description="查天气")
agent = create_openai_tools_agent(llm, [weather_tool], prompt)

代码拆解:

  • Tool(类):包装函数为工具,让模型识别和调用。。参数:name(名称)、func(函数)、description(描述)。
  • create_openai_tools_agent(函数):创建代理。参数:llm(模型)、tools(工具列表)、prompt(提示词)。

langchain 中,Tool 的设计本质上是为了满足以下两个核心需求:

  1. 动态选择Agent 需要根据用户的输入和上下文,从多个工具中选择最合适的工具。
  2. 链式集成:工具需要能够无缝地嵌入到复杂的工作流(如 ChainAgent)中,与其他模块协作完成任务。
weather_tool = Tool(
    name="Weather API",
    func=get_weather,
    description="Get the current weather for a specific location."
)

calculator_tool = Tool(
    name="Calculator",
    func=calculate,
    description="Evaluate a mathematical expression."
)

当用户输入“纽约的天气怎么样?”时,Agent 会根据 description 判断应该调用 Weather API 工具,而不是 Calculator 工具。Tool 提供了一个统一的接口(run 方法),使得它可以轻松地嵌入到 Chain 或其他工作流中。例如:

from langchain.chains import SimpleSequentialChain

# 创建一个链
chain = SimpleSequentialChain(chains=[weather_tool, summarize_tool])

# 执行链
result = chain.run("New York")

在这个例子中,weather_toolsummarize_tool 被串联起来,形成一个完整的流程。如果没有 Tool 封装,你需要手动处理每个函数的调用逻辑,这会增加复杂性。如果你直接调用函数或 API,确实可以完成简单的任务,但在复杂的场景中会面临以下问题:首先是缺乏动态选择能力 直接调用函数时,你需要手动编写逻辑来决定调用哪个函数。例如:

user_input = "What's the weather in New York?"

if "weather" in user_input:
    result = get_weather("New York")
elif "calculate" in user_input:
    result = calculate(user_input)
else:
    result = "I don't understand."

这种硬编码的方式有几个问题:

  • 扩展性差:每增加一个新功能,都需要修改代码。
  • 维护成本高:随着功能增多,判断逻辑会变得越来越复杂。
  • 不够智能:无法利用语言模型的能力进行语义理解。

langchain 中,链(Chain)是一个重要的概念,用于将多个模块串联起来完成复杂任务。如果直接调用函数,你需要手动处理每个函数的输入和输出格式,确保它们能够无缝协作。例如:

def chain_workflow(input):
    step1_result = get_weather(input)
    step2_result = summarize(step1_result)
    return step2_result

这种方式的问题在于:

  • 耦合性强:每个步骤都紧密依赖于前一个步骤的输出格式。
  • 灵活性差:如果需要调整工作流(如插入新步骤),需要修改整个代码结构。

而使用 Tool 封装后,每个工具都可以通过统一的接口被调用,简化了链式集成的过程。

Tool 的动态选择与链式集成示例 以下是一个完整的示例,展示如何使用 Tool 实现动态选择和链式集成:

(1)定义工具

from langchain.tools import Tool

def get_weather(location: str) -> str:
    return f"The weather in {location} is sunny."

def calculate(expression: str) -> str:
    return str(eval(expression))

weather_tool = Tool(
    name="Weather API",
    func=get_weather,
    description="Get the current weather for a specific location."
)

calculator_tool = Tool(
    name="Calculator",
    func=calculate,
    description="Evaluate a mathematical expression."
)

(2)创建 Agent 并动态选择工具

from langchain.agents import initialize_agent, AgentType
from langchain.llms import OpenAI

# 初始化语言模型
llm = OpenAI(temperature=0)

# 初始化 Agent
agent = initialize_agent(
    tools=[weather_tool, calculator_tool],
    llm=llm,
    agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    verbose=True
)

# 用户输入
user_input = "What's the weather in New York?"
response = agent.run(user_input)
print(response)  # 输出:The weather in New York is sunny.

在这个例子中,Agent 根据用户输入动态选择了 Weather API 工具。

(3)创建链并集成工具

from langchain.chains import SimpleSequentialChain

# 创建链
chain = SimpleSequentialChain(chains=[weather_tool, calculator_tool])

# 执行链
result = chain.run("New York")
print(result)

🔘 链式组合(Runnable / LCEL):搭流水线

from langchain_core.runnables import RunnablePassthrough

chain = (
    RunnablePassthrough.assign(history=lambda x: "历史")
    | prompt
    | llm
    | parser
)
result = chain.invoke({"input": "你好"})
  • RunnablePassthrough 是一个类,属于 LangChain 的 Runnable 家族,核心作用是“透传”(pass through),也就是把输入原封不动地传递下去,同时可以选择性地做一些加工,即如果想动态的再去加工这次的输入,用方法:assign 添加字段。(在 LCEL 中,链是通过 |(管道符)串起来的,每个环节的输出是下一个环节的输入。RunnablePassthrough.assign 的特别之处在于,它能让你在链的某个阶段“插一脚”,往输入里塞点额外的东西,而不干扰主流程。)

🟡 LangChain 实战体验

假设我们正在打造一个智能助手,它的任务很简单:能记住用户的对话历史并根据上下文回答问题就行。用户先说“我叫小明”,然后问“我叫什么名字?”,助手应该回答“你叫小明”。这个需求虽然基础,但涵盖了 LangChain 的核心能力:通过“链”组合模块,实现上下文感知的问答。我们从头开始设计,把 LangChain 的关键组件逐步融入,配上代码,一步步构建这个助手。

🔘ChatOpenAI LLM

首先,我们需要一个“大脑”来生成回答,也就是调用语言模型的节点。假设我们选择 OpenAI 的 GPT-3.5-turbo 模型作为助手的内核。LangChain 提供了自带的对象 ChatOpenAI,我们可以创建一个实例,命名为 LLMAPI,并设置 API 密钥、模型名称和温度参数。温度设为 0.7,表示回答稍微有点创意但不失准确。这个节点就像助手的“思考中枢”,准备好接收输入并输出回答。

from langchain_openai import ChatOpenAI

# 创建 LLM 节点
LLMAPI = ChatOpenAI(
    openai_api_key="your-api-key",  # 替换为你的实际密钥
    model_name="gpt-3.5-turbo",
    temperature=0.7
)

🔘ChatPromptTemplate 模板

有了“大脑”,下一步是设计提示词,告诉模型它的身份和任务。LangChain 用自带的对象 ChatPromptTemplate 来实现这个功能,它的方法 from_messages 接受一个消息列表,每个消息由角色和内容组成。我们先定义一个 system 角色,内容是“你是一个智能助手,根据用户的提问和上下文提供准确的回答”,这给助手定了个基调。但如果只写 system 和用户的当前输入(human 角色,用 {input} 表示),助手就只能看到眼前的问题,无法记住之前的对话,这显然无法满足我们的场景需求。

为了让助手有“记忆”,我们引入 LangChain 自带的对象 MessagesPlaceholder。它像一个动态插槽,可以插入对话历史。我们给它取个名字 chat_history,插在 systemhuman 之间。这样,提示词模板就包含三部分:系统指令、历史消息、当前输入。这个节点我们命名为 Prompt,它不仅是输入的起点,还通过 MessagesPlaceholder 搭建了上下文的桥梁。

from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# 创建提示词节点
Prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,根据用户的提问和上下文提供准确的回答。"),
    MessagesPlaceholder(variable_name="chat_history"),  # 动态插入对话历史
    ("human", "{input}")  # 用户当前输入
])

🔘ConversationBufferMemory 记忆

但这里有个问题:chat_history 从哪里来?MessagesPlaceholder 只是个占位符,不会自己生成历史。这就需要一个专门管理上下文的节点。有人可能会误以为 LangChain 的 StrOutputParser 是干这个的,因为名字里有“Parser”,听起来像在解析上下文。但这是个易混淆点——StrOutputParser 的实际作用是把模型的复杂输出(比如 AIMessage 对象)解析成简单字符串,跟上下文管理无关。真正的“记忆库”应该是 LangChain 自带的对象 ConversationBufferMemory。我们创建一个实例 memory,设置 return_messages=True,确保它返回的消息格式(List[BaseMessage])能匹配 MessagesPlaceholder 的需求。

from langchain.memory import ConversationBufferMemory

# 创建上下文节点
memory = ConversationBufferMemory(return_messages=True)  # 保存并返回消息历史

🔘StrOutputParser 与 RunnablePassthrough

现在,我们有了三个核心部件:LLMAPI(模型)、Prompt(提示词)、memory(上下文)。但它们还得串起来形成一个“链”,这就要用到 LangChain 的 LCEL 语法,那根竖杠 | 表示前一个节点的输出作为后一个节点的输入。不过,顺序得设计好。如果写成 LLMAPI | Prompt,就像先让模型说话再告诉它说什么,逻辑不通。正确的流程是:先准备输入(提示词 + 上下文),再送给模型,最后处理输出。

假设用户输入“我叫小明”,我们希望链能自动加载历史(如果有),组合提示,然后生成回答,最后保存上下文。为此,我们需要一个“输入装配”步骤。LangChain 提供了自带对象 RunnablePassthrough,它能传递输入并动态添加字段。我们用它的 assign 方法从 memory 加载 chat_history,这就像在输入里加了个“记忆包”。然后,这个输入流入 Prompt,生成完整的消息列表,再交给 LLMAPI 生成回答。模型输出可能是复杂对象,我们用 StrOutputParser 解析成字符串。

🔘完整的链

from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough

# 创建输出解析器
parser = StrOutputParser()

# 构建链
chain = (
    RunnablePassthrough.assign(chat_history=lambda x: memory.load_memory_variables({})["chat_history"])
    | Prompt
    | LLMAPI
    | parser
)

让我们运行一下,看看效果。第一次,用户输入“我叫小明”:

# 测试第一次输入
input_text = "我叫小明"
result = chain.invoke({"input": input_text})
print(result)  # 输出类似: "好的,小明,我记住了"

# 保存对话历史
memory.save_context({"input": input_text}, {"output": result})

第二次,用户问“我叫什么名字?”:

# 测试第二次输入
result2 = chain.invoke({"input": "我叫什么名字?"})
print(result2)  # 输出: "你叫小明"

整个过程是这样的:用户输入“我叫小明”后,RunnablePassthrough 检查 memory,发现历史为空,就只带着当前输入走下去。Prompt 把系统指令和输入组合成提示,LLMAPI 生成回答,parser 解析成字符串。保存到 memory 后,第二次输入“我叫什么名字?”时,RunnablePassthroughmemory 加载历史,Prompt 组合出包含“小明”的提示,模型看到上下文,自然回答“你叫小明”。环环相扣,上下文无缝衔接。

这个链是“顺序型”的基础版本。如果场景复杂,比如用户问“今天天气怎么样?”,我们可以用 RunnableBranch 加判断逻辑,或者在 Prompt 前挂载检索器实现 RAG,从外部数据源找答案。每个节点还能扩展,比如加数据清洗、工具调用,潜力无限。


我们已经有一个能记住对话历史的智能助手,比如用户说“我叫小明”,再问“我叫什么名字?”,它能回答“你叫小明”。但现在,场景升级了:用户可能问“Python 的创始人是谁?”(需要外部知识库)、“我的订单号 12345 的状态是什么?”(需要查 MySQL 数据库)、“输入里有脏话怎么办?”(需要数据清洗),甚至“今天北京的天气如何?”(需要调用外部 API)。这些需求超出了基础模型的能力,我们需要拓展每个节点,让助手变得更强大。以下是逐步实现的过程。

🔘挂载 RAG,引入外部知识库

假设用户问“Python 的创始人是谁?”,模型可能不知道,或者回答不准确。我们可以通过 RAG(Retrieval-Augmented Generation)从外部知识库检索信息,增强回答。LangChain 支持 RAG 的核心是“检索器”(Retriever),我们可以把这个功能挂载在 Prompt 节点之前,作为输入的增强步骤。

想象我们有一堆关于编程历史的 PDF 文档,我们先用 LangChain 自带的对象 PyPDFLoader 加载这些文档,再用 RecursiveCharacterTextSplitter(自带对象)切分成小块,然后用 FAISS(向量数据库)和 OpenAIEmbeddings(自带对象)构建索引,形成一个检索器 retriever。这个检索器能根据用户输入找到相关文档片段。

from langchain.document_loaders import PyPDFLoader(PDF加载模块)
from langchain.text_splitter import RecursiveCharacterTextSplitter(切分模块)
from langchain_openai import OpenAIEmbeddings(嵌入模块)
from langchain.vectorstores import FAISS(向量数据库)

# 加载外部知识库(假设有一堆 PDF 文件)
loader = PyPDFLoader("programming_history.pdf")
documents = loader.load()

# 切分文档
text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=200)
docs = text_splitter.split_documents(documents)

# 创建向量数据库和检索器
embeddings = OpenAIEmbeddings(openai_api_key="your-api-key")
vectorstore = FAISS.from_documents(docs, embeddings)
retriever = vectorstore.as_retriever()

现在,我们把 retriever 融入链中。通过自带对象 RunnablePassthrough,我们在输入处理时添加检索结果。Prompt 模板也要调整,增加一个 {context} 字段来接收检索到的内容。这样,链的第一步变成了:接收用户输入,检索相关文档,组合成提示。

from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder

# 调整 Prompt,加入检索上下文
Prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,根据用户的提问、上下文和外部知识提供准确回答。外部知识:{context}"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}")
])

🔘连接本地 MySQL 数据库

假设用户问“我的订单号 12345 的状态是什么?”,我们需要从本地 MySQL 数据库查询订单状态。LangChain 提供了与 SQL 数据库交互的工具,我们可以用自带对象 SQLDatabaseSQLDatabaseChain 来实现。但为了更灵活,我们创建一个自定义工具,挂载到链的某个节点。

先定义一个函数 query_order_status,用 Python 的 mysql-connector 连接数据库并查询:

import mysql.connector

def query_order_status(order_id):
    conn = mysql.connector.connect(
        host="localhost",
        user="your_username",
        password="your_password",
        database="shop_db"
    )
    cursor = conn.cursor()
    cursor.execute("SELECT status FROM orders WHERE order_id = %s", (order_id,))
    result = cursor.fetchone()
    conn.close()
    return result[0] if result else "订单未找到"

然后,用 LangChain 自带对象 Tool 将这个函数包装成工具,命名为 order_lookup_tool,并集成到链中。

我们可以在 LLMAPI 节点后加一个工具调用步骤,但更优雅的方式是让模型自己决定是否调用工具,这需要用到 LangChain 的 Agent 机制。稍后会整合。

from langchain.tools import Tool

order_lookup_tool = Tool(
    name="OrderLookup",
    func=query_order_status,
    description="根据订单号查询订单状态"
)

🔘数据清洗

假设用户输入“我操,Langchian这玩意儿到底谁发明的?”,我们不希望脏话直接传给模型。数据清洗可以放在输入处理的早期,挂载到 RunnablePassthrough 节点。我们定义一个清洗函数 clean_input,用简单规则替换脏话,然后用自带对象 RunnableLambda 包装成可执行节点。

from langchain_core.runnables import RunnableLambda

def clean_input(inputs):
    text = inputs["input"]
    dirty_words = {"我操": "哎呀", "操": "哎"}  # 简单替换规则
    for dirty, clean in dirty_words.items():
        text = text.replace(dirty, clean)
    inputs["input"] = text
    return inputs

cleaner = RunnableLambda(clean_input)

RunnableLambdalangchain 中的一个工具,用于将普通的 Python 函数包装成一个可以在链(Chain)或工作流中执行的节点。它将函数集成到链中,让普通函数可以像其他模块一样被链式调用。ToolRunnableLambda 都可以封装函数,但它们的设计目的和适用场景完全不同:Tool 是为 Agent 动态选择工具而设计的。它通过 description 提供语义化信息,让 LLM 根据用户输入决定是否调用某个工具。Tool 通常用于封装外部 API 或复杂功能(如天气查询、计算器等),这些功能需要在运行时根据上下文动态调用。数据清洗是一个固定的预处理步骤,与用户意图无关,也不需要 LLM 来决定是否执行。因此,使用 Tool 是不必要的。:RunnableLambda 适用于那些不需要动态选择、只需要按顺序执行的逻辑。它直接将普通函数包装成链中的一个节点,无需额外的语义化描述或动态选择机制。相比 ToolRunnableLambda 更加轻量,适合处理简单的任务(如数据清洗、格式转换等)。

🔘 调用外部 API 工具

假设用户问“今天北京的天气如何?”,我们需要调用天气 API(比如 OpenWeatherMap)。定义一个函数 get_weather,用 requests 获取数据,然后包装成工具:

import requests

def get_weather(city):
    api_key = "your_weather_api_key"
    url = f"http://api.openweathermap.org/data/2.5/weather?q={city}&appid={api_key}&units=metric"
    response = requests.get(url).json()
    if response.get("cod") == 200:
        temp = response["main"]["temp"]
        return f"{city} 今天温度是 {temp}°C"
    return "无法获取天气信息"

weather_tool = Tool(
    name="WeatherCheck",
    func=get_weather,
    description="查询指定城市的天气"
)

🔘 整合所有节点,构建完整链

现在,我们有 RAG 检索器、订单查询工具、数据清洗、天气工具,怎么把它们整合呢?单纯的顺序链不够用了,因为模型需要根据输入决定调用哪个工具。这时候,LangChain 的 Agent 机制派上用场。我们用自带对象 create_openai_tools_agent 创建一个代理,让模型自己选择工具,同时保留上下文记忆。

from langchain_openai import ChatOpenAI
from langchain.prompts import ChatPromptTemplate, MessagesPlaceholder
from langchain.memory import ConversationBufferMemory
from langchain_core.output_parsers import StrOutputParser
from langchain_core.runnables import RunnablePassthrough
from langchain.agents import create_openai_tools_agent, AgentExecutor

# LLM 节点
LLMAPI = ChatOpenAI(openai_api_key="your-api-key", model_name="gpt-3.5-turbo", temperature=0.7)

# 提示词节点(含 RAG 上下文)
Prompt = ChatPromptTemplate.from_messages([
    ("system", "你是一个智能助手,根据用户的提问、上下文和外部知识提供准确回答。外部知识:{context}"),
    MessagesPlaceholder(variable_name="chat_history"),
    ("human", "{input}")
])

# 上下文记忆
memory = ConversationBufferMemory(return_messages=True)

# 工具列表
tools = [order_lookup_tool, weather_tool]

# 构建带 RAG 和清洗的输入链
input_chain = (
    cleaner  # 数据清洗
    | RunnablePassthrough.assign(context=lambda x: retriever.get_relevant_documents(x["input"]))  # RAG 检索
    | RunnablePassthrough.assign(chat_history=lambda x: memory.load_memory_variables({})["chat_history"])  # 加载历史
)

# 创建 Agent
agent = create_openai_tools_agent(LLMAPI, tools, Prompt)
agent_executor = AgentExecutor(agent=agent, tools=tools, memory=memory)

# 测试
print(agent_executor.invoke({"input": "我叫小明"}))  # 输出:{"output": "好的,小明,我记住了"}
print(agent_executor.invoke({"input": "我叫什么名字?"}))  # 输出:{"output": "你叫小明"}
print(agent_executor.invoke({"input": "Python 的创始人是谁?"}))  # 输出:{"output": "Python 的创始人是 Guido van Rossum"}
print(agent_executor.invoke({"input": "我的订单号 12345 的状态是什么?"}))  # 输出:{"output": "订单状态:已发货"}
print(agent_executor.invoke({"input": "我操,今天北京天气如何?"}))  # 输出:{"output": "哎呀,北京今天温度是 15°C"}

整个链的流程是这样的:用户输入先经过 cleaner 清洗脏话,然后 RunnablePassthrough 加载 RAG 检索结果和对话历史,组合成完整输入。Prompt 把这些塞进提示模板,交给 LLMAPI。模型通过 Agent 机制判断是否调用工具(订单查询或天气 API),最后输出回答并保存到 memory。每个节点都得到了拓展:Prompt 加了 RAG 上下文,输入链加了清洗和检索,LLMAPI 通过 Agent 支持工具调用。