跳到主要内容

使用 AgentExecutor 构建 Agent(旧版)

重要

本节将介绍使用旧版 LangChain AgentExecutor 构建 Agent。 这些对于入门来说是不错的选择,但超过一定程度后,您可能需要它们不提供的灵活性和控制。 对于使用更高级的 Agent,我们建议查看 LangGraph Agent迁移指南

语言模型本身无法执行操作,它们只能输出文本。LangChain 的一个重要用例是创建智能体。智能体是使用 LLM 作为推理引擎的系统,用于确定要执行哪些操作以及这些操作的输入应该是什么。然后可以将这些操作的结果反馈给智能体,它会确定是否需要更多操作,或者是否可以完成。

在本教程中,我们将构建一个可以与多个不同工具交互的智能体:一个是本地数据库,另一个是搜索引擎。您将能够向此智能体提出问题,观察它调用工具,并与之进行对话。

概念

我们将涵盖的概念有:

  • 使用语言模型,特别是它们的工具调用能力
  • 创建一个检索器,向我们的智能体公开特定信息
  • 使用搜索工具在线查找内容
  • 聊天历史,它允许聊天机器人“记住”过去的交互,并在回应后续问题时将其考虑在内。
  • 使用 LangSmith 调试和跟踪您的应用程序

设置

Jupyter Notebook

本指南(以及文档中的大多数其他指南)使用 Jupyter notebooks,并假设读者也使用。Jupyter notebooks 非常适合学习如何使用 LLM 系统,因为通常事情可能会出错(意外的输出、API 故障等),而在交互式环境中浏览指南是更好地理解它们的好方法。

本教程和其他教程或许在 Jupyter notebook 中运行最为方便。请参阅此处以获取有关如何安装的说明。

安装

要安装 LangChain,请运行

pip install langchain

有关更多详细信息,请参阅我们的安装指南

LangSmith

您使用 LangChain 构建的许多应用程序将包含多个步骤,其中包含多个 LLM 调用。随着这些应用程序变得越来越复杂,能够检查您的链或智能体内部究竟发生了什么变得至关重要。最好的方法是使用 LangSmith

在上面的链接注册后,请确保设置您的环境变量以开始记录跟踪

export LANGCHAIN_TRACING_V2="true"
export LANGCHAIN_API_KEY="..."

或者,如果在 notebook 中,您可以使用以下方式设置它们

import getpass
import os

os.environ["LANGCHAIN_TRACING_V2"] = "true"
os.environ["LANGCHAIN_API_KEY"] = getpass.getpass()

定义工具

我们首先需要创建我们想要使用的工具。我们将使用两个工具:Tavily(在线搜索),然后是我们将创建的本地索引上的检索器

Tavily

我们在 LangChain 中内置了一个工具,可以轻松地将 Tavily 搜索引擎用作工具。请注意,这需要一个 API 密钥 - 它们有免费层级,但如果您没有或不想创建一个,您可以随时忽略此步骤。

创建 API 密钥后,您需要将其导出为

export TAVILY_API_KEY="..."
from langchain_community.tools.tavily_search import TavilySearchResults
API 参考:TavilySearchResults
search = TavilySearchResults(max_results=2)
search.invoke("what is the weather in SF")
[{'url': 'https://www.weatherapi.com/',
'content': "{'location': {'name': 'San Francisco', 'region': 'California', 'country': 'United States of America', 'lat': 37.78, 'lon': -122.42, 'tz_id': 'America/Los_Angeles', 'localtime_epoch': 1714000492, 'localtime': '2024-04-24 16:14'}, 'current': {'last_updated_epoch': 1713999600, 'last_updated': '2024-04-24 16:00', 'temp_c': 15.6, 'temp_f': 60.1, 'is_day': 1, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/day/122.png', 'code': 1009}, 'wind_mph': 10.5, 'wind_kph': 16.9, 'wind_degree': 330, 'wind_dir': 'NNW', 'pressure_mb': 1018.0, 'pressure_in': 30.06, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 72, 'cloud': 100, 'feelslike_c': 15.6, 'feelslike_f': 60.1, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 5.0, 'gust_mph': 14.8, 'gust_kph': 23.8}}"},
{'url': 'https://www.weathertab.com/en/c/e/04/united-states/california/san-francisco/',
'content': 'San Francisco Weather Forecast for Apr 2024 - Risk of Rain Graph. Rain Risk Graph: Monthly Overview. Bar heights indicate rain risk percentages. Yellow bars mark low-risk days, while black and grey bars signal higher risks. Grey-yellow bars act as buffers, advising to keep at least one day clear from the riskier grey and black days, guiding ...'}]

