Files
langchain/docs/versioned_docs/version-0.2.x/tutorials/agents.ipynb
Harrison Chase eec4d5802e cr
2024-04-24 07:59:00 -07:00

694 lines
23 KiB
Plaintext

{
"cells": [
{
"cell_type": "raw",
"id": "17546ebb",
"metadata": {},
"source": [
"---\n",
"sidebar_position: 4\n",
"---"
]
},
{
"cell_type": "markdown",
"id": "f4c03f40-1328-412d-8a48-1db0cd481b77",
"metadata": {},
"source": [
"# Build an Agent\n",
"\n",
"To best understand the agent framework, let's build an agent that has two tools: one to look things up online, and one to look up specific data that we've exposed via a retriever.\n",
"\n",
":::{.callout-important}\n",
"This section will cover building with LangChain Agents. LangChain Agents are fine for getting started, but past a certain point you will likely want flexibility and control that they do not offer. For working with more advanced agents, we'd reccommend checking out [LangGraph](/docs/concepts/#langgraph)\n",
":::\n",
"\n",
"## Concepts\n",
"\n",
"Concepts we will cover are:\n",
"- Using [language models](/docs/concepts/#chat-models)\n",
"- Using [PromptTemplates](/docs/concepts/#prompt-templates) and [OutputParsers](/docs/concepts/#output-parsers)\n",
"- Creating a [Retriever](/docs/concepts/#retrievers) to expose specific information to our agent\n",
"- Using a [Tool](/docs/concepts/#tools) to look up things online\n",
"- Debugging and tracing your application using [LangSmith](/docs/concepts/#langsmith)\n",
"\n",
"## Setup\n",
"\n",
"### Jupyter Notebook\n",
"\n",
"This guide (and most of the other guides in the documentation) uses [Jupyter notebooks](https://jupyter.org/) and assumes the reader is as well. Jupyter notebooks are perfect for learning how to work with LLM systems because oftentimes things can go wrong (unexpected output, API down, etc) and going through guides in an interactive environment is a great way to better understand them.\n",
"\n",
"You do not NEED to go through the guide in a Jupyter Notebook, but it is recommended. See [here](https://jupyter.org/install) for instructions on how to install.\n",
"\n",
"### Installation\n",
"\n",
"To install LangChain run:\n",
"\n",
"```{=mdx}\n",
"import Tabs from '@theme/Tabs';\n",
"import TabItem from '@theme/TabItem';\n",
"import CodeBlock from \"@theme/CodeBlock\";\n",
"\n",
"<Tabs>\n",
" <TabItem value=\"pip\" label=\"Pip\" default>\n",
" <CodeBlock language=\"bash\">pip install langchain</CodeBlock>\n",
" </TabItem>\n",
" <TabItem value=\"conda\" label=\"Conda\">\n",
" <CodeBlock language=\"bash\">conda install langchain -c conda-forge</CodeBlock>\n",
" </TabItem>\n",
"</Tabs>\n",
"\n",
"```\n",
"\n",
"\n",
"For more details, see our [Installation guide](/docs/get_started/installation).\n",
"\n",
"### LangSmith\n",
"\n",
"Many of the applications you build with LangChain will contain multiple steps with multiple invocations of LLM calls.\n",
"As these applications get more and more complex, it becomes crucial to be able to inspect what exactly is going on inside your chain or agent.\n",
"The best way to do this is with [LangSmith](https://smith.langchain.com).\n",
"\n",
"After you sign up at the link above, make sure to set your environment variables to start logging traces:\n",
"\n",
"```shell\n",
"export LANGCHAIN_TRACING_V2=\"true\"\n",
"export LANGCHAIN_API_KEY=\"...\"\n",
"```\n",
"\n",
"Or, if in a notebook, you can set them with:\n",
"\n",
"```python\n",
"import getpass\n",
"import os\n",
"\n",
"os.environ[\"LANGCHAIN_TRACING_V2\"] = \"true\"\n",
"os.environ[\"LANGCHAIN_API_KEY\"] = getpass.getpass()\n",
"```\n",
"\n",
"## Define tools\n",
"\n",
"We first need to create the tools we want to use. We will use two tools: [Tavily](/docs/integrations/tools/tavily_search) (to search online) and then a retriever over a local index we will create"
]
},
{
"cell_type": "markdown",
"id": "c335d1bf",
"metadata": {},
"source": [
"### [Tavily](/docs/integrations/tools/tavily_search)\n",
"\n",
"We have a built-in tool in LangChain to easily use Tavily search engine as tool.\n",
"Note that this requires an API key - they have a free tier, but if you don't have one or don't want to create one, you can always ignore this step.\n",
"\n",
"Once you create your API key, you will need to export that as:\n",
"\n",
"```bash\n",
"export TAVILY_API_KEY=\"...\"\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "482ce13d",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.tools.tavily_search import TavilySearchResults"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "9cc86c0b",
"metadata": {},
"outputs": [],
"source": [
"search = TavilySearchResults(max_results=2)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "e593bbf6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[{'url': 'https://www.weatherapi.com/',\n",
" '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': 1713914854, 'localtime': '2024-04-23 16:27'}, 'current': {'last_updated_epoch': 1713914100, 'last_updated': '2024-04-23 16:15', 'temp_c': 16.1, 'temp_f': 61.0, 'is_day': 1, 'condition': {'text': 'Overcast', 'icon': '//cdn.weatherapi.com/weather/64x64/day/122.png', 'code': 1009}, 'wind_mph': 16.1, 'wind_kph': 25.9, 'wind_degree': 210, 'wind_dir': 'SSW', 'pressure_mb': 1016.0, 'pressure_in': 29.99, 'precip_mm': 0.0, 'precip_in': 0.0, 'humidity': 58, 'cloud': 100, 'feelslike_c': 16.1, 'feelslike_f': 61.0, 'vis_km': 16.0, 'vis_miles': 9.0, 'uv': 4.0, 'gust_mph': 20.5, 'gust_kph': 33.0}}\"},\n",
" {'url': 'https://forecast.weather.gov/zipcity.php?inputstring=San francisco,CA',\n",
" 'content': 'Current conditions at SAN FRANCISCO DOWNTOWN (SFOC1) Lat: 37.77056°NLon: 122.42694°WElev: 150.0ft. NA. 54°F. 12°C. ... 1am PDT Apr 23, 2024-6pm PDT Apr 29, 2024 . Forecast Discussion . Additional Resources. ... National Weather Service; San Francisco Bay Area, CA; Comments? Questions? Please Contact Us. Disclaimer; Information Quality;'}]"
]
},
"execution_count": 5,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"search.invoke(\"what is the weather in SF\")"
]
},
{
"cell_type": "markdown",
"id": "e8097977",
"metadata": {},
"source": [
"### Retriever\n",
"\n",
"We will also create a retriever over some data of our own. For a deeper explanation of each step here, see [this tutorial](/docs/tutorials/rag)."
]
},
{
"cell_type": "code",
"execution_count": 6,
"id": "9c9ce713",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.document_loaders import WebBaseLoader\n",
"from langchain_community.vectorstores import FAISS\n",
"from langchain_openai import OpenAIEmbeddings\n",
"from langchain_text_splitters import RecursiveCharacterTextSplitter\n",
"\n",
"loader = WebBaseLoader(\"https://docs.smith.langchain.com/overview\")\n",
"docs = loader.load()\n",
"documents = RecursiveCharacterTextSplitter(\n",
" chunk_size=1000, chunk_overlap=200\n",
").split_documents(docs)\n",
"vector = FAISS.from_documents(documents, OpenAIEmbeddings())\n",
"retriever = vector.as_retriever()"
]
},
{
"cell_type": "code",
"execution_count": 8,
"id": "dae53ec6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"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'})"
]
},
"execution_count": 8,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"retriever.invoke(\"how to upload a dataset\")[0]"
]
},
{
"cell_type": "markdown",
"id": "04aeca39",
"metadata": {},
"source": [
"Now that we have populated our index that we will do doing retrieval over, we can easily turn it into a tool (the format needed for an agent to properly use it)"
]
},
{
"cell_type": "code",
"execution_count": 9,
"id": "117594b5",
"metadata": {},
"outputs": [],
"source": [
"from langchain.tools.retriever import create_retriever_tool"
]
},
{
"cell_type": "code",
"execution_count": 10,
"id": "7280b031",
"metadata": {},
"outputs": [],
"source": [
"retriever_tool = create_retriever_tool(\n",
" retriever,\n",
" \"langsmith_search\",\n",
" \"Search for information about LangSmith. For any questions about LangSmith, you must use this tool!\",\n",
")"
]
},
{
"cell_type": "markdown",
"id": "c3b47c1d",
"metadata": {},
"source": [
"### Tools\n",
"\n",
"Now that we have created both, we can create a list of tools that we will use downstream."
]
},
{
"cell_type": "code",
"execution_count": 11,
"id": "b8e8e710",
"metadata": {},
"outputs": [],
"source": [
"tools = [search, retriever_tool]"
]
},
{
"cell_type": "markdown",
"id": "40ccec80",
"metadata": {},
"source": [
"## Create the agent\n",
"\n",
"Now that we have defined the tools, we can create the agent. We will be using a tool calling agent - for more information on this type of agent, as well as other options, see [this guide](/docs/concepts/#agent_types/).\n",
"\n",
"First, we choose the LLM we want to be guiding the agent."
]
},
{
"cell_type": "code",
"execution_count": 12,
"id": "f70b0fad",
"metadata": {},
"outputs": [],
"source": [
"from langchain_openai import ChatOpenAI\n",
"\n",
"llm = ChatOpenAI(model=\"gpt-3.5-turbo-0125\", temperature=0)"
]
},
{
"cell_type": "markdown",
"id": "5d1a95ce",
"metadata": {},
"source": [
"Next, we choose the prompt we want to use to guide the agent.\n",
"\n",
"If you want to see the contents of this prompt and have access to LangSmith, you can go to:\n",
"\n",
"https://smith.langchain.com/hub/hwchase17/openai-functions-agent"
]
},
{
"cell_type": "code",
"execution_count": 13,
"id": "af83d3e3",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"[SystemMessagePromptTemplate(prompt=PromptTemplate(input_variables=[], template='You are a helpful assistant')),\n",
" MessagesPlaceholder(variable_name='chat_history', optional=True),\n",
" HumanMessagePromptTemplate(prompt=PromptTemplate(input_variables=['input'], template='{input}')),\n",
" MessagesPlaceholder(variable_name='agent_scratchpad')]"
]
},
"execution_count": 13,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from langchain import hub\n",
"\n",
"# Get the prompt to use - you can modify this!\n",
"prompt = hub.pull(\"hwchase17/openai-functions-agent\")\n",
"prompt.messages"
]
},
{
"cell_type": "markdown",
"id": "f8014c9d",
"metadata": {},
"source": [
"Now, we can initalize the agent with the LLM, the prompt, and the tools. The agent is responsible for taking in input and deciding what actions to take. Crucially, the Agent does not execute those actions - that is done by the AgentExecutor (next step). For more information about how to think about these components, see our [conceptual guide](/docs/modules/agents/concepts/)."
]
},
{
"cell_type": "code",
"execution_count": 14,
"id": "89cf72b4-6046-4b47-8f27-5522d8cb8036",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import create_tool_calling_agent\n",
"\n",
"agent = create_tool_calling_agent(llm, tools, prompt)"
]
},
{
"cell_type": "markdown",
"id": "1a58c9f8",
"metadata": {},
"source": [
"Finally, we combine the agent (the brains) with the tools inside the AgentExecutor (which will repeatedly call the agent and execute tools)."
]
},
{
"cell_type": "code",
"execution_count": 27,
"id": "ce33904a",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import AgentExecutor\n",
"\n",
"agent_executor = AgentExecutor(agent=agent, tools=tools)"
]
},
{
"cell_type": "markdown",
"id": "e4df0e06",
"metadata": {},
"source": [
"## Run the agent\n",
"\n",
"We can now run the agent on a few queries! Note that for now, these are all **stateless** queries (it won't remember previous interactions)."
]
},
{
"cell_type": "code",
"execution_count": 28,
"id": "114ba50d",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input': 'hi!', 'output': 'Hello! How can I assist you today?'}"
]
},
"execution_count": 28,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor.invoke({\"input\": \"hi!\"})"
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "3fa4780a",
"metadata": {
"scrolled": true
},
"outputs": [
{
"data": {
"text/plain": [
"{'input': 'how can langsmith help with testing?',\n",
" 'output': 'LangSmith is a platform for building production-grade LLM applications that can help with testing by closely monitoring and evaluating your application. It allows you to ship quickly and with confidence. LangSmith provides tracing capabilities, evaluation features, production monitoring, and automations to streamline the testing process. Additionally, LangSmith offers a User Guide, Pricing information, Self-Hosting options, Proxy capabilities, and a Prompt Hub for managing prompts effectively. If you have sensitive data that cannot be logged, LangSmith offers private deployment options and self-hosting solutions.'}"
]
},
"execution_count": 29,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor.invoke({\"input\": \"how can langsmith help with testing?\"})"
]
},
{
"cell_type": "markdown",
"id": "f2d94242",
"metadata": {},
"source": [
"Example LangSmith trace: https://smith.langchain.com/public/e14254af-0cc1-4a99-bb24-15c50c559dd7/r"
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "77c2f769",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input': 'whats the weather in sf?',\n",
" 'output': 'The current weather in San Francisco is overcast with a temperature of 61.0°F (16.1°C). The wind speed is 25.9 kph coming from the south-southwest direction. The humidity is at 58%, and there is no precipitation at the moment.'}"
]
},
"execution_count": 30,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor.invoke({\"input\": \"whats the weather in sf?\"})"
]
},
{
"cell_type": "markdown",
"id": "c174f838",
"metadata": {},
"source": [
"Example LangSmith trace: https://smith.langchain.com/public/a04a067b-3bf4-47b3-acec-4055ff33a87a/r"
]
},
{
"cell_type": "markdown",
"id": "022cbc8a",
"metadata": {},
"source": [
"## Adding in memory\n",
"\n",
"As mentioned earlier, this agent is stateless. This means it does not remember previous interactions. To give it memory we need to pass in previous `chat_history`. Note: it needs to be called `chat_history` because of the prompt we are using. If we use a different prompt, we could change the variable name"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "c4073e35",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input': 'hi! my name is bob',\n",
" 'chat_history': [],\n",
" 'output': 'Hello Bob! How can I assist you today?'}"
]
},
"execution_count": 31,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Here we pass in an empty list of messages for chat_history because it is the first message in the chat\n",
"agent_executor.invoke({\"input\": \"hi! my name is bob\", \"chat_history\": []})"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "9dc5ed68",
"metadata": {},
"outputs": [],
"source": [
"from langchain_core.messages import AIMessage, HumanMessage"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "550e0c6e",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'chat_history': [HumanMessage(content='hi! my name is bob'),\n",
" AIMessage(content='Hello Bob! How can I assist you today?')],\n",
" 'input': \"what's my name?\",\n",
" 'output': 'Your name is Bob. How can I assist you further, Bob?'}"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor.invoke(\n",
" {\n",
" \"chat_history\": [\n",
" HumanMessage(content=\"hi! my name is bob\"),\n",
" AIMessage(content=\"Hello Bob! How can I assist you today?\"),\n",
" ],\n",
" \"input\": \"what's my name?\",\n",
" }\n",
")"
]
},
{
"cell_type": "markdown",
"id": "07b3bcf2",
"metadata": {},
"source": [
"If we want to keep track of these messages automatically, we can wrap this in a RunnableWithMessageHistory. For more information on how to use this, see [this guide](/docs/expression_language/how_to/message_history/)."
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "8edd96e6",
"metadata": {},
"outputs": [],
"source": [
"from langchain_community.chat_message_histories import ChatMessageHistory\n",
"from langchain_core.runnables.history import RunnableWithMessageHistory"
]
},
{
"cell_type": "code",
"execution_count": 35,
"id": "6e76552a",
"metadata": {},
"outputs": [],
"source": [
"message_history = ChatMessageHistory()"
]
},
{
"cell_type": "code",
"execution_count": 36,
"id": "828d1e95",
"metadata": {},
"outputs": [],
"source": [
"agent_with_chat_history = RunnableWithMessageHistory(\n",
" agent_executor,\n",
" # This is needed because in most real world scenarios, a session id is needed\n",
" # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
" lambda session_id: message_history,\n",
" input_messages_key=\"input\",\n",
" history_messages_key=\"chat_history\",\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 37,
"id": "1f5932b6",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input': \"hi! I'm bob\",\n",
" 'chat_history': [],\n",
" 'output': 'Hello Bob! How can I assist you today?'}"
]
},
"execution_count": 37,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_with_chat_history.invoke(\n",
" {\"input\": \"hi! I'm bob\"},\n",
" # This is needed because in most real world scenarios, a session id is needed\n",
" # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
" config={\"configurable\": {\"session_id\": \"<foo>\"}},\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 38,
"id": "ae627966",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"{'input': \"what's my name?\",\n",
" 'chat_history': [HumanMessage(content=\"hi! I'm bob\"),\n",
" AIMessage(content='Hello Bob! How can I assist you today?')],\n",
" 'output': 'Your name is Bob! How can I help you, Bob?'}"
]
},
"execution_count": 38,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_with_chat_history.invoke(\n",
" {\"input\": \"what's my name?\"},\n",
" # This is needed because in most real world scenarios, a session id is needed\n",
" # It isn't really used here because we are using a simple in memory ChatMessageHistory\n",
" config={\"configurable\": {\"session_id\": \"<foo>\"}},\n",
")"
]
},
{
"cell_type": "markdown",
"id": "6de2798e",
"metadata": {},
"source": [
"Example LangSmith trace: https://smith.langchain.com/public/98c8d162-60ae-4493-aa9f-992d87bd0429/r"
]
},
{
"cell_type": "markdown",
"id": "c029798f",
"metadata": {},
"source": [
"## Conclusion\n",
"\n",
"That's a wrap! In this quick start we covered how to create a simple agent. Agents are a complex topic, and there's lot to learn! \n",
"\n",
":::{.callout-important}\n",
"This section covered building with LangChain Agents. LangChain Agents are fine for getting started, but past a certain point you will likely want flexibility and control that they do not offer. For working with more advanced agents, we'd reccommend checking out [LangGraph](/docs/concepts/#langgraph)\n",
":::\n",
"\n",
"If you want to continue using LangChain agents, some good advanced guides are:\n",
"\n",
"- [How to create a custom agent](/docs/how_to/custom_agent)\n",
"- [How to stream responses from an agent](/docs/how_to/agents_streaming)\n",
"- [How to return structured output from an agent](/docs/how_to/agent_structured)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "e3ec3244",
"metadata": {},
"outputs": [],
"source": []
}
],
"metadata": {
"kernelspec": {
"display_name": "Python 3 (ipykernel)",
"language": "python",
"name": "python3"
},
"language_info": {
"codemirror_mode": {
"name": "ipython",
"version": 3
},
"file_extension": ".py",
"mimetype": "text/x-python",
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.10.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}