当前位置: 首页 > news >正文

LangChain 代理(Agents)学习

在 LangChain 中,代理(Agents) 是一个核心概念,它赋予了大型语言模型(LLM)“思考”和“行动”的能力。传统上,LLM 只是接收输入并生成输出,但代理模型允许 LLM 动态地决定要执行哪些操作,调用哪些外部工具,并根据观察结果进行迭代,直到达成特定目标。这使得 LLM 能够解决更复杂、更需要自主性的任务。

核心概念:思考、行动、观察循环

代理的核心在于一个持续的思考(Thought)行动(Action)观察(Observation) 循环(TAB)。

  1. 思考 (Thought):LLM 基于当前任务、已有的知识和可用的工具描述,进行推理并决定下一步应该做什么。这通常涉及选择一个工具,并确定调用该工具所需的参数。
  2. 行动 (Action):代理执行 LLM 选择的工具,将 LLM 决定的参数传递给工具。
  3. 观察 (Observation):工具执行完成后,其输出(结果或错误)被返回给代理。这个输出就是 LLM 的“观察结果”。
  4. 循环:LLM 接收到观察结果后,再次进入“思考”阶段,评估行动的效果,并决定是否需要进一步的行动,或者任务是否已经完成并可以给出最终答案。

这个循环一直持续,直到 LLM 认为任务完成,并生成最终的响应。

代理执行循环
交给LLM思考
思考:要执行什么?
行动:选择工具 X, 参数
观察:工具输出
思考:任务完成?
代理执行器 AgentExecutor
用户请求
LLM - 代理大脑
可用工具列表
工具 X
任务是否完成?
最终答案

1. 工具(Tools):代理的“手和脚”

代理之所以能“行动”,是因为它们可以访问和使用各种工具(Tools)。工具是 LLM 可以调用的函数或 API,用于执行特定任务,例如搜索信息、执行计算、访问数据库、发送邮件等。

工具的构成:

  • 名称 (Name):一个唯一的、描述性的名称,供 LLM 在思考时引用。
  • 描述 (Description):详细说明工具的功能、用途以及期望的输入格式。这是 LLM 理解如何使用工具的关键。
  • 函数 (Function):实际执行任务的 Python 函数。

创建一个简单的工具,用于计算一个数的平方。

# tool_example.pyfrom langchain_core.tools import tool# 装饰器 @tool 将普通函数转化为 LangChain 工具
@tool
def square(number: int) -> int:"""计算一个整数的平方。这个工具接收一个整数作为输入,返回其平方值。"""return number * number# 你可以直接调用工具函数
print(f"5 的平方是: {square.invoke(5)}")# 工具对象本身有描述和名称
print(f"\n工具名称: {square.name}")
print(f"工具描述: {square.description}")

说明:

  • 我们使用 @tool 装饰器将一个普通的 Python 函数 square 转换成 LangChain 可以识别的工具。
  • 函数的 docstring 自动作为工具的描述,参数类型提示帮助 LangChain 理解输入。
  • LLM 在决定使用工具时,会根据这个描述来判断工具是否符合需求。

2. 代理类型(Agent Types):ReAct 模式

LangChain 支持多种代理类型和构建方法。最常见且功能强大的模式之一是 ReAct (Reasoning and Acting)

原论文为:REACT: SYNERGIZING REASONING AND ACTING IN LANGUAGE MODELS

ReAct 代理的原理是:LLM 通过生成思考(Thought)行动(Action)观察(Observation) 文本来模拟其推理过程。

  • 思考 (Thought):代理解释它正在做什么,为什么。
  • 行动 (Action):代理选择一个工具并指定其输入。
  • 观察 (Observation):工具的输出。

这个过程会在提示中逐步构建,让 LLM 看到自己过去的推理过程和工具执行结果,从而进行更复杂的决策。

ReAct 代理决策流
生成 Thought
生成 Action
工具输出
如果任务完成
观察结果反馈给LLM,进行下一轮思考
初始问题/目标
思考, 我需要做什么
行动, 选择工具
工具执行
观察, 工具返回结果
最终答案

使用一个简单的搜索工具来演示 ReAct 代理。

