Update agent docs, move to use-case sub-directory (#9344)

Re-structure and add new agent page
pull/9951/head
Lance Martin 11 months ago committed by GitHub
parent 985873c497
commit 2ab04a4e32
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 236 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 74 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 166 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 42 KiB

Binary file not shown.

After

Width:  |  Height:  |  Size: 177 KiB

@ -1,565 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "517a9fd4",
"metadata": {},
"source": [
"# BabyAGI User Guide\n",
"\n",
"This notebook demonstrates how to implement [BabyAGI](https://github.com/yoheinakajima/babyagi/tree/main) by [Yohei Nakajima](https://twitter.com/yoheinakajima). BabyAGI is an AI agent that can generate and pretend to execute tasks based on a given objective.\n",
"\n",
"This guide will help you understand the components to create your own recursive agents.\n",
"\n",
"Although BabyAGI uses specific vectorstores/model providers (Pinecone, OpenAI), one of the benefits of implementing it with LangChain is that you can easily swap those out for different options. In this implementation we use a FAISS vectorstore (because it runs locally and is free)."
]
},
{
"cell_type": "markdown",
"id": "556af556",
"metadata": {},
"source": [
"## Install and Import Required Modules"
]
},
{
"cell_type": "code",
"execution_count": 116,
"id": "c8a354b6",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from collections import deque\n",
"from typing import Dict, List, Optional, Any\n",
"\n",
"from langchain import LLMChain, OpenAI, PromptTemplate\n",
"from langchain.embeddings import OpenAIEmbeddings\n",
"from langchain.llms import BaseLLM\n",
"from langchain.vectorstores.base import VectorStore\n",
"from pydantic import BaseModel, Field\n",
"from langchain.chains.base import Chain"
]
},
{
"cell_type": "markdown",
"id": "09f70772",
"metadata": {},
"source": [
"## Connect to the Vector Store\n",
"\n",
"Depending on what vectorstore you use, this step may look different."
]
},
{
"cell_type": "code",
"execution_count": 71,
"id": "794045d4",
"metadata": {},
"outputs": [],
"source": [
"from langchain.vectorstores import FAISS\n",
"from langchain.docstore import InMemoryDocstore"
]
},
{
"cell_type": "code",
"execution_count": 72,
"id": "6e0305eb",
"metadata": {},
"outputs": [],
"source": [
"# Define your embedding model\n",
"embeddings_model = OpenAIEmbeddings()\n",
"# Initialize the vectorstore as empty\n",
"import faiss\n",
"\n",
"embedding_size = 1536\n",
"index = faiss.IndexFlatL2(embedding_size)\n",
"vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})"
]
},
{
"cell_type": "markdown",
"id": "0f3b72bf",
"metadata": {},
"source": [
"## Define the Chains\n",
"\n",
"BabyAGI relies on three LLM chains:\n",
"- Task creation chain to select new tasks to add to the list\n",
"- Task prioritization chain to re-prioritize tasks\n",
"- Execution Chain to execute the tasks"
]
},
{
"cell_type": "code",
"execution_count": 73,
"id": "bf4bd5cd",
"metadata": {},
"outputs": [],
"source": [
"class TaskCreationChain(LLMChain):\n",
" \"\"\"Chain to generates tasks.\"\"\"\n",
"\n",
" @classmethod\n",
" def from_llm(cls, llm: BaseLLM, verbose: bool = True) -> LLMChain:\n",
" \"\"\"Get the response parser.\"\"\"\n",
" task_creation_template = (\n",
" \"You are a task creation AI that uses the result of an execution agent\"\n",
" \" to create new tasks with the following objective: {objective},\"\n",
" \" The last completed task has the result: {result}.\"\n",
" \" This result was based on this task description: {task_description}.\"\n",
" \" These are incomplete tasks: {incomplete_tasks}.\"\n",
" \" Based on the result, create new tasks to be completed\"\n",
" \" by the AI system that do not overlap with incomplete tasks.\"\n",
" \" Return the tasks as an array.\"\n",
" )\n",
" prompt = PromptTemplate(\n",
" template=task_creation_template,\n",
" input_variables=[\n",
" \"result\",\n",
" \"task_description\",\n",
" \"incomplete_tasks\",\n",
" \"objective\",\n",
" ],\n",
" )\n",
" return cls(prompt=prompt, llm=llm, verbose=verbose)"
]
},
{
"cell_type": "code",
"execution_count": 74,
"id": "b6488ffe",
"metadata": {},
"outputs": [],
"source": [
"class TaskPrioritizationChain(LLMChain):\n",
" \"\"\"Chain to prioritize tasks.\"\"\"\n",
"\n",
" @classmethod\n",
" def from_llm(cls, llm: BaseLLM, verbose: bool = True) -> LLMChain:\n",
" \"\"\"Get the response parser.\"\"\"\n",
" task_prioritization_template = (\n",
" \"You are a task prioritization AI tasked with cleaning the formatting of and reprioritizing\"\n",
" \" the following tasks: {task_names}.\"\n",
" \" Consider the ultimate objective of your team: {objective}.\"\n",
" \" Do not remove any tasks. Return the result as a numbered list, like:\"\n",
" \" #. First task\"\n",
" \" #. Second task\"\n",
" \" Start the task list with number {next_task_id}.\"\n",
" )\n",
" prompt = PromptTemplate(\n",
" template=task_prioritization_template,\n",
" input_variables=[\"task_names\", \"next_task_id\", \"objective\"],\n",
" )\n",
" return cls(prompt=prompt, llm=llm, verbose=verbose)"
]
},
{
"cell_type": "code",
"execution_count": 84,
"id": "b43cd580",
"metadata": {},
"outputs": [],
"source": [
"class ExecutionChain(LLMChain):\n",
" \"\"\"Chain to execute tasks.\"\"\"\n",
"\n",
" @classmethod\n",
" def from_llm(cls, llm: BaseLLM, verbose: bool = True) -> LLMChain:\n",
" \"\"\"Get the response parser.\"\"\"\n",
" execution_template = (\n",
" \"You are an AI who performs one task based on the following objective: {objective}.\"\n",
" \" Take into account these previously completed tasks: {context}.\"\n",
" \" Your task: {task}.\"\n",
" \" Response:\"\n",
" )\n",
" prompt = PromptTemplate(\n",
" template=execution_template,\n",
" input_variables=[\"objective\", \"context\", \"task\"],\n",
" )\n",
" return cls(prompt=prompt, llm=llm, verbose=verbose)"
]
},
{
"cell_type": "markdown",
"id": "3ad996c5",
"metadata": {},
"source": [
"### Define the BabyAGI Controller\n",
"\n",
"BabyAGI composes the chains defined above in a (potentially-)infinite loop."
]
},
{
"cell_type": "code",
"execution_count": 85,
"id": "0ada0636",
"metadata": {},
"outputs": [],
"source": [
"def get_next_task(\n",
" task_creation_chain: LLMChain,\n",
" result: Dict,\n",
" task_description: str,\n",
" task_list: List[str],\n",
" objective: str,\n",
") -> List[Dict]:\n",
" \"\"\"Get the next task.\"\"\"\n",
" incomplete_tasks = \", \".join(task_list)\n",
" response = task_creation_chain.run(\n",
" result=result,\n",
" task_description=task_description,\n",
" incomplete_tasks=incomplete_tasks,\n",
" objective=objective,\n",
" )\n",
" new_tasks = response.split(\"\\n\")\n",
" return [{\"task_name\": task_name} for task_name in new_tasks if task_name.strip()]"
]
},
{
"cell_type": "code",
"execution_count": 86,
"id": "d35250ad",
"metadata": {},
"outputs": [],
"source": [
"def prioritize_tasks(\n",
" task_prioritization_chain: LLMChain,\n",
" this_task_id: int,\n",
" task_list: List[Dict],\n",
" objective: str,\n",
") -> List[Dict]:\n",
" \"\"\"Prioritize tasks.\"\"\"\n",
" task_names = [t[\"task_name\"] for t in task_list]\n",
" next_task_id = int(this_task_id) + 1\n",
" response = task_prioritization_chain.run(\n",
" task_names=task_names, next_task_id=next_task_id, objective=objective\n",
" )\n",
" new_tasks = response.split(\"\\n\")\n",
" prioritized_task_list = []\n",
" for task_string in new_tasks:\n",
" if not task_string.strip():\n",
" continue\n",
" task_parts = task_string.strip().split(\".\", 1)\n",
" if len(task_parts) == 2:\n",
" task_id = task_parts[0].strip()\n",
" task_name = task_parts[1].strip()\n",
" prioritized_task_list.append({\"task_id\": task_id, \"task_name\": task_name})\n",
" return prioritized_task_list"
]
},
{
"cell_type": "code",
"execution_count": 87,
"id": "e3f1840c",
"metadata": {},
"outputs": [],
"source": [
"def _get_top_tasks(vectorstore, query: str, k: int) -> List[str]:\n",
" \"\"\"Get the top k tasks based on the query.\"\"\"\n",
" results = vectorstore.similarity_search_with_score(query, k=k)\n",
" if not results:\n",
" return []\n",
" sorted_results, _ = zip(*sorted(results, key=lambda x: x[1], reverse=True))\n",
" return [str(item.metadata[\"task\"]) for item in sorted_results]\n",
"\n",
"\n",
"def execute_task(\n",
" vectorstore, execution_chain: LLMChain, objective: str, task: str, k: int = 5\n",
") -> str:\n",
" \"\"\"Execute a task.\"\"\"\n",
" context = _get_top_tasks(vectorstore, query=objective, k=k)\n",
" return execution_chain.run(objective=objective, context=context, task=task)"
]
},
{
"cell_type": "code",
"execution_count": 137,
"id": "1e978938",
"metadata": {},
"outputs": [],
"source": [
"class BabyAGI(Chain, BaseModel):\n",
" \"\"\"Controller model for the BabyAGI agent.\"\"\"\n",
"\n",
" task_list: deque = Field(default_factory=deque)\n",
" task_creation_chain: TaskCreationChain = Field(...)\n",
" task_prioritization_chain: TaskPrioritizationChain = Field(...)\n",
" execution_chain: ExecutionChain = Field(...)\n",
" task_id_counter: int = Field(1)\n",
" vectorstore: VectorStore = Field(init=False)\n",
" max_iterations: Optional[int] = None\n",
"\n",
" class Config:\n",
" \"\"\"Configuration for this pydantic object.\"\"\"\n",
"\n",
" arbitrary_types_allowed = True\n",
"\n",
" def add_task(self, task: Dict):\n",
" self.task_list.append(task)\n",
"\n",
" def print_task_list(self):\n",
" print(\"\\033[95m\\033[1m\" + \"\\n*****TASK LIST*****\\n\" + \"\\033[0m\\033[0m\")\n",
" for t in self.task_list:\n",
" print(str(t[\"task_id\"]) + \": \" + t[\"task_name\"])\n",
"\n",
" def print_next_task(self, task: Dict):\n",
" print(\"\\033[92m\\033[1m\" + \"\\n*****NEXT TASK*****\\n\" + \"\\033[0m\\033[0m\")\n",
" print(str(task[\"task_id\"]) + \": \" + task[\"task_name\"])\n",
"\n",
" def print_task_result(self, result: str):\n",
" print(\"\\033[93m\\033[1m\" + \"\\n*****TASK RESULT*****\\n\" + \"\\033[0m\\033[0m\")\n",
" print(result)\n",
"\n",
" @property\n",
" def input_keys(self) -> List[str]:\n",
" return [\"objective\"]\n",
"\n",
" @property\n",
" def output_keys(self) -> List[str]:\n",
" return []\n",
"\n",
" def _call(self, inputs: Dict[str, Any]) -> Dict[str, Any]:\n",
" \"\"\"Run the agent.\"\"\"\n",
" objective = inputs[\"objective\"]\n",
" first_task = inputs.get(\"first_task\", \"Make a todo list\")\n",
" self.add_task({\"task_id\": 1, \"task_name\": first_task})\n",
" num_iters = 0\n",
" while True:\n",
" if self.task_list:\n",
" self.print_task_list()\n",
"\n",
" # Step 1: Pull the first task\n",
" task = self.task_list.popleft()\n",
" self.print_next_task(task)\n",
"\n",
" # Step 2: Execute the task\n",
" result = execute_task(\n",
" self.vectorstore, self.execution_chain, objective, task[\"task_name\"]\n",
" )\n",
" this_task_id = int(task[\"task_id\"])\n",
" self.print_task_result(result)\n",
"\n",
" # Step 3: Store the result in Pinecone\n",
" result_id = f\"result_{task['task_id']}_{num_iters}\"\n",
" self.vectorstore.add_texts(\n",
" texts=[result],\n",
" metadatas=[{\"task\": task[\"task_name\"]}],\n",
" ids=[result_id],\n",
" )\n",
"\n",
" # Step 4: Create new tasks and reprioritize task list\n",
" new_tasks = get_next_task(\n",
" self.task_creation_chain,\n",
" result,\n",
" task[\"task_name\"],\n",
" [t[\"task_name\"] for t in self.task_list],\n",
" objective,\n",
" )\n",
" for new_task in new_tasks:\n",
" self.task_id_counter += 1\n",
" new_task.update({\"task_id\": self.task_id_counter})\n",
" self.add_task(new_task)\n",
" self.task_list = deque(\n",
" prioritize_tasks(\n",
" self.task_prioritization_chain,\n",
" this_task_id,\n",
" list(self.task_list),\n",
" objective,\n",
" )\n",
" )\n",
" num_iters += 1\n",
" if self.max_iterations is not None and num_iters == self.max_iterations:\n",
" print(\n",
" \"\\033[91m\\033[1m\" + \"\\n*****TASK ENDING*****\\n\" + \"\\033[0m\\033[0m\"\n",
" )\n",
" break\n",
" return {}\n",
"\n",
" @classmethod\n",
" def from_llm(\n",
" cls, llm: BaseLLM, vectorstore: VectorStore, verbose: bool = False, **kwargs\n",
" ) -> \"BabyAGI\":\n",
" \"\"\"Initialize the BabyAGI Controller.\"\"\"\n",
" task_creation_chain = TaskCreationChain.from_llm(llm, verbose=verbose)\n",
" task_prioritization_chain = TaskPrioritizationChain.from_llm(\n",
" llm, verbose=verbose\n",
" )\n",
" execution_chain = ExecutionChain.from_llm(llm, verbose=verbose)\n",
" return cls(\n",
" task_creation_chain=task_creation_chain,\n",
" task_prioritization_chain=task_prioritization_chain,\n",
" execution_chain=execution_chain,\n",
" vectorstore=vectorstore,\n",
" **kwargs,\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "05ba762e",
"metadata": {},
"source": [
"### Run the BabyAGI\n",
"\n",
"Now it's time to create the BabyAGI controller and watch it try to accomplish your objective."
]
},
{
"cell_type": "code",
"execution_count": 138,
"id": "3d220b69",
"metadata": {},
"outputs": [],
"source": [
"OBJECTIVE = \"Write a weather report for SF today\""
]
},
{
"cell_type": "code",
"execution_count": 139,
"id": "8a8e5543",
"metadata": {},
"outputs": [],
"source": [
"llm = OpenAI(temperature=0)"
]
},
{
"cell_type": "code",
"execution_count": 140,
"id": "3d69899b",
"metadata": {},
"outputs": [],
"source": [
"# Logging of LLMChains\n",
"verbose = False\n",
"# If None, will keep on going forever\n",
"max_iterations: Optional[int] = 3\n",
"baby_agi = BabyAGI.from_llm(\n",
" llm=llm, vectorstore=vectorstore, verbose=verbose, max_iterations=max_iterations\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 141,
"id": "f7957b51",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"1: Make a todo list\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"1: Make a todo list\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"\n",
"\n",
"1. Check the temperature range for the day.\n",
"2. Gather temperature data for SF today.\n",
"3. Analyze the temperature data and create a weather report.\n",
"4. Publish the weather report.\n",
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"2: Gather data on the expected temperature range for the day.\n",
"3: Collect data on the expected precipitation for the day.\n",
"4: Analyze the data and create a weather report.\n",
"5: Check the current weather conditions in SF.\n",
"6: Publish the weather report.\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"2: Gather data on the expected temperature range for the day.\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"\n",
"\n",
"I have gathered data on the expected temperature range for the day in San Francisco. The forecast is for temperatures to range from a low of 55 degrees Fahrenheit to a high of 68 degrees Fahrenheit.\n",
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"3: Check the current weather conditions in SF.\n",
"4: Calculate the average temperature for the day in San Francisco.\n",
"5: Determine the probability of precipitation for the day in San Francisco.\n",
"6: Identify any potential weather warnings or advisories for the day in San Francisco.\n",
"7: Research any historical weather patterns for the day in San Francisco.\n",
"8: Compare the expected temperature range to the historical average for the day in San Francisco.\n",
"9: Collect data on the expected precipitation for the day.\n",
"10: Analyze the data and create a weather report.\n",
"11: Publish the weather report.\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"3: Check the current weather conditions in SF.\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"\n",
"\n",
"I am checking the current weather conditions in SF. According to the data I have gathered, the temperature in SF today is currently around 65 degrees Fahrenheit with clear skies. The temperature range for the day is expected to be between 60 and 70 degrees Fahrenheit.\n",
"\u001b[91m\u001b[1m\n",
"*****TASK ENDING*****\n",
"\u001b[0m\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"{'objective': 'Write a weather report for SF today'}"
]
},
"execution_count": 141,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"baby_agi({\"objective\": OBJECTIVE})"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "898a210b",
"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.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -1,647 +0,0 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "517a9fd4",
"metadata": {},
"source": [
"# BabyAGI with Tools\n",
"\n",
"This notebook builds on top of [baby agi](baby_agi.html), but shows how you can swap out the execution chain. The previous execution chain was just an LLM which made stuff up. By swapping it out with an agent that has access to tools, we can hopefully get real reliable information"
]
},
{
"cell_type": "markdown",
"id": "556af556",
"metadata": {},
"source": [
"## Install and Import Required Modules"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "c8a354b6",
"metadata": {},
"outputs": [],
"source": [
"import os\n",
"from collections import deque\n",
"from typing import Dict, List, Optional, Any\n",
"\n",
"from langchain import LLMChain, OpenAI, PromptTemplate\n",
"from langchain.embeddings import OpenAIEmbeddings\n",
"from langchain.llms import BaseLLM\n",
"from langchain.vectorstores.base import VectorStore\n",
"from pydantic import BaseModel, Field\n",
"from langchain.chains.base import Chain"
]
},
{
"cell_type": "markdown",
"id": "09f70772",
"metadata": {},
"source": [
"## Connect to the Vector Store\n",
"\n",
"Depending on what vectorstore you use, this step may look different."
]
},
{
"cell_type": "code",
"execution_count": 2,
"id": "794045d4",
"metadata": {},
"outputs": [],
"source": [
"%pip install faiss-cpu > /dev/null\n",
"%pip install google-search-results > /dev/null\n",
"from langchain.vectorstores import FAISS\n",
"from langchain.docstore import InMemoryDocstore"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "6e0305eb",
"metadata": {},
"outputs": [],
"source": [
"# Define your embedding model\n",
"embeddings_model = OpenAIEmbeddings()\n",
"# Initialize the vectorstore as empty\n",
"import faiss\n",
"\n",
"embedding_size = 1536\n",
"index = faiss.IndexFlatL2(embedding_size)\n",
"vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})"
]
},
{
"cell_type": "markdown",
"id": "0f3b72bf",
"metadata": {},
"source": [
"## Define the Chains\n",
"\n",
"BabyAGI relies on three LLM chains:\n",
"- Task creation chain to select new tasks to add to the list\n",
"- Task prioritization chain to re-prioritize tasks\n",
"- Execution Chain to execute the tasks\n",
"\n",
"\n",
"NOTE: in this notebook, the Execution chain will now be an agent."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "bf4bd5cd",
"metadata": {},
"outputs": [],
"source": [
"class TaskCreationChain(LLMChain):\n",
" \"\"\"Chain to generates tasks.\"\"\"\n",
"\n",
" @classmethod\n",
" def from_llm(cls, llm: BaseLLM, verbose: bool = True) -> LLMChain:\n",
" \"\"\"Get the response parser.\"\"\"\n",
" task_creation_template = (\n",
" \"You are an task creation AI that uses the result of an execution agent\"\n",
" \" to create new tasks with the following objective: {objective},\"\n",
" \" The last completed task has the result: {result}.\"\n",
" \" This result was based on this task description: {task_description}.\"\n",
" \" These are incomplete tasks: {incomplete_tasks}.\"\n",
" \" Based on the result, create new tasks to be completed\"\n",
" \" by the AI system that do not overlap with incomplete tasks.\"\n",
" \" Return the tasks as an array.\"\n",
" )\n",
" prompt = PromptTemplate(\n",
" template=task_creation_template,\n",
" input_variables=[\n",
" \"result\",\n",
" \"task_description\",\n",
" \"incomplete_tasks\",\n",
" \"objective\",\n",
" ],\n",
" )\n",
" return cls(prompt=prompt, llm=llm, verbose=verbose)"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "b6488ffe",
"metadata": {},
"outputs": [],
"source": [
"class TaskPrioritizationChain(LLMChain):\n",
" \"\"\"Chain to prioritize tasks.\"\"\"\n",
"\n",
" @classmethod\n",
" def from_llm(cls, llm: BaseLLM, verbose: bool = True) -> LLMChain:\n",
" \"\"\"Get the response parser.\"\"\"\n",
" task_prioritization_template = (\n",
" \"You are an task prioritization AI tasked with cleaning the formatting of and reprioritizing\"\n",
" \" the following tasks: {task_names}.\"\n",
" \" Consider the ultimate objective of your team: {objective}.\"\n",
" \" Do not remove any tasks. Return the result as a numbered list, like:\"\n",
" \" #. First task\"\n",
" \" #. Second task\"\n",
" \" Start the task list with number {next_task_id}.\"\n",
" )\n",
" prompt = PromptTemplate(\n",
" template=task_prioritization_template,\n",
" input_variables=[\"task_names\", \"next_task_id\", \"objective\"],\n",
" )\n",
" return cls(prompt=prompt, llm=llm, verbose=verbose)"
]
},
{
"cell_type": "code",
"execution_count": 46,
"id": "b43cd580",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import ZeroShotAgent, Tool, AgentExecutor\n",
"from langchain import OpenAI, SerpAPIWrapper, LLMChain\n",
"\n",
"todo_prompt = PromptTemplate.from_template(\n",
" \"You are a planner who is an expert at coming up with a todo list for a given objective. Come up with a todo list for this objective: {objective}\"\n",
")\n",
"todo_chain = LLMChain(llm=OpenAI(temperature=0), prompt=todo_prompt)\n",
"search = SerpAPIWrapper()\n",
"tools = [\n",
" Tool(\n",
" name=\"Search\",\n",
" func=search.run,\n",
" description=\"useful for when you need to answer questions about current events\",\n",
" ),\n",
" Tool(\n",
" name=\"TODO\",\n",
" func=todo_chain.run,\n",
" description=\"useful for when you need to come up with todo lists. Input: an objective to create a todo list for. Output: a todo list for that objective. Please be very clear what the objective is!\",\n",
" ),\n",
"]\n",
"\n",
"\n",
"prefix = \"\"\"You are an AI who performs one task based on the following objective: {objective}. Take into account these previously completed tasks: {context}.\"\"\"\n",
"suffix = \"\"\"Question: {task}\n",
"{agent_scratchpad}\"\"\"\n",
"prompt = ZeroShotAgent.create_prompt(\n",
" tools,\n",
" prefix=prefix,\n",
" suffix=suffix,\n",
" input_variables=[\"objective\", \"task\", \"context\", \"agent_scratchpad\"],\n",
")"
]
},
{
"cell_type": "markdown",
"id": "3ad996c5",
"metadata": {},
"source": [
"### Define the BabyAGI Controller\n",
"\n",
"BabyAGI composes the chains defined above in a (potentially-)infinite loop."
]
},
{
"cell_type": "code",
"execution_count": 47,
"id": "0ada0636",
"metadata": {},
"outputs": [],
"source": [
"def get_next_task(\n",
" task_creation_chain: LLMChain,\n",
" result: Dict,\n",
" task_description: str,\n",
" task_list: List[str],\n",
" objective: str,\n",
") -> List[Dict]:\n",
" \"\"\"Get the next task.\"\"\"\n",
" incomplete_tasks = \", \".join(task_list)\n",
" response = task_creation_chain.run(\n",
" result=result,\n",
" task_description=task_description,\n",
" incomplete_tasks=incomplete_tasks,\n",
" objective=objective,\n",
" )\n",
" new_tasks = response.split(\"\\n\")\n",
" return [{\"task_name\": task_name} for task_name in new_tasks if task_name.strip()]"
]
},
{
"cell_type": "code",
"execution_count": 48,
"id": "d35250ad",
"metadata": {},
"outputs": [],
"source": [
"def prioritize_tasks(\n",
" task_prioritization_chain: LLMChain,\n",
" this_task_id: int,\n",
" task_list: List[Dict],\n",
" objective: str,\n",
") -> List[Dict]:\n",
" \"\"\"Prioritize tasks.\"\"\"\n",
" task_names = [t[\"task_name\"] for t in task_list]\n",
" next_task_id = int(this_task_id) + 1\n",
" response = task_prioritization_chain.run(\n",
" task_names=task_names, next_task_id=next_task_id, objective=objective\n",
" )\n",
" new_tasks = response.split(\"\\n\")\n",
" prioritized_task_list = []\n",
" for task_string in new_tasks:\n",
" if not task_string.strip():\n",
" continue\n",
" task_parts = task_string.strip().split(\".\", 1)\n",
" if len(task_parts) == 2:\n",
" task_id = task_parts[0].strip()\n",
" task_name = task_parts[1].strip()\n",
" prioritized_task_list.append({\"task_id\": task_id, \"task_name\": task_name})\n",
" return prioritized_task_list"
]
},
{
"cell_type": "code",
"execution_count": 49,
"id": "e3f1840c",
"metadata": {},
"outputs": [],
"source": [
"def _get_top_tasks(vectorstore, query: str, k: int) -> List[str]:\n",
" \"\"\"Get the top k tasks based on the query.\"\"\"\n",
" results = vectorstore.similarity_search_with_score(query, k=k)\n",
" if not results:\n",
" return []\n",
" sorted_results, _ = zip(*sorted(results, key=lambda x: x[1], reverse=True))\n",
" return [str(item.metadata[\"task\"]) for item in sorted_results]\n",
"\n",
"\n",
"def execute_task(\n",
" vectorstore, execution_chain: LLMChain, objective: str, task: str, k: int = 5\n",
") -> str:\n",
" \"\"\"Execute a task.\"\"\"\n",
" context = _get_top_tasks(vectorstore, query=objective, k=k)\n",
" return execution_chain.run(objective=objective, context=context, task=task)"
]
},
{
"cell_type": "code",
"execution_count": 50,
"id": "1e978938",
"metadata": {},
"outputs": [],
"source": [
"class BabyAGI(Chain, BaseModel):\n",
" \"\"\"Controller model for the BabyAGI agent.\"\"\"\n",
"\n",
" task_list: deque = Field(default_factory=deque)\n",
" task_creation_chain: TaskCreationChain = Field(...)\n",
" task_prioritization_chain: TaskPrioritizationChain = Field(...)\n",
" execution_chain: AgentExecutor = Field(...)\n",
" task_id_counter: int = Field(1)\n",
" vectorstore: VectorStore = Field(init=False)\n",
" max_iterations: Optional[int] = None\n",
"\n",
" class Config:\n",
" \"\"\"Configuration for this pydantic object.\"\"\"\n",
"\n",
" arbitrary_types_allowed = True\n",
"\n",
" def add_task(self, task: Dict):\n",
" self.task_list.append(task)\n",
"\n",
" def print_task_list(self):\n",
" print(\"\\033[95m\\033[1m\" + \"\\n*****TASK LIST*****\\n\" + \"\\033[0m\\033[0m\")\n",
" for t in self.task_list:\n",
" print(str(t[\"task_id\"]) + \": \" + t[\"task_name\"])\n",
"\n",
" def print_next_task(self, task: Dict):\n",
" print(\"\\033[92m\\033[1m\" + \"\\n*****NEXT TASK*****\\n\" + \"\\033[0m\\033[0m\")\n",
" print(str(task[\"task_id\"]) + \": \" + task[\"task_name\"])\n",
"\n",
" def print_task_result(self, result: str):\n",
" print(\"\\033[93m\\033[1m\" + \"\\n*****TASK RESULT*****\\n\" + \"\\033[0m\\033[0m\")\n",
" print(result)\n",
"\n",
" @property\n",
" def input_keys(self) -> List[str]:\n",
" return [\"objective\"]\n",
"\n",
" @property\n",
" def output_keys(self) -> List[str]:\n",
" return []\n",
"\n",
" def _call(self, inputs: Dict[str, Any]) -> Dict[str, Any]:\n",
" \"\"\"Run the agent.\"\"\"\n",
" objective = inputs[\"objective\"]\n",
" first_task = inputs.get(\"first_task\", \"Make a todo list\")\n",
" self.add_task({\"task_id\": 1, \"task_name\": first_task})\n",
" num_iters = 0\n",
" while True:\n",
" if self.task_list:\n",
" self.print_task_list()\n",
"\n",
" # Step 1: Pull the first task\n",
" task = self.task_list.popleft()\n",
" self.print_next_task(task)\n",
"\n",
" # Step 2: Execute the task\n",
" result = execute_task(\n",
" self.vectorstore, self.execution_chain, objective, task[\"task_name\"]\n",
" )\n",
" this_task_id = int(task[\"task_id\"])\n",
" self.print_task_result(result)\n",
"\n",
" # Step 3: Store the result in Pinecone\n",
" result_id = f\"result_{task['task_id']}_{num_iters}\"\n",
" self.vectorstore.add_texts(\n",
" texts=[result],\n",
" metadatas=[{\"task\": task[\"task_name\"]}],\n",
" ids=[result_id],\n",
" )\n",
"\n",
" # Step 4: Create new tasks and reprioritize task list\n",
" new_tasks = get_next_task(\n",
" self.task_creation_chain,\n",
" result,\n",
" task[\"task_name\"],\n",
" [t[\"task_name\"] for t in self.task_list],\n",
" objective,\n",
" )\n",
" for new_task in new_tasks:\n",
" self.task_id_counter += 1\n",
" new_task.update({\"task_id\": self.task_id_counter})\n",
" self.add_task(new_task)\n",
" self.task_list = deque(\n",
" prioritize_tasks(\n",
" self.task_prioritization_chain,\n",
" this_task_id,\n",
" list(self.task_list),\n",
" objective,\n",
" )\n",
" )\n",
" num_iters += 1\n",
" if self.max_iterations is not None and num_iters == self.max_iterations:\n",
" print(\n",
" \"\\033[91m\\033[1m\" + \"\\n*****TASK ENDING*****\\n\" + \"\\033[0m\\033[0m\"\n",
" )\n",
" break\n",
" return {}\n",
"\n",
" @classmethod\n",
" def from_llm(\n",
" cls, llm: BaseLLM, vectorstore: VectorStore, verbose: bool = False, **kwargs\n",
" ) -> \"BabyAGI\":\n",
" \"\"\"Initialize the BabyAGI Controller.\"\"\"\n",
" task_creation_chain = TaskCreationChain.from_llm(llm, verbose=verbose)\n",
" task_prioritization_chain = TaskPrioritizationChain.from_llm(\n",
" llm, verbose=verbose\n",
" )\n",
" llm_chain = LLMChain(llm=llm, prompt=prompt)\n",
" tool_names = [tool.name for tool in tools]\n",
" agent = ZeroShotAgent(llm_chain=llm_chain, allowed_tools=tool_names)\n",
" agent_executor = AgentExecutor.from_agent_and_tools(\n",
" agent=agent, tools=tools, verbose=True\n",
" )\n",
" return cls(\n",
" task_creation_chain=task_creation_chain,\n",
" task_prioritization_chain=task_prioritization_chain,\n",
" execution_chain=agent_executor,\n",
" vectorstore=vectorstore,\n",
" **kwargs,\n",
" )"
]
},
{
"cell_type": "markdown",
"id": "05ba762e",
"metadata": {},
"source": [
"### Run the BabyAGI\n",
"\n",
"Now it's time to create the BabyAGI controller and watch it try to accomplish your objective."
]
},
{
"cell_type": "code",
"execution_count": 51,
"id": "3d220b69",
"metadata": {},
"outputs": [],
"source": [
"OBJECTIVE = \"Write a weather report for SF today\""
]
},
{
"cell_type": "code",
"execution_count": 52,
"id": "8a8e5543",
"metadata": {},
"outputs": [],
"source": [
"llm = OpenAI(temperature=0)"
]
},
{
"cell_type": "code",
"execution_count": 53,
"id": "3d69899b",
"metadata": {},
"outputs": [],
"source": [
"# Logging of LLMChains\n",
"verbose = False\n",
"# If None, will keep on going forever\n",
"max_iterations: Optional[int] = 3\n",
"baby_agi = BabyAGI.from_llm(\n",
" llm=llm, vectorstore=vectorstore, verbose=verbose, max_iterations=max_iterations\n",
")"
]
},
{
"cell_type": "code",
"execution_count": 54,
"id": "f7957b51",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"1: Make a todo list\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"1: Make a todo list\n",
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: I need to gather data on the current weather conditions in SF\n",
"Action: Search\n",
"Action Input: Current weather conditions in SF\u001b[0m\n",
"Observation: \u001b[36;1m\u001b[1;3mHigh 67F. Winds WNW at 10 to 15 mph. Clear to partly cloudy.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to make a todo list\n",
"Action: TODO\n",
"Action Input: Write a weather report for SF today\u001b[0m\n",
"Observation: \u001b[33;1m\u001b[1;3m\n",
"\n",
"1. Research current weather conditions in San Francisco\n",
"2. Gather data on temperature, humidity, wind speed, and other relevant weather conditions\n",
"3. Analyze data to determine current weather trends\n",
"4. Write a brief introduction to the weather report\n",
"5. Describe current weather conditions in San Francisco\n",
"6. Discuss any upcoming weather changes\n",
"7. Summarize the weather report\n",
"8. Proofread and edit the report\n",
"9. Submit the report\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I now know the final answer\n",
"Final Answer: A weather report for SF today should include research on current weather conditions in San Francisco, gathering data on temperature, humidity, wind speed, and other relevant weather conditions, analyzing data to determine current weather trends, writing a brief introduction to the weather report, describing current weather conditions in San Francisco, discussing any upcoming weather changes, summarizing the weather report, proofreading and editing the report, and submitting the report.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"A weather report for SF today should include research on current weather conditions in San Francisco, gathering data on temperature, humidity, wind speed, and other relevant weather conditions, analyzing data to determine current weather trends, writing a brief introduction to the weather report, describing current weather conditions in San Francisco, discussing any upcoming weather changes, summarizing the weather report, proofreading and editing the report, and submitting the report.\n",
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"2: Gather data on temperature, humidity, wind speed, and other relevant weather conditions\n",
"3: Analyze data to determine current weather trends\n",
"4: Write a brief introduction to the weather report\n",
"5: Describe current weather conditions in San Francisco\n",
"6: Discuss any upcoming weather changes\n",
"7: Summarize the weather report\n",
"8: Proofread and edit the report\n",
"9: Submit the report\n",
"1: Research current weather conditions in San Francisco\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"2: Gather data on temperature, humidity, wind speed, and other relevant weather conditions\n",
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: I need to search for the current weather conditions in SF\n",
"Action: Search\n",
"Action Input: Current weather conditions in SF\u001b[0m\n",
"Observation: \u001b[36;1m\u001b[1;3mHigh 67F. Winds WNW at 10 to 15 mph. Clear to partly cloudy.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I need to make a todo list\n",
"Action: TODO\n",
"Action Input: Create a weather report for SF today\u001b[0m\n",
"Observation: \u001b[33;1m\u001b[1;3m\n",
"\n",
"1. Gather current weather data for SF, including temperature, wind speed, humidity, and precipitation.\n",
"2. Research historical weather data for SF to compare current conditions.\n",
"3. Analyze current and historical data to determine any trends or patterns.\n",
"4. Create a visual representation of the data, such as a graph or chart.\n",
"5. Write a summary of the weather report, including key findings and any relevant information.\n",
"6. Publish the weather report on a website or other platform.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I now know the final answer\n",
"Final Answer: Today in San Francisco, the temperature is 67F with winds WNW at 10 to 15 mph. The sky is clear to partly cloudy.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"Today in San Francisco, the temperature is 67F with winds WNW at 10 to 15 mph. The sky is clear to partly cloudy.\n",
"\u001b[95m\u001b[1m\n",
"*****TASK LIST*****\n",
"\u001b[0m\u001b[0m\n",
"3: Research current weather conditions in San Francisco\n",
"4: Compare the current weather conditions in San Francisco to the average for this time of year.\n",
"5: Identify any potential weather-related hazards in the area.\n",
"6: Research any historical weather patterns in San Francisco.\n",
"7: Analyze data to determine current weather trends\n",
"8: Include any relevant data from nearby cities in the report.\n",
"9: Include any relevant data from the National Weather Service in the report.\n",
"10: Include any relevant data from local news sources in the report.\n",
"11: Include any relevant data from online weather sources in the report.\n",
"12: Include any relevant data from local meteorologists in the report.\n",
"13: Include any relevant data from local weather stations in the report.\n",
"14: Include any relevant data from satellite images in the report.\n",
"15: Describe current weather conditions in San Francisco\n",
"16: Discuss any upcoming weather changes\n",
"17: Write a brief introduction to the weather report\n",
"18: Summarize the weather report\n",
"19: Proofread and edit the report\n",
"20: Submit the report\n",
"\u001b[92m\u001b[1m\n",
"*****NEXT TASK*****\n",
"\u001b[0m\u001b[0m\n",
"3: Research current weather conditions in San Francisco\n",
"\n",
"\n",
"\u001b[1m> Entering new AgentExecutor chain...\u001b[0m\n",
"\u001b[32;1m\u001b[1;3mThought: I need to search for current weather conditions in San Francisco\n",
"Action: Search\n",
"Action Input: Current weather conditions in San Francisco\u001b[0m\n",
"Observation: \u001b[36;1m\u001b[1;3mTodaySun 04/09 High 67 · 1% Precip. ; TonightSun 04/09 Low 49 · 9% Precip. ; TomorrowMon 04/10 High 64 · 11% Precip.\u001b[0m\n",
"Thought:\u001b[32;1m\u001b[1;3m I now know the final answer\n",
"Final Answer: Today in San Francisco, the high temperature is 67 degrees with 1% chance of precipitation. The low temperature tonight is 49 degrees with 9% chance of precipitation. Tomorrow's high temperature is 64 degrees with 11% chance of precipitation.\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"\u001b[93m\u001b[1m\n",
"*****TASK RESULT*****\n",
"\u001b[0m\u001b[0m\n",
"Today in San Francisco, the high temperature is 67 degrees with 1% chance of precipitation. The low temperature tonight is 49 degrees with 9% chance of precipitation. Tomorrow's high temperature is 64 degrees with 11% chance of precipitation.\n",
"\u001b[91m\u001b[1m\n",
"*****TASK ENDING*****\n",
"\u001b[0m\u001b[0m\n"
]
},
{
"data": {
"text/plain": [
"{'objective': 'Write a weather report for SF today'}"
]
},
"execution_count": 54,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"baby_agi({\"objective\": OBJECTIVE})"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "898a210b",
"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.9.1"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

@ -0,0 +1,718 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "842dd272",
"metadata": {},
"source": [
"# Agents\n",
"\n",
"[![Open In Collab](https://colab.research.google.com/assets/colab-badge.svg)](https://colab.research.google.com/github/langchain-ai/langchain/blob/master/docs/extras/use_cases/more/agents/agents.ipynb)\n",
"\n",
"## Use case \n",
"\n",
"LLM-based agents are powerful general problem solvers.\n",
"\n",
"The [primary LLM agent components](https://lilianweng.github.io/posts/2023-06-23-agent/) include at least 3 things:\n",
"\n",
"* `Planning`: The ability to break down tasks into smaller sub-goals\n",
"* `Memory`: The ability to retain and recall information\n",
"* `Tools`: The ability to get information from external sources (e.g., APIs)\n",
"\n",
"Unlike LLMs simply connected to [APIs](/docs/use_cases/apis/apis), agents [can](https://www.youtube.com/watch?v=DWUdGhRrv2c):\n",
"\n",
"* Self-correct\n",
"* Handle multi-hop tasks (several intermediate \"hops\" or steps to arrive at a conclusion)\n",
"* Tackle long time horizon tasks (that require access to long-term memory)\n",
"\n",
"![Image description](/img/agents_use_case_1.png)\n",
"\n",
"## Overview \n",
"\n",
"LangChain has [many agent types](/docs/modules/agents/agent_types/).\n",
"\n",
"Nearly all agents will use the following components:\n",
" \n",
"**Planning**\n",
" \n",
"* `Prompt`: Can given the LLM [personality](https://arxiv.org/pdf/2304.03442.pdf), context (e.g, via retrieval from memory), or strategies for learninng (e.g., [chain-of-thought](https://lilianweng.github.io/posts/2023-03-15-prompt-engineering/#chain-of-thought-cot)).\n",
"* `Agent` Responsible for deciding what step to take next using an LLM with the `Prompt`\n",
"\n",
"**Memory**\n",
"\n",
"* This can be short or long-term, allowing the agent to persist information.\n",
"\n",
"**Tools**\n",
"\n",
"* Tools are functions that an agent can call.\n",
"\n",
"But, there are some taxonomic differences:\n",
"\n",
"* `Action agents`: Designed to decide the sequence of actions (tool use) (e.g., OpenAI functions agents, ReAct agents).\n",
"* `Simulation agents`: Designed for role-play often in simulated enviorment (e.g., Generative Agents, CAMEL).\n",
"* `Autonomous agents`: Designed for indepdent execution towards long term goals (e.g., BabyAGI, Auto-GPT).\n",
"\n",
"This will focus on `Action agents`.\n",
"\n",
"\n",
"## Quickstart "
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3a704c7a",
"metadata": {},
"outputs": [],
"source": [
"! pip install langchain openai google-search-results\n",
"\n",
"# Set env var OPENAI_API_KEY and SERPAPI_API_KEY or load from a .env file\n",
"# import dotenv\n",
"\n",
"# dotenv.load_env()"
]
},
{
"cell_type": "markdown",
"id": "639d41ad",
"metadata": {},
"source": [
"`Tools`\n",
"\n",
"LangChain has [many tools](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/agents/load_tools.py) for Agents that we can load easily.\n",
"\n",
"Let's load search and a calcultor."
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "c60001c9",
"metadata": {},
"outputs": [],
"source": [
"# Tool\n",
"from langchain.agents import load_tools\n",
"from langchain.chat_models import ChatOpenAI\n",
"llm = ChatOpenAI(temperature=0)\n",
"tools = load_tools([\"serpapi\", \"llm-math\"], llm=llm)"
]
},
{
"cell_type": "markdown",
"id": "431ba30b",
"metadata": {},
"source": [
"`Agent`\n",
"\n",
"The [`OPENAI_FUNCTIONS` agent](/docs/modules/agents/agent_types/openai_functions_agent) is a good action agent to start with.\n",
"\n",
"OpenAI models have been fine-tuned to recognize when function should be called."
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "d636395f",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'As of 2023, the estimated population of Canada is approximately 39,858,480 people.'"
]
},
"execution_count": 4,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Prompt\n",
"from langchain.agents import AgentExecutor\n",
"from langchain.schema import SystemMessage\n",
"from langchain.agents import OpenAIFunctionsAgent\n",
"system_message = SystemMessage(content=\"You are a search assistant.\")\n",
"prompt = OpenAIFunctionsAgent.create_prompt(system_message=system_message)\n",
"\n",
"# Agent\n",
"search_agent = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=prompt)\n",
"agent_executor = AgentExecutor(agent=search_agent, tools=tools, verbose=False)\n",
"\n",
"# Run\n",
"agent_executor.run(\"How many people live in canada as of 2023?\")"
]
},
{
"cell_type": "markdown",
"id": "27842380",
"metadata": {},
"source": [
"Great, we have created a simple search agent with a tool!\n",
"\n",
"Note that we use an agent executor, which is the runtime for an agent. \n",
"\n",
"This is what calls the agent and executes the actions it chooses. \n",
"\n",
"Pseudocode for this runtime is below:\n",
"```\n",
"next_action = agent.get_action(...)\n",
"while next_action != AgentFinish:\n",
" observation = run(next_action)\n",
" next_action = agent.get_action(..., next_action, observation)\n",
"return next_action\n",
"```\n",
"\n",
"While this may seem simple, there are several complexities this runtime handles for you, including:\n",
"\n",
"* Handling cases where the agent selects a non-existent tool\n",
"* Handling cases where the tool errors\n",
"* Handling cases where the agent produces output that cannot be parsed into a tool invocation\n",
"* Logging and observability at all levels (agent decisions, tool calls) either to stdout or LangSmith.\n"
]
},
{
"cell_type": "markdown",
"id": "0b93c7d0",
"metadata": {},
"source": [
"## Memory \n",
"\n",
"### Short-term memory\n",
"\n",
"Of course, `memory` is needed to enable conversation / persistence of information.\n",
"\n",
"LangChain has many options for [short-term memory](/docs/modules/memory/types/), which are frequently used in [chat](/docs/modules/memory/adding_memory.html). \n",
"\n",
"They can be [employed with agents](/docs/modules/memory/agent_with_memory) too.\n",
"\n",
"`ConversationBufferMemory` is a popular choice for short-term memory.\n",
"\n",
"We set `MEMORY_KEY`, which can be referenced by the prompt later.\n",
"\n",
"Now, let's add memory to our agent."
]
},
{
"cell_type": "code",
"execution_count": 23,
"id": "1d291015",
"metadata": {},
"outputs": [],
"source": [
"# Memory \n",
"from langchain.memory import ConversationBufferMemory\n",
"MEMORY_KEY = \"chat_history\"\n",
"memory = ConversationBufferMemory(memory_key=MEMORY_KEY, return_messages=True)\n",
"\n",
"# Prompt w/ placeholder for memory\n",
"from langchain.schema import SystemMessage\n",
"from langchain.agents import OpenAIFunctionsAgent\n",
"from langchain.prompts import MessagesPlaceholder\n",
"system_message = SystemMessage(content=\"You are a search assistant tasked with using Serpapi to answer questions.\")\n",
"prompt = OpenAIFunctionsAgent.create_prompt(\n",
" system_message=system_message,\n",
" extra_prompt_messages=[MessagesPlaceholder(variable_name=MEMORY_KEY)]\n",
")\n",
"\n",
"# Agent\n",
"search_agent_memory = OpenAIFunctionsAgent(llm=llm, tools=tools, prompt=prompt, memory=memory)\n",
"agent_executor_memory = AgentExecutor(agent=search_agent_memory, tools=tools, memory=memory, verbose=False)"
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "b4b2249a",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'As of August 2023, the estimated population of Canada is approximately 38,781,291 people.'"
]
},
"execution_count": 24,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor_memory.run(\"How many people live in Canada as of August, 2023?\")"
]
},
{
"cell_type": "code",
"execution_count": 25,
"id": "4d31b0cf",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'As of August 2023, the largest province in Canada is Ontario, with a population of over 15 million people.'"
]
},
"execution_count": 25,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent_executor_memory.run(\"What is the population of its largest provence as of August, 2023?\")"
]
},
{
"cell_type": "markdown",
"id": "3606c32a",
"metadata": {},
"source": [
"Looking at the [trace](https://smith.langchain.com/public/4425a131-ec90-4aaa-acd8-5b880c7452a3/r), we can what is happening:\n",
"\n",
"* The chat history is passed to the LLMs\n",
"* This gives context to `its` in `What is the population of its largest provence as of August, 2023?`\n",
"* The LLM generates a function call to the search tool\n",
"\n",
"```\n",
"function_call:\n",
" name: Search\n",
" arguments: |-\n",
" {\n",
" \"query\": \"population of largest province in Canada as of August 2023\"\n",
" }\n",
"```\n",
"\n",
"* The search is executed\n",
"* The results frum search are passed back to the LLM for synthesis into an answer\n",
"\n",
"![Image description](/img/oai_function_agent.png)"
]
},
{
"cell_type": "markdown",
"id": "384e37f8",
"metadata": {},
"source": [
"### Long-term memory \n",
"\n",
"Vectorstores are great options for long-term memory."
]
},
{
"cell_type": "code",
"execution_count": 24,
"id": "1489746c",
"metadata": {},
"outputs": [],
"source": [
"import faiss\n",
"from langchain.vectorstores import FAISS\n",
"from langchain.docstore import InMemoryDocstore\n",
"from langchain.embeddings import OpenAIEmbeddings\n",
"embedding_size = 1536\n",
"embeddings_model = OpenAIEmbeddings()\n",
"index = faiss.IndexFlatL2(embedding_size)\n",
"vectorstore = FAISS(embeddings_model.embed_query, index, InMemoryDocstore({}), {})"
]
},
{
"cell_type": "markdown",
"id": "9668ef5d",
"metadata": {},
"source": [
"### Going deeper \n",
"\n",
"* Explore projects using long-term memory, such as [autonomous agents](/docs/use_cases/autonomous_agents/autonomous_agents)."
]
},
{
"cell_type": "markdown",
"id": "43fe2bb3",
"metadata": {},
"source": [
"## Tools \n",
"\n",
"As mentioned above, LangChain has [many tools](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/agents/load_tools.py) for Agents that we can load easily.\n",
"\n",
"We can also define [custom tools](/docs/modules/agents/tools/custom_tools). For example, here is a search tool.\n",
"\n",
"* The `Tool` dataclass wraps functions that accept a single string input and returns a string output.\n",
"* `return_direct` determines whether to return the tool's output directly. \n",
"* Setting this to `True` means that after the tool is called, the `AgentExecutor` will stop looping."
]
},
{
"cell_type": "code",
"execution_count": 29,
"id": "7357e496",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import Tool, tool\n",
"from langchain.utilities import GoogleSearchAPIWrapper\n",
"search = GoogleSearchAPIWrapper()\n",
"search_tool = [\n",
" Tool(\n",
" name=\"Search\",\n",
" func=search.run,\n",
" description=\"useful for when you need to answer questions about current events\",\n",
" return_direct=True,\n",
" )\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "c6ef5bfa",
"metadata": {},
"source": [
"To make it easier to define custom tools, a `@tool` decorator is provided. \n",
"\n",
"This decorator can be used to quickly create a Tool from a simple function."
]
},
{
"cell_type": "code",
"execution_count": 30,
"id": "b6308c69",
"metadata": {},
"outputs": [],
"source": [
"# Tool\n",
"@tool\n",
"def get_word_length(word: str) -> int:\n",
" \"\"\"Returns the length of a word.\"\"\"\n",
" return len(word)\n",
"word_length_tool = [get_word_length]"
]
},
{
"cell_type": "markdown",
"id": "83c104d7",
"metadata": {},
"source": [
"### Going deeper\n",
"\n",
"**Toolkits**\n",
"\n",
"* Toolkits are groups of tools needed to accomplish specific objectives.\n",
"* [Here](/docs/integrations/toolkits/) are > 15 different agent toolkits (e.g., Gmail, Pandas, etc). \n",
"\n",
"Here is a simple way to think about agents vs the various chains covered in other docs:\n",
"\n",
"![Image description](/img/agents_vs_chains.png)"
]
},
{
"cell_type": "markdown",
"id": "5eefe4a0",
"metadata": {},
"source": [
"## Agents\n",
"\n",
"There's a number of [action agent types](docs/modules/agents/agent_types/) available in LangChain.\n",
"\n",
"* [ReAct](/docs/modules/agents/agent_types/react.html): This is the most general purpose action agent using the [ReAct framework](https://arxiv.org/pdf/2205.00445.pdf), which can work with [Docstores](/docs/modules/agents/agent_types/react_docstore.html) or [Multi-tool Inputs](/docs/modules/agents/agent_types/structured_chat.html).\n",
"* [OpenAI functions](/docs/modules/agents/agent_types/openai_functions_agent.html): Designed to work with OpenAI function-calling models.\n",
"* [Conversational](/docs/modules/agents/agent_types/chat_conversation_agent.html): This agent is designed to be used in conversational settings\n",
"* [Self-ask with search](/docs/modules/agents/agent_types/self_ask_with_search.html): Designed to lookup factual answers to questions\n",
"\n",
"### OpenAI Functions agent\n",
"\n",
"As shown in Quickstart, let's continue with [`OpenAI functions` agent](/docs/modules/agents/agent_types/).\n",
"\n",
"This uses OpenAI models, which are fine-tuned to detect when a function should to be called.\n",
"\n",
"They will respond with the inputs that should be passed to the function.\n",
"\n",
"But, we can unpack it, first with a custom prompt:"
]
},
{
"cell_type": "code",
"execution_count": 31,
"id": "1c2deb4a",
"metadata": {},
"outputs": [],
"source": [
"# Memory\n",
"MEMORY_KEY = \"chat_history\"\n",
"memory = ConversationBufferMemory(memory_key=MEMORY_KEY, return_messages=True)\n",
"\n",
"# Prompt\n",
"from langchain.schema import SystemMessage\n",
"from langchain.agents import OpenAIFunctionsAgent\n",
"system_message = SystemMessage(content=\"You are very powerful assistant, but bad at calculating lengths of words.\")\n",
"prompt = OpenAIFunctionsAgent.create_prompt(\n",
" system_message=system_message,\n",
" extra_prompt_messages=[MessagesPlaceholder(variable_name=MEMORY_KEY)]\n",
")"
]
},
{
"cell_type": "markdown",
"id": "ee317a45",
"metadata": {},
"source": [
"Define agent:"
]
},
{
"cell_type": "code",
"execution_count": 32,
"id": "460dab9b",
"metadata": {},
"outputs": [],
"source": [
"# Agent \n",
"from langchain.agents import OpenAIFunctionsAgent\n",
"agent = OpenAIFunctionsAgent(llm=llm, tools=word_length_tool, prompt=prompt)"
]
},
{
"cell_type": "markdown",
"id": "184e6c23",
"metadata": {},
"source": [
"Run agent:"
]
},
{
"cell_type": "code",
"execution_count": 33,
"id": "f4f27d37",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"'There are 5 letters in the word \"educa\".'"
]
},
"execution_count": 33,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"# Run the executer, including short-term memory we created\n",
"agent_executor = AgentExecutor(agent=agent, tools=word_length_tool, memory=memory, verbose=False)\n",
"agent_executor.run(\"how many letters in the word educa?\")"
]
},
{
"cell_type": "markdown",
"id": "e4d9217e",
"metadata": {},
"source": [
"### ReAct agent\n",
"\n",
"[ReAct](https://arxiv.org/abs/2210.03629) agents are another popular framework.\n",
"\n",
"There has been lots of work on [LLM reasoning](https://ai.googleblog.com/2022/05/language-models-perform-reasoning-via.html), such as chain-of-thought prompting.\n",
"\n",
"There also has been work on LLM action-taking to generate obervations, such as [Say-Can](https://say-can.github.io/).\n",
"\n",
"ReAct marries these two ideas:\n",
"\n",
"![Image description](/img/ReAct.png)\n",
" \n",
"It uses a charecteristic `Thought`, `Action`, `Observation` [pattern in the output](https://lilianweng.github.io/posts/2023-06-23-agent/).\n",
" \n",
"We can use `initialize_agent` to create the ReAct agent from a list of available types [here](https://github.com/langchain-ai/langchain/blob/master/libs/langchain/langchain/agents/types.py):\n",
"\n",
"```\n",
"* AgentType.ZERO_SHOT_REACT_DESCRIPTION: ZeroShotAgent\n",
"* AgentType.REACT_DOCSTORE: ReActDocstoreAgent\n",
"* AgentType.SELF_ASK_WITH_SEARCH: SelfAskWithSearchAgent\n",
"* AgentType.CONVERSATIONAL_REACT_DESCRIPTION: ConversationalAgent\n",
"* AgentType.CHAT_ZERO_SHOT_REACT_DESCRIPTION: ChatAgent\n",
"* AgentType.CHAT_CONVERSATIONAL_REACT_DESCRIPTION: ConversationalChatAgent\n",
"* AgentType.STRUCTURED_CHAT_ZERO_SHOT_REACT_DESCRIPTION: StructuredChatAgent\n",
"* AgentType.OPENAI_FUNCTIONS: OpenAIFunctionsAgent\n",
"* AgentType.OPENAI_MULTI_FUNCTIONS: OpenAIMultiFunctionsAgent\n",
"```"
]
},
{
"cell_type": "code",
"execution_count": 41,
"id": "85f033d3",
"metadata": {},
"outputs": [],
"source": [
"from langchain.agents import AgentType\n",
"from langchain.agents import initialize_agent\n",
"MEMORY_KEY = \"chat_history\"\n",
"memory = ConversationBufferMemory(memory_key=MEMORY_KEY, return_messages=True)\n",
"react_agent = initialize_agent(search_tool, llm, agent=AgentType.ZERO_SHOT_REACT_DESCRIPTION, verbose=False, memory=memory)"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "7d05a26c",
"metadata": {},
"outputs": [],
"source": [
"react_agent(\"How many people live in Canada as of August, 2023?\")"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "9b626dc5",
"metadata": {},
"outputs": [],
"source": [
"react_agent(\"What is the population of its largest provence as of August, 2023?\")"
]
},
{
"cell_type": "markdown",
"id": "d4df0638",
"metadata": {},
"source": [
"LangSmith can help us run diagnostics on the ReAct agent:\n",
"\n",
"The [ReAct agent](https://smith.langchain.com/public/3d8d0a15-d73f-44f3-9f81-037f7031c592/r) fails to pass chat history to LLM, gets wrong answer.\n",
" \n",
"The OAI functions agent does and [gets right answer](https://smith.langchain.com/public/4425a131-ec90-4aaa-acd8-5b880c7452a3/r), as shown above.\n",
" \n",
"Also the search tool result for [ReAct](https://smith.langchain.com/public/6473e608-fc9d-47c9-a8a4-2ef7f2801d82/r) is worse than [OAI](https://smith.langchain.com/public/4425a131-ec90-4aaa-acd8-5b880c7452a3/r/26b85fa9-e33a-4028-8650-1714f8b3db96).\n",
"\n",
"Collectivly, this tells us: carefully inspect Agent traces and tool outputs. \n",
"\n",
"As we saw with the [SQL use case](/docs/use_cases/sql), `ReAct agents` can be work very well for specific problems. \n",
"\n",
"But, as shown here, the result is degraded relative to what we see with the OpenAI agent."
]
},
{
"cell_type": "markdown",
"id": "5cde8f9a",
"metadata": {},
"source": [
"### Custom\n",
"\n",
"Let's peel it back even further to define our own action agent.\n",
"\n",
"We can [create a custom agent](/docs/modules/agents/how_to/custom_agent.html) to unpack the central pieces:\n",
"\n",
"* `Tools`: The tools the agent has available to use\n",
"* `Agent`: decides which action to take"
]
},
{
"cell_type": "code",
"execution_count": 34,
"id": "3313f5cd",
"metadata": {},
"outputs": [
{
"data": {
"text/plain": [
"\"The current population of Canada is 38,808,843 as of Tuesday, August 1, 2023, based on Worldometer elaboration of the latest United Nations data 1. Canada 2023\\xa0... Mar 22, 2023 ... Record-high population growth in the year 2022. Canada's population was estimated at 39,566,248 on January 1, 2023, after a record population\\xa0... Jun 19, 2023 ... As of June 16, 2023, there are now 40 million Canadians! This is a historic milestone for Canada and certainly cause for celebration. It is also\\xa0... Jun 28, 2023 ... Canada's population was estimated at 39,858,480 on April 1, 2023, an increase of 292,232 people (+0.7%) from January 1, 2023. The main driver of population growth is immigration, and to a lesser extent, natural growth. Demographics of Canada · Population pyramid of Canada in 2023. May 2, 2023 ... On January 1, 2023, Canada's population was estimated to be 39,566,248, following an unprecedented increase of 1,050,110 people between January\\xa0... Canada ranks 37th by population among countries of the world, comprising about 0.5% of the world's total, with over 40.0 million Canadians as of 2023. The current population of Canada in 2023 is 38,781,291, a 0.85% increase from 2022. The population of Canada in 2022 was 38,454,327, a 0.78% increase from 2021. Whether a given sub-nation is a province or a territory depends upon how its power and authority are derived. Provinces were given their power by the\\xa0... Jun 28, 2023 ... Index to the latest information from the Census of Population. ... 2023. Census in Brief: Multilingualism of Canadian households\\xa0...\""
]
},
"execution_count": 34,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"from typing import List, Tuple, Any, Union\n",
"from langchain.schema import AgentAction, AgentFinish\n",
"from langchain.agents import Tool, AgentExecutor, BaseSingleActionAgent\n",
"\n",
"class FakeAgent(BaseSingleActionAgent):\n",
" \"\"\"Fake Custom Agent.\"\"\"\n",
"\n",
" @property\n",
" def input_keys(self):\n",
" return [\"input\"]\n",
"\n",
" def plan(\n",
" self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any\n",
" ) -> Union[AgentAction, AgentFinish]:\n",
" \"\"\"Given input, decided what to do.\n",
"\n",
" Args:\n",
" intermediate_steps: Steps the LLM has taken to date,\n",
" along with observations\n",
" **kwargs: User inputs.\n",
"\n",
" Returns:\n",
" Action specifying what tool to use.\n",
" \"\"\"\n",
" return AgentAction(tool=\"Search\", tool_input=kwargs[\"input\"], log=\"\")\n",
"\n",
" async def aplan(\n",
" self, intermediate_steps: List[Tuple[AgentAction, str]], **kwargs: Any\n",
" ) -> Union[AgentAction, AgentFinish]:\n",
" \"\"\"Given input, decided what to do.\n",
"\n",
" Args:\n",
" intermediate_steps: Steps the LLM has taken to date,\n",
" along with observations\n",
" **kwargs: User inputs.\n",
"\n",
" Returns:\n",
" Action specifying what tool to use.\n",
" \"\"\"\n",
" return AgentAction(tool=\"Search\", tool_input=kwargs[\"input\"], log=\"\")\n",
" \n",
"fake_agent = FakeAgent()\n",
"fake_agent_executor = AgentExecutor.from_agent_and_tools(agent=fake_agent, \n",
" tools=search_tool, \n",
" verbose=False)\n",
"\n",
"fake_agent_executor.run(\"How many people live in canada as of 2023?\")"
]
},
{
"cell_type": "markdown",
"id": "1335f0c6",
"metadata": {},
"source": [
"## Runtime\n",
"\n",
"The `AgentExecutor` class is the main agent runtime supported by LangChain. \n",
"\n",
"However, there are other, more experimental runtimes for `autonomous_agents`:\n",
" \n",
"* Plan-and-execute Agent\n",
"* Baby AGI\n",
"* Auto GPT\n",
"\n",
"Explore more about:\n",
"\n",
"* [`Simulation agents`](/docs/modules/agents/agent_use_cases/agent_simulations): Designed for role-play often in simulated enviorment (e.g., Generative Agents, CAMEL).\n",
"* [`Autonomous agents`](/docs/modules/agents/agent_use_cases/autonomous_agents): Designed for indepdent execution towards long term goals (e.g., BabyAGI, Auto-GPT).\n",
"\n"
]
}
],
"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.9.16"
}
},
"nbformat": 4,
"nbformat_minor": 5
}

File diff suppressed because one or more lines are too long

@ -249,7 +249,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.9.1"
"version": "3.9.16"
}
},
"nbformat": 4,

@ -245,7 +245,7 @@
"name": "python",
"nbconvert_exporter": "python",
"pygments_lexer": "ipython3",
"version": "3.11.2"
"version": "3.9.16"
}
},
"nbformat": 4,
Loading…
Cancel
Save