diff --git a/examples/sk_agent.py b/examples/sk_agent.py deleted file mode 100644 index 647ea4380..000000000 --- a/examples/sk_agent.py +++ /dev/null @@ -1,82 +0,0 @@ -#!/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 UserRequirement -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=UserRequirement)) - - -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=UserRequirement)) - - -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=UserRequirement)) - - -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=UserRequirement)) # it will choose mathskill.Add - - -if __name__ == "__main__": - asyncio.run(main()) diff --git a/metagpt/actions/research.py b/metagpt/actions/research.py index 5086f10cf..bad8f139c 100644 --- a/metagpt/actions/research.py +++ b/metagpt/actions/research.py @@ -10,7 +10,6 @@ from pydantic import TypeAdapter, model_validator from metagpt.actions import Action from metagpt.config2 import config from metagpt.logs import logger -from metagpt.tools.search_engine import SearchEngine from metagpt.tools.web_browser_engine import WebBrowserEngine from metagpt.utils.common import OutputParser from metagpt.utils.text import generate_prompt_chunk, reduce_message_length diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py deleted file mode 100644 index 71df55fcc..000000000 --- a/metagpt/roles/sk_agent.py +++ /dev/null @@ -1,87 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/13 12:23 -@Author : femto Zheng -@File : sk_agent.py -@Modified By: mashenquan, 2023-11-1. In accordance with Chapter 2.2.1 and 2.2.2 of RFC 116, utilize the new message - distribution feature for message filtering. -""" -from typing import Any, Callable, Union - -from pydantic import Field -from semantic_kernel import Kernel -from semantic_kernel.planning import SequentialPlanner -from semantic_kernel.planning.action_planner.action_planner import ActionPlanner -from semantic_kernel.planning.basic_planner import BasicPlanner, Plan - -from metagpt.actions import UserRequirement -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. - """ - - name: str = "Sunshine" - profile: str = "sk_agent" - goal: str = "Execute task based on passed in task description" - constraints: str = "" - - plan: Plan = Field(default=None, exclude=True) - planner_cls: Any = None - planner: Union[BasicPlanner, SequentialPlanner, ActionPlanner] = None - kernel: Kernel = Field(default_factory=Kernel) - import_semantic_skill_from_directory: Callable = Field(default=None, exclude=True) - import_skill: Callable = Field(default=None, exclude=True) - - def __init__(self, **data: Any) -> None: - """Initializes the Engineer role with given attributes.""" - super().__init__(**data) - self.set_actions([ExecuteTask()]) - self._watch([UserRequirement]) - self.kernel = make_sk_kernel() - - # how funny the interface is inconsistent - if self.planner_cls == BasicPlanner or self.planner_cls is None: - self.planner = BasicPlanner() - elif self.planner_cls in [SequentialPlanner, ActionPlanner]: - self.planner = self.planner_cls(self.kernel) - else: - raise Exception(f"Unsupported planner of type {self.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 - result = None - 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=self.rc.todo) - self.rc.memory.add(msg) - return msg diff --git a/metagpt/tools/search_engine.py b/metagpt/tools/search_engine.py index 767f4aaba..1372a8c7a 100644 --- a/metagpt/tools/search_engine.py +++ b/metagpt/tools/search_engine.py @@ -9,34 +9,12 @@ import importlib from typing import Callable, Coroutine, Literal, Optional, Union, overload from pydantic import BaseModel, ConfigDict, model_validator -from semantic_kernel.skill_definition import sk_function from metagpt.configs.search_config import SearchConfig from metagpt.logs import logger from metagpt.tools import SearchEngineType -class SkSearchEngine: - """A search engine class for executing searches. - - Attributes: - search_engine: The search engine instance used for executing searches. - """ - - def __init__(self, **kwargs): - self.search_engine = SearchEngine(**kwargs) - - @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(BaseModel): """A model for configuring and executing searches with different search engines. diff --git a/metagpt/utils/make_sk_kernel.py b/metagpt/utils/make_sk_kernel.py deleted file mode 100644 index 67d865b37..000000000 --- a/metagpt/utils/make_sk_kernel.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/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.config2 import config - - -def make_sk_kernel(): - kernel = sk.Kernel() - if llm := config.get_azure_llm(): - kernel.add_chat_service( - "chat_completion", - AzureChatCompletion(deployment_name=llm.model, base_url=llm.base_url, api_key=llm.api_key), - ) - elif llm := config.get_openai_llm(): - kernel.add_chat_service( - "chat_completion", - OpenAIChatCompletion(ai_model_id=llm.model, api_key=llm.api_key), - ) - - return kernel diff --git a/tests/metagpt/planner/__init__.py b/tests/metagpt/planner/__init__.py deleted file mode 100644 index 85e01b36b..000000000 --- a/tests/metagpt/planner/__init__.py +++ /dev/null @@ -1,7 +0,0 @@ -#!/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 deleted file mode 100644 index 1bc451db8..000000000 --- a/tests/metagpt/planner/test_action_planner.py +++ /dev/null @@ -1,32 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/16 20:03 -@Author : femto Zheng -@File : test_basic_planner.py -@Modified By: mashenquan, 2023-11-1. In accordance with Chapter 2.2.1 and 2.2.2 of RFC 116, utilize the new message - distribution feature for message handling. -""" -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 UserRequirement -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.put_message(Message(content=task, cause_by=UserRequirement)) - await role._observe() - 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 deleted file mode 100644 index f406143ee..000000000 --- a/tests/metagpt/planner/test_basic_planner.py +++ /dev/null @@ -1,37 +0,0 @@ -#!/usr/bin/env python -# -*- coding: utf-8 -*- -""" -@Time : 2023/9/16 20:03 -@Author : femto Zheng -@File : test_basic_planner.py -@Modified By: mashenquan, 2023-11-1. In accordance with Chapter 2.2.1 and 2.2.2 of RFC 116, utilize the new message - distribution feature for message handling. -""" -import pytest -from semantic_kernel.core_skills import TextSkill - -from metagpt.actions import UserRequirement -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.put_message(Message(content=task, cause_by=UserRequirement)) - await role._observe() - 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 diff --git a/tests/metagpt/serialize_deserialize/test_sk_agent.py b/tests/metagpt/serialize_deserialize/test_sk_agent.py deleted file mode 100644 index a4bf4ec6d..000000000 --- a/tests/metagpt/serialize_deserialize/test_sk_agent.py +++ /dev/null @@ -1,21 +0,0 @@ -# -*- coding: utf-8 -*- -# @Desc : -import pytest - -from metagpt.roles.sk_agent import SkAgent - - -@pytest.mark.asyncio -async def test_sk_agent_serdeser(): - role = SkAgent() - ser_role_dict = role.model_dump(exclude={"import_semantic_skill_from_directory", "import_skill"}) - assert "name" in ser_role_dict - assert "planner" in ser_role_dict - - new_role = SkAgent(**ser_role_dict) - assert new_role.name == "Sunshine" - assert len(new_role.actions) == 1 - - -if __name__ == "__main__": - pytest.main([__file__, "-s"])