# react_agent_example.pyfrom langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools import DuckDuckGoSearchRun
import os# --- 配置部分 ---
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"# 检查 API 密钥是否已设置
if not os.getenv("OPENAI_API_KEY"):print("错误:请设置环境变量 OPENAI_API_KEY 或在代码中取消注释并设置您的密钥。")exit()print("--- ReAct 代理示例开始 ---")# 1. 定义工具
# 这里使用 DuckDuckGoSearchRun 作为搜索工具
search_tool = DuckDuckGoSearchRun(name="duckduckgo_search")
tools = [search_tool]
print(f"已定义工具: {search_tool.name}")# 2. 定义 LLM (代理的“大脑”)
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
print("已定义 LLM (gpt-3.5-turbo)。")# 3. 定义代理使用的提示模板
# LangChain Hub 提供了许多预构建的提示模板
# "hwchase17/react" 是一个标准的 ReAct 模式提示
prompt = hub.pull("hwchase17/react")
print("已从 LangChain Hub 拉取 ReAct 提示模板。")# 4. 创建 ReAct 代理
# create_react_agent 结合 LLM、工具和 ReAct 提示来构建代理逻辑
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
print("ReAct 代理已创建。")# 5. 创建代理执行器 (AgentExecutor)
# AgentExecutor 负责运行代理,管理思考-行动-观察循环,并处理工具的调用
# verbose=True 会打印代理的思考过程,方便调试
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
print("代理执行器已创建。")# 6. 调用代理
print("\n--- 代理开始执行任务 ---")
# 模型可能是不知道这个问题的答案的,那么她会根据提供的工具找到合适然后去调用她获取结果
response = agent_executor.invoke({"input": "奥巴马的生日是哪天?他出生那年发生了什么大事?"})
print("\n--- 代理执行结束 ---")
print(f"\n最终答案: {response['output']}")
  • DuckDuckGoSearchRun 是一个现成的搜索工具。
  • hub.pull("hwchase17/react") 从 LangChain Hub 下载了一个预定义的 ReAct 模式的提示模板。这个模板指导 LLM 按照“Thought, Action, Observation”的格式进行推理。
  • create_react_agent 函数将 LLM、工具列表和 ReAct 提示结合起来,构建了代理的推理逻辑。
  • AgentExecutor 是实际运行代理的引擎。它负责驱动整个思考-行动-观察循环,调用工具,并将结果反馈给 LLM。verbose=True 是一个非常实用的参数,它会打印代理在幕后的所有思考和行动,帮助我们理解其决策过程。

3. 内存与代理(Agents with Memory)

在对话场景中,代理需要记住之前的对话内容才能进行有意义的交互。LangChain 代理可以与**内存(Memory)**模块集成,以保持对话上下文。

当代理与内存结合时,对话历史会被添加到 LLM 的提示中,以便 LLM 在进行思考和决策时能够访问完整的对话上下文。

带内存的代理
加载历史
将历史注入到LLM提示
生成 Thought/Action
执行工具
工具输出
任务完成
保存新历史
再次请求,从内存加载历史
代理执行器 AgentExecutor
用户请求
内存
LLM - 代理大脑
工具列表
工具 X
最终答案
用户请求

修改上一个 ReAct 代理示例,为其添加对话记忆。

# agent_with_memory_example.pyfrom langchain_openai import ChatOpenAI
from langchain import hub
from langchain.agents import AgentExecutor, create_react_agent
from langchain_community.tools import DuckDuckGoSearchRun
from langchain.memory import ConversationBufferMemory
from langchain_core.runnables import RunnablePassthrough
from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder
import os# --- 配置部分 ---
# 请替换为您的实际 OpenAI API 密钥
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"# 检查 API 密钥是否已设置
if not os.getenv("OPENAI_API_KEY"):print("错误:请设置环境变量 OPENAI_API_KEY 或在代码中取消注释并设置您的密钥。")exit()print("--- 带内存的 ReAct 代理示例开始 ---")# 1. 定义工具
search_tool = DuckDuckGoSearchRun(name="duckduckgo_search")
tools = [search_tool]
print(f"已定义工具: {search_tool.name}")# 2. 定义 LLM
llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
print("已定义 LLM (gpt-3.5-turbo)。")# 3. 定义内存
# ConversationBufferMemory 会记住完整的对话历史
memory = ConversationBufferMemory(memory_key="chat_history", return_messages=True)
print("已定义对话内存。")# 4. 定义代理使用的提示模板 (需要包含 chat_history 占位符)
# 为了让代理能够使用内存,其提示模板必须包含一个 MessagesPlaceholder 用于历史消息
# 我们拉取 ReAct 提示,并手动添加历史占位符
base_prompt = hub.pull("hwchase17/react")
prompt = base_prompt.partial(chat_history=MessagesPlaceholder(variable_name="chat_history"))
print("已拉取 ReAct 提示模板,并添加了 chat_history 占位符。")# 5. 创建 ReAct 代理
agent = create_react_agent(llm=llm, tools=tools, prompt=prompt)
print("ReAct 代理已创建。")# 6. 创建代理执行器 (AgentExecutor)
# 将 memory 参数传递给 AgentExecutor
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, memory=memory, handle_parsing_errors=True)
print("代理执行器已创建,并集成内存。")# 7. 调用代理进行多轮对话
print("\n--- 代理开始多轮对话任务 ---")# 第一轮对话
print("\n用户: 你好,我叫小明。")
response1 = agent_executor.invoke({"input": "你好,我叫小明。"})
print(f"代理: {response1['output']}")# 第二轮对话
print("\n用户: 你能帮我查一下今天的天气吗?")
response2 = agent_executor.invoke({"input": "你能帮我查一下今天的天气吗?"})
print(f"代理: {response2['output']}") # 注意:这里代理没有天气工具,会尝试搜索或说明无法完成# 第三轮对话:询问之前的名字,验证记忆
print("\n用户: 我叫什么名字?")
response3 = agent_executor.invoke({"input": "我叫什么名字?"})
print(f"代理: {response3['output']}")print("\n--- 带内存的 ReAct 代理示例结束 ---")
  • ConversationBufferMemory 是 LangChain 提供的一种内存类型,它可以存储整个对话历史。
  • memory_key="chat_history" 指定了在代理的提示模板中,对话历史将通过哪个键来注入。
  • MessagesPlaceholder(variable_name="chat_history") 是一个占位符,它会在运行时被实际的 chat_history 消息列表填充。
  • memory=memory 传递给 AgentExecutor 是集成内存的关键。

