引言
Agent(智能体)是LangChain生态系统中的核心概念,它允许语言模型不仅仅输出文本,而是能够采取行动并与外部世界交互。在本章中,我们将深入探讨如何创建和运行Agent,使其能够利用多种工具完成复杂任务。
单独来说,语言模型无法采取行动 - 它们只能输出文本。LangChain的一个重要用例是创建代理(Agent)。代理是使用LLM作为推理引擎的系统,用于确定应采取哪些行动以及这些行动的输入应该是什么。然后可以将这些行动的结果反馈给代理,并确定是否需要更多行动,或者是否可以结束。
在本章中,我们将构建一个可以与多种不同工具进行交互的代理:一个是本地数据库,另一个是搜索引擎。您将能够向该代理提问,观察它调用工具,并与它进行对话。
关键概念
在开始构建Agent之前,我们需要了解几个关键概念:
- 语言模型:特别是它们的工具调用能力检索器(Retrievers):向我们的代理公开特定信息的组件工具(Tools):代理可以使用的功能,如搜索引擎聊天历史(Chat History):允许代理"记住"过去的交互LangSmith:用于调试和跟踪应用程序的工具
环境设置
安装必要的包
首先,我们需要安装LangChain及其相关依赖:
pip install langchain langchain-openai langchain-community tavily-python
设置LangSmith(可选但推荐)
LangSmith是一个强大的工具,可以帮助我们调试和跟踪代理的行为:
import os# 设置LangSmith环境变量os.environ["LANGCHAIN_TRACING_V2"] = "true"os.environ["LANGCHAIN_API_KEY"] = "your-langsmith-api-key" # 替换为你的API密钥os.environ["LANGCHAIN_PROJECT"] = "langchain-agent-tutorial" # 项目名称
定义工具
Agent的强大之处在于它可以使用各种工具。在这个例子中,我们将创建两种类型的工具:
- Tavily搜索工具(用于在线搜索)基于本地数据的检索工具
1. 设置Tavily搜索工具
Tavily是一个专为AI应用设计的搜索引擎API。首先,我们需要设置Tavily API密钥:
import osos.environ["TAVILY_API_KEY"] = "your-tavily-api-key" # 替换为你的API密钥from langchain_community.tools.tavily_search import TavilySearchResults# 创建搜索工具search_tool = TavilySearchResults(max_results=3)
2. 创建本地检索工具
接下来,我们将创建一个基于本地数据的检索工具。这个工具将允许代理从我们提供的文档中检索信息。
from langchain_community.document_loaders import TextLoaderfrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain_community.vectorstores import FAISSfrom langchain_openai import OpenAIEmbeddingsfrom langchain.tools.retriever import create_retriever_tool# 1. 加载文档loader = TextLoader("./data/company_info.txt") # 替换为你的文档路径documents = loader.load()# 2. 分割文档text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)splits = text_splitter.split_documents(documents)# 3. 创建向量存储embeddings = OpenAIEmbeddings()vectorstore = FAISS.from_documents(splits, embeddings)# 4. 创建检索器retriever = vectorstore.as_retriever()# 5. 将检索器转换为工具retriever_tool = create_retriever_tool( retriever, "company_info_search", "搜索公司内部信息,包括产品、服务、政策等。当问题涉及公司特定信息时使用此工具。")
整合工具
现在我们有了两个工具,可以将它们整合到一个列表中:
tools = [search_tool, retriever_tool]
设置语言模型
接下来,我们需要设置一个支持工具调用的语言模型:
from langchain_openai import ChatOpenAI# 创建语言模型model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)# 让模型了解可用的工具model_with_tools = model.bind_tools(tools)
测试模型的工具调用能力
在创建完整的代理之前,让我们测试一下模型的工具调用能力:
from langchain_core.messages import HumanMessage# 测试普通消息response = model_with_tools.invoke([ HumanMessage(content="今天天气怎么样?")])print("普通响应:", response.content)# 测试需要工具调用的消息response = model_with_tools.invoke([ HumanMessage(content="搜索关于量子计算的最新进展")])print("工具调用:", response.tool_calls)
创建代理
现在,我们可以创建一个完整的代理,它能够自动决定何时使用工具以及如何处理工具返回的结果:
from langchain.agents import create_tool_calling_agentfrom langchain.agents import AgentExecutorfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder# 1. 创建提示模板prompt = ChatPromptTemplate.from_messages([ ("system", """你是一个有用的AI助手,可以回答用户的问题。 你可以使用以下工具来帮助回答问题: {tools} 使用以下格式: 问题: 用户的问题 思考: 你应该总是思考要做什么 行动: 工具名称 (参数) 观察: 工具的结果 思考: 我现在知道答案了 回答: 对用户问题的回答 当你不需要使用工具时,直接回答用户的问题。 {chat_history} """), MessagesPlaceholder(variable_name="chat_history"), ("human", "{input}"),])# 2. 创建代理agent = create_tool_calling_agent(model, tools, prompt)# 3. 创建代理执行器agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)
运行代理
现在我们可以运行代理并测试它的能力:
# 测试不需要工具的查询result = agent_executor.invoke({"input": "你好,请介绍一下你自己"})print(result["output"])# 测试需要检索器的查询result = agent_executor.invoke({"input": "我们公司的主要产品是什么?"})print(result["output"])# 测试需要搜索工具的查询result = agent_executor.invoke({"input": "2023年诺贝尔物理学奖获得者是谁?"})print(result["output"])
添加记忆功能
目前,我们的代理是无状态的,它不会记住之前的交互。为了添加记忆功能,我们需要传递聊天历史:
# 手动管理聊天历史from langchain_core.messages import AIMessage, HumanMessagechat_history = []# 第一次交互result = agent_executor.invoke({ "input": "我们公司最畅销的产品是什么?", "chat_history": chat_history})print(result["output"])# 更新聊天历史chat_history.append(HumanMessage(content="我们公司最畅销的产品是什么?"))chat_history.append(AIMessage(content=result["output"]))# 第二次交互(引用前一个问题)result = agent_executor.invoke({ "input": "它的价格是多少?", "chat_history": chat_history})print(result["output"])
自动管理聊天历史
为了更方便地管理聊天历史,我们可以使用RunnableWithMessageHistory
:
from langchain_core.runnables.history import RunnableWithMessageHistoryfrom langchain_community.chat_message_histories import ChatMessageHistory# 创建消息存储message_history = ChatMessageHistory()# 包装代理执行器agent_with_memory = RunnableWithMessageHistory( agent_executor, lambda session_id: message_history, input_messages_key="input", history_messages_key="chat_history")# 使用带有记忆的代理result = agent_with_memory.invoke( {"input": "你能告诉我们公司的退款政策吗?"}, config={"configurable": {"session_id": "unique-session-id"}})print(result["output"])# 后续问题result = agent_with_memory.invoke( {"input": "如果产品已经使用了,还能退款吗?"}, config={"configurable": {"session_id": "unique-session-id"}})print(result["output"])
使用LangSmith跟踪和调试
LangSmith提供了强大的可视化和调试功能,帮助我们理解代理的决策过程:
# 执行代理并在LangSmith中跟踪result = agent_executor.invoke({"input": "比较我们的产品与竞争对手的区别"})# 查看LangSmith跟踪print("查看详细跟踪:https://smith.langchain.com/project/langchain-agent-tutorial")
完整代码示例
下面是一个完整的代码示例,整合了上述所有概念:
import osfrom langchain_openai import ChatOpenAIfrom langchain_community.tools.tavily_search import TavilySearchResultsfrom langchain_community.document_loaders import TextLoaderfrom langchain_text_splitters import RecursiveCharacterTextSplitterfrom langchain_community.vectorstores import FAISSfrom langchain_openai import OpenAIEmbeddingsfrom langchain.tools.retriever import create_retriever_toolfrom langchain.agents import create_tool_calling_agent, AgentExecutorfrom langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholderfrom langchain_core.messages import AIMessage, HumanMessagefrom langchain_core.runnables.history import RunnableWithMessageHistoryfrom langchain_community.chat_message_histories import ChatMessageHistory# 设置API密钥os.environ["OPENAI_API_KEY"] = "your-openai-api-key"os.environ["TAVILY_API_KEY"] = "your-tavily-api-key"# 设置LangSmith(可选)os.environ["LANGCHAIN_TRACING_V2"] = "true"os.environ["LANGCHAIN_API_KEY"] = "your-langsmith-api-key"os.environ["LANGCHAIN_PROJECT"] = "langchain-agent-tutorial"# 创建Tavily搜索工具search_tool = TavilySearchResults(max_results=3)# 创建本地检索工具# 假设我们有一个包含公司信息的文本文件with open("company_info.txt", "w") as f: f.write(""" 我们的公司成立于2010年,是一家专注于人工智能解决方案的科技公司。 我们的主要产品包括: 1. AI助手Pro - 我们最畅销的产品,售价¥999/年 2. 智能数据分析平台 - 企业版售价¥5,999/年 3. 自动化客服系统 - 根据规模定价,起价¥2,999/月 我们的退款政策: - 购买后14天内,如果对产品不满意,可以申请全额退款 - 已经使用的产品不支持退款 - 企业版产品需要联系客户经理处理退款事宜 我们的竞争优势: - 专有的自然语言处理技术,准确率比竞争对手高15% - 7x24小时客户支持 - 定制化解决方案能力 - 本地化部署选项,保护数据隐私 """)loader = TextLoader("company_info.txt")documents = loader.load()text_splitter = RecursiveCharacterTextSplitter(chunk_size=1000, chunk_overlap=100)splits = text_splitter.split_documents(documents)embeddings = OpenAIEmbeddings()vectorstore = FAISS.from_documents(splits, embeddings)retriever = vectorstore.as_retriever()retriever_tool = create_retriever_tool( retriever, "company_info_search", "搜索公司内部信息,包括产品、服务、政策等。当问题涉及公司特定信息时使用此工具。")# 整合工具tools = [search_tool, retriever_tool]# 创建语言模型model = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)# 创建提示模板prompt = ChatPromptTemplate.from_messages([ ("system", """你是一个有用的AI助手,可以回答用户的问题。 你可以使用以下工具来帮助回答问题: {tools} 使用以下格式: 问题: 用户的问题 思考: 你应该总是思考要做什么 行动: 工具名称 (参数) 观察: 工具的结果 思考: 我现在知道答案了 回答: 对用户问题的回答 当你不需要使用工具时,直接回答用户的问题。 """), MessagesPlaceholder(variable_name="chat_history"), ("human", "{input}"),])# 创建代理agent = create_tool_calling_agent(model, tools, prompt)# 创建代理执行器agent_executor = AgentExecutor(agent=agent, tools=tools, verbose=True)# 创建带有记忆的代理message_history = ChatMessageHistory()agent_with_memory = RunnableWithMessageHistory( agent_executor, lambda session_id: message_history, input_messages_key="input", history_messages_key="chat_history")# 使用代理print("=== 测试不需要工具的查询 ===")result = agent_executor.invoke({"input": "你好,请介绍一下你自己"})print(result["output"])print("\n=== 测试需要检索器的查询 ===")result = agent_executor.invoke({"input": "我们公司的主要产品是什么?"})print(result["output"])print("\n=== 测试需要搜索工具的查询 ===")result = agent_executor.invoke({"input": "2023年诺贝尔物理学奖获得者是谁?"})print(result["output"])print("\n=== 测试带有记忆的代理 ===")result = agent_with_memory.invoke( {"input": "我们公司最畅销的产品是什么?"}, config={"configurable": {"session_id": "unique-session-id"}})print(result["output"])result = agent_with_memory.invoke( {"input": "它的价格是多少?"}, config={"configurable": {"session_id": "unique-session-id"}})print(result["output"])# 清理import osif os.path.exists("company_info.txt"): os.remove("company_info.txt")
高级Agent开发技巧
1. 自定义代理类型
LangChain提供了多种代理类型,每种都有不同的决策流程:
# 使用ReAct代理from langchain.agents import create_react_agentreact_agent = create_react_agent(model, tools, prompt)
2. 处理工具错误
在实际应用中,工具可能会失败。我们可以添加错误处理:
# 创建带有错误处理的代理执行器agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, handle_tool_error=True, # 启用工具错误处理 max_iterations=5 # 限制最大迭代次数)
3. 添加人类反馈循环
在某些情况下,我们可能希望在代理执行关键操作前获取人类确认:
from langchain.agents import AgentExecutordef should_continue(output): """决定是否继续执行或请求人类确认""" if "critical_operation" in output: user_input = input("代理想执行关键操作。是否继续?(y/n): ") return user_input.lower() == 'y' return True# 创建带有人类反馈的代理执行器agent_executor = AgentExecutor( agent=agent, tools=tools, verbose=True, handle_tool_error=True, intermediate_steps_callback=should_continue)
结论
在本章中,我们学习了如何使用LangChain创建和运行Agent智能体。我们探讨了以下关键点:
- Agent的基本概念和工作原理如何创建和使用工具(搜索工具和检索工具)如何设置语言模型并使其能够调用工具如何创建完整的代理系统如何添加记忆功能,使代理能够记住过去的交互如何使用LangSmith跟踪和调试代理行为
Agent是LangChain生态系统中最强大的功能之一,它允许我们创建能够与外部世界交互的智能系统。通过合理设计工具和提示,我们可以构建出能够解决各种复杂问题的智能代理。
随着您对LangChain的深入了解,您可能希望探索更高级的代理框架,如LangGraph,它提供了更大的灵活性和控制性。