检索器

我们还将创建一个在我们自己的数据上的检索器。有关此处每个步骤的更深入解释,请参阅本教程

from langchain_community.document_loaders import WebBaseLoader
from langchain_community.vectorstores import FAISS
from langchain_openai import OpenAIEmbeddings
from langchain_text_splitters import RecursiveCharacterTextSplitter

loader = WebBaseLoader("https://docs.smith.langchain.com/overview")
docs = loader.load()
documents = RecursiveCharacterTextSplitter(
chunk_size=1000, chunk_overlap=200
).split_documents(docs)
vector = FAISS.from_documents(documents, OpenAIEmbeddings())
retriever = vector.as_retriever()
retriever.invoke("how to upload a dataset")[0]
Document(page_content='# The data to predict and grade over    evaluators=[exact_match], # The evaluators to score the results    experiment_prefix="sample-experiment", # The name of the experiment    metadata={      "version": "1.0.0",      "revision_id": "beta"    },)import { Client, Run, Example } from \'langsmith\';import { runOnDataset } from \'langchain/smith\';import { EvaluationResult } from \'langsmith/evaluation\';const client = new Client();// Define dataset: these are your test casesconst datasetName = "Sample Dataset";const dataset = await client.createDataset(datasetName, {    description: "A sample dataset in LangSmith."});await client.createExamples({    inputs: [        { postfix: "to LangSmith" },        { postfix: "to Evaluations in LangSmith" },    ],    outputs: [        { output: "Welcome to LangSmith" },        { output: "Welcome to Evaluations in LangSmith" },    ],    datasetId: dataset.id,});// Define your evaluatorconst exactMatch = async ({ run, example }: { run: Run; example?:', metadata={'source': 'https://docs.smith.langchain.com/overview', 'title': 'Getting started with LangSmith | \uf8ffü¶úÔ∏è\uf8ffüõ†Ô∏è LangSmith', 'description': 'Introduction', 'language': 'en'})

现在我们已经填充了我们将进行检索的索引,我们可以轻松地将其转换为工具(智能体正确使用它所需的格式)

from langchain.tools.retriever import create_retriever_tool
retriever_tool = create_retriever_tool(
retriever,
"langsmith_search",
"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!",
)

工具

现在我们已经创建了这两个工具,我们可以创建一个将在下游使用的工具列表。

tools = [search, retriever_tool]

使用语言模型

接下来,让我们学习如何使用语言模型来调用工具。LangChain 支持许多不同的语言模型,您可以互换使用 - 选择您想要使用的模型!

pip install -qU langchain-openai
import getpass
import os

if not os.environ.get("OPENAI_API_KEY"):
os.environ["OPENAI_API_KEY"] = getpass.getpass("Enter API key for OpenAI: ")

from langchain_openai import ChatOpenAI

model = ChatOpenAI(model="gpt-4")

您可以通过传入消息列表来调用语言模型。默认情况下,响应是一个 content 字符串。

from langchain_core.messages import HumanMessage

response = model.invoke([HumanMessage(content="hi!")])
response.content
API 参考:HumanMessage
'Hello! How can I assist you today?'