4. 代理与 RAG(检索增强生成)结合

代理和 RAG 的结合是 LLM 应用的强大模式。代理可以决定何时使用 RAG 工具来检索信息,而不是直接依赖于其内部知识。这使得代理能够回答关于私有数据或实时信息的问题。

构建 RAG 工具通常涉及将之前学习的 RAG 链包装成一个工具。

代理与 RAG 工具
RAG 工具内部
LLM思考/决策
决定使用 RAG 工具
选择并执行 RAG 工具
查询
上下文
RAG 工具的输出作为观察结果返回给代理LLM
任务完成
代理执行器 AgentExecutor
用户问题
LLM - 代理大脑
工具列表
最终答案
检索器
RAG 工具
相关文档
LLM - RAG 生成器
RAG 答案

到这里我们可以体会到 RAG 也只是一个特殊的链而已,把他作为一个工具使用即可,和先前并没有太多的不同

# agent_with_rag_tool_example.pyfrom langchain_openai import ChatOpenAI, OpenAIEmbeddings
from langchain_community.document_loaders import TextLoader
from langchain_text_splitters import RecursiveCharacterTextSplitter
from langchain_community.vectorstores import Chroma
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.runnables import RunnablePassthrough
from langchain_core.output_parsers import StrOutputParser
from langchain_core.tools import tool # 导入 tool 装饰器
from langchain.agents import AgentExecutor, create_react_agent
from langchain import hub
import os# --- 配置部分 ---
# 请替换为您的实际 OpenAI API 密钥
# os.environ["OPENAI_API_KEY"] = "YOUR_OPENAI_API_KEY"# 检查 API 密钥是否已设置
if not os.getenv("OPENAI_API_KEY"):print("错误:请设置环境变量 OPENAI_API_KEY 或在代码中取消注释并设置您的密钥。")exit()print("--- 代理与 RAG 工具示例开始 ---")# --- 1. 构建 RAG 链并将其包装成工具 ---# 1.1 准备数据
rag_doc_content = """
2023年,全球人工智能领域取得了突破性进展,尤其是大型语言模型(LLM)的应用变得更加广泛。
其中,LangChain 作为一个LLM应用开发框架,极大地简化了复杂LLM应用链的构建。
另一个重要的框架是LlamaIndex,它专注于数据管理和索引,使得LLM能够更高效地从海量数据中检索信息。
这两个框架在RAG(检索增强生成)领域都发挥着关键作用。
李明是2023年人工智能峰会的主席,他强调了AI伦理的重要性。
"""
with open("agent_rag_doc.txt", "w", encoding="utf-8") as f:f.write(rag_doc_content)loader = TextLoader("agent_rag_doc.txt", encoding="utf-8")
docs = loader.load()
text_splitter = RecursiveCharacterTextSplitter(chunk_size=200, chunk_overlap=20)
splits = text_splitter.split_documents(docs)embeddings = OpenAIEmbeddings(model="text-embedding-ada-002")
vectorstore = Chroma.from_documents(documents=splits, embedding=embeddings)
retriever = vectorstore.as_retriever()# 1.2 定义 RAG 核心链
rag_llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
rag_prompt_template = ChatPromptTemplate.from_template("""根据以下上下文信息回答问题:
{context}问题:{question}
""")
rag_output_parser = StrOutputParser()rag_chain = ({"context": retriever, "question": RunnablePassthrough()}| rag_prompt_template| rag_llm| rag_output_parser
)
print("RAG 核心链已构建。")# 1.3 将 RAG 链包装成工具
@tool
def knowledge_base_qa(query: str) -> str:"""这是一个基于内部知识库的问答工具。当用户问到与人工智能发展、LangChain、LlamaIndex 或特定人物(如李明)相关的问题时,可以使用此工具进行查询。输入应该是用户的问题。"""print(f"\n--- RAG 工具被调用,查询: {query} ---")response = rag_chain.invoke(query)print(f"--- RAG 工具返回: {response} ---\n")return responsetools = [knowledge_base_qa]
print(f"已将 RAG 链包装成工具: {knowledge_base_qa.name}")# --- 2. 构建并执行代理 ---# 2.1 定义 LLM (代理的“大脑”)
agent_llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)
print("已定义代理 LLM。")# 2.2 定义代理使用的提示模板
agent_prompt = hub.pull("hwchase17/react")
print("已从 LangChain Hub 拉取 ReAct 提示模板。")# 2.3 创建 ReAct 代理
agent = create_react_agent(llm=agent_llm, tools=tools, prompt=agent_prompt)
print("ReAct 代理已创建。")# 2.4 创建代理执行器
agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True, handle_parsing_errors=True)
print("代理执行器已创建。")# 2.5 调用代理
print("\n--- 代理开始执行任务 (包含 RAG 工具调用) ---")# 问题1:代理会调用 RAG 工具
question1 = "2023年人工智能峰会的主席是谁?他强调了什么?"
print(f"\n用户问题 1: {question1}")
response1 = agent_executor.invoke({"input": question1})
print(f"\n代理最终答案 1: {response1['output']}")# 问题2:代理可能直接回答,或尝试使用 RAG 工具但无相关结果
question2 = "苹果公司的CEO是谁?"
print(f"\n用户问题 2: {question2}")
response2 = agent_executor.invoke({"input": question2})
print(f"\n代理最终答案 2: {response2['output']}")print("\n--- 代理与 RAG 工具示例结束 ---")# 清理模拟文件
os.remove("agent_rag_doc.txt")
  • 我们首先构建了一个独立的 LangChain RAG 链 (rag_chain)。
  • 然后,我们使用 @tool 装饰器将这个 rag_chain 封装在一个名为 knowledge_base_qa 的函数中。这个函数接收用户问题,并调用 rag_chain 来获取答案。
  • 这个 knowledge_base_qa 工具被添加到代理的工具列表中。
  • 当代理接收到关于其知识库中内容的问题时(例如“2023年人工智能峰会的主席是谁?”),它会通过 ReAct 模式推理,决定调用 knowledge_base_qa 工具来获取答案。
  • 当问题超出其知识库范围时(例如“苹果公司的CEO是谁?”),代理可能会尝试搜索(如果提供了搜索工具),或者直接说明无法回答。
