From 89ad59876bb05ae412f8cf180487abda9ec54aa1 Mon Sep 17 00:00:00 2001 From: garylin2099 Date: Wed, 5 Jun 2024 14:39:41 +0800 Subject: [PATCH] format and add fixed sop compatibility --- metagpt/roles/di/engineer2.py | 46 +--------- metagpt/roles/di/role_zero.py | 27 ++++-- metagpt/roles/di/team_leader.py | 1 + tests/metagpt/roles/di/run_engineer2.py | 106 ++++++++++++++++++++++++ 4 files changed, 128 insertions(+), 52 deletions(-) create mode 100644 tests/metagpt/roles/di/run_engineer2.py diff --git a/metagpt/roles/di/engineer2.py b/metagpt/roles/di/engineer2.py index 673f3ff64..538976826 100644 --- a/metagpt/roles/di/engineer2.py +++ b/metagpt/roles/di/engineer2.py @@ -1,22 +1,19 @@ from __future__ import annotations -import asyncio - from pydantic import model_validator from metagpt.prompts.di.engineer2 import ENGINEER2_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero from metagpt.tools.libs.editor import Editor -from test3 import design_doc_2048, design_doc_snake, task_doc_2048, task_doc_snake class Engineer2(RoleZero): name: str = "Alex" profile: str = "Engineer" goal: str = "Take on game, app, and web development" - tools: str = ["Plan", "Editor:write,read,write_content", "RoleZero"] instruction: str = ENGINEER2_INSTRUCTION + tools: str = ["Plan", "Editor:write,read,write_content", "RoleZero"] editor: Editor = Editor() @model_validator(mode="after") @@ -32,44 +29,3 @@ class Engineer2(RoleZero): "RoleZero.reply_to_human": self.reply_to_human, } return self - - -GAME_REQ_2048 = f""" -Create a 2048 game, follow the design doc and task doc. Write your code under /Users/gary/Files/temp/workspace/2048_game/src. -After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. -Notice: You MUST implement the full code, don't leave comment without implementation! -Design doc: -{task_doc_2048} -Task doc: -{design_doc_2048} -""" -GAME_REQ_SNAKE = f""" -Create a snake game, follow the design doc and task doc. Write your code under /Users/gary/Files/temp/workspace/snake_game/src. -After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. -Notice: You MUST implement the full code, don't leave comment without implementation! -Design doc: -{task_doc_snake} -Task doc: -{design_doc_snake} -""" -GAME_REQ_2048_NO_DOC = """ -Create a 2048 game with pygame. Write your code under /Users/gary/Files/temp/workspace/2048_game/src. -Consider what files you will write, break down the requests to multiple tasks and write one file in each task. -After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. -Notice: You MUST implement the full code, don't leave comment without implementation! -""" -GAME_INC_REQ_2048 = """ -I found an issue with the 2048 code: when tiles are merged, no new tiles pop up. -Write code review for the codes (game.py, main.py, ui.py) under under /Users/gary/Files/temp/workspace/2048_game_bugs/src. -Then correct any issues you find. You can review all code in one time, and solve issues in one time. -""" -GAME_INC_REQ_SNAKE = """ -Found this issue, TypeError: generate_new_position() missing 1 required positional argument: 'snake_body' -Write code review for the codes (food.py, game.py, main.py, snake.py, ui.py) under under /Users/gary/Files/temp/workspace/snake_game_bugs/src. -Then correct any issues you find. You can review all code in one time, and solve issues in one time. -""" -CASUAL_CHAT = """what's your name?""" - -if __name__ == "__main__": - engineer2 = Engineer2() - asyncio.run(engineer2.run(GAME_REQ_2048_NO_DOC)) diff --git a/metagpt/roles/di/role_zero.py b/metagpt/roles/di/role_zero.py index f1518f8be..afd4a68c4 100644 --- a/metagpt/roles/di/role_zero.py +++ b/metagpt/roles/di/role_zero.py @@ -23,8 +23,9 @@ from metagpt.utils.common import CodeParser @register_tool(include_functions=["ask_human", "reply_to_human"]) class RoleZero(Role): - """A role serving as the basis for other MGX roles.""" + """A role who can think and act dynamically""" + # Basic Info name: str = "Zero" profile: str = "RoleZero" goal: str = "" @@ -32,21 +33,26 @@ class RoleZero(Role): cmd_prompt: str = CMD_PROMPT instruction: str = ROLE_INSTRUCTION + # React Mode react_mode: Literal["react"] = "react" max_react_loop: int = 20 # used for react mode - user_requirement: str = "" - command_rsp: str = "" # the raw string containing the commands - commands: list[dict] = [] # commands to be executed - memory_k: int = 20 # number of memories (messages) to use as historical context - + # Tools tools: list[str] = [] # Use special symbol [""] to indicate use of all registered tools tool_recommender: ToolRecommender = None tool_execution_map: dict[str, callable] = {} special_tool_commands: list[str] = ["Plan.finish_current_task", "end"] + # Experience experience_retriever: ExpRetriever = DummyExpRetriever() + # Others + user_requirement: str = "" + command_rsp: str = "" # the raw string containing the commands + commands: list[dict] = [] # commands to be executed + memory_k: int = 20 # number of memories (messages) to use as historical context + use_fixed_sop: bool = False + @model_validator(mode="after") def set_plan_and_tool(self) -> "RoleZero": # We force using this parameter for DataAnalyst @@ -70,6 +76,10 @@ class RoleZero(Role): async def _think(self) -> bool: """Useful in 'react' mode. Use LLM to decide whether and what to do next.""" + # Compatibility + if self.use_fixed_sop: + return await super()._think() + ### 0. Preparation ### if not self.rc.todo and not self.rc.news: return False @@ -98,7 +108,7 @@ class RoleZero(Role): tools = await self.tool_recommender.recommend_tools() tool_info = json.dumps({tool.name: tool.schemas for tool in tools}) - ### Make Decision ### + ### Make Decision Dynamically ### prompt = self.cmd_prompt.format( plan_status=plan_status, current_task=current_task, @@ -114,6 +124,9 @@ class RoleZero(Role): return True async def _act(self) -> Message: + if self.use_fixed_sop: + return await super()._act() + try: commands = json.loads(CodeParser.parse_code(block=None, lang="json", text=self.command_rsp)) except Exception as e: diff --git a/metagpt/roles/di/team_leader.py b/metagpt/roles/di/team_leader.py index 3a4c71254..da9e2b3ed 100644 --- a/metagpt/roles/di/team_leader.py +++ b/metagpt/roles/di/team_leader.py @@ -22,6 +22,7 @@ class TeamLeader(RoleZero): max_react_loop: int = 1 # TeamLeader only reacts once each time tools: list[str] = ["Plan", "RoleZero", "TeamLeader"] + experience_retriever: ExpRetriever = SimpleExpRetriever() @model_validator(mode="after") diff --git a/tests/metagpt/roles/di/run_engineer2.py b/tests/metagpt/roles/di/run_engineer2.py new file mode 100644 index 000000000..3ce0457e5 --- /dev/null +++ b/tests/metagpt/roles/di/run_engineer2.py @@ -0,0 +1,106 @@ +import asyncio + +from metagpt.roles.di.engineer2 import Engineer2 + +DESIGN_DOC_2048 = '{"Implementation approach":"We will use the Pygame library to implement the 2048 game logic and user interface. Pygame is a set of Python modules designed for writing video games, which will help us create a responsive and visually appealing UI. For the mobile responsiveness, we will ensure that the game scales appropriately on different screen sizes. We will also use the Pygame GUI library to create buttons for restarting the game and choosing difficulty levels.","File list":["main.py","game.py","ui.py"],"Data structures and interfaces":"\\nclassDiagram\\n class Game {\\n -grid: list[list[int]]\\n -score: int\\n +__init__()\\n +move(direction: str) bool\\n +merge() bool\\n +spawn_tile() None\\n +is_game_over() bool\\n +reset() None\\n }\\n class UI {\\n -game: Game\\n +__init__(game: Game)\\n +draw_grid() None\\n +draw_score() None\\n +draw_buttons() None\\n +handle_input() None\\n }\\n class Main {\\n -ui: UI\\n +main() None\\n }\\n Main --> UI\\n UI --> Game\\n","Program call flow":"\\nsequenceDiagram\\n participant M as Main\\n participant U as UI\\n participant G as Game\\n M->>U: __init__(game)\\n U->>G: __init__()\\n M->>U: draw_grid()\\n U->>G: move(direction)\\n G-->>U: return bool\\n U->>G: merge()\\n G-->>U: return bool\\n U->>G: spawn_tile()\\n G-->>U: return None\\n U->>G: is_game_over()\\n G-->>U: return bool\\n U->>G: reset()\\n G-->>U: return None\\n M->>U: draw_score()\\n M->>U: draw_buttons()\\n M->>U: handle_input()\\n","Anything UNCLEAR":"Clarification needed on the specific design elements for the UI to ensure it meets the \'beautiful\' requirement. Additionally, we need to confirm the exact difficulty levels and how they should affect the game mechanics."}' + +TASK_DOC_2048 = '{"Required Python packages":["pygame==2.0.1","pygame_gui==0.5.7"],"Required Other language third-party packages":["No third-party dependencies required"],"Logic Analysis":[["game.py","Contains Game class with methods: __init__, move, merge, spawn_tile, is_game_over, reset"],["ui.py","Contains UI class with methods: __init__, draw_grid, draw_score, draw_buttons, handle_input"],["main.py","Contains Main class with method: main, initializes UI and Game"]],"Task list":["game.py","ui.py","main.py"],"Full API spec":"","Shared Knowledge":"`game.py` contains core game logic and state management. `ui.py` handles all user interface elements and interactions. `main.py` serves as the entry point to initialize and run the game.","Anything UNCLEAR":"Clarification needed on the specific design elements for the UI to ensure it meets the \'beautiful\' requirement. Additionally, we need to confirm the exact difficulty levels and how they should affect the game mechanics."}' + +DESIGN_DOC_SNAKE = """ +{ + "Implementation approach": "We will use the Pygame library to create the CLI-based snake game. Pygame is a set of Python modules designed for writing video games, which will help us handle graphics, sound, and input. The game will be structured into different modules to handle the main game loop, snake movement, food generation, collision detection, and user interface. We will ensure the game is engaging and responsive by optimizing the game loop and input handling. The score display and different speed levels will be implemented to enhance the user experience.", + "File list": [ + "main.py", + "game.py", + "snake.py", + "food.py", + "ui.py" + ], + "Data structures and interfaces": "\nclassDiagram\n class Main {\n +main() void\n }\n class Game {\n -Snake snake\n -Food food\n -int score\n -int speed\n +__init__(speed: int)\n +run() void\n +restart() void\n +update_score() void\n }\n class Snake {\n -list body\n -str direction\n +__init__()\n +move() void\n +change_direction(new_direction: str) void\n +check_collision() bool\n +grow() void\n }\n class Food {\n -tuple position\n +__init__()\n +generate_new_position() void\n }\n class UI {\n +display_score(score: int) void\n +display_game_over() void\n +display_game(snake: Snake, food: Food) void\n }\n Main --> Game\n Game --> Snake\n Game --> Food\n Game --> UI\n", + "Program call flow": "\nsequenceDiagram\n participant M as Main\n participant G as Game\n participant S as Snake\n participant F as Food\n participant U as UI\n M->>G: __init__(speed)\n M->>G: run()\n G->>S: __init__()\n G->>F: __init__()\n loop Game Loop\n G->>S: move()\n G->>S: check_collision()\n alt Collision Detected\n G->>G: restart()\n G->>U: display_game_over()\n else No Collision\n G->>F: generate_new_position()\n G->>S: grow()\n G->>G: update_score()\n G->>U: display_score(score)\n end\n G->>U: display_game(snake, food)\n end\n", + "Anything UNCLEAR": "Currently, all aspects of the project are clear." +} +""" +TASK_DOC_SNAKE = """ +{ + "Required Python packages": [ + "pygame==2.0.1" + ], + "Required Other language third-party packages": [ + "No third-party dependencies required" + ], + "Logic Analysis": [ + [ + "main.py", + "Contains the main function to initialize and start the game. Imports Game from game.py." + ], + [ + "game.py", + "Contains the Game class which manages the game loop, score, and speed. Imports Snake from snake.py, Food from food.py, and UI from ui.py." + ], + [ + "snake.py", + "Contains the Snake class which handles snake movement, direction changes, collision detection, and growth." + ], + [ + "food.py", + "Contains the Food class which handles food position generation." + ], + [ + "ui.py", + "Contains the UI class which handles displaying the score, game over screen, and the game state." + ] + ], + "Task list": [ + "snake.py", + "food.py", + "ui.py", + "game.py", + "main.py" + ], + "Full API spec": "", + "Shared Knowledge": "`game.py` contains the main game loop and integrates all other modules (snake, food, UI).", + "Anything UNCLEAR": "Currently, all aspects of the project are clear." +} +""" + +GAME_REQ_2048 = f""" +Create a 2048 game, follow the design doc and task doc. Write your code under /Users/gary/Files/temp/workspace/2048_game/src. +After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. +Notice: You MUST implement the full code, don't leave comment without implementation! +Design doc: +{TASK_DOC_2048} +Task doc: +{DESIGN_DOC_2048} +""" +GAME_REQ_SNAKE = f""" +Create a snake game, follow the design doc and task doc. Write your code under /Users/gary/Files/temp/workspace/snake_game/src. +After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. +Notice: You MUST implement the full code, don't leave comment without implementation! +Design doc: +{TASK_DOC_SNAKE} +Task doc: +{DESIGN_DOC_SNAKE} +""" +GAME_REQ_2048_NO_DOC = """ +Create a 2048 game with pygame. Write your code under /Users/gary/Files/temp/workspace/2048_game/src. +Consider what files you will write, break down the requests to multiple tasks and write one file in each task. +After writing all codes, write a code review for the codes, make improvement or adjustment based on the review. +Notice: You MUST implement the full code, don't leave comment without implementation! +""" +GAME_INC_REQ_2048 = """ +I found an issue with the 2048 code: when tiles are merged, no new tiles pop up. +Write code review for the codes (game.py, main.py, ui.py) under under /Users/gary/Files/temp/workspace/2048_game_bugs/src. +Then correct any issues you find. You can review all code in one time, and solve issues in one time. +""" +GAME_INC_REQ_SNAKE = """ +Found this issue, TypeError: generate_new_position() missing 1 required positional argument: 'snake_body' +Write code review for the codes (food.py, game.py, main.py, snake.py, ui.py) under under /Users/gary/Files/temp/workspace/snake_game_bugs/src. +Then correct any issues you find. You can review all code in one time, and solve issues in one time. +""" +CASUAL_CHAT = """what's your name?""" + + +if __name__ == "__main__": + engineer2 = Engineer2() + asyncio.run(engineer2.run(GAME_REQ_2048_NO_DOC))