我们现在可以看到启用此模型进行工具调用是什么样的。为了启用该功能,我们使用 .bind_tools 向语言模型提供有关这些工具的知识

model_with_tools = model.bind_tools(tools)

我们现在可以调用模型。让我们首先用一条普通消息调用它,看看它是如何响应的。我们可以查看 content 字段以及 tool_calls 字段。

response = model_with_tools.invoke([HumanMessage(content="Hi!")])

print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
ContentString: Hello! How can I assist you today?
ToolCalls: []

现在,让我们尝试使用一些期望调用工具的输入来调用它。

response = model_with_tools.invoke([HumanMessage(content="What's the weather in SF?")])

print(f"ContentString: {response.content}")
print(f"ToolCalls: {response.tool_calls}")
ContentString: 
ToolCalls: [{'name': 'tavily_search_results_json', 'args': {'query': 'current weather in San Francisco'}, 'id': 'call_4HteVahXkRAkWjp6dGXryKZX'}]

我们可以看到现在没有内容,但是有一个工具调用!它希望我们调用 Tavily 搜索工具。

这还不是调用该工具 - 它只是告诉我们要调用它。为了真正调用它,我们将需要创建我们的智能体。

创建智能体

现在我们已经定义了工具和 LLM,我们可以创建智能体。我们将使用一个工具调用智能体 - 有关这种类型的智能体以及其他选项的更多信息,请参阅本指南

我们可以首先选择我们想要用来指导智能体的提示。

如果您想查看此提示的内容并访问 LangSmith,您可以转到

https://smith.langchain.com/hub/hwchase17/openai-functions-agent

from langchain import hub

# Get the prompt to use - you can modify this!
prompt = hub.pull("hwchase17/openai-functions-agent")
prompt.messages
API 参考:hub
[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')),
MessagesPlaceholder(variable_name='chat_history', optional=True),
HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')),
MessagesPlaceholder(variable_name='agent_scratchpad')]

现在,我们可以使用 LLM、提示和工具初始化智能体。智能体负责接收输入并决定要采取的操作。至关重要的是,智能体不执行这些操作 - 这由 AgentExecutor 完成(下一步)。有关如何考虑这些组件的更多信息,请参阅我们的概念指南

请注意,我们传入的是 model,而不是 model_with_tools。这是因为 create_tool_calling_agent 会在后台为我们调用 .bind_tools

from langchain.agents import create_tool_calling_agent

agent = create_tool_calling_agent(model, tools, prompt)

最后,我们将智能体(大脑)与 AgentExecutor(将反复调用智能体并执行工具)中的工具结合起来。

from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(agent=agent, tools=tools)
API 参考:AgentExecutor

运行智能体

我们现在可以在一些查询上运行智能体!请注意,目前,这些都是无状态查询(它不会记住以前的交互)。

首先,让我们看看在不需要调用工具时它是如何响应的

agent_executor.invoke({"input": "hi!"})
{'input': 'hi!', 'output': 'Hello! How can I assist you today?'}

为了准确了解后台发生的事情(并确保它没有调用工具),我们可以查看LangSmith 跟踪

现在,让我们在应该调用检索器的示例中尝试一下

agent_executor.invoke({"input": "how can langsmith help with testing?"})
{'input': 'how can langsmith help with testing?',
'output': 'LangSmith is a platform that aids in building production-grade Language Learning Model (LLM) applications. It can assist with testing in several ways:\n\n1. **Monitoring and Evaluation**: LangSmith allows close monitoring and evaluation of your application. This helps you to ensure the quality of your application and deploy it with confidence.\n\n2. **Tracing**: LangSmith has tracing capabilities that can be beneficial for debugging and understanding the behavior of your application.\n\n3. **Evaluation Capabilities**: LangSmith has built-in tools for evaluating the performance of your LLM. \n\n4. **Prompt Hub**: This is a prompt management tool built into LangSmith that can help in testing different prompts and their responses.\n\nPlease note that to use LangSmith, you would need to install it and create an API key. The platform offers Python and Typescript SDKs for utilization. It works independently and does not require the use of LangChain.'}

