diff --git a/.gitignore b/.gitignore index 6352a90e5..e03eab3d3 100644 --- a/.gitignore +++ b/.gitignore @@ -148,8 +148,7 @@ allure-results .DS_Store .vscode - -*.txt +log.txt docs/scripts/set_env.sh key.yaml output.json @@ -164,3 +163,4 @@ workspace/* tmp output.wav metagpt/roles/idea_agent.py +.aider* diff --git a/examples/sk_agent.py b/examples/sk_agent.py new file mode 100644 index 000000000..f60e7299b --- /dev/null +++ b/examples/sk_agent.py @@ -0,0 +1,82 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/13 12:36 +@Author : femto Zheng +@File : sk_agent.py +""" +import asyncio + +from semantic_kernel.core_skills import FileIOSkill, MathSkill, TextSkill, TimeSkill +from semantic_kernel.planning import SequentialPlanner + +# from semantic_kernel.planning import SequentialPlanner +from semantic_kernel.planning.action_planner.action_planner import ActionPlanner + +from metagpt.actions import BossRequirement +from metagpt.const import SKILL_DIRECTORY +from metagpt.roles.sk_agent import SkAgent +from metagpt.schema import Message +from metagpt.tools.search_engine import SkSearchEngine + + +async def main(): + await basic_planner_example() + await action_planner_example() + + # await sequential_planner_example() + # await basic_planner_web_search_example() + + +async def basic_planner_example(): + task = """ + Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French. + Convert the text to uppercase""" + role = SkAgent() + + # let's give the agent some skills + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "SummarizeSkill") + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill") + role.import_skill(TextSkill(), "TextSkill") + # using BasicPlanner + await role.run(Message(content=task, cause_by=BossRequirement)) + + +async def sequential_planner_example(): + task = """ + Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French. + Convert the text to uppercase""" + role = SkAgent(planner_cls=SequentialPlanner) + + # let's give the agent some skills + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "SummarizeSkill") + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill") + role.import_skill(TextSkill(), "TextSkill") + # using BasicPlanner + await role.run(Message(content=task, cause_by=BossRequirement)) + + +async def basic_planner_web_search_example(): + task = """ + Question: Who made the 1989 comic book, the film version of which Jon Raymond Polito appeared in?""" + role = SkAgent() + + role.import_skill(SkSearchEngine(), "WebSearchSkill") + # role.import_semantic_skill_from_directory(skills_directory, "QASkill") + + await role.run(Message(content=task, cause_by=BossRequirement)) + + +async def action_planner_example(): + role = SkAgent(planner_cls=ActionPlanner) + # let's give the agent 4 skills + role.import_skill(MathSkill(), "math") + role.import_skill(FileIOSkill(), "fileIO") + role.import_skill(TimeSkill(), "time") + role.import_skill(TextSkill(), "text") + task = "What is the sum of 110 and 990?" + await role.run(Message(content=task, cause_by=BossRequirement)) # it will choose mathskill.Add + + +if __name__ == "__main__": + asyncio.run(main()) diff --git a/metagpt/actions/execute_task.py b/metagpt/actions/execute_task.py new file mode 100644 index 000000000..afdeda323 --- /dev/null +++ b/metagpt/actions/execute_task.py @@ -0,0 +1,17 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/13 12:26 +@Author : femto Zheng +@File : execute_task.py +""" +from metagpt.actions import Action +from metagpt.schema import Message + + +class ExecuteTask(Action): + def __init__(self, name="ExecuteTask", context: list[Message] = None, llm=None): + super().__init__(name, context, llm) + + def run(self, *args, **kwargs): + pass diff --git a/metagpt/const.py b/metagpt/const.py index 35b4c9fa7..b8b08628e 100644 --- a/metagpt/const.py +++ b/metagpt/const.py @@ -12,9 +12,11 @@ def get_project_root(): """Search upwards to find the project root directory.""" current_path = Path.cwd() while True: - if (current_path / '.git').exists() or \ - (current_path / '.project_root').exists() or \ - (current_path / '.gitignore').exists(): + if ( + (current_path / ".git").exists() + or (current_path / ".project_root").exists() + or (current_path / ".gitignore").exists() + ): return current_path parent_path = current_path.parent if parent_path == current_path: @@ -23,16 +25,18 @@ def get_project_root(): PROJECT_ROOT = get_project_root() -DATA_PATH = PROJECT_ROOT / 'data' -WORKSPACE_ROOT = PROJECT_ROOT / 'workspace' -PROMPT_PATH = PROJECT_ROOT / 'metagpt/prompts' -UT_PATH = PROJECT_ROOT / 'data/ut' +DATA_PATH = PROJECT_ROOT / "data" +WORKSPACE_ROOT = PROJECT_ROOT / "workspace" +PROMPT_PATH = PROJECT_ROOT / "metagpt/prompts" +UT_PATH = PROJECT_ROOT / "data/ut" SWAGGER_PATH = UT_PATH / "files/api/" UT_PY_PATH = UT_PATH / "files/ut/" API_QUESTIONS_PATH = UT_PATH / "files/question/" YAPI_URL = "http://yapi.deepwisdomai.com/" -TMP = PROJECT_ROOT / 'tmp' +TMP = PROJECT_ROOT / "tmp" RESEARCH_PATH = DATA_PATH / "research" TUTORIAL_PATH = DATA_PATH / "tutorial_docx" +SKILL_DIRECTORY = PROJECT_ROOT / "metagpt/skills" + MEM_TTL = 24 * 30 * 3600 diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py new file mode 100644 index 000000000..b27841d74 --- /dev/null +++ b/metagpt/roles/sk_agent.py @@ -0,0 +1,76 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/13 12:23 +@Author : femto Zheng +@File : sk_agent.py +""" +from semantic_kernel.planning import SequentialPlanner +from semantic_kernel.planning.action_planner.action_planner import ActionPlanner +from semantic_kernel.planning.basic_planner import BasicPlanner + +from metagpt.actions import BossRequirement +from metagpt.actions.execute_task import ExecuteTask +from metagpt.logs import logger +from metagpt.roles import Role +from metagpt.schema import Message +from metagpt.utils.make_sk_kernel import make_sk_kernel + + +class SkAgent(Role): + """ + Represents an SkAgent implemented using semantic kernel + + Attributes: + name (str): Name of the SkAgent. + profile (str): Role profile, default is 'sk_agent'. + goal (str): Goal of the SkAgent. + constraints (str): Constraints for the SkAgent. + """ + + def __init__( + self, + name: str = "Sunshine", + profile: str = "sk_agent", + goal: str = "Execute task based on passed in task description", + constraints: str = "", + planner_cls=BasicPlanner, + ) -> None: + """Initializes the Engineer role with given attributes.""" + super().__init__(name, profile, goal, constraints) + self._init_actions([ExecuteTask()]) + self._watch([BossRequirement]) + self.kernel = make_sk_kernel() + + # how funny the interface is inconsistent + if planner_cls == BasicPlanner: + self.planner = planner_cls() + elif planner_cls in [SequentialPlanner, ActionPlanner]: + self.planner = planner_cls(self.kernel) + else: + raise f"Unsupported planner of type {planner_cls}" + + self.import_semantic_skill_from_directory = self.kernel.import_semantic_skill_from_directory + self.import_skill = self.kernel.import_skill + + async def _think(self) -> None: + self._set_state(0) + # how funny the interface is inconsistent + if isinstance(self.planner, BasicPlanner): + self.plan = await self.planner.create_plan_async(self._rc.important_memory[-1].content, self.kernel) + logger.info(self.plan.generated_plan) + elif any(isinstance(self.planner, cls) for cls in [SequentialPlanner, ActionPlanner]): + self.plan = await self.planner.create_plan_async(self._rc.important_memory[-1].content) + + async def _act(self) -> Message: + # how funny the interface is inconsistent + if isinstance(self.planner, BasicPlanner): + result = await self.planner.execute_plan_async(self.plan, self.kernel) + elif any(isinstance(self.planner, cls) for cls in [SequentialPlanner, ActionPlanner]): + result = (await self.plan.invoke_async()).result + logger.info(result) + + msg = Message(content=result, role=self.profile, cause_by=type(self._rc.todo)) + self._rc.memory.add(msg) + # logger.debug(f"{response}") + return msg diff --git a/metagpt/skills/SummarizeSkill/MakeAbstractReadable/config.json b/metagpt/skills/SummarizeSkill/MakeAbstractReadable/config.json new file mode 100644 index 000000000..0bd48b77a --- /dev/null +++ b/metagpt/skills/SummarizeSkill/MakeAbstractReadable/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Given a scientific white paper abstract, rewrite it to make it more readable", + "completion": { + "max_tokens": 4000, + "temperature": 0.0, + "top_p": 1.0, + "presence_penalty": 0.0, + "frequency_penalty": 2.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/SummarizeSkill/MakeAbstractReadable/skprompt.txt b/metagpt/skills/SummarizeSkill/MakeAbstractReadable/skprompt.txt new file mode 100644 index 000000000..5501e19b7 --- /dev/null +++ b/metagpt/skills/SummarizeSkill/MakeAbstractReadable/skprompt.txt @@ -0,0 +1,5 @@ +{{$input}} + +== +Summarize, using a user friendly, using simple grammar. Don't use subjects like "we" "our" "us" "your". +== \ No newline at end of file diff --git a/metagpt/skills/SummarizeSkill/Notegen/config.json b/metagpt/skills/SummarizeSkill/Notegen/config.json new file mode 100644 index 000000000..f7e1c355e --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Notegen/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Automatically generate compact notes for any text or text document.", + "completion": { + "max_tokens": 256, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/SummarizeSkill/Notegen/skprompt.txt b/metagpt/skills/SummarizeSkill/Notegen/skprompt.txt new file mode 100644 index 000000000..b3f4d203b --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Notegen/skprompt.txt @@ -0,0 +1,21 @@ +Analyze the following extract taken from a document. +- Produce key points for memory. +- Give memory a name. +- Extract only points worth remembering. +- Be brief. Conciseness is very important. +- Use broken English. +You will use this memory to analyze the rest of this document, and for other relevant tasks. + +[Input] +My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest. +My story was immortalized by Shakespeare in a play. ++++++ +Family History +- Macbeth, King Scotland +- Wife Lady Macbeth, No Kids +- Dog Toby McDuff. Hunter, dead. +- Shakespeare play + +[Input] +[[{{$input}}]] ++++++ diff --git a/metagpt/skills/SummarizeSkill/Summarize/config.json b/metagpt/skills/SummarizeSkill/Summarize/config.json new file mode 100644 index 000000000..7ba5cf02d --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Summarize/config.json @@ -0,0 +1,21 @@ +{ + "schema": 1, + "type": "completion", + "description": "Summarize given text or any text document", + "completion": { + "max_tokens": 512, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "Text to summarize", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/SummarizeSkill/Summarize/skprompt.txt b/metagpt/skills/SummarizeSkill/Summarize/skprompt.txt new file mode 100644 index 000000000..5597e1350 --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Summarize/skprompt.txt @@ -0,0 +1,23 @@ +[SUMMARIZATION RULES] +DONT WASTE WORDS +USE SHORT, CLEAR, COMPLETE SENTENCES. +DO NOT USE BULLET POINTS OR DASHES. +USE ACTIVE VOICE. +MAXIMIZE DETAIL, MEANING +FOCUS ON THE CONTENT + +[BANNED PHRASES] +This article +This document +This page +This material +[END LIST] + +Summarize: +Hello how are you? ++++++ +Hello + +Summarize this +{{$input}} ++++++ \ No newline at end of file diff --git a/metagpt/skills/SummarizeSkill/Topics/config.json b/metagpt/skills/SummarizeSkill/Topics/config.json new file mode 100644 index 000000000..b2cd9985c --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Topics/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Analyze given text or document and extract key topics worth remembering", + "completion": { + "max_tokens": 128, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/SummarizeSkill/Topics/skprompt.txt b/metagpt/skills/SummarizeSkill/Topics/skprompt.txt new file mode 100644 index 000000000..cb7a28c13 --- /dev/null +++ b/metagpt/skills/SummarizeSkill/Topics/skprompt.txt @@ -0,0 +1,28 @@ +Analyze the following extract taken from a document and extract key topics. +- Topics only worth remembering. +- Be brief. Short phrases. +- Can use broken English. +- Conciseness is very important. +- Topics can include names of memories you want to recall. +- NO LONG SENTENCES. SHORT PHRASES. +- Return in JSON +[Input] +My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest. +My tragic story was immortalized by Shakespeare in a play. +[Output] +{ + "topics": [ + "Macbeth", + "King of Scotland", + "Lady Macbeth", + "Dog", + "Toby McDuff", + "Shakespeare", + "Play", + "Tragedy" + ] +} ++++++ +[Input] +{{$input}} +[Output] \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/Acronym/config.json b/metagpt/skills/WriterSkill/Acronym/config.json new file mode 100644 index 000000000..c48414856 --- /dev/null +++ b/metagpt/skills/WriterSkill/Acronym/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Generate an acronym for the given concept or phrase", + "completion": { + "max_tokens": 100, + "temperature": 0.5, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/Acronym/skprompt.txt b/metagpt/skills/WriterSkill/Acronym/skprompt.txt new file mode 100644 index 000000000..1c2e8a6aa --- /dev/null +++ b/metagpt/skills/WriterSkill/Acronym/skprompt.txt @@ -0,0 +1,25 @@ +Generate a suitable acronym pair for the concept. Creativity is encouraged, including obscure references. +The uppercase letters in the acronym expansion must agree with the letters of the acronym + +Q: A technology for detecting moving objects, their distance and velocity using radio waves. +A: R.A.D.A.R: RAdio Detection And Ranging. + +Q: A weapon that uses high voltage electricity to incapacitate the target +A. T.A.S.E.R: Thomas A. Swift’s Electric Rifle + +Q: Equipment that lets a diver breathe underwater +A: S.C.U.B.A: Self Contained Underwater Breathing Apparatus. + +Q: Reminder not to complicated subject matter. +A. K.I.S.S: Keep It Simple Stupid + +Q: A national organization for investment in space travel, rockets, space ships, space exploration +A. N.A.S.A: National Aeronautics Space Administration + +Q: Agreement that governs trade among North American countries. +A: N.A.F.T.A: North American Free Trade Agreement. + +Q: Organization to protect the freedom and security of its member countries in North America and Europe. +A: N.A.T.O: North Atlantic Treaty Organization. + +Q:{{$input}} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/AcronymGenerator/config.json b/metagpt/skills/WriterSkill/AcronymGenerator/config.json new file mode 100644 index 000000000..1dab1fe9f --- /dev/null +++ b/metagpt/skills/WriterSkill/AcronymGenerator/config.json @@ -0,0 +1,15 @@ +{ + "schema": 1, + "type": "completion", + "description": "Given a request to generate an acronym from a string, generate an acronym and provide the acronym explanation.", + "completion": { + "max_tokens": 256, + "temperature": 0.7, + "top_p": 1.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + "stop_sequences": [ + "#" + ] + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/AcronymGenerator/skprompt.txt b/metagpt/skills/WriterSkill/AcronymGenerator/skprompt.txt new file mode 100644 index 000000000..5bf0b987d --- /dev/null +++ b/metagpt/skills/WriterSkill/AcronymGenerator/skprompt.txt @@ -0,0 +1,54 @@ +# Name of a super artificial intelligence +J.A.R.V.I.S. = Just A Really Very Intelligent System. +# Name for a new young beautiful assistant +F.R.I.D.A.Y. = Female Replacement Intelligent Digital Assistant Youth. +# Mirror to check what's behind +B.A.R.F. = Binary Augmented Retro-Framing. +# Pair of powerful glasses created by a genius that is now dead +E.D.I.T.H. = Even Dead I’m The Hero. +# A company building and selling computers +I.B.M. = Intelligent Business Machine. +# A super computer that is sentient. +H.A.L = Heuristically programmed ALgorithmic computer. +# an intelligent bot that helps with productivity. +C.O.R.E. = Central Optimization Routines and Efficiency. +# an intelligent bot that helps with productivity. +P.A.L. = Personal Assistant Light. +# an intelligent bot that helps with productivity. +A.I.D.A. = Artificial Intelligence Digital Assistant. +# an intelligent bot that helps with productivity. +H.E.R.A. = Human Emulation and Recognition Algorithm. +# an intelligent bot that helps with productivity. +I.C.A.R.U.S. = Intelligent Control and Automation of Research and Utility Systems. +# an intelligent bot that helps with productivity. +N.E.M.O. = Networked Embedded Multiprocessor Orchestration. +# an intelligent bot that helps with productivity. +E.P.I.C. = Enhanced Productivity and Intelligence through Computing. +# an intelligent bot that helps with productivity. +M.A.I.A. = Multipurpose Artificial Intelligence Assistant. +# an intelligent bot that helps with productivity. +A.R.I.A. = Artificial Reasoning and Intelligent Assistant. +# An incredibly smart entity developed with complex math, that helps me being more productive. +O.M.E.G.A. = Optimized Mathematical Entity for Generalized Artificial intelligence. +# An incredibly smart entity developed with complex math, that helps me being more productive. +P.Y.T.H.O.N. = Precise Yet Thorough Heuristic Optimization Network. +# An incredibly smart entity developed with complex math, that helps me being more productive. +A.P.O.L.L.O. = Adaptive Probabilistic Optimization Learning Library for Online Applications. +# An incredibly smart entity developed with complex math, that helps me being more productive. +S.O.L.I.D. = Self-Organizing Logical Intelligent Data-base. +# An incredibly smart entity developed with complex math, that helps me being more productive. +D.E.E.P. = Dynamic Estimation and Prediction. +# An incredibly smart entity developed with complex math, that helps me being more productive. +B.R.A.I.N. = Biologically Realistic Artificial Intelligence Network. +# An incredibly smart entity developed with complex math, that helps me being more productive. +C.O.G.N.I.T.O. = COmputational and Generalized INtelligence TOolkit. +# An incredibly smart entity developed with complex math, that helps me being more productive. +S.A.G.E. = Symbolic Artificial General Intelligence Engine. +# An incredibly smart entity developed with complex math, that helps me being more productive. +Q.U.A.R.K. = Quantum Universal Algorithmic Reasoning Kernel. +# An incredibly smart entity developed with complex math, that helps me being more productive. +S.O.L.V.E. = Sophisticated Operational Logic and Versatile Expertise. +# An incredibly smart entity developed with complex math, that helps me being more productive. +C.A.L.C.U.L.U.S. = Cognitively Advanced Logic and Computation Unit for Learning and Understanding Systems. + +# {{$INPUT}} diff --git a/metagpt/skills/WriterSkill/AcronymReverse/config.json b/metagpt/skills/WriterSkill/AcronymReverse/config.json new file mode 100644 index 000000000..eed5c5191 --- /dev/null +++ b/metagpt/skills/WriterSkill/AcronymReverse/config.json @@ -0,0 +1,15 @@ +{ + "schema": 1, + "type": "completion", + "description": "Given a single word or acronym, generate the expanded form matching the acronym letters.", + "completion": { + "max_tokens": 256, + "temperature": 0.5, + "top_p": 1.0, + "presence_penalty": 0.8, + "frequency_penalty": 0.0, + "stop_sequences": [ + "#END#" + ] + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/AcronymReverse/skprompt.txt b/metagpt/skills/WriterSkill/AcronymReverse/skprompt.txt new file mode 100644 index 000000000..7c1d649a9 --- /dev/null +++ b/metagpt/skills/WriterSkill/AcronymReverse/skprompt.txt @@ -0,0 +1,24 @@ +# acronym: Devis +Sentences matching the acronym: +1. Dragons Eat Very Interesting Snacks +2. Develop Empathy and Vision to Increase Success +3. Don't Expect Vampires In Supermarkets +#END# + +# acronym: Christmas +Sentences matching the acronym: +1. Celebrating Harmony and Respect in a Season of Togetherness, Merriment, and True joy +2. Children Have Real Interest Since The Mystery And Surprise Thrills +3. Christmas Helps Reduce Inner Stress Through Mistletoe And Sleigh excursions +#END# + +# acronym: noWare +Sentences matching the acronym: +1. No One Wants an App that Randomly Erases everything +2. Nourishing Oatmeal With Almond, Raisin, and Egg toppings +3. Notice Opportunity When Available and React Enthusiastically +#END# + +Reverse the following acronym back to a funny sentence. Provide 3 examples. +# acronym: {{$INPUT}} +Sentences matching the acronym: diff --git a/metagpt/skills/WriterSkill/Brainstorm/config.json b/metagpt/skills/WriterSkill/Brainstorm/config.json new file mode 100644 index 000000000..f50a354e7 --- /dev/null +++ b/metagpt/skills/WriterSkill/Brainstorm/config.json @@ -0,0 +1,22 @@ +{ + "schema": 1, + "type": "completion", + "description": "Given a goal or topic description generate a list of ideas", + "completion": { + "max_tokens": 2000, + "temperature": 0.5, + "top_p": 1.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + "stop_sequences": ["##END##"] + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "A topic description or goal.", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/WriterSkill/Brainstorm/skprompt.txt b/metagpt/skills/WriterSkill/Brainstorm/skprompt.txt new file mode 100644 index 000000000..6a8b92086 --- /dev/null +++ b/metagpt/skills/WriterSkill/Brainstorm/skprompt.txt @@ -0,0 +1,8 @@ +Must: brainstorm ideas and create a list. +Must: use a numbered list. +Must: only one list. +Must: end list with ##END## +Should: no more than 10 items. +Should: at least 3 items. +Topic: {{$INPUT}} +Start. diff --git a/metagpt/skills/WriterSkill/EmailGen/config.json b/metagpt/skills/WriterSkill/EmailGen/config.json new file mode 100644 index 000000000..d43eab348 --- /dev/null +++ b/metagpt/skills/WriterSkill/EmailGen/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Write an email from the given bullet points", + "completion": { + "max_tokens": 256, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/EmailGen/skprompt.txt b/metagpt/skills/WriterSkill/EmailGen/skprompt.txt new file mode 100644 index 000000000..26f4933fb --- /dev/null +++ b/metagpt/skills/WriterSkill/EmailGen/skprompt.txt @@ -0,0 +1,16 @@ +Rewrite my bullet points into complete sentences. Use a polite and inclusive tone. + +[Input] +- Macbeth, King Scotland +- Married, Wife Lady Macbeth, No Kids +- Dog Toby McDuff. Hunter, dead. +- Shakespeare play ++++++ +The story of Macbeth +My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest. +My story was immortalized by Shakespeare in a play. + ++++++ +[Input] +{{$input}} ++++++ diff --git a/metagpt/skills/WriterSkill/EmailTo/config.json b/metagpt/skills/WriterSkill/EmailTo/config.json new file mode 100644 index 000000000..5f0d6ee6e --- /dev/null +++ b/metagpt/skills/WriterSkill/EmailTo/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Turn bullet points into an email to someone, using a polite tone", + "completion": { + "max_tokens": 256, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/EmailTo/skprompt.txt b/metagpt/skills/WriterSkill/EmailTo/skprompt.txt new file mode 100644 index 000000000..cc6b5c962 --- /dev/null +++ b/metagpt/skills/WriterSkill/EmailTo/skprompt.txt @@ -0,0 +1,31 @@ +Rewrite my bullet points into an email featuring complete sentences. Use a polite and inclusive tone. + +[Input] +Toby, + +- Macbeth, King Scotland +- Married, Wife Lady Macbeth, No Kids +- Dog Toby McDuff. Hunter, dead. +- Shakespeare play + +Thanks, +Dexter + ++++++ +Hi Toby, + +The story of Macbeth +My name is Macbeth. I used to be King of Scotland, but I died. My wife's name is Lady Macbeth and we were married for 15 years. We had no children. Our beloved dog Toby McDuff was a famous hunter of rats in the forest. +My story was immortalized by Shakespeare in a play. + +Thanks, +Dexter + ++++++ +[Input] +{{$to}} +{{$input}} + +Thanks, +{{$sender}} ++++++ diff --git a/metagpt/skills/WriterSkill/EnglishImprover/config.json b/metagpt/skills/WriterSkill/EnglishImprover/config.json new file mode 100644 index 000000000..4d10af469 --- /dev/null +++ b/metagpt/skills/WriterSkill/EnglishImprover/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Translate text to English and improve it", + "completion": { + "max_tokens": 3000, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/EnglishImprover/skprompt.txt b/metagpt/skills/WriterSkill/EnglishImprover/skprompt.txt new file mode 100644 index 000000000..09b80036c --- /dev/null +++ b/metagpt/skills/WriterSkill/EnglishImprover/skprompt.txt @@ -0,0 +1,11 @@ +I want you to act as an English translator, spelling corrector and improver. +I will speak to you in any language and you will detect the language, translate it and answer in the corrected and improved version of my text, in English. +I want you to replace my simplified A0-level words and sentences with more beautiful and elegant, upper level English words and sentences. +Keep the meaning same, but make them more literary. +I want you to only reply the correction, the improvements and nothing else, do not write explanations. + +Sentence: """ +{{$INPUT}} +""" + +Translation: diff --git a/metagpt/skills/WriterSkill/NovelChapter/config.json b/metagpt/skills/WriterSkill/NovelChapter/config.json new file mode 100644 index 000000000..3568c6955 --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelChapter/config.json @@ -0,0 +1,36 @@ +{ + "schema": 1, + "type": "completion", + "description": "Write a chapter of a novel.", + "completion": { + "max_tokens": 2048, + "temperature": 0.3, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "A synopsis of what the chapter should be about.", + "defaultValue": "" + }, + { + "name": "theme", + "description": "The theme or topic of this novel.", + "defaultValue": "" + }, + { + "name": "previousChapter", + "description": "The synopsis of the previous chapter.", + "defaultValue": "" + }, + { + "name": "chapterIndex", + "description": "The number of the chapter to write.", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/WriterSkill/NovelChapter/skprompt.txt b/metagpt/skills/WriterSkill/NovelChapter/skprompt.txt new file mode 100644 index 000000000..4fb85a538 --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelChapter/skprompt.txt @@ -0,0 +1,20 @@ +[CONTEXT] + +THEME OF STORY: +{{$theme}} + +PREVIOUS CHAPTER: +{{$previousChapter}} + +[END CONTEXT] + + +WRITE THIS CHAPTER USING [CONTEXT] AND +CHAPTER SYNOPSIS. DO NOT REPEAT SYNOPSIS IN THE OUTPUT + +Chapter Synopsis: +{{$input}} + +Chapter {{$chapterIndex}} + + diff --git a/metagpt/skills/WriterSkill/NovelChapterWithNotes/config.json b/metagpt/skills/WriterSkill/NovelChapterWithNotes/config.json new file mode 100644 index 000000000..02b9e613a --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelChapterWithNotes/config.json @@ -0,0 +1,41 @@ +{ + "schema": 1, + "type": "completion", + "description": "Write a chapter of a novel using notes about the chapter to write.", + "completion": { + "max_tokens": 1024, + "temperature": 0.5, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "What the novel should be about.", + "defaultValue": "" + }, + { + "name": "theme", + "description": "The theme of this novel.", + "defaultValue": "" + }, + { + "name": "notes", + "description": "Notes useful to write this chapter.", + "defaultValue": "" + }, + { + "name": "previousChapter", + "description": "The previous chapter synopsis.", + "defaultValue": "" + }, + { + "name": "chapterIndex", + "description": "The number of the chapter to write.", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/WriterSkill/NovelChapterWithNotes/skprompt.txt b/metagpt/skills/WriterSkill/NovelChapterWithNotes/skprompt.txt new file mode 100644 index 000000000..650bd50d9 --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelChapterWithNotes/skprompt.txt @@ -0,0 +1,19 @@ +[CONTEXT] + +THEME OF STORY: +{{$theme}} + +NOTES OF STORY SO FAR - USE AS REFERENCE +{{$notes}} + +PREVIOUS CHAPTER, USE AS REFERENCE: +{{$previousChapter}} + +[END CONTEXT] + + +WRITE THIS CHAPTER CONTINUING STORY, USING [CONTEXT] AND CHAPTER SYNOPSIS BELOW. DO NOT REPEAT SYNOPSIS IN THE CHAPTER. DON'T REPEAT PREVIOUS CHAPTER. + +{{$input}} + +Chapter {{$chapterIndex}} diff --git a/metagpt/skills/WriterSkill/NovelOutline/config.json b/metagpt/skills/WriterSkill/NovelOutline/config.json new file mode 100644 index 000000000..a34622f7b --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelOutline/config.json @@ -0,0 +1,31 @@ +{ + "schema": 1, + "type": "completion", + "description": "Generate a list of chapter synopsis for a novel or novella", + "completion": { + "max_tokens": 2048, + "temperature": 0.1, + "top_p": 0.5, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "What the novel should be about.", + "defaultValue": "" + }, + { + "name": "chapterCount", + "description": "The number of chapters to generate.", + "defaultValue": "" + }, + { + "name": "endMarker", + "description": "The marker to use to end each chapter.", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/WriterSkill/NovelOutline/skprompt.txt b/metagpt/skills/WriterSkill/NovelOutline/skprompt.txt new file mode 100644 index 000000000..05f725acb --- /dev/null +++ b/metagpt/skills/WriterSkill/NovelOutline/skprompt.txt @@ -0,0 +1,12 @@ +I want to write a {{$chapterCount}} chapter novella about: +{{$input}} + +There MUST BE {{$chapterCount}} CHAPTERS. + +INVENT CHARACTERS AS YOU SEE FIT. BE HIGHLY CREATIVE AND/OR FUNNY. +WRITE SYNOPSIS FOR EACH CHAPTER. INCLUDE INFORMATION ABOUT CHARACTERS ETC. SINCE EACH +CHAPTER WILL BE WRITTEN BY A DIFFERENT WRITER, YOU MUST INCLUDE ALL PERTINENT INFORMATION +IN EACH SYNOPSIS + +YOU MUST END EACH SYNOPSIS WITH {{$endMarker}} + diff --git a/metagpt/skills/WriterSkill/Rewrite/config.json b/metagpt/skills/WriterSkill/Rewrite/config.json new file mode 100644 index 000000000..175ade9d9 --- /dev/null +++ b/metagpt/skills/WriterSkill/Rewrite/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Automatically generate compact notes for any text or text document", + "completion": { + "max_tokens": 256, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/Rewrite/skprompt.txt b/metagpt/skills/WriterSkill/Rewrite/skprompt.txt new file mode 100644 index 000000000..37f8d03fc --- /dev/null +++ b/metagpt/skills/WriterSkill/Rewrite/skprompt.txt @@ -0,0 +1,6 @@ +Rewrite the given text like it was written in this style or by: {{$style}}. +MUST RETAIN THE MEANING AND FACTUAL CONTENT AS THE ORIGINAL. + + +{{$input}} + diff --git a/metagpt/skills/WriterSkill/ShortPoem/config.json b/metagpt/skills/WriterSkill/ShortPoem/config.json new file mode 100644 index 000000000..0cc3da6c8 --- /dev/null +++ b/metagpt/skills/WriterSkill/ShortPoem/config.json @@ -0,0 +1,21 @@ +{ + "schema": 1, + "type": "completion", + "description": "Turn a scenario into a short and entertaining poem.", + "completion": { + "max_tokens": 60, + "temperature": 0.5, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + }, + "input": { + "parameters": [ + { + "name": "input", + "description": "The scenario to turn into a poem.", + "defaultValue": "" + } + ] + } +} diff --git a/metagpt/skills/WriterSkill/ShortPoem/skprompt.txt b/metagpt/skills/WriterSkill/ShortPoem/skprompt.txt new file mode 100644 index 000000000..bc42fcba6 --- /dev/null +++ b/metagpt/skills/WriterSkill/ShortPoem/skprompt.txt @@ -0,0 +1,2 @@ +Generate a short funny poem or limerick to explain the given event. Be creative and be funny. Let your imagination run wild. +Event:{{$input}} diff --git a/metagpt/skills/WriterSkill/StoryGen/config.json b/metagpt/skills/WriterSkill/StoryGen/config.json new file mode 100644 index 000000000..212831341 --- /dev/null +++ b/metagpt/skills/WriterSkill/StoryGen/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Generate a list of synopsis for a novel or novella with sub-chapters", + "completion": { + "max_tokens": 250, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} diff --git a/metagpt/skills/WriterSkill/StoryGen/skprompt.txt b/metagpt/skills/WriterSkill/StoryGen/skprompt.txt new file mode 100644 index 000000000..661df013c --- /dev/null +++ b/metagpt/skills/WriterSkill/StoryGen/skprompt.txt @@ -0,0 +1,10 @@ +ONLY USE XML TAGS IN THIS LIST: +[XML TAG LIST] +list: Surround any lists with this tag +synopsis: An outline of the chapter to write +[END LIST] + +EMIT WELL FORMED XML ALWAYS. Code should be CDATA. + + +{{$input}} diff --git a/metagpt/skills/WriterSkill/TellMeMore/config.json b/metagpt/skills/WriterSkill/TellMeMore/config.json new file mode 100644 index 000000000..28b6b4e5c --- /dev/null +++ b/metagpt/skills/WriterSkill/TellMeMore/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Summarize given text or any text document", + "completion": { + "max_tokens": 500, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/TellMeMore/skprompt.txt b/metagpt/skills/WriterSkill/TellMeMore/skprompt.txt new file mode 100644 index 000000000..143ce3a65 --- /dev/null +++ b/metagpt/skills/WriterSkill/TellMeMore/skprompt.txt @@ -0,0 +1,7 @@ +>>>>>The following is part of a {{$conversationtype}}. +{{$input}} + +>>>>>The following is an overview of a previous part of the {{$conversationtype}}, focusing on "{{$focusarea}}". +{{$previousresults}} + +>>>>>In 250 words or less, write a verbose and detailed overview of the {{$conversationtype}} focusing solely on "{{$focusarea}}". \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/Translate/config.json b/metagpt/skills/WriterSkill/Translate/config.json new file mode 100644 index 000000000..8134ce8dd --- /dev/null +++ b/metagpt/skills/WriterSkill/Translate/config.json @@ -0,0 +1,15 @@ +{ + "schema": 1, + "type": "completion", + "description": "Translate the input into a language of your choice", + "completion": { + "max_tokens": 2000, + "temperature": 0.7, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0, + "stop_sequences": [ + "[done]" + ] + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/Translate/skprompt.txt b/metagpt/skills/WriterSkill/Translate/skprompt.txt new file mode 100644 index 000000000..d5f2fa8c1 --- /dev/null +++ b/metagpt/skills/WriterSkill/Translate/skprompt.txt @@ -0,0 +1,7 @@ +Translate the input below into {{$language}} + +MAKE SURE YOU ONLY USE {{$language}}. + +{{$input}} + +Translation: diff --git a/metagpt/skills/WriterSkill/TwoSentenceSummary/config.json b/metagpt/skills/WriterSkill/TwoSentenceSummary/config.json new file mode 100644 index 000000000..833bd5950 --- /dev/null +++ b/metagpt/skills/WriterSkill/TwoSentenceSummary/config.json @@ -0,0 +1,12 @@ +{ + "schema": 1, + "type": "completion", + "description": "Summarize given text in two sentences or less", + "completion": { + "max_tokens": 100, + "temperature": 0.0, + "top_p": 0.0, + "presence_penalty": 0.0, + "frequency_penalty": 0.0 + } +} \ No newline at end of file diff --git a/metagpt/skills/WriterSkill/TwoSentenceSummary/skprompt.txt b/metagpt/skills/WriterSkill/TwoSentenceSummary/skprompt.txt new file mode 100644 index 000000000..b8f657a93 --- /dev/null +++ b/metagpt/skills/WriterSkill/TwoSentenceSummary/skprompt.txt @@ -0,0 +1,4 @@ +Summarize the following text in two sentences or less. +[BEGIN TEXT] +{{$input}} +[END TEXT] diff --git a/metagpt/tools/search_engine.py b/metagpt/tools/search_engine.py index d28700054..4ac078714 100644 --- a/metagpt/tools/search_engine.py +++ b/metagpt/tools/search_engine.py @@ -5,15 +5,32 @@ @Author : alexanderwu @File : search_engine.py """ -from __future__ import annotations +# from __future__ import annotations import importlib from typing import Callable, Coroutine, Literal, overload +from semantic_kernel.skill_definition import sk_function + from metagpt.config import CONFIG from metagpt.tools import SearchEngineType +class SkSearchEngine: + def __init__(self): + self.search_engine = SearchEngine() + + @sk_function( + description="searches results from Google. Useful when you need to find short " + "and succinct answers about a specific topic. Input should be a search query.", + name="searchAsync", + input_description="search", + ) + async def run(self, query: str) -> str: + result = await self.search_engine.run(query) + return result + + class SearchEngine: """Class representing a search engine. @@ -25,6 +42,7 @@ class SearchEngine: run_func: The function to run the search. engine: The search engine type. """ + def __init__( self, engine: SearchEngineType | None = None, @@ -33,7 +51,7 @@ class SearchEngine: engine = engine or CONFIG.search_engine if engine == SearchEngineType.SERPAPI_GOOGLE: module = "metagpt.tools.search_engine_serpapi" - run_func = importlib.import_module(module).SerpAPIWrapper().run + run_func = importlib.import_module(module).SerpAPIWrapper().run elif engine == SearchEngineType.SERPER_GOOGLE: module = "metagpt.tools.search_engine_serper" run_func = importlib.import_module(module).SerperWrapper().run diff --git a/metagpt/utils/make_sk_kernel.py b/metagpt/utils/make_sk_kernel.py new file mode 100644 index 000000000..5e919abeb --- /dev/null +++ b/metagpt/utils/make_sk_kernel.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/13 12:29 +@Author : femto Zheng +@File : make_sk_kernel.py +""" +import semantic_kernel as sk +from semantic_kernel.connectors.ai.open_ai.services.azure_chat_completion import ( + AzureChatCompletion, +) +from semantic_kernel.connectors.ai.open_ai.services.open_ai_chat_completion import ( + OpenAIChatCompletion, +) + +from metagpt.config import CONFIG + + +def make_sk_kernel(): + kernel = sk.Kernel() + if CONFIG.openai_api_type == "azure": + kernel.add_chat_service( + "chat_completion", + AzureChatCompletion(CONFIG.deployment_name, CONFIG.openai_api_base, CONFIG.openai_api_key), + ) + else: + kernel.add_chat_service( + "chat_completion", + OpenAIChatCompletion( + CONFIG.openai_api_model, CONFIG.openai_api_key, org_id=None, endpoint=CONFIG.openai_api_base + ), + ) + + return kernel diff --git a/requirements.txt b/requirements.txt index 741ae74df..4f163b961 100644 --- a/requirements.txt +++ b/requirements.txt @@ -37,4 +37,5 @@ anthropic==0.3.6 typing-inspect==0.8.0 typing_extensions==4.5.0 libcst==1.0.1 -qdrant-client==1.4.0 \ No newline at end of file +qdrant-client==1.4.0 +semantic-kernel==0.3.10.dev0 \ No newline at end of file diff --git a/tests/metagpt/planner/__init__.py b/tests/metagpt/planner/__init__.py new file mode 100644 index 000000000..85e01b36b --- /dev/null +++ b/tests/metagpt/planner/__init__.py @@ -0,0 +1,7 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/16 20:03 +@Author : femto Zheng +@File : __init__.py +""" diff --git a/tests/metagpt/planner/test_action_planner.py b/tests/metagpt/planner/test_action_planner.py new file mode 100644 index 000000000..5ab9a493f --- /dev/null +++ b/tests/metagpt/planner/test_action_planner.py @@ -0,0 +1,29 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/16 20:03 +@Author : femto Zheng +@File : test_basic_planner.py +""" +import pytest +from semantic_kernel.core_skills import FileIOSkill, MathSkill, TextSkill, TimeSkill +from semantic_kernel.planning.action_planner.action_planner import ActionPlanner + +from metagpt.actions import BossRequirement +from metagpt.roles.sk_agent import SkAgent +from metagpt.schema import Message + + +@pytest.mark.asyncio +async def test_action_planner(): + role = SkAgent(planner_cls=ActionPlanner) + # let's give the agent 4 skills + role.import_skill(MathSkill(), "math") + role.import_skill(FileIOSkill(), "fileIO") + role.import_skill(TimeSkill(), "time") + role.import_skill(TextSkill(), "text") + task = "What is the sum of 110 and 990?" + role.recv(Message(content=task, cause_by=BossRequirement)) + + await role._think() # it will choose mathskill.Add + assert "1100" == (await role._act()).content diff --git a/tests/metagpt/planner/test_basic_planner.py b/tests/metagpt/planner/test_basic_planner.py new file mode 100644 index 000000000..03a82ec5e --- /dev/null +++ b/tests/metagpt/planner/test_basic_planner.py @@ -0,0 +1,34 @@ +#!/usr/bin/env python +# -*- coding: utf-8 -*- +""" +@Time : 2023/9/16 20:03 +@Author : femto Zheng +@File : test_basic_planner.py +""" +import pytest +from semantic_kernel.core_skills import TextSkill + +from metagpt.actions import BossRequirement +from metagpt.const import SKILL_DIRECTORY +from metagpt.roles.sk_agent import SkAgent +from metagpt.schema import Message + + +@pytest.mark.asyncio +async def test_basic_planner(): + task = """ + Tomorrow is Valentine's day. I need to come up with a few date ideas. She speaks French so write it in French. + Convert the text to uppercase""" + role = SkAgent() + + # let's give the agent some skills + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "SummarizeSkill") + role.import_semantic_skill_from_directory(SKILL_DIRECTORY, "WriterSkill") + role.import_skill(TextSkill(), "TextSkill") + # using BasicPlanner + role.recv(Message(content=task, cause_by=BossRequirement)) + await role._think() + # assuming sk_agent will think he needs WriterSkill.Brainstorm and WriterSkill.Translate + assert "WriterSkill.Brainstorm" in role.plan.generated_plan.result + assert "WriterSkill.Translate" in role.plan.generated_plan.result + # assert "SALUT" in (await role._act()).content #content will be some French