mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-05-24 14:15:17 +02:00
Merge branch 'new_main' of github.com:better629/MetaGPT into new_main
This commit is contained in:
commit
86b167da93
7 changed files with 134 additions and 12 deletions
|
|
@ -4,6 +4,9 @@
|
|||
@Time : 2023/12/11 18:45
|
||||
@Author : alexanderwu
|
||||
@File : action_node.py
|
||||
|
||||
NOTE: You should use typing.List instead of list to do type annotation. Because in the markdown extraction process,
|
||||
we can use typing to extract the type of the node, but we cannot use built-in list to extract.
|
||||
"""
|
||||
import json
|
||||
from typing import Any, Dict, Generic, List, Optional, Tuple, Type, TypeVar
|
||||
|
|
@ -41,10 +44,10 @@ Fill in the above nodes based on the format example.
|
|||
"""
|
||||
|
||||
|
||||
def dict_to_markdown(d, prefix="-", postfix="\n"):
|
||||
def dict_to_markdown(d, prefix="##", kv_sep="\n", postfix="\n"):
|
||||
markdown_str = ""
|
||||
for key, value in d.items():
|
||||
markdown_str += f"{prefix} {key}: {value}{postfix}"
|
||||
markdown_str += f"{prefix}{key}{kv_sep}{value}{postfix}"
|
||||
return markdown_str
|
||||
|
||||
|
||||
|
|
|
|||
|
|
@ -5,6 +5,8 @@
|
|||
@Author : alexanderwu
|
||||
@File : design_api_an.py
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.logs import logger
|
||||
from metagpt.utils.mermaid import MMC1, MMC2
|
||||
|
|
@ -22,7 +24,7 @@ PROJECT_NAME = ActionNode(
|
|||
|
||||
FILE_LIST = ActionNode(
|
||||
key="File list",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Only need relative paths. ALWAYS write a main.py or app.py here",
|
||||
example=["main.py", "game.py"],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,26 +5,28 @@
|
|||
@Author : alexanderwu
|
||||
@File : project_management_an.py
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.logs import logger
|
||||
|
||||
REQUIRED_PYTHON_PACKAGES = ActionNode(
|
||||
key="Required Python packages",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Provide required Python packages in requirements.txt format.",
|
||||
example=["flask==1.1.2", "bcrypt==3.2.0"],
|
||||
)
|
||||
|
||||
REQUIRED_OTHER_LANGUAGE_PACKAGES = ActionNode(
|
||||
key="Required Other language third-party packages",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="List down the required packages for languages other than Python.",
|
||||
example=["No third-party dependencies required"],
|
||||
)
|
||||
|
||||
LOGIC_ANALYSIS = ActionNode(
|
||||
key="Logic Analysis",
|
||||
expected_type=list[list[str]],
|
||||
expected_type=List[List[str]],
|
||||
instruction="Provide a list of files with the classes/methods/functions to be implemented, "
|
||||
"including dependency analysis and imports.",
|
||||
example=[
|
||||
|
|
@ -35,7 +37,7 @@ LOGIC_ANALYSIS = ActionNode(
|
|||
|
||||
TASK_LIST = ActionNode(
|
||||
key="Task list",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Break down the tasks into a list of filenames, prioritized by dependency order.",
|
||||
example=["game.py", "main.py"],
|
||||
)
|
||||
|
|
|
|||
|
|
@ -5,6 +5,7 @@
|
|||
@Author : alexanderwu
|
||||
@File : write_prd_an.py
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
from metagpt.logs import logger
|
||||
|
|
@ -39,14 +40,14 @@ PROJECT_NAME = ActionNode(
|
|||
|
||||
PRODUCT_GOALS = ActionNode(
|
||||
key="Product Goals",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Provide up to three clear, orthogonal product goals.",
|
||||
example=["Create an engaging user experience", "Improve accessibility, be responsive", "More beautiful UI"],
|
||||
)
|
||||
|
||||
USER_STORIES = ActionNode(
|
||||
key="User Stories",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Provide up to 3 to 5 scenario-based user stories.",
|
||||
example=[
|
||||
"As a player, I want to be able to choose difficulty levels",
|
||||
|
|
@ -59,7 +60,7 @@ USER_STORIES = ActionNode(
|
|||
|
||||
COMPETITIVE_ANALYSIS = ActionNode(
|
||||
key="Competitive Analysis",
|
||||
expected_type=list[str],
|
||||
expected_type=List[str],
|
||||
instruction="Provide 5 to 7 competitive products.",
|
||||
example=[
|
||||
"2048 Game A: Simple interface, lacks responsive features",
|
||||
|
|
@ -98,7 +99,7 @@ REQUIREMENT_ANALYSIS = ActionNode(
|
|||
|
||||
REQUIREMENT_POOL = ActionNode(
|
||||
key="Requirement Pool",
|
||||
expected_type=list[list[str]],
|
||||
expected_type=List[List[str]],
|
||||
instruction="List down the top-5 requirements with their priority (P0, P1, P2).",
|
||||
example=[["P0", "The main code ..."], ["P0", "The game algorithm ..."]],
|
||||
)
|
||||
|
|
|
|||
38
metagpt/actions/write_review.py
Normal file
38
metagpt/actions/write_review.py
Normal file
|
|
@ -0,0 +1,38 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Author : alexanderwu
|
||||
@File : write_review.py
|
||||
"""
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions import Action
|
||||
from metagpt.actions.action_node import ActionNode
|
||||
|
||||
REVIEW = ActionNode(
|
||||
key="Review",
|
||||
expected_type=List[str],
|
||||
instruction="Act as an experienced Reviewer and review the given output. Ask a series of critical questions, "
|
||||
"concisely and clearly, to help the writer improve their work.",
|
||||
example=[
|
||||
"This is a good PRD, but I think it can be improved by adding more details.",
|
||||
],
|
||||
)
|
||||
|
||||
LGTM = ActionNode(
|
||||
key="LGTM",
|
||||
expected_type=str,
|
||||
instruction="LGTM/LBTM. If the output is good enough, give a LGTM (Looks Good To Me) to the writer, "
|
||||
"else LBTM (Looks Bad To Me).",
|
||||
example="LGTM",
|
||||
)
|
||||
|
||||
WRITE_REVIEW_NODE = ActionNode.from_children("WRITE_REVIEW_NODE", [REVIEW, LGTM])
|
||||
|
||||
|
||||
class WriteReview(Action):
|
||||
"""This class allows LLM to further mine noteworthy details based on specific "##TOPIC"(discussion topic) and
|
||||
"##RECORD" (discussion records), thereby deepening the discussion."""
|
||||
|
||||
async def run(self, context):
|
||||
return await WRITE_REVIEW_NODE.fill(context=context, llm=self.llm, schema="markdown")
|
||||
|
|
@ -23,7 +23,7 @@ import traceback
|
|||
import typing
|
||||
from pathlib import Path
|
||||
from typing import Any
|
||||
from typing import List, Tuple, Union
|
||||
from typing import List, Tuple, Union, get_args, get_origin
|
||||
|
||||
import aiofiles
|
||||
import loguru
|
||||
|
|
@ -135,8 +135,31 @@ class OutputParser:
|
|||
parsed_data[block] = content
|
||||
return parsed_data
|
||||
|
||||
@staticmethod
|
||||
def extract_content(text, tag="CONTENT"):
|
||||
# Use regular expression to extract content between [CONTENT] and [/CONTENT]
|
||||
extracted_content = re.search(rf"\[{tag}\](.*?)\[/{tag}\]", text, re.DOTALL)
|
||||
|
||||
if extracted_content:
|
||||
return extracted_content.group(1).strip()
|
||||
else:
|
||||
return "No content found between [CONTENT] and [/CONTENT] tags."
|
||||
|
||||
@staticmethod
|
||||
def is_supported_list_type(i):
|
||||
origin = get_origin(i)
|
||||
if origin is not List:
|
||||
return False
|
||||
|
||||
args = get_args(i)
|
||||
if args == (str,) or args == (Tuple[str, str],) or args == (List[str],):
|
||||
return True
|
||||
|
||||
return False
|
||||
|
||||
@classmethod
|
||||
def parse_data_with_mapping(cls, data, mapping):
|
||||
data = cls.extract_content(text=data)
|
||||
block_dict = cls.parse_blocks(data)
|
||||
parsed_data = {}
|
||||
for block, content in block_dict.items():
|
||||
|
|
|
|||
53
tests/metagpt/actions/test_write_review.py
Normal file
53
tests/metagpt/actions/test_write_review.py
Normal file
|
|
@ -0,0 +1,53 @@
|
|||
#!/usr/bin/env python
|
||||
# -*- coding: utf-8 -*-
|
||||
"""
|
||||
@Time : 2023/12/20 15:01
|
||||
@Author : alexanderwu
|
||||
@File : test_write_review.py
|
||||
"""
|
||||
import pytest
|
||||
|
||||
from metagpt.actions.write_review import WriteReview
|
||||
|
||||
CONTEXT = """
|
||||
{
|
||||
"Language": "zh_cn",
|
||||
"Programming Language": "Python",
|
||||
"Original Requirements": "写一个简单的2048",
|
||||
"Project Name": "game_2048",
|
||||
"Product Goals": [
|
||||
"创建一个引人入胜的用户体验",
|
||||
"确保高性能",
|
||||
"提供可定制的功能"
|
||||
],
|
||||
"User Stories": [
|
||||
"作为用户,我希望能够选择不同的难度级别",
|
||||
"作为玩家,我希望在每局游戏结束后能看到我的得分"
|
||||
],
|
||||
"Competitive Analysis": [
|
||||
"Python Snake Game: 界面简单,缺乏高级功能"
|
||||
],
|
||||
"Competitive Quadrant Chart": "quadrantChart\n title \"Reach and engagement of campaigns\"\n x-axis \"Low Reach\" --> \"High Reach\"\n y-axis \"Low Engagement\" --> \"High Engagement\"\n quadrant-1 \"我们应该扩展\"\n quadrant-2 \"需要推广\"\n quadrant-3 \"重新评估\"\n quadrant-4 \"可能需要改进\"\n \"Campaign A\": [0.3, 0.6]\n \"Campaign B\": [0.45, 0.23]\n \"Campaign C\": [0.57, 0.69]\n \"Campaign D\": [0.78, 0.34]\n \"Campaign E\": [0.40, 0.34]\n \"Campaign F\": [0.35, 0.78]\n \"Our Target Product\": [0.5, 0.6]",
|
||||
"Requirement Analysis": "产品应该用户友好。",
|
||||
"Requirement Pool": [
|
||||
[
|
||||
"P0",
|
||||
"主要代码..."
|
||||
],
|
||||
[
|
||||
"P0",
|
||||
"游戏算法..."
|
||||
]
|
||||
],
|
||||
"UI Design draft": "基本功能描述,简单的风格和布局。",
|
||||
"Anything UNCLEAR": "..."
|
||||
}
|
||||
"""
|
||||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_review():
|
||||
write_review = WriteReview()
|
||||
review = await write_review.run(CONTEXT)
|
||||
assert review.instruct_content
|
||||
assert review.get("LGTM") in ["LGTM", "LBTM"]
|
||||
Loading…
Add table
Add a link
Reference in a new issue