diff --git a/examples/agent_creator.py b/examples/agent_creator.py index e908fe6ee..fe883bdf4 100644 --- a/examples/agent_creator.py +++ b/examples/agent_creator.py @@ -61,7 +61,7 @@ class AgentCreator(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([CreateAgent]) + self.add_actions([CreateAgent]) async def _act(self) -> Message: logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})") diff --git a/examples/build_customized_agent.py b/examples/build_customized_agent.py index 6c3219efc..a0c8ddfb3 100644 --- a/examples/build_customized_agent.py +++ b/examples/build_customized_agent.py @@ -57,7 +57,7 @@ class SimpleCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteCode]) + self.add_actions([SimpleWriteCode]) async def _act(self) -> Message: logger.info(f"{self._setting}: to do {self.rc.todo}({self.rc.todo.name})") @@ -76,7 +76,7 @@ class RunnableCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteCode, SimpleRunCode]) + self.add_actions([SimpleWriteCode, SimpleRunCode]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _act(self) -> Message: diff --git a/examples/build_customized_multi_agents.py b/examples/build_customized_multi_agents.py index 73278c08c..aceb3f2ab 100644 --- a/examples/build_customized_multi_agents.py +++ b/examples/build_customized_multi_agents.py @@ -46,7 +46,7 @@ class SimpleCoder(Role): def __init__(self, **kwargs): super().__init__(**kwargs) self._watch([UserRequirement]) - self._init_actions([SimpleWriteCode]) + self.add_actions([SimpleWriteCode]) class SimpleWriteTest(Action): @@ -75,7 +75,7 @@ class SimpleTester(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteTest]) + self.add_actions([SimpleWriteTest]) # self._watch([SimpleWriteCode]) self._watch([SimpleWriteCode, SimpleWriteReview]) # feel free to try this too @@ -114,7 +114,7 @@ class SimpleReviewer(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([SimpleWriteReview]) + self.add_actions([SimpleWriteReview]) self._watch([SimpleWriteTest]) diff --git a/examples/debate.py b/examples/debate.py index eb0a09839..b47eba3cd 100644 --- a/examples/debate.py +++ b/examples/debate.py @@ -49,7 +49,7 @@ class Debator(Role): def __init__(self, **data: Any): super().__init__(**data) - self._init_actions([SpeakAloud]) + self.add_actions([SpeakAloud]) self._watch([UserRequirement, SpeakAloud]) async def _observe(self) -> int: diff --git a/metagpt/context.py b/metagpt/context.py index 293beb9b5..495fe9e2f 100644 --- a/metagpt/context.py +++ b/metagpt/context.py @@ -104,7 +104,7 @@ class Context(LLMMixin, BaseModel): class ContextMixin: - """Mixin class for configurable objects""" + """Mixin class for configurable objects: Priority: more specific < parent""" _context: Optional[Context] = None diff --git a/metagpt/roles/architect.py b/metagpt/roles/architect.py index c6ceaccb7..a22a1c926 100644 --- a/metagpt/roles/architect.py +++ b/metagpt/roles/architect.py @@ -33,7 +33,7 @@ class Architect(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) # Initialize actions specific to the Architect role - self._init_actions([WriteDesign]) + self.add_actions([WriteDesign]) # Set events or actions the Architect should watch or be aware of self._watch({WritePRD}) diff --git a/metagpt/roles/engineer.py b/metagpt/roles/engineer.py index 98744383c..ad0c1ac92 100644 --- a/metagpt/roles/engineer.py +++ b/metagpt/roles/engineer.py @@ -84,7 +84,7 @@ class Engineer(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([WriteCode]) + self.add_actions([WriteCode]) self._watch([WriteTasks, SummarizeCode, WriteCode, WriteCodeReview, FixBug]) self.code_todos = [] self.summarize_todos = [] diff --git a/metagpt/roles/invoice_ocr_assistant.py b/metagpt/roles/invoice_ocr_assistant.py index 8635f4307..de7d3f8a3 100644 --- a/metagpt/roles/invoice_ocr_assistant.py +++ b/metagpt/roles/invoice_ocr_assistant.py @@ -60,7 +60,7 @@ class InvoiceOCRAssistant(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([InvoiceOCR]) + self.add_actions([InvoiceOCR]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _act(self) -> Message: @@ -82,10 +82,10 @@ class InvoiceOCRAssistant(Role): resp = await todo.run(file_path) if len(resp) == 1: # Single file support for questioning based on OCR recognition results - self._init_actions([GenerateTable, ReplyQuestion]) + self.add_actions([GenerateTable, ReplyQuestion]) self.orc_data = resp[0] else: - self._init_actions([GenerateTable]) + self.add_actions([GenerateTable]) self.set_todo(None) content = INVOICE_OCR_SUCCESS diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index 7f1a49231..a35dcb3a0 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -33,7 +33,7 @@ class ProductManager(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([PrepareDocuments, WritePRD]) + self.add_actions([PrepareDocuments, WritePRD]) self._watch([UserRequirement, PrepareDocuments]) self.todo_action = any_to_name(PrepareDocuments) diff --git a/metagpt/roles/project_manager.py b/metagpt/roles/project_manager.py index 1fad4afc2..7fa16b1e5 100644 --- a/metagpt/roles/project_manager.py +++ b/metagpt/roles/project_manager.py @@ -33,5 +33,5 @@ class ProjectManager(Role): def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self._init_actions([WriteTasks]) + self.add_actions([WriteTasks]) self._watch([WriteDesign]) diff --git a/metagpt/roles/qa_engineer.py b/metagpt/roles/qa_engineer.py index 7da0af072..80b0fd39a 100644 --- a/metagpt/roles/qa_engineer.py +++ b/metagpt/roles/qa_engineer.py @@ -44,7 +44,7 @@ class QaEngineer(Role): # FIXME: a bit hack here, only init one action to circumvent _think() logic, # will overwrite _think() in future updates - self._init_actions([WriteTest]) + self.add_actions([WriteTest]) self._watch([SummarizeCode, WriteTest, RunCode, DebugError]) self.test_round = 0 diff --git a/metagpt/roles/researcher.py b/metagpt/roles/researcher.py index 5110c6485..e877778f6 100644 --- a/metagpt/roles/researcher.py +++ b/metagpt/roles/researcher.py @@ -34,7 +34,7 @@ class Researcher(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions( + self.add_actions( [CollectLinks(name=self.name), WebBrowseAndSummarize(name=self.name), ConductResearch(name=self.name)] ) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) diff --git a/metagpt/roles/role.py b/metagpt/roles/role.py index 73d82e369..42996bea8 100644 --- a/metagpt/roles/role.py +++ b/metagpt/roles/role.py @@ -23,7 +23,7 @@ from __future__ import annotations from enum import Enum -from typing import Any, Iterable, Optional, Set, Type +from typing import Any, Iterable, Optional, Set, Type, Union from pydantic import BaseModel, ConfigDict, Field, SerializeAsAny, model_validator @@ -222,20 +222,18 @@ class Role(SerializationMixin): def _init_action_system_message(self, action: Action): action.set_prefix(self._get_prefix()) - def refresh_system_message(self): - self.llm.system_prompt = self._get_prefix() + def add_action(self, action: Action): + """Add action to the role.""" + self.add_actions([action]) - def set_memory(self, memory: Memory): - self.rc.memory = memory + def add_actions(self, actions: list[Union[Action, Type[Action]]]): + """Add actions to the role. - def init_actions(self, actions): - self._init_actions(actions) - - def _init_actions(self, actions): - self._reset() - for idx, action in enumerate(actions): + Args: + actions: list of Action classes or instances + """ + for action in actions: if not isinstance(action, Action): - ## 默认初始化 i = action(name="", llm=self.llm) else: if self.is_human and not isinstance(action.llm, HumanProvider): @@ -247,7 +245,7 @@ class Role(SerializationMixin): i = action self._init_action_system_message(i) self.actions.append(i) - self.states.append(f"{idx}. {action}") + self.states.append(f"{len(self.actions)}. {action}") def _set_react_mode(self, react_mode: str, max_react_loop: int = 1): """Set strategy of the Role reacting to observed Message. Variation lies in how @@ -302,7 +300,7 @@ class Role(SerializationMixin): self.rc.env = env if env: env.set_addresses(self, self.addresses) - self.refresh_system_message() # add env message to system message + self.llm.system_prompt = self._get_prefix() @property def action_count(self): diff --git a/metagpt/roles/sales.py b/metagpt/roles/sales.py index ca1cfee85..8da930888 100644 --- a/metagpt/roles/sales.py +++ b/metagpt/roles/sales.py @@ -38,5 +38,5 @@ class Sales(Role): action = SearchAndSummarize(name="", engine=SearchEngineType.CUSTOM_ENGINE, search_func=store.asearch) else: action = SearchAndSummarize() - self._init_actions([action]) + self.add_actions([action]) self._watch([UserRequirement]) diff --git a/metagpt/roles/searcher.py b/metagpt/roles/searcher.py index e713f7697..f37bd4704 100644 --- a/metagpt/roles/searcher.py +++ b/metagpt/roles/searcher.py @@ -48,12 +48,12 @@ class Searcher(Role): engine (SearchEngineType): The type of search engine to use. """ super().__init__(**kwargs) - self._init_actions([SearchAndSummarize(engine=self.engine)]) + self.add_actions([SearchAndSummarize(engine=self.engine)]) def set_search_func(self, search_func): """Sets a custom search function for the searcher.""" action = SearchAndSummarize(name="", engine=SearchEngineType.CUSTOM_ENGINE, search_func=search_func) - self._init_actions([action]) + self.add_actions([action]) async def _act_sp(self) -> Message: """Performs the search action in a single process.""" diff --git a/metagpt/roles/sk_agent.py b/metagpt/roles/sk_agent.py index 8921774f0..468905fce 100644 --- a/metagpt/roles/sk_agent.py +++ b/metagpt/roles/sk_agent.py @@ -52,7 +52,7 @@ class SkAgent(Role): def __init__(self, **data: Any) -> None: """Initializes the Engineer role with given attributes.""" super().__init__(**data) - self._init_actions([ExecuteTask()]) + self.add_actions([ExecuteTask()]) self._watch([UserRequirement]) self.kernel = make_sk_kernel() diff --git a/metagpt/roles/teacher.py b/metagpt/roles/teacher.py index fb547f56b..b4ffd01d3 100644 --- a/metagpt/roles/teacher.py +++ b/metagpt/roles/teacher.py @@ -47,7 +47,7 @@ class Teacher(Role): for topic in TeachingPlanBlock.TOPICS: act = WriteTeachingPlanPart(context=self.rc.news[0].content, topic=topic, llm=self.llm) actions.append(act) - self._init_actions(actions) + self.add_actions(actions) if self.rc.todo is None: self._set_state(0) diff --git a/metagpt/roles/tutorial_assistant.py b/metagpt/roles/tutorial_assistant.py index 10bd82c60..d296c7b3f 100644 --- a/metagpt/roles/tutorial_assistant.py +++ b/metagpt/roles/tutorial_assistant.py @@ -40,7 +40,7 @@ class TutorialAssistant(Role): def __init__(self, **kwargs): super().__init__(**kwargs) - self._init_actions([WriteDirectory(language=self.language)]) + self.add_actions([WriteDirectory(language=self.language)]) self._set_react_mode(react_mode=RoleReactMode.BY_ORDER.value) async def _handle_directory(self, titles: Dict) -> Message: @@ -63,7 +63,7 @@ class TutorialAssistant(Role): directory += f"- {key}\n" for second_dir in first_dir[key]: directory += f" - {second_dir}\n" - self._init_actions(actions) + self.add_actions(actions) async def _act(self) -> Message: """Perform an action as determined by the role. diff --git a/tests/metagpt/serialize_deserialize/test_serdeser_base.py b/tests/metagpt/serialize_deserialize/test_serdeser_base.py index ddb47a3e2..c97cea597 100644 --- a/tests/metagpt/serialize_deserialize/test_serdeser_base.py +++ b/tests/metagpt/serialize_deserialize/test_serdeser_base.py @@ -67,7 +67,7 @@ class RoleA(Role): def __init__(self, **kwargs): super(RoleA, self).__init__(**kwargs) - self._init_actions([ActionPass]) + self.add_actions([ActionPass]) self._watch([UserRequirement]) @@ -79,7 +79,7 @@ class RoleB(Role): def __init__(self, **kwargs): super(RoleB, self).__init__(**kwargs) - self._init_actions([ActionOK, ActionRaise]) + self.add_actions([ActionOK, ActionRaise]) self._watch([ActionPass]) self.rc.react_mode = RoleReactMode.BY_ORDER @@ -92,7 +92,7 @@ class RoleC(Role): def __init__(self, **kwargs): super(RoleC, self).__init__(**kwargs) - self._init_actions([ActionOK, ActionRaise]) + self.add_actions([ActionOK, ActionRaise]) self._watch([UserRequirement]) self.rc.react_mode = RoleReactMode.BY_ORDER self.rc.memory.ignore_id = True diff --git a/tests/metagpt/test_role.py b/tests/metagpt/test_role.py index 52d08e92e..20c8dba6d 100644 --- a/tests/metagpt/test_role.py +++ b/tests/metagpt/test_role.py @@ -33,7 +33,7 @@ class MockAction(Action): class MockRole(Role): def __init__(self, name="", profile="", goal="", constraints="", desc=""): super().__init__(name=name, profile=profile, goal=goal, constraints=constraints, desc=desc) - self._init_actions([MockAction()]) + self.add_actions([MockAction()]) def test_basic(): @@ -111,7 +111,7 @@ async def test_send_to(): def test_init_action(): role = Role() - role.init_actions([MockAction, MockAction]) + role.add_actions([MockAction, MockAction]) assert role.action_count == 2 @@ -127,7 +127,7 @@ async def test_recover(): role.publish_message(None) role.llm = mock_llm - role.init_actions([MockAction, MockAction]) + role.add_actions([MockAction, MockAction]) role.recovered = True role.latest_observed_msg = Message(content="recover_test") role.rc.state = 0 @@ -144,7 +144,7 @@ async def test_think_act(): mock_llm.aask.side_effect = ["ok"] role = Role() - role.init_actions([MockAction]) + role.add_actions([MockAction]) await role.think() role.rc.memory.add(Message("run")) assert len(role.get_memories()) == 1