http://www.dtcms.com/a/276081.html

相关文章:

  • 网页五子棋-对战
  • python学习打卡:DAY 37 早停策略和模型权重的保存
  • web网站无法抓包排查;burp无法抓包情况
  • comfyUI-controlNet-线稿软边缘
  • c++中的STL
  • Day59
  • 智能制造——解读50页智能工厂系统集成总体解决方案【附全文阅读】
  • python学习打卡:DAY 40 训练和测试的规范写法
  • 深入详解:决策树在医学影像领域心脏疾病诊断的应用及实现细节
  • 苦练Python第9天:if-else分支九剑
  • 影刀rpa初级选择题答案-02网页自动化-源码-初级证书
  • 6. JVM直接内存
  • 菜鸟的C#学习(二)
  • 动手开发 MCP Server (Datawhale AI夏令营)
  • TensorBoard
  • 全栈开发知识
  • 计算机毕业设计springboot阳阳助农电商平台 基于Spring Boot的阳阳助农电商平台设计与开发 Spring Boot框架下的阳阳助农电商平台构建
  • 苦练Python第7天:布尔七日斩
  • 模拟电路--供复习和嵌入式学习
  • 威联通docker容器安装宝塔面板
  • VUE3 el-table 主子表 显示
  • ICCV2025 特征点检测 图像匹配 RIPE
  • 【Elasticsearch 】search_throttled
  • Spark计算性能优化实战指南
  • 面试现场:奇哥扮猪吃老虎,RocketMQ高级原理吊打面试官
  • 一文理解锂电池充电、过放修复与电量测量:从原理到实战
  • Redis Cluster 手动部署(小白的“升级打怪”成长之路)
  • 工业软件出海的ERP-PLM-MES一体化解决方案
  • MCP实验
  • Java 大视界 -- Java 大数据机器学习模型在电商用户复购行为预测与客户关系维护中的应用(343)