diff --git a/examples/sk_agent.py b/examples/sk_agent.py index b74c0c449..5efee0cf3 100644 --- a/examples/sk_agent.py +++ b/examples/sk_agent.py @@ -8,8 +8,8 @@ import asyncio import os -import semantic_kernel 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 @@ -17,41 +17,70 @@ 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 +from metagpt.tools.search_engine import SkSearchEngine + +# Get the directory of the current file +current_file_directory = os.path.dirname(os.path.abspath(__file__)) +# Construct the skills_directory by joining the parent directory and "skillss" +skills_directory = os.path.join(current_file_directory, "..", "metagpt", "skills") +# Normalize the path to ensure it's in the correct format +skills_directory = os.path.normpath(skills_directory) 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() - # Get the directory of the current file - current_file_directory = os.path.dirname(os.path.abspath(__file__)) + # let's give the agent some skills + role.import_semantic_skill_from_directory(skills_directory, "SummarizeSkill") + role.import_semantic_skill_from_directory(skills_directory, "WriterSkill") + role.import_skill(TextSkill(), "TextSkill") + # using BasicPlanner + await role.run(Message(content=task, cause_by=BossRequirement)) - # Construct the skills_directory by joining the parent directory and "skillss" - skills_directory = os.path.join(current_file_directory, "..", "metagpt", "skills") - # Normalize the path to ensure it's in the correct format - skills_directory = os.path.normpath(skills_directory) +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(skills_directory, "SummarizeSkill") role.import_semantic_skill_from_directory(skills_directory, "WriterSkill") - role.import_skill(semantic_kernel.core_skills.TextSkill(), "TextSkill") - + role.import_skill(TextSkill(), "TextSkill") # using BasicPlanner await role.run(Message(content=task, cause_by=BossRequirement)) - # #using SequentialPlanner - # role = SkAgent(planner_cls=SequentialPlanner) - # 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 diff --git a/metagpt/actions/execute_task.py b/metagpt/actions/execute_task.py index c9883262c..afdeda323 100644 --- a/metagpt/actions/execute_task.py +++ b/metagpt/actions/execute_task.py @@ -10,9 +10,8 @@ from metagpt.schema import Message class ExecuteTask(Action): - def __init__(self, name="ExecuteTask", context: list[Message] = None, llm=None, role=None): + def __init__(self, name="ExecuteTask", context: list[Message] = None, llm=None): super().__init__(name, context, llm) - self.role = role def run(self, *args, **kwargs): pass diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py index 5a7e2e68d..e12144ca9 100644 --- a/metagpt/roles/sk_agent.py +++ b/metagpt/roles/sk_agent.py @@ -5,8 +5,6 @@ @Author : femto Zheng @File : sk_agent.py """ -from functools import partial - from semantic_kernel.planning import SequentialPlanner from semantic_kernel.planning.action_planner.action_planner import ActionPlanner from semantic_kernel.planning.basic_planner import BasicPlanner @@ -40,7 +38,7 @@ class SkAgent(Role): ) -> None: """Initializes the Engineer role with given attributes.""" super().__init__(name, profile, goal, constraints) - self._init_actions([ExecuteTask(role=self)]) + self._init_actions([ExecuteTask()]) self._watch([BossRequirement]) self.kernel = make_sk_kernel() @@ -50,8 +48,8 @@ class SkAgent(Role): elif planner_cls in [SequentialPlanner, ActionPlanner]: self.planner = planner_cls(self.kernel) - self.import_semantic_skill_from_directory = partial(self.kernel.import_semantic_skill_from_directory) - self.import_skill = partial(self.kernel.import_skill) + 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) 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