让我们看一下LangSmith 跟踪,以确保它实际上正在调用它。

现在,让我们尝试一个需要调用搜索工具的示例

agent_executor.invoke({"input": "whats the weather in sf?"})
{'input': 'whats the weather in sf?',
'output': 'The current weather in San Francisco is partly cloudy with a temperature of 16.1°C (61.0°F). The wind is coming from the WNW at a speed of 10.5 mph. The humidity is at 67%. [source](https://www.weatherapi.com/)'}

我们可以查看LangSmith 跟踪,以确保它正在有效地调用搜索工具。

添加内存

如前所述,此智能体是无状态的。这意味着它不会记住以前的交互。要为其提供内存,我们需要传入以前的 chat_history。注意:由于我们使用的提示,它需要被称为 chat_history。如果我们使用不同的提示,我们可以更改变量名称

# Here we pass in an empty list of messages for chat_history because it is the first message in the chat
agent_executor.invoke({"input": "hi! my name is bob", "chat_history": []})
{'input': 'hi! my name is bob',
'chat_history': [],
'output': 'Hello Bob! How can I assist you today?'}
from langchain_core.messages import AIMessage, HumanMessage
API 参考:AIMessage | HumanMessage
agent_executor.invoke(
{
"chat_history": [
HumanMessage(content="hi! my name is bob"),
AIMessage(content="Hello Bob! How can I assist you today?"),
],
"input": "what's my name?",
}
)
{'chat_history': [HumanMessage(content='hi! my name is bob'),
AIMessage(content='Hello Bob! How can I assist you today?')],
'input': "what's my name?",
'output': 'Your name is Bob. How can I assist you further?'}

如果我们想要自动跟踪这些消息,我们可以将其包装在 RunnableWithMessageHistory 中。有关如何使用此功能的更多信息,请参阅本指南

from langchain_community.chat_message_histories import ChatMessageHistory
from langchain_core.chat_history import BaseChatMessageHistory
from langchain_core.runnables.history import RunnableWithMessageHistory

store = {}


def get_session_history(session_id: str) -> BaseChatMessageHistory:
if session_id not in store:
store[session_id] = ChatMessageHistory()
return store[session_id]

由于我们有多个输入,我们需要指定两件事

  • input_messages_key:用于添加到对话历史记录的输入键。
  • history_messages_key:用于将加载的消息添加到其中的键。
agent_with_chat_history = RunnableWithMessageHistory(
agent_executor,
get_session_history,
input_messages_key="input",
history_messages_key="chat_history",
)
agent_with_chat_history.invoke(
{"input": "hi! I'm bob"},
config={"configurable": {"session_id": "<foo>"}},
)
{'input': "hi! I'm bob",
'chat_history': [],
'output': 'Hello Bob! How can I assist you today?'}
agent_with_chat_history.invoke(
{"input": "what's my name?"},
config={"configurable": {"session_id": "<foo>"}},
)
{'input': "what's my name?",
'chat_history': [HumanMessage(content="hi! I'm bob"),
AIMessage(content='Hello Bob! How can I assist you today?')],
'output': 'Your name is Bob.'}

LangSmith 跟踪示例: https://smith.langchain.com/public/98c8d162-60ae-4493-aa9f-992d87bd0429/r

结论

就到这里!在本快速入门中,我们介绍了如何创建一个简单的代理。代理是一个复杂的主题,有很多东西需要学习!

重要

本节介绍了使用 LangChain 代理进行构建。它们对于入门来说很好,但是超过一定程度后,您可能需要它们不提供的灵活性和控制。要开发更高级的代理,我们建议查看 LangGraph

如果您想继续使用 LangChain 代理,以下是一些好的高级指南:


此页面是否有帮助?