add write_review action and its test

This commit is contained in:
geekan 2023-12-20 15:04:25 +08:00
parent 1ab0ae99a9
commit 6959d40e6d
4 changed files with 119 additions and 3 deletions

View file

@ -41,10 +41,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

View file

@ -0,0 +1,40 @@
#!/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
# from metagpt.llm import LLM
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="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")

View file

@ -18,7 +18,7 @@ import os
import platform
import re
import typing
from typing import List, Tuple, Union
from typing import List, Tuple, Union, get_args, get_origin
import aiofiles
import loguru
@ -129,8 +129,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():

View 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"]