Compare commits

...

12 Commits

Author SHA1 Message Date
Harrison Chase
f8bd49021e cr 2023-04-16 14:49:11 -07:00
Harrison Chase
5af1da7b38 cr 2023-04-16 14:03:05 -07:00
Harrison Chase
d3c92ed203 cr 2023-04-16 13:37:08 -07:00
Harrison Chase
e438969ab7 Merge branch 'master' into harrison/tools-refactor 2023-04-16 13:18:44 -07:00
Harrison Chase
db0a9c14cf cr 2023-04-16 09:10:56 -07:00
Harrison Chase
21a1ac36b5 cr 2023-04-16 09:06:00 -07:00
Harrison Chase
57f4309fa8 tools refactor 2023-04-15 18:11:02 -07:00
Harrison Chase
94c83fa5d1 cr 2023-04-15 14:35:16 -07:00
Harrison Chase
961ce77f8d Merge branch 'master' into harrison/autogpt 2023-04-15 13:38:48 -07:00
Harrison Chase
a38c992703 cr 2023-04-15 11:42:41 -07:00
Harrison Chase
3564568b4a cr 2023-04-14 17:45:01 -07:00
Harrison Chase
9860c09fa2 autogpt 2023-04-14 15:31:26 -07:00
12 changed files with 1006 additions and 18 deletions

View File

