mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-08 15:05:17 +02:00
save doc in folder by default, change WriteTasks.run, add run examples
This commit is contained in:
parent
4b90d1d24f
commit
fcf254a691
6 changed files with 127 additions and 80 deletions
|
|
@ -11,7 +11,6 @@
|
|||
@Modified By: mashenquan, 2024/5/31. Implement Chapter 3 of RFC 236.
|
||||
"""
|
||||
import json
|
||||
import uuid
|
||||
from pathlib import Path
|
||||
from typing import List, Optional, Union
|
||||
|
||||
|
|
@ -87,84 +86,48 @@ class WriteDesign(Action):
|
|||
output_pathname (str, optional): The output path name of file that the system design should be saved to.
|
||||
|
||||
Returns:
|
||||
AIMessage: An AIMessage object containing the system design.
|
||||
str: The file path of the generated system design.
|
||||
|
||||
Example:
|
||||
# Write a new system design.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
|
||||
# Modify an exists system design.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> legacy_design_filename = "/path/to/exists/design/filename"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
|
||||
# Write a new system design with the given PRD(Product Requirement Document).
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> prd_filename = "/path/to/prd/filename"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
|
||||
# Modify an exists system design with the given PRD(Product Requirement Document).
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> prd_filename = "/path/to/prd/filename"
|
||||
>>> legacy_design_filename = "/path/to/exists/design/filename"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, prd_filename=prd_filename)
|
||||
>>> print(result)
|
||||
TSystem Design filename: "/path/to/design/filename"
|
||||
|
||||
# Write a new system design and save to the path name.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> user_requirement = "Write system design for a snake game"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> output_pathname = "/path/to/design/filename"
|
||||
>>> output_pathname = "snake_game/docs/system_design.json"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
System Design filename: "/absolute/path/to/snake_game/docs/system_design.json"
|
||||
|
||||
# Modify an exists system design and save to the path name.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
# Rewrite an existing system design and save to the path name.
|
||||
>>> user_requirement = "Write system design for a snake game, include new features such as a web UI"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> legacy_design_filename = "/path/to/exists/design/filename"
|
||||
>>> output_pathname = "/path/to/design/filename"
|
||||
>>> legacy_design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
|
||||
>>> output_pathname = "/absolute/path/to/snake_game/docs/system_design_new.json"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
System Design filename: "/absolute/path/to/snake_game/docs/system_design_new.json"
|
||||
|
||||
# Write a new system design with the given PRD(Product Requirement Document) and save to the path name.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
>>> user_requirement = "Write system design for a snake game based on the PRD at /absolute/path/to/snake_game/docs/prd.json"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> prd_filename = "/path/to/prd/filename"
|
||||
>>> output_pathname = "/path/to/design/filename"
|
||||
>>> prd_filename = "/absolute/path/to/snake_game/docs/prd.json"
|
||||
>>> output_pathname = "/absolute/path/to/snake_game/docs/sytem_design.json"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
System Design filename: "/absolute/path/to/snake_game/docs/sytem_design.json"
|
||||
|
||||
# Modify an exists system design with the given PRD(Product Requirement Document) and save to the path name.
|
||||
>>> user_requirement = "Your user requirements"
|
||||
# Rewrite an existing system design with the given PRD(Product Requirement Document) and save to the path name.
|
||||
>>> user_requirement = "Write system design for a snake game, include new features such as a web UI"
|
||||
>>> extra_info = "Your extra information"
|
||||
>>> prd_filename = "/path/to/prd/filename"
|
||||
>>> legacy_design_filename = "/path/to/exists/design/filename"
|
||||
>>> output_pathname = "/path/to/design/filename"
|
||||
>>> prd_filename = "/absolute/path/to/snake_game/docs/prd.json"
|
||||
>>> legacy_design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
|
||||
>>> output_pathname = "/absolute/path/to/snake_game/docs/system_design_new.json"
|
||||
>>> action = WriteDesign()
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, legacy_design_filename=legacy_design_filename, prd_filename=prd_filename, output_pathname=output_pathname)
|
||||
>>> result = await action.run(user_requirement=user_requirement, extra_info=extra_info, prd_filename=prd_filename, legacy_design_filename=legacy_design_filename, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
System Design filename: "/path/to/design/filename"
|
||||
System Design filename: "/absolute/path/to/snake_game/docs/system_design_new.json"
|
||||
"""
|
||||
if not with_messages:
|
||||
return await self._execute_api(
|
||||
|
|
@ -301,9 +264,10 @@ class WriteDesign(Action):
|
|||
)
|
||||
|
||||
if not output_pathname:
|
||||
output_path = DEFAULT_WORKSPACE_ROOT
|
||||
output_path.mkdir(parents=True, exist_ok=True)
|
||||
output_pathname = Path(output_path) / f"{uuid.uuid4().hex}.json"
|
||||
output_pathname = Path(output_pathname) / "docs" / "sytem_design.json"
|
||||
output_pathname.mkdir(parents=True, exist_ok=True)
|
||||
elif not Path(output_pathname).is_absolute():
|
||||
output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname
|
||||
output_pathname = Path(output_pathname)
|
||||
await awrite(filename=output_pathname, data=design.content)
|
||||
output_filename = output_pathname.parent / f"{output_pathname.stem}-class-diagram"
|
||||
|
|
|
|||
|
|
@ -19,11 +19,16 @@ from pydantic import BaseModel, Field
|
|||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.actions.project_management_an import PM_NODE, REFINED_PM_NODE
|
||||
from metagpt.const import PACKAGE_REQUIREMENTS_FILENAME
|
||||
from metagpt.const import DEFAULT_WORKSPACE_ROOT, PACKAGE_REQUIREMENTS_FILENAME
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import AIMessage, Document, Documents, Message
|
||||
from metagpt.tools.tool_registry import register_tool
|
||||
from metagpt.utils.common import aread, to_markdown_code_block
|
||||
from metagpt.utils.common import (
|
||||
aread,
|
||||
awrite,
|
||||
save_json_to_markdown,
|
||||
to_markdown_code_block,
|
||||
)
|
||||
from metagpt.utils.project_repo import ProjectRepo
|
||||
from metagpt.utils.report import DocsReporter
|
||||
|
||||
|
|
@ -44,7 +49,13 @@ class WriteTasks(Action):
|
|||
input_args: Optional[BaseModel] = Field(default=None, exclude=True)
|
||||
|
||||
async def run(
|
||||
self, with_messages: List[Message] = None, *, user_requirement: str = "", design_filename: str = "", **kwargs
|
||||
self,
|
||||
with_messages: List[Message] = None,
|
||||
*,
|
||||
user_requirement: str = "",
|
||||
design_filename: str = "",
|
||||
output_pathname: str = "",
|
||||
**kwargs,
|
||||
) -> Union[AIMessage, str]:
|
||||
"""
|
||||
Write a project schedule given a project system design file.
|
||||
|
|
@ -52,29 +63,33 @@ class WriteTasks(Action):
|
|||
Args:
|
||||
user_requirement (str, optional): A string specifying the user's requirements. Defaults to an empty string.
|
||||
design_filename (str): The filename of the project system design file. Defaults to an empty string.
|
||||
output_pathname (str, optional): The output path name of file that the project schedule should be saved to.
|
||||
**kwargs: Additional keyword arguments.
|
||||
|
||||
Returns:
|
||||
AIMessage: The generated project schedule.
|
||||
str: Path to the generated project schedule.
|
||||
|
||||
Example:
|
||||
# Write a new project schedule.
|
||||
>>> design_filename = "/path/to/design/filename"
|
||||
# Write a project schedule with a given system design.
|
||||
>>> design_filename = "/absolute/path/to/snake_game/docs/system_design.json"
|
||||
>>> output_pathname = "/absolute/path/to/snake_game/docs/project_schedule.json"
|
||||
>>> action = WriteTasks()
|
||||
>>> result = await action.run(design_filename=design_filename)
|
||||
>>> result = await action.run(design_filename=design_filename, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
The project schedule is balabala...
|
||||
The project schedule is at /absolute/path/to/snake_game/docs/project_schedule.json
|
||||
|
||||
# Write a new project schedule with the user requirement.
|
||||
>>> design_filename = "/path/to/design/filename"
|
||||
>>> user_requirement = "Your user requirements"
|
||||
# Write a project schedule with a user requirement.
|
||||
>>> user_requirement = "Write project schedule for a snake game following these requirements: ..."
|
||||
>>> output_pathname = "/absolute/path/to/snake_game/docs/project_schedule.json"
|
||||
>>> action = WriteTasks()
|
||||
>>> result = await action.run(design_filename=design_filename, user_requirement=user_requirement)
|
||||
>>> result = await action.run(user_requirement=user_requirement, output_pathname=output_pathname)
|
||||
>>> print(result)
|
||||
The project schedule is balabala...
|
||||
The project schedule is at /absolute/path/to/snake_game/docs/project_schedule.json
|
||||
"""
|
||||
if not with_messages:
|
||||
return await self._execute_api(user_requirement=user_requirement, design_filename=design_filename)
|
||||
return await self._execute_api(
|
||||
user_requirement=user_requirement, design_filename=design_filename, output_pathname=output_pathname
|
||||
)
|
||||
|
||||
self.input_args = with_messages[-1].instruct_content
|
||||
self.repo = ProjectRepo(self.input_args.project_path)
|
||||
|
|
@ -158,10 +173,23 @@ class WriteTasks(Action):
|
|||
packages.add(pkg)
|
||||
await self.repo.save(filename=PACKAGE_REQUIREMENTS_FILENAME, content="\n".join(packages))
|
||||
|
||||
async def _execute_api(self, user_requirement: str = "", design_filename: str = "") -> str:
|
||||
async def _execute_api(
|
||||
self, user_requirement: str = "", design_filename: str = "", output_pathname: str = ""
|
||||
) -> str:
|
||||
context = to_markdown_code_block(user_requirement)
|
||||
if not design_filename:
|
||||
if design_filename:
|
||||
content = await aread(filename=design_filename)
|
||||
context += to_markdown_code_block(content)
|
||||
node = await self._run_new_tasks(context)
|
||||
return node.instruct_content.model_dump_json()
|
||||
file_content = node.instruct_content.model_dump_json()
|
||||
|
||||
if not output_pathname:
|
||||
output_pathname = Path(output_pathname) / "docs" / "project_schedule.json"
|
||||
output_pathname.mkdir(parents=True, exist_ok=True)
|
||||
elif not Path(output_pathname).is_absolute():
|
||||
output_pathname = DEFAULT_WORKSPACE_ROOT / output_pathname
|
||||
output_pathname = Path(output_pathname)
|
||||
await awrite(filename=output_pathname, data=file_content)
|
||||
await save_json_to_markdown(content=file_content, output_filename=output_pathname.with_suffix(".md"))
|
||||
|
||||
return f'Project Schedule filename: "{str(output_pathname)}"'
|
||||
|
|
|
|||
|
|
@ -195,7 +195,7 @@ class RoleZero(Role):
|
|||
outputs.append(output)
|
||||
except Exception as e:
|
||||
tb = traceback.format_exc()
|
||||
logger.exception(e + tb)
|
||||
logger.exception(str(e) + tb)
|
||||
outputs.append(output + f": {tb}")
|
||||
break # Stop executing if any command fails
|
||||
else:
|
||||
|
|
|
|||
|
|
@ -2,6 +2,7 @@ import asyncio
|
|||
import os
|
||||
|
||||
from metagpt.roles.architect import Architect
|
||||
from metagpt.schema import Message
|
||||
|
||||
DESIGN_DOC_SNAKE = """
|
||||
{
|
||||
|
|
@ -30,9 +31,9 @@ async def main(requirement):
|
|||
with open("temp_design.json", "w") as f:
|
||||
f.write(DESIGN_DOC_SNAKE)
|
||||
architect = Architect()
|
||||
await architect.run(requirement)
|
||||
await architect.run(Message(content=requirement, send_to="Bob"))
|
||||
os.remove("temp_design.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main(WRITE_SNAKE))
|
||||
asyncio.run(main(REWRITE_SNAKE))
|
||||
|
|
|
|||
18
tests/metagpt/roles/di/run_product_manager.py
Normal file
18
tests/metagpt/roles/di/run_product_manager.py
Normal file
|
|
@ -0,0 +1,18 @@
|
|||
import asyncio
|
||||
|
||||
from metagpt.roles.product_manager import ProductManager
|
||||
|
||||
WRITE_2048 = """Write a PRD for a cli 2048 game"""
|
||||
|
||||
REWRITE_2048 = """Rewrite the prd at /Users/gary/Files/temp/workspace/2048_game/docs/prd.json, add a web UI"""
|
||||
|
||||
CASUAL_CHAT = """What's your name?"""
|
||||
|
||||
|
||||
async def main(requirement):
|
||||
product_manager = ProductManager()
|
||||
await product_manager.run(requirement)
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main(WRITE_2048))
|
||||
36
tests/metagpt/roles/di/run_project_manager.py
Normal file
36
tests/metagpt/roles/di/run_project_manager.py
Normal file
|
|
@ -0,0 +1,36 @@
|
|||
import asyncio
|
||||
import os
|
||||
|
||||
from metagpt.roles.project_manager import ProjectManager
|
||||
from metagpt.schema import Message
|
||||
|
||||
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."}'
|
||||
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."
|
||||
}
|
||||
"""
|
||||
REQ = """Write a project schedule based on the design at temp_design.json"""
|
||||
CASUAL_CHAT = """what's your name?"""
|
||||
|
||||
|
||||
async def main(requirement):
|
||||
with open("temp_design.json", "w") as f:
|
||||
f.write(DESIGN_DOC_2048)
|
||||
project_manager = ProjectManager()
|
||||
await project_manager.run(Message(content=requirement, send_to="Eve"))
|
||||
os.remove("temp_design.json")
|
||||
|
||||
|
||||
if __name__ == "__main__":
|
||||
asyncio.run(main(REQ))
|
||||
Loading…
Add table
Add a link
Reference in a new issue