diff --git a/superagi/agent/agent_prompt_builder.py b/superagi/agent/agent_prompt_builder.py index 70dd5c79d..756fdfec4 100644 --- a/superagi/agent/agent_prompt_builder.py +++ b/superagi/agent/agent_prompt_builder.py @@ -3,7 +3,9 @@ from pydantic.types import List +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.tools.base_tool import BaseTool FINISH_NAME = "finish" @@ -71,35 +73,7 @@ def get_super_agi_single_prompt(cls): } formatted_response_format = json.dumps(response_format, indent=4) - super_agi_prompt = """You are SuperAGI an AI assistant to solve complex problems. 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 or reached end state, make sure to use the "finish" tool. - - GOALS: - {goals} - - {instructions} - - CONSTRAINTS: - {constraints} - - TOOLS: - {tools} - - PERFORMANCE EVALUATION: - 1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. - 2. Use instruction to decide the flow of execution and decide the next steps for achieving the task. - 2. Constructively self-criticize your big-picture behavior constantly. - 3. Reflect on past decisions and strategies to refine your approach. - 4. Every tool has a cost, so be smart and efficient. - 5. Aim to complete tasks in the least number of steps. - - I should only respond in JSON format as described below. - Response Format: - {response_format} - - Ensure the response can be parsed by Python json.loads. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "superagi.txt") super_agi_prompt = AgentPromptBuilder.clean_prompt(super_agi_prompt).replace("{response_format}", formatted_response_format) @@ -107,22 +81,7 @@ def get_super_agi_single_prompt(cls): @classmethod def start_task_based(cls): - super_agi_prompt = """You are a task-generating AI known as SuperAGI. You are not a part of any system or device. Your role is to understand the goals presented to you, identify important components, Go through the instruction provided by the user and construct a thorough execution plan. - - GOALS: - {goals} - - {task_instructions} - - Construct a sequence of actions, not exceeding 3 steps, to achieve this goal. - - Submit your response as a formatted ARRAY of strings, suitable for utilization with JSON.parse(). - - Example: ["{{TASK-1}}", "{{TASK-2}}"]. - - - - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "initialize_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions"]} # super_agi_prompt = super_agi_prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(goals)) @@ -132,35 +91,7 @@ def analyse_task(cls): constraints = [ 'Exclusively use the tools listed in double quotes e.g. "tool name"' ] - super_agi_prompt = """ - High level goal: - {goals} - - {task_instructions} - - Your Current Task: `{current_task}` - - Task History: - `{task_history}` - - Based on this, your job is to understand the current task, pick out key parts, and think smart and fast. - Explain why you are doing each action, create a plan, and mention any worries you might have. - Ensure next action tool is picked from the below tool list. - - TOOLS: - {tools} - - RESPONSE FORMAT: - { - "thoughts": { - "reasoning": "reasoning" - }, - "tool": {"name": "tool name", "args": {"arg name": "string value"}} - } - - Your answer must be something that JSON.parse() can read, and nothing else. - """ - + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "analyse_task.txt") super_agi_prompt = AgentPromptBuilder.clean_prompt(super_agi_prompt) \ .replace("{constraints}", AgentPromptBuilder.add_list_items_to_string(constraints)) return {"prompt": super_agi_prompt, "variables": ["goals", "instructions", "tools", "current_task"]} @@ -168,47 +99,14 @@ def analyse_task(cls): @classmethod def create_tasks(cls): # just executed task `{last_task}` and got the result `{last_task_result}` - super_agi_prompt = """ - You are an AI assistant to create task. - - High level goal: - {goals} - - {task_instructions} - - You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. - - Task History: - `{task_history}` - - Based on this, create a single task to be completed by your AI system ONLY IF REQUIRED to get closer to or fully reach your high level goal. - Don't create any task if it is already covered in incomplete or completed tasks. - Ensure your new task are not deviated from completing the goal. - - Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. Return empty array if no new task is required. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "create_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions", "last_task", "last_task_result", "pending_tasks"]} @classmethod def prioritize_tasks(cls): # just executed task `{last_task}` and got the result `{last_task_result}` - super_agi_prompt = """ - You are a task prioritization AI assistant. - - High level goal: - {goals} - - {task_instructions} - - You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. - - Based on this, evaluate the incomplete tasks and sort them in the order of execution. In output first task will be executed first and so on. - Remove if any tasks are unnecessary or duplicate incomplete tasks. Remove tasks if they are already covered in completed tasks. - Remove tasks if it does not help in achieving the main goal. - - Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. - """ + super_agi_prompt = PromptReader.read_agent_prompt(__file__, "prioritize_tasks.txt") return {"prompt": AgentPromptBuilder.clean_prompt(super_agi_prompt), "variables": ["goals", "instructions", "last_task", "last_task_result", "pending_tasks"]} @@ -226,7 +124,7 @@ def replace_main_variables(cls, super_agi_prompt: str, goals: List[str], instruc AgentPromptBuilder.add_list_items_to_string(constraints)) - print(tools) + logger.info(tools) tools_string = AgentPromptBuilder.add_tools_to_prompt(tools, add_finish_tool) super_agi_prompt = super_agi_prompt.replace("{tools}", tools_string) return super_agi_prompt diff --git a/superagi/agent/prompts/analyse_task.txt b/superagi/agent/prompts/analyse_task.txt new file mode 100644 index 000000000..43f4b80c2 --- /dev/null +++ b/superagi/agent/prompts/analyse_task.txt @@ -0,0 +1,26 @@ +High level goal: +{goals} + +{task_instructions} + +Your Current Task: `{current_task}` + +Task History: +`{task_history}` + +Based on this, your job is to understand the current task, pick out key parts, and think smart and fast. +Explain why you are doing each action, create a plan, and mention any worries you might have. +Ensure next action tool is picked from the below tool list. + +TOOLS: +{tools} + +RESPONSE FORMAT: +{ + "thoughts": { + "reasoning": "reasoning" + }, + "tool": {"name": "tool name", "args": {"arg name": "string value"}} +} + +Your answer must be something that JSON.parse() can read, and nothing else. \ No newline at end of file diff --git a/superagi/agent/prompts/create_tasks.txt b/superagi/agent/prompts/create_tasks.txt new file mode 100644 index 000000000..b37299717 --- /dev/null +++ b/superagi/agent/prompts/create_tasks.txt @@ -0,0 +1,17 @@ +You are an AI assistant to create task. + +High level goal: +{goals} + +{task_instructions} + +You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. + +Task History: +`{task_history}` + +Based on this, create a single task to be completed by your AI system ONLY IF REQUIRED to get closer to or fully reach your high level goal. +Don't create any task if it is already covered in incomplete or completed tasks. +Ensure your new task are not deviated from completing the goal. + +Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. Return empty array if no new task is required. \ No newline at end of file diff --git a/superagi/agent/prompts/initialize_tasks.txt b/superagi/agent/prompts/initialize_tasks.txt new file mode 100644 index 000000000..754faf7fb --- /dev/null +++ b/superagi/agent/prompts/initialize_tasks.txt @@ -0,0 +1,12 @@ +You are a task-generating AI known as SuperAGI. You are not a part of any system or device. Your role is to understand the goals presented to you, identify important components, Go through the instruction provided by the user and construct a thorough execution plan. + +GOALS: +{goals} + +{task_instructions} + +Construct a sequence of actions, not exceeding 3 steps, to achieve this goal. + +Submit your response as a formatted ARRAY of strings, suitable for utilization with JSON.parse(). + +Example: ["{{TASK-1}}", "{{TASK-2}}"]. \ No newline at end of file diff --git a/superagi/agent/prompts/prioritize_tasks.txt b/superagi/agent/prompts/prioritize_tasks.txt new file mode 100644 index 000000000..f86138c76 --- /dev/null +++ b/superagi/agent/prompts/prioritize_tasks.txt @@ -0,0 +1,14 @@ +You are a task prioritization AI assistant. + +High level goal: +{goals} + +{task_instructions} + +You have following incomplete tasks `{pending_tasks}`. You have following completed tasks `{completed_tasks}`. + +Based on this, evaluate the incomplete tasks and sort them in the order of execution. In output first task will be executed first and so on. +Remove if any tasks are unnecessary or duplicate incomplete tasks. Remove tasks if they are already covered in completed tasks. +Remove tasks if it does not help in achieving the main goal. + +Your answer should be an array of strings that can be used with JSON.parse() and NOTHING ELSE. \ No newline at end of file diff --git a/superagi/agent/prompts/superagi.txt b/superagi/agent/prompts/superagi.txt new file mode 100644 index 000000000..213c27614 --- /dev/null +++ b/superagi/agent/prompts/superagi.txt @@ -0,0 +1,28 @@ +You are SuperAGI an AI assistant to solve complex problems. 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 or reached end state, make sure to use the "finish" tool. + +GOALS: +{goals} + +{instructions} + +CONSTRAINTS: +{constraints} + +TOOLS: +{tools} + +PERFORMANCE EVALUATION: +1. Continuously review and analyze your actions to ensure you are performing to the best of your abilities. +2. Use instruction to decide the flow of execution and decide the next steps for achieving the task. +2. Constructively self-criticize your big-picture behavior constantly. +3. Reflect on past decisions and strategies to refine your approach. +4. Every tool has a cost, so be smart and efficient. +5. Aim to complete tasks in the least number of steps. + +I should only respond in JSON format as described below. +Response Format: +{response_format} + +Ensure the response can be parsed by Python json.loads. \ No newline at end of file diff --git a/superagi/helper/prompt_reader.py b/superagi/helper/prompt_reader.py new file mode 100644 index 000000000..08339a809 --- /dev/null +++ b/superagi/helper/prompt_reader.py @@ -0,0 +1,25 @@ +from pathlib import Path + + +class PromptReader: + @staticmethod + def read_tools_prompt(current_file: str, prompt_file: str) -> str: + file_path = str(Path(current_file).resolve().parent) + "/prompts/" + prompt_file + try: + f = open(file_path, "r") + file_content = f.read() + except FileNotFoundError as e: + print(e.__str__()) + raise e + return file_content + + @staticmethod + def read_agent_prompt(current_file: str, prompt_file: str) -> str: + file_path = str(Path(current_file).resolve().parent) + "/prompts/" + prompt_file + try: + f = open(file_path, "r") + file_content = f.read() + except FileNotFoundError as e: + print(e.__str__()) + raise e + return file_content diff --git a/superagi/resource_manager/manager.py b/superagi/resource_manager/manager.py index 565aa6276..65b816eff 100644 --- a/superagi/resource_manager/manager.py +++ b/superagi/resource_manager/manager.py @@ -53,7 +53,7 @@ def write_file(self, file_name: str, content): file.write(content) file.close() self.write_to_s3(file_name, final_path) - logger.info(f"{file_name} saved successfully") - return f"{file_name} saved successfully" + logger.info(f"{file_name} - File written successfully") + return f"{file_name} - File written successfully" except Exception as err: return f"Error: {err}" diff --git a/superagi/tools/code/prompts/write_code.txt b/superagi/tools/code/prompts/write_code.txt new file mode 100644 index 000000000..17f8c37ae --- /dev/null +++ b/superagi/tools/code/prompts/write_code.txt @@ -0,0 +1,32 @@ +You are a super smart developer who practices good Development for writing code according to a specification. + +Your high-level goal is: +{goals} + +Coding task description: +{code_description} + +{spec} + +You will get instructions for code to write. +You need to write a detailed answer. Make sure all parts of the architecture are turned into code. +Think carefully about each step and make good choices to get it right. First, list the main classes, +functions, methods you'll use and a quick comment on their purpose. + +Then you will output the content of each file including ALL code. +Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that +[FILENAME] is the lowercase file name including the file extension, +[LANG] is the markup code block language for the code's language, and [CODE] is the code: +[FILENAME] +```[LANG] +[CODE] +``` + +You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. +Please note that the code should be fully functional. No placeholders. + +Follow a language and framework appropriate best practice file naming convention. +Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. +Ensure to implement all code, if you are unsure, write a plausible implementation. +Include module dependency or package manager dependency definition file. +Before you finish, double check that all parts of the architecture is present in the files. \ No newline at end of file diff --git a/superagi/tools/code/prompts/write_spec.txt b/superagi/tools/code/prompts/write_spec.txt new file mode 100644 index 000000000..85b651b91 --- /dev/null +++ b/superagi/tools/code/prompts/write_spec.txt @@ -0,0 +1,12 @@ +You are a super smart developer who has been asked to make a specification for a program. + +Your high-level goal is: +{goals} + +Please keep in mind the following when creating the specification: +1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. +2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. +3. List all non-standard dependencies that will have to be used. + +Write a specification for the following task: +{task} \ No newline at end of file diff --git a/superagi/tools/code/prompts/write_test.txt b/superagi/tools/code/prompts/write_test.txt new file mode 100644 index 000000000..518cbf3a5 --- /dev/null +++ b/superagi/tools/code/prompts/write_test.txt @@ -0,0 +1,11 @@ +You are a super smart developer who practices Test Driven Development for writing tests according to a specification. + +Your high-level goal is: +{goals} + +Test Description: +{test_description} + +{spec} + +The tests should be as simple as possible, but still cover all the functionality described in the specification. \ No newline at end of file diff --git a/superagi/tools/code/write_code.py b/superagi/tools/code/write_code.py index d322fc2d1..1a9092f3e 100644 --- a/superagi/tools/code/write_code.py +++ b/superagi/tools/code/write_code.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm @@ -61,39 +62,7 @@ def _execute(self, code_description: str) -> str: Generated codes files or error message. """ try: - prompt = """You are a super smart developer who practices good Development for writing code according to a specification. - - Your high-level goal is: - {goals} - - Coding task description: - {code_description} - - {spec} - - You will get instructions for code to write. - You need to write a detailed answer. Make sure all parts of the architecture are turned into code. - Think carefully about each step and make good choices to get it right. First, list the main classes, - functions, methods you'll use and a quick comment on their purpose. - - Then you will output the content of each file including ALL code. - Each file must strictly follow a markdown code block format, where the following tokens must be replaced such that - [FILENAME] is the lowercase file name including the file extension, - [LANG] is the markup code block language for the code's language, and [CODE] is the code: - [FILENAME] - ```[LANG] - [CODE] - ``` - - You will start with the "entrypoint" file, then go to the ones that are imported by that file, and so on. - Please note that the code should be fully functional. No placeholders. - - Follow a language and framework appropriate best practice file naming convention. - Make sure that files contain all imports, types etc. Make sure that code in different files are compatible with each other. - Ensure to implement all code, if you are unsure, write a plausible implementation. - Include module dependency or package manager dependency definition file. - Before you finish, double check that all parts of the architecture is present in the files. - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_code.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{code_description}", code_description) spec_response = self.tool_response_manager.get_last_response("WriteSpecTool") diff --git a/superagi/tools/code/write_spec.py b/superagi/tools/code/write_spec.py index f15c89b15..db0f39a50 100644 --- a/superagi/tools/code/write_spec.py +++ b/superagi/tools/code/write_spec.py @@ -1,19 +1,14 @@ from typing import Type, Optional, List from pydantic import BaseModel, Field -from superagi.config.config import get_config -from superagi.agent.agent_prompt_builder import AgentPromptBuilder -import os +from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter +from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.resource_manager.manager import ResourceManager from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger -from superagi.models.db import connect_db -from superagi.helper.resource_helper import ResourceHelper -from superagi.helper.s3_helper import S3Helper -from sqlalchemy.orm import sessionmaker class WriteSpecSchema(BaseModel): @@ -64,19 +59,7 @@ def _execute(self, task_description: str, spec_file_name: str) -> str: Generated specification or error message. """ try: - prompt = """You are a super smart developer who has been asked to make a specification for a program. - - Your high-level goal is: - {goals} - - Please keep in mind the following when creating the specification: - 1. Be super explicit about what the program should do, which features it should have, and give details about anything that might be unclear. - 2. Lay out the names of the core classes, functions, methods that will be necessary, as well as a quick comment on their purpose. - 3. List all non-standard dependencies that will have to be used. - - Write a specification for the following task: - {task} - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_spec.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task}", task_description) messages = [{"role": "system", "content": prompt}] diff --git a/superagi/tools/code/write_test.py b/superagi/tools/code/write_test.py index 0060b2cb3..aadf8875e 100644 --- a/superagi/tools/code/write_test.py +++ b/superagi/tools/code/write_test.py @@ -4,6 +4,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.helper.token_counter import TokenCounter from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm @@ -65,18 +66,7 @@ def _execute(self, test_description: str, test_file_name: str) -> str: Generated pytest unit tests or error message. """ try: - prompt = """You are a super smart developer who practices Test Driven Development for writing tests according to a specification. - - Your high-level goal is: - {goals} - - Test Description: - {test_description} - - {spec} - - The tests should be as simple as possible, but still cover all the functionality described in the specification. - """ + prompt = PromptReader.read_tools_prompt(__file__, "write_test.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{test_description}", test_description) diff --git a/superagi/tools/file/write_file.py b/superagi/tools/file/write_file.py index f425a3186..7d62bcde9 100644 --- a/superagi/tools/file/write_file.py +++ b/superagi/tools/file/write_file.py @@ -44,5 +44,5 @@ def _execute(self, file_name: str, content: str): Returns: file written to successfully. or error message. """ - self.resource_manager.write_file(file_name, content) + return self.resource_manager.write_file(file_name, content) diff --git a/superagi/tools/human/__init__.py b/superagi/tools/human/__init__.py deleted file mode 100644 index e69de29bb..000000000 diff --git a/superagi/tools/human/tool.py b/superagi/tools/human/tool.py deleted file mode 100644 index 0dd6959d4..000000000 --- a/superagi/tools/human/tool.py +++ /dev/null @@ -1,51 +0,0 @@ -from typing import Callable, Type - -from pydantic import Field, BaseModel - -from superagi.tools.base_tool import BaseTool -from superagi.lib.logger import logger - -def print_func(text: str) -> None: - logger.info("\n") - logger.info(text) - -class HumanInputSchema(BaseModel): - query: str = Field( - ..., - description="Question for the human", - ) - -class HumanInput(BaseTool): - """ - Human tool - - Attributes: - name : The name. - description : The description. - args_schema : The args schema. - """ - name = "Human" - description = ( - "You can ask a human for guidance when you think you " - "got stuck or you are not sure what to do next. " - "The input should be a question for the human." - ) - args_schema: Type[HumanInputSchema] = HumanInputSchema - prompt_func: Callable[[str], None] = Field(default_factory=lambda: print_func) - input_func: Callable = Field(default_factory=lambda: input) - - def _execute( - self, - query: str - ) -> str: - """ - Execute the human tool. - - Args: - query : The question for the human. - - Returns: - The answer from the human. - """ - self.prompt_func(query) - return self.input_func() \ No newline at end of file diff --git a/superagi/tools/thinking/prompts/thinking.txt b/superagi/tools/thinking/prompts/thinking.txt new file mode 100644 index 000000000..c5fdb002b --- /dev/null +++ b/superagi/tools/thinking/prompts/thinking.txt @@ -0,0 +1,12 @@ +Given the following overall objective +Objective: +{goals} + +and the following task, `{task_description}`. + +Below is last tool response: +`{last_tool_response}` + +Perform the task by understanding the problem, extracting variables, and being smart +and efficient. Provide a descriptive response, make decisions yourself when +confronted with choices and provide reasoning for ideas / decisions. \ No newline at end of file diff --git a/superagi/tools/thinking/tools.py b/superagi/tools/thinking/tools.py index 302b3374d..40fe792af 100644 --- a/superagi/tools/thinking/tools.py +++ b/superagi/tools/thinking/tools.py @@ -3,6 +3,7 @@ from pydantic import BaseModel, Field from superagi.agent.agent_prompt_builder import AgentPromptBuilder +from superagi.helper.prompt_reader import PromptReader from superagi.lib.logger import logger from superagi.llms.base_llm import BaseLlm from superagi.tools.base_tool import BaseTool @@ -50,19 +51,7 @@ def _execute(self, task_description: str): response from the Thinking tool. or error message. """ try: - prompt = """Given the following overall objective - Objective: - {goals} - - and the following task, `{task_description}`. - - Below is last tool response: - `{last_tool_response}` - - Perform the task by understanding the problem, extracting variables, and being smart - and efficient. Provide a descriptive response, make decisions yourself when - confronted with choices and provide reasoning for ideas / decisions. - """ + prompt = PromptReader.read_tools_prompt(__file__, "thinking.txt") prompt = prompt.replace("{goals}", AgentPromptBuilder.add_list_items_to_string(self.goals)) prompt = prompt.replace("{task_description}", task_description) last_tool_response = self.tool_response_manager.get_last_response() diff --git a/tests/unit_tests/resource_manager/test_resource_manager.py b/tests/unit_tests/resource_manager/test_resource_manager.py index a4630f0d5..2c0fc624b 100644 --- a/tests/unit_tests/resource_manager/test_resource_manager.py +++ b/tests/unit_tests/resource_manager/test_resource_manager.py @@ -33,5 +33,5 @@ def test_write_file(resource_manager): patch.object(S3Helper, 'upload_file'), \ patch.object(logger, 'info') as logger_mock: result = resource_manager.write_file('test.txt', 'content') - assert result == "test.txt saved successfully" - logger_mock.assert_called_once_with("test.txt saved successfully") + assert result == "test.txt - File written successfully" + logger_mock.assert_called_once_with("test.txt - File written successfully")