@@ -0,0 +1,484 @@
{
"cells": [
{
"cell_type": "markdown",
"id": "14f8b67b",
"metadata": {},
"source": [
"# AutoGPT\n",
"\n",
"Implementation of https://github.com/Significant-Gravitas/Auto-GPT but with LangChain primitives (LLMs, PromptTemplates, VectorStores, Embeddings, Tools)"
]
},
{
"cell_type": "markdown",
"id": "192496a7",
"metadata": {},
"source": [
"## Set up tools\n",
"\n",
"We'll set up an AutoGPT with a search tool, and write-file tool, and a read-file tool"
]
},
{
"cell_type": "code",
"execution_count": 1,
"id": "7c2c9b54",
"metadata": {},
"outputs": [],
"source": [
"from langchain.utilities import SerpAPIWrapper\n",
"from langchain.agents import Tool\n",
"from langchain.tools.file_management.write import WriteFileTool\n",
"from langchain.tools.file_management.read import ReadFileTool\n",
"\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. You should ask targeted questions\"\n",
" ),\n",
" WriteFileTool(),\n",
" ReadFileTool(),\n",
"]"
]
},
{
"cell_type": "markdown",
"id": "8e39ee28",
"metadata": {},
"source": [
"## Set up memory\n",
"\n",
"The memory here is used for the agents intermediate steps"
]
},
{
"cell_type": "code",
"execution_count": 3,
"id": "72bc204d",
"metadata": {},
"outputs": [],
"source": [
"from langchain.vectorstores import FAISS\n",
"from langchain.docstore import InMemoryDocstore\n",
"from langchain.embeddings import OpenAIEmbeddings"
]
},
{
"cell_type": "code",
"execution_count": 4,
"id": "1df7b724",
"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": "e40fd657",
"metadata": {},
"source": [
"## Setup model and AutoGPT\n",
"\n",
"Initialize everything! We will use ChatOpenAI model"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "3393bc23",
"metadata": {},
"outputs": [],
"source": [
"from langchain.auto_agents.autogpt.agent import AutoGPT\n",
"from langchain.chat_models import ChatOpenAI"
]
},
{
"cell_type": "code",
"execution_count": 5,
"id": "709c08c2",
"metadata": {},
"outputs": [],
"source": [
"agent = AutoGPT.from_llm_and_tools(\n",
" ai_name=\"Tom\",\n",
" ai_role=\"Assistant\",\n",
" tools=tools,\n",
" llm=ChatOpenAI(temperature=0),\n",
" memory=vectorstore.as_retriever()\n",
")\n",
"# Set verbose to be true\n",
"agent.chain.verbose = True"
]
},
{
"cell_type": "markdown",
"id": "fc9b51ba",
"metadata": {},
"source": [
"## Run an example\n",
"\n",
"Here we will make it write a weather report for SF"
]
},
{
"cell_type": "code",
"execution_count": 7,
"id": "c032b182",
"metadata": {
"scrolled": false
},
"outputs": [
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
"Prompt after formatting:\n",
"\u001b[32;1m\u001b[1;3mSystem: You are Tom, Assistant\n",
"Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications. If you have completed all your tasks, make sure to use the \"finish\" command.\n",
"\n",
"GOALS:\n",
"\n",
"1. write a weather report for SF today\n",
"\n",
"\n",
"Constraints:\n",
"1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.\n",
"2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.\n",
"3. No user assistance\n",
"4. Exclusively use the commands listed in double quotes e.g. \"command name\"\n",
"\n",
"Commands:\n",
"1. search: useful for when you need to answer questions about current events. You should ask targeted questions, args: \"tool_input\": \"\"\n",
"2. write_file: Write file to disk, args: \"file_path\": \"name of file\", \"text\": \"text to write to file\"\n",
"3. read_file: Read file from disk, args: \"file_path\": \"name of file\"\n",
"4. finish: use this to signal that you have finished all your objectives, args: \"response\": \"final response to let people know you have finished your objectives\"\n",
"\n",
"Resources:\n",
"1. Internet access for searches and information gathering.\n",
"2. Long Term memory management.\n",
"3. GPT-3.5 powered Agents for delegation of simple tasks.\n",
"4. File output.\n",
"\n",
"Performance Evaluation:\n",
"1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\n",
"2. Constructively self-criticize your big-picture behavior constantly.\n",
"3. Reflect on past decisions and strategies to refine your approach.\n",
"4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.\n",
"\n",
"You should only respond in JSON format as described below \n",
"Response Format: \n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"thought\",\n",
" \"reasoning\": \"reasoning\",\n",
" \"plan\": \"- short bulleted\\n- list that conveys\\n- long-term plan\",\n",
" \"criticism\": \"constructive self-criticism\",\n",
" \"speak\": \"thoughts summary to say to user\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"command name\",\n",
" \"args\": {\n",
" \"arg name\": \"value\"\n",
" }\n",
" }\n",
"} \n",
"Ensure the response can be parsed by Python json.loads\n",
"System: The current time and date is Sun Apr 16 14:07:39 2023\n",
"System: This reminds you of these events from your past:\n",
"[]\n",
"\n",
"\n",
"Human: Determine which next command to use, and respond using the format specified above:\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"I will start by writing a weather report for San Francisco today. I will use the 'search' command to find the current weather conditions.\",\n",
" \"reasoning\": \"I need to gather information about the current weather conditions in San Francisco to write an accurate weather report.\",\n",
" \"plan\": \"- Use the 'search' command to find the current weather conditions in San Francisco\\n- Write a weather report based on the information gathered\",\n",
" \"criticism\": \"I need to make sure that the information I gather is accurate and up-to-date.\",\n",
" \"speak\": \"I will use the 'search' command to find the current weather conditions in San Francisco.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"search\",\n",
" \"args\": {\n",
" \"tool_input\": \"current weather conditions in San Francisco\"\n",
" }\n",
" }\n",
"}\n",
"\n",
"\n",
"\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
"Prompt after formatting:\n",
"\u001b[32;1m\u001b[1;3mSystem: You are Tom, Assistant\n",
"Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications. If you have completed all your tasks, make sure to use the \"finish\" command.\n",
"\n",
"GOALS:\n",
"\n",
"1. write a weather report for SF today\n",
"\n",
"\n",
"Constraints:\n",
"1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.\n",
"2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.\n",
"3. No user assistance\n",
"4. Exclusively use the commands listed in double quotes e.g. \"command name\"\n",
"\n",
"Commands:\n",
"1. search: useful for when you need to answer questions about current events. You should ask targeted questions, args: \"tool_input\": \"\"\n",
"2. write_file: Write file to disk, args: \"file_path\": \"name of file\", \"text\": \"text to write to file\"\n",
"3. read_file: Read file from disk, args: \"file_path\": \"name of file\"\n",
"4. finish: use this to signal that you have finished all your objectives, args: \"response\": \"final response to let people know you have finished your objectives\"\n",
"\n",
"Resources:\n",
"1. Internet access for searches and information gathering.\n",
"2. Long Term memory management.\n",
"3. GPT-3.5 powered Agents for delegation of simple tasks.\n",
"4. File output.\n",
"\n",
"Performance Evaluation:\n",
"1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\n",
"2. Constructively self-criticize your big-picture behavior constantly.\n",
"3. Reflect on past decisions and strategies to refine your approach.\n",
"4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.\n",
"\n",
"You should only respond in JSON format as described below \n",
"Response Format: \n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"thought\",\n",
" \"reasoning\": \"reasoning\",\n",
" \"plan\": \"- short bulleted\\n- list that conveys\\n- long-term plan\",\n",
" \"criticism\": \"constructive self-criticism\",\n",
" \"speak\": \"thoughts summary to say to user\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"command name\",\n",
" \"args\": {\n",
" \"arg name\": \"value\"\n",
" }\n",
" }\n",
"} \n",
"Ensure the response can be parsed by Python json.loads\n",
"System: The current time and date is Sun Apr 16 14:07:48 2023\n",
"System: This reminds you of these events from your past:\n",
"['Assistant Reply: {\\n \"thoughts\": {\\n \"text\": \"I will start by writing a weather report for San Francisco today. I will use the \\'search\\' command to find the current weather conditions.\",\\n \"reasoning\": \"I need to gather information about the current weather conditions in San Francisco to write an accurate weather report.\",\\n \"plan\": \"- Use the \\'search\\' command to find the current weather conditions in San Francisco\\\\n- Write a weather report based on the information gathered\",\\n \"criticism\": \"I need to make sure that the information I gather is accurate and up-to-date.\",\\n \"speak\": \"I will use the \\'search\\' command to find the current weather conditions in San Francisco.\"\\n },\\n \"command\": {\\n \"name\": \"search\",\\n \"args\": {\\n \"tool_input\": \"current weather conditions in San Francisco\"\\n }\\n }\\n} \\nResult: Command search returned: Cloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION. ']\n",
"\n",
"\n",
"Human: Determine which next command to use, and respond using the format specified above:\n",
"AI: {\n",
" \"thoughts\": {\n",
" \"text\": \"I will start by writing a weather report for San Francisco today. I will use the 'search' command to find the current weather conditions.\",\n",
" \"reasoning\": \"I need to gather information about the current weather conditions in San Francisco to write an accurate weather report.\",\n",
" \"plan\": \"- Use the 'search' command to find the current weather conditions in San Francisco\\n- Write a weather report based on the information gathered\",\n",
" \"criticism\": \"I need to make sure that the information I gather is accurate and up-to-date.\",\n",
" \"speak\": \"I will use the 'search' command to find the current weather conditions in San Francisco.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"search\",\n",
" \"args\": {\n",
" \"tool_input\": \"current weather conditions in San Francisco\"\n",
" }\n",
" }\n",
"}\n",
"System: Command search returned: Cloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION.\n",
"Human: Determine which next command to use, and respond using the format specified above:\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"I will now write a weather report for San Francisco based on the information gathered. I will use the 'write_file' command to save the report to a file.\",\n",
" \"reasoning\": \"I need to write a weather report based on the information gathered from the 'search' command.\",\n",
" \"plan\": \"- Use the 'write_file' command to save the weather report to a file\",\n",
" \"criticism\": \"I need to make sure that the weather report is accurate and well-written.\",\n",
" \"speak\": \"I will use the 'write_file' command to save the weather report to a file.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"write_file\",\n",
" \"args\": {\n",
" \"file_path\": \"weather_report_sf.txt\",\n",
" \"text\": \"San Francisco Weather Report:\\n\\nCloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION.\"\n",
" }\n",
" }\n",
"}\n"
]
},
{
"name": "stdout",
"output_type": "stream",
"text": [
"\n",
"\n",
"\u001b[1m> Entering new LLMChain chain...\u001b[0m\n",
"Prompt after formatting:\n",
"\u001b[32;1m\u001b[1;3mSystem: You are Tom, Assistant\n",
"Your decisions must always be made independently without seeking user assistance. Play to your strengths as an LLM and pursue simple strategies with no legal complications. If you have completed all your tasks, make sure to use the \"finish\" command.\n",
"\n",
"GOALS:\n",
"\n",
"1. write a weather report for SF today\n",
"\n",
"\n",
"Constraints:\n",
"1. ~4000 word limit for short term memory. Your short term memory is short, so immediately save important information to files.\n",
"2. If you are unsure how you previously did something or want to recall past events, thinking about similar events will help you remember.\n",
"3. No user assistance\n",
"4. Exclusively use the commands listed in double quotes e.g. \"command name\"\n",
"\n",
"Commands:\n",
"1. search: useful for when you need to answer questions about current events. You should ask targeted questions, args: \"tool_input\": \"\"\n",
"2. write_file: Write file to disk, args: \"file_path\": \"name of file\", \"text\": \"text to write to file\"\n",
"3. read_file: Read file from disk, args: \"file_path\": \"name of file\"\n",
"4. finish: use this to signal that you have finished all your objectives, args: \"response\": \"final response to let people know you have finished your objectives\"\n",
"\n",
"Resources:\n",
"1. Internet access for searches and information gathering.\n",
"2. Long Term memory management.\n",
"3. GPT-3.5 powered Agents for delegation of simple tasks.\n",
"4. File output.\n",
"\n",
"Performance Evaluation:\n",
"1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities.\n",
"2. Constructively self-criticize your big-picture behavior constantly.\n",
"3. Reflect on past decisions and strategies to refine your approach.\n",
"4. Every command has a cost, so be smart and efficient. Aim to complete tasks in the least number of steps.\n",
"\n",
"You should only respond in JSON format as described below \n",
"Response Format: \n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"thought\",\n",
" \"reasoning\": \"reasoning\",\n",
" \"plan\": \"- short bulleted\\n- list that conveys\\n- long-term plan\",\n",
" \"criticism\": \"constructive self-criticism\",\n",
" \"speak\": \"thoughts summary to say to user\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"command name\",\n",
" \"args\": {\n",
" \"arg name\": \"value\"\n",
" }\n",
" }\n",
"} \n",
"Ensure the response can be parsed by Python json.loads\n",
"System: The current time and date is Sun Apr 16 14:07:57 2023\n",
"System: This reminds you of these events from your past:\n",
"['Assistant Reply: {\\n \"thoughts\": {\\n \"text\": \"I will now write a weather report for San Francisco based on the information gathered. I will use the \\'write_file\\' command to save the report to a file.\",\\n \"reasoning\": \"I need to write a weather report based on the information gathered from the \\'search\\' command.\",\\n \"plan\": \"- Use the \\'write_file\\' command to save the weather report to a file\",\\n \"criticism\": \"I need to make sure that the weather report is accurate and well-written.\",\\n \"speak\": \"I will use the \\'write_file\\' command to save the weather report to a file.\"\\n },\\n \"command\": {\\n \"name\": \"write_file\",\\n \"args\": {\\n \"file_path\": \"weather_report_sf.txt\",\\n \"text\": \"San Francisco Weather Report:\\\\n\\\\nCloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION.\"\\n }\\n }\\n} \\nResult: Command write_file returned: File written to successfully. ', 'Assistant Reply: {\\n \"thoughts\": {\\n \"text\": \"I will start by writing a weather report for San Francisco today. I will use the \\'search\\' command to find the current weather conditions.\",\\n \"reasoning\": \"I need to gather information about the current weather conditions in San Francisco to write an accurate weather report.\",\\n \"plan\": \"- Use the \\'search\\' command to find the current weather conditions in San Francisco\\\\n- Write a weather report based on the information gathered\",\\n \"criticism\": \"I need to make sure that the information I gather is accurate and up-to-date.\",\\n \"speak\": \"I will use the \\'search\\' command to find the current weather conditions in San Francisco.\"\\n },\\n \"command\": {\\n \"name\": \"search\",\\n \"args\": {\\n \"tool_input\": \"current weather conditions in San Francisco\"\\n }\\n }\\n} \\nResult: Command search returned: Cloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION. ']\n",
"\n",
"\n",
"Human: Determine which next command to use, and respond using the format specified above:\n",
"AI: {\n",
" \"thoughts\": {\n",
" \"text\": \"I will start by writing a weather report for San Francisco today. I will use the 'search' command to find the current weather conditions.\",\n",
" \"reasoning\": \"I need to gather information about the current weather conditions in San Francisco to write an accurate weather report.\",\n",
" \"plan\": \"- Use the 'search' command to find the current weather conditions in San Francisco\\n- Write a weather report based on the information gathered\",\n",
" \"criticism\": \"I need to make sure that the information I gather is accurate and up-to-date.\",\n",
" \"speak\": \"I will use the 'search' command to find the current weather conditions in San Francisco.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"search\",\n",
" \"args\": {\n",
" \"tool_input\": \"current weather conditions in San Francisco\"\n",
" }\n",
" }\n",
"}\n",
"System: Command search returned: Cloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION.\n",
"Human: Determine which next command to use, and respond using the format specified above:\n",
"AI: {\n",
" \"thoughts\": {\n",
" \"text\": \"I will now write a weather report for San Francisco based on the information gathered. I will use the 'write_file' command to save the report to a file.\",\n",
" \"reasoning\": \"I need to write a weather report based on the information gathered from the 'search' command.\",\n",
" \"plan\": \"- Use the 'write_file' command to save the weather report to a file\",\n",
" \"criticism\": \"I need to make sure that the weather report is accurate and well-written.\",\n",
" \"speak\": \"I will use the 'write_file' command to save the weather report to a file.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"write_file\",\n",
" \"args\": {\n",
" \"file_path\": \"weather_report_sf.txt\",\n",
" \"text\": \"San Francisco Weather Report:\\n\\nCloudy skies early, followed by partial clearing. High 56F. Winds W at 10 to 20 mph. PRECIPITATION.\"\n",
" }\n",
" }\n",
"}\n",
"System: Command write_file returned: File written to successfully.\n",
"Human: Determine which next command to use, and respond using the format specified above:\u001b[0m\n",
"\n",
"\u001b[1m> Finished chain.\u001b[0m\n",
"{\n",
" \"thoughts\": {\n",
" \"text\": \"I have completed all my tasks. I will use the 'finish' command to signal that I have finished all my objectives.\",\n",
" \"reasoning\": \"I have completed the task of writing a weather report for San Francisco and there are no other tasks assigned to me.\",\n",
" \"plan\": \"- Use the 'finish' command to signal that I have finished all my objectives\",\n",
" \"criticism\": \"I need to make sure that I have completed all my tasks before using the 'finish' command.\",\n",
" \"speak\": \"I will use the 'finish' command to signal that I have finished all my objectives.\"\n",
" },\n",
" \"command\": {\n",
" \"name\": \"finish\",\n",
" \"args\": {\n",
" \"response\": \"I have completed all my objectives.\"\n",
" }\n",
" }\n",
"}\n"
]
},
{
"data": {
"text/plain": [
"'I have completed all my objectives.'"
]
},
"execution_count": 7,
"metadata": {},
"output_type": "execute_result"
}
],
"source": [
"agent.run([\"write a weather report for SF today\"])"
]
},
{
"cell_type": "code",
"execution_count": null,
"id": "aa264f26",
"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
}

View File

@@ -16,6 +16,7 @@ from langchain.agents.agent_toolkits import (
create_vectorstore_agent,
create_vectorstore_router_agent,
)
from langchain.agents.autogpt.agent import AutoGPT
from langchain.agents.agent_types import AgentType
from langchain.agents.conversational.base import ConversationalAgent
from langchain.agents.conversational_chat.base import ConversationalChatAgent
@@ -28,6 +29,7 @@ from langchain.agents.self_ask_with_search.base import SelfAskWithSearchChain
from langchain.agents.tools import Tool, tool
__all__ = [
"AutoGPT",
"MRKLChain",
"SelfAskWithSearchChain",
"ReActChain",

View File

View File

@@ -0,0 +1,110 @@
from __future__ import annotations
from typing import List, Optional
from langchain.agents.autogpt.output_parser import (
AutoGPTOutputParser,
BaseAutoGPTOutputParser,
)
from langchain.agents.autogpt.prompt import AutoGPTPrompt
from langchain.agents.autogpt.prompt_generator import FINISH_NAME
from langchain.chains.llm import LLMChain
from langchain.chat_models.base import BaseChatModel
from langchain.schema import (
AIMessage,
BaseMessage,
Document,
HumanMessage,
SystemMessage,
)
from langchain.tools.base import BaseTool
from langchain.vectorstores.base import VectorStoreRetriever
class AutoGPT:
"""Agent class for interacting with Auto-GPT."""
def __init__(
self,
ai_name: str,
memory: VectorStoreRetriever,
chain: LLMChain,
output_parser: BaseAutoGPTOutputParser,
tools: List[BaseTool],
):
self.ai_name = ai_name
self.memory = memory
self.full_message_history: List[BaseMessage] = []
self.next_action_count = 0
self.chain = chain
self.output_parser = output_parser
self.tools = tools
@classmethod
def from_llm_and_tools(
cls,
ai_name: str,
ai_role: str,
memory: VectorStoreRetriever,
tools: List[BaseTool],
llm: BaseChatModel,
output_parser: Optional[BaseAutoGPTOutputParser] = None,
) -> AutoGPT:
prompt = AutoGPTPrompt(
ai_name=ai_name,
ai_role=ai_role,
tools=tools,
input_variables=["memory", "messages", "goals", "user_input"],
token_counter=llm.get_num_tokens,
)
chain = LLMChain(llm=llm, prompt=prompt)
return cls(
ai_name, memory, chain, output_parser or AutoGPTOutputParser(), tools
)
def run(self, goals: List[str]) -> str:
user_input = (
"Determine which next command to use, "
"and respond using the format specified above:"
)
# Interaction Loop
loop_count = 0
while True:
# Discontinue if continuous limit is reached
loop_count += 1
# Send message to AI, get response
assistant_reply = self.chain.run(
goals=goals,
messages=self.full_message_history,
memory=self.memory,
user_input=user_input,
)
# Print Assistant thoughts
print(assistant_reply)
self.full_message_history.append(HumanMessage(content=user_input))
self.full_message_history.append(AIMessage(content=assistant_reply))
# Get command name and arguments
action = self.output_parser.parse(assistant_reply)
tools = {t.name: t for t in self.tools}
if action.name == FINISH_NAME:
return action.args["response"]
if action.name in tools:
tool = tools[action.name]
observation = tool.run(action.args)
result = f"Command {tool.name} returned: {observation}"
else:
result = (
f"Unknown command '{action.name}'. "
f"Please refer to the 'COMMANDS' list for available "
f"commands and only respond in the specified JSON format."
)
memory_to_add = (
f"Assistant Reply: {assistant_reply} " f"\nResult: {result} "
)
self.memory.add_documents([Document(page_content=memory_to_add)])
self.full_message_history.append(SystemMessage(content=result))

View File

@@ -0,0 +1,30 @@
from typing import Any, Dict, List
from pydantic import Field
from langchain.memory.chat_memory import BaseChatMemory, get_prompt_input_key
from langchain.vectorstores.base import VectorStoreRetriever
class AutoGPTMemory(BaseChatMemory):
retriever: VectorStoreRetriever = Field(exclude=True)
"""VectorStoreRetriever object to connect to."""
@property
def memory_variables(self) -> List[str]:
return ["chat_history", "relevant_context"]
def _get_prompt_input_key(self, inputs: Dict[str, Any]) -> str:
"""Get the input key for the prompt."""
if self.input_key is None:
return get_prompt_input_key(inputs, self.memory_variables)
return self.input_key
def load_memory_variables(self, inputs: Dict[str, Any]) -> Dict[str, Any]:
input_key = self._get_prompt_input_key(inputs)
query = inputs[input_key]
docs = self.retriever.get_relevant_documents(query)
return {
"chat_history": self.chat_memory.messages[-10:],
"relevant_context": docs,
}

View File

@@ -0,0 +1,25 @@
import json
from abc import abstractmethod
from typing import Dict, NamedTuple
from langchain.schema import BaseOutputParser
class AutoGPTAction(NamedTuple):
name: str
args: Dict
class BaseAutoGPTOutputParser(BaseOutputParser):
@abstractmethod
def parse(self, text: str) -> AutoGPTAction:
"""Return AutoGPTAction"""
class AutoGPTOutputParser(BaseAutoGPTOutputParser):
def parse(self, text: str) -> AutoGPTAction:
parsed = json.loads(text)
return AutoGPTAction(
name=parsed["command"]["name"],
args=parsed["command"]["args"],
)

View File

@@ -0,0 +1,75 @@
import time
from typing import Any, Callable, List
from pydantic import BaseModel
from langchain.agents.autogpt.prompt_generator import get_prompt
from langchain.prompts.chat import (
BaseChatPromptTemplate,
)
from langchain.schema import BaseMessage, HumanMessage, SystemMessage
from langchain.tools.base import BaseTool
from langchain.vectorstores.base import VectorStoreRetriever
class AutoGPTPrompt(BaseChatPromptTemplate, BaseModel):
ai_name: str
ai_role: str
tools: List[BaseTool]
token_counter: Callable[[str], int]
send_token_limit: int = 4196
def construct_full_prompt(self, goals: List[str]) -> str:
prompt_start = """Your decisions must always be made independently
without seeking user assistance. Play to your strengths
as an LLM and pursue simple strategies with no legal complications.
If you have completed all your tasks,
make sure to use the "finish" command."""
# Construct full prompt
full_prompt = (
f"You are {self.ai_name}, {self.ai_role}\n{prompt_start}\n\nGOALS:\n\n"
)
for i, goal in enumerate(goals):
full_prompt += f"{i+1}. {goal}\n"
full_prompt += f"\n\n{get_prompt(self.tools)}"
return full_prompt
def format_messages(self, **kwargs: Any) -> List[BaseMessage]:
base_prompt = SystemMessage(content=self.construct_full_prompt(kwargs["goals"]))
time_prompt = SystemMessage(
content=f"The current time and date is {time.strftime('%c')}"
)
used_tokens = self.token_counter(base_prompt.content) + self.token_counter(
time_prompt.content
)
memory: VectorStoreRetriever = kwargs["memory"]
previous_messages = kwargs["messages"]
relevant_docs = memory.get_relevant_documents(str(previous_messages[-10:]))
relevant_memory = [d.page_content for d in relevant_docs]
relevant_memory_tokens = sum(
[self.token_counter(doc) for doc in relevant_memory]
)
while used_tokens + relevant_memory_tokens > 2500:
relevant_memory = relevant_memory[:-1]
relevant_memory_tokens = sum(
[self.token_counter(doc) for doc in relevant_memory]
)
content_format = (
f"This reminds you of these events "
f"from your past:\n{relevant_memory}\n\n"
)
memory_message = SystemMessage(content=content_format)
used_tokens += len(memory_message.content)
historical_messages: List[BaseMessage] = []
for message in previous_messages[-10:][::-1]:
message_tokens = self.token_counter(message.content)
if used_tokens + message_tokens > self.send_token_limit - 1000:
break
historical_messages = [message] + historical_messages
input_message = HumanMessage(content=kwargs["user_input"])
messages: List[BaseMessage] = [base_prompt, time_prompt, memory_message]
messages += historical_messages
messages.append(input_message)
return messages

View File

@@ -0,0 +1,187 @@
import json
from typing import List
from langchain.tools.base import BaseTool
FINISH_NAME = "finish"
class PromptGenerator:
"""A class for generating custom prompt strings.
Does this based on constraints, commands, resources, and performance evaluations.
"""
def __init__(self) -> None:
"""Initialize the PromptGenerator object.
Starts with empty lists of constraints, commands, resources,
and performance evaluations.
"""
self.constraints: List[str] = []
self.commands: List[BaseTool] = []
self.resources: List[str] = []
self.performance_evaluation: List[str] = []
self.response_format = {
"thoughts": {
"text": "thought",
"reasoning": "reasoning",
"plan": "- short bulleted\n- list that conveys\n- long-term plan",
"criticism": "constructive self-criticism",
"speak": "thoughts summary to say to user",
},
"command": {"name": "command name", "args": {"arg name": "value"}},
}
def add_constraint(self, constraint: str) -> None:
"""
Add a constraint to the constraints list.
Args:
constraint (str): The constraint to be added.
"""
self.constraints.append(constraint)
def add_tool(self, tool: BaseTool) -> None:
self.commands.append(tool)
def _generate_command_string(self, tool: BaseTool) -> str:
args_string = ", ".join(
f'"{arg.name}": "{arg.description}"' for arg in tool.args
)
return f"{tool.name}: {tool.description}, args: {args_string}"
def add_resource(self, resource: str) -> None:
"""
Add a resource to the resources list.
Args:
resource (str): The resource to be added.
"""
self.resources.append(resource)
def add_performance_evaluation(self, evaluation: str) -> None:
"""
Add a performance evaluation item to the performance_evaluation list.
Args:
evaluation (str): The evaluation item to be added.
"""
self.performance_evaluation.append(evaluation)
def _generate_numbered_list(self, items: list, item_type: str = "list") -> str:
"""
Generate a numbered list from given items based on the item_type.
Args:
items (list): A list of items to be numbered.
item_type (str, optional): The type of items in the list.
Defaults to 'list'.
Returns:
str: The formatted numbered list.
"""
if item_type == "command":
command_strings = [
f"{i + 1}. {self._generate_command_string(item)}"
for i, item in enumerate(items)
]
finish_description = (
"use this to signal that you have finished all your objectives"
)
finish_args = (
'"response": "final response to let '
'people know you have finished your objectives"'
)
finish_string = (
f"{len(items) + 1}. {FINISH_NAME}: "
f"{finish_description}, args: {finish_args}"
)
return "\n".join(command_strings + [finish_string])
else:
return "\n".join(f"{i+1}. {item}" for i, item in enumerate(items))
def generate_prompt_string(self) -> str:
"""Generate a prompt string.
Returns:
str: The generated prompt string.
"""
formatted_response_format = json.dumps(self.response_format, indent=4)
prompt_string = (
f"Constraints:\n{self._generate_numbered_list(self.constraints)}\n\n"
f"Commands:\n"
f"{self._generate_numbered_list(self.commands, item_type='command')}\n\n"
f"Resources:\n{self._generate_numbered_list(self.resources)}\n\n"
f"Performance Evaluation:\n"
f"{self._generate_numbered_list(self.performance_evaluation)}\n\n"
f"You should only respond in JSON format as described below "
f"\nResponse Format: \n{formatted_response_format} "
f"\nEnsure the response can be parsed by Python json.loads"
)
return prompt_string
def get_prompt(tools: List[BaseTool]) -> str:
"""This function generates a prompt string.
It includes various constraints, commands, resources, and performance evaluations.
Returns:
str: The generated prompt string.
"""
# Initialize the PromptGenerator object
prompt_generator = PromptGenerator()
# Add constraints to the PromptGenerator object
prompt_generator.add_constraint(
"~4000 word limit for short term memory. "
"Your short term memory is short, "
"so immediately save important information to files."
)
prompt_generator.add_constraint(
"If you are unsure how you previously did something "
"or want to recall past events, "
"thinking about similar events will help you remember."
)
prompt_generator.add_constraint("No user assistance")
prompt_generator.add_constraint(
'Exclusively use the commands listed in double quotes e.g. "command name"'
)
# Add commands to the PromptGenerator object
for tool in tools:
prompt_generator.add_tool(tool)
# Add resources to the PromptGenerator object
prompt_generator.add_resource(
"Internet access for searches and information gathering."
)
prompt_generator.add_resource("Long Term memory management.")
prompt_generator.add_resource(
"GPT-3.5 powered Agents for delegation of simple tasks."
)
prompt_generator.add_resource("File output.")
# Add performance evaluations to the PromptGenerator object
prompt_generator.add_performance_evaluation(
"Continuously review and analyze your actions "
"to ensure you are performing to the best of your abilities."
)
prompt_generator.add_performance_evaluation(
"Constructively self-criticize your big-picture behavior constantly."
)
prompt_generator.add_performance_evaluation(
"Reflect on past decisions and strategies to refine your approach."
)
prompt_generator.add_performance_evaluation(
"Every command has a cost, so be smart and efficient. "
"Aim to complete tasks in the least number of steps."
)
# Generate the prompt string
prompt_string = prompt_generator.generate_prompt_string()
return prompt_string

View File

@@ -1,7 +1,8 @@
"""Base implementation for tools or skills."""
from abc import abstractmethod
from typing import Any, Optional
import inspect
from abc import ABC, abstractmethod
from typing import Any, Dict, List, Optional, Union
from pydantic import BaseModel, Extra, Field, validator
@@ -9,11 +10,15 @@ from langchain.callbacks import get_callback_manager
from langchain.callbacks.base import BaseCallbackManager
class BaseTool(BaseModel):
"""Class responsible for defining a tool or skill for an LLM."""
class ArgInfo(BaseModel):
name: str
description: str
class BaseTool(ABC, BaseModel):
name: str
description: str
tool_args: Optional[List[ArgInfo]] = None
return_direct: bool = False
verbose: bool = False
callback_manager: BaseCallbackManager = Field(default_factory=get_callback_manager)
@@ -24,6 +29,15 @@ class BaseTool(BaseModel):
extra = Extra.forbid
arbitrary_types_allowed = True
@property
def args(self) -> List[ArgInfo]:
if self.tool_args is None:
# Get the name expected in the run function
var_names = inspect.signature(self._run).parameters.keys()
return [ArgInfo(name=name, description="") for name in var_names]
else:
return self.tool_args
@validator("callback_manager", pre=True, always=True)
def set_callback_manager(
cls, callback_manager: Optional[BaseCallbackManager]
@@ -35,39 +49,42 @@ class BaseTool(BaseModel):
return callback_manager or get_callback_manager()
@abstractmethod
def _run(self, tool_input: str) -> str:
def _run(self, *args: Any, **kwargs: Any) -> str:
"""Use the tool."""
@abstractmethod
async def _arun(self, tool_input: str) -> str:
async def _arun(self, *args: Any, **kwargs: Any) -> str:
"""Use the tool asynchronously."""
def __call__(self, tool_input: str) -> str:
"""Make tools callable with str input."""
return self.run(tool_input)
def run(
self,
tool_input: str,
tool_input: Union[str, Dict],
verbose: Optional[bool] = None,
start_color: Optional[str] = "green",
color: Optional[str] = "green",
**kwargs: Any
) -> str:
"""Run the tool."""
if isinstance(tool_input, str):
if len(self.args) > 1:
raise ValueError("Cannot pass in a string when > 1 argument expected")
key = self.args[0].name
run_input = {key: tool_input}
else:
run_input = tool_input
if not self.verbose and verbose is not None:
verbose_ = verbose
else:
verbose_ = self.verbose
self.callback_manager.on_tool_start(
{"name": self.name, "description": self.description},
tool_input,
str(run_input),
verbose=verbose_,
color=start_color,
**kwargs,
)
try:
observation = self._run(tool_input)
observation = self._run(**run_input)
except (Exception, KeyboardInterrupt) as e:
self.callback_manager.on_tool_error(e, verbose=verbose_)
raise e
@@ -78,13 +95,20 @@ class BaseTool(BaseModel):
async def arun(
self,
tool_input: str,
tool_input: Union[str, Dict],
verbose: Optional[bool] = None,
start_color: Optional[str] = "green",
color: Optional[str] = "green",
**kwargs: Any
) -> str:
"""Run the tool asynchronously."""
if isinstance(tool_input, str):
if len(self.args) > 1:
raise ValueError("Cannot pass in a string when > 1 argument expected")
key = self.args[0].name
run_input = {key: tool_input}
else:
run_input = tool_input
if not self.verbose and verbose is not None:
verbose_ = verbose
else:
@@ -92,7 +116,7 @@ class BaseTool(BaseModel):
if self.callback_manager.is_async:
await self.callback_manager.on_tool_start(
{"name": self.name, "description": self.description},
tool_input,
str(run_input),
verbose=verbose_,
color=start_color,
**kwargs,
@@ -100,14 +124,14 @@ class BaseTool(BaseModel):
else:
self.callback_manager.on_tool_start(
{"name": self.name, "description": self.description},
tool_input,
str(run_input),
verbose=verbose_,
color=start_color,
**kwargs,
)
try:
# We then call the tool on the tool input to get an observation
observation = await self._arun(tool_input)
observation = await self._arun(**run_input)
except (Exception, KeyboardInterrupt) as e:
if self.callback_manager.is_async:
await self.callback_manager.on_tool_error(e, verbose=verbose_)
@@ -123,3 +147,7 @@ class BaseTool(BaseModel):
observation, verbose=verbose_, color=color, name=self.name, **kwargs
)
return observation
def __call__(self, tool_input: str) -> str:
"""Make tool callable."""
return self.run(tool_input)

View File

@@ -0,0 +1,20 @@
from typing import List
from langchain.tools.base import ArgInfo, BaseTool
class ReadFileTool(BaseTool):
name: str = "read_file"
tool_args: List[ArgInfo] = [ArgInfo(name="file_path", description="name of file")]
description: str = "Read file from disk"
def _run(self, file_path: str) -> str:
try:
with open(file_path, "r", encoding="utf-8") as f:
content = f.read()
return content
except Exception as e:
return "Error: " + str(e)
async def _arun(self, tool_input: str) -> str:
raise NotImplementedError

View File

@@ -0,0 +1,27 @@
import os
from typing import List
from langchain.tools.base import ArgInfo, BaseTool
class WriteFileTool(BaseTool):
name: str = "write_file"
tool_args: List[ArgInfo] = [
ArgInfo(name="file_path", description="name of file"),
ArgInfo(name="text", description="text to write to file"),
]
description: str = "Write file to disk"
def _run(self, file_path: str, text: str) -> str:
try:
directory = os.path.dirname(file_path)
if not os.path.exists(directory) and directory:
os.makedirs(directory)
with open(file_path, "w", encoding="utf-8") as f:
f.write(text)
return "File written to successfully."
except Exception as e:
return "Error: " + str(e)
async def _arun(self, tool_input: str) -> str:
raise NotImplementedError