mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-08 15:05:17 +02:00
Merge pull request #73 from qa6300525/2023-07-10_chengmaoyu
2023 07 10 chengmaoyu
This commit is contained in:
commit
a538f9a3ca
7 changed files with 175 additions and 68 deletions
|
|
@ -115,6 +115,8 @@ ## Tutorial: Initiating a startup
|
|||
|
||||
```shell
|
||||
python startup.py "Write a cli snake game"
|
||||
# Use code review will cost more money, but will opt for better code quality.
|
||||
python startup.py "Write a cli snake game" --code_review True
|
||||
```
|
||||
|
||||
After running the script, you can find your new project in the `workspace/` directory.
|
||||
|
|
|
|||
|
|
@ -70,6 +70,8 @@ ## 示例:启动一个创业公司
|
|||
|
||||
```shell
|
||||
python startup.py "写一个命令行贪吃蛇"
|
||||
# 开启code review模式会会花费更多的money, 但是会提升代码质量和成功率
|
||||
python startup.py "写一个命令行贪吃蛇" --code_review True
|
||||
```
|
||||
|
||||
运行脚本后,您可以在 `workspace/` 目录中找到您的新项目。
|
||||
|
|
|
|||
|
|
@ -11,30 +11,36 @@ from metagpt.const import WORKSPACE_ROOT
|
|||
from metagpt.logs import logger
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.common import CodeParser
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed
|
||||
|
||||
PROMPT_TEMPLATE = """
|
||||
NOTICE
|
||||
Role: You are a professional engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language)
|
||||
ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
|
||||
|
||||
## Code: {filename} Write code with triple quoto, based on the following list and context.
|
||||
1. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT.
|
||||
2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets
|
||||
3. Attention1: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE.
|
||||
4. Attention2: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN.
|
||||
5. Think before writing: What should be implemented and provided in this document?
|
||||
6. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
|
||||
7. Do not use public member functions that do not exist in your design.
|
||||
|
||||
-----
|
||||
# Context
|
||||
{context}
|
||||
-----
|
||||
NOTICE
|
||||
1. Role: You are an engineer; the main goal is to write PEP8 compliant, elegant, modular, easy to read and maintain Python 3.9 code (but you can also use other programming language)
|
||||
2. Requirement: Based on the context, implement one following code file, note to return only in code form, your code will be part of the entire project, so please implement complete, reliable, reusable code snippets
|
||||
3. Attention1: Use '##' to split sections, not '#', and '## <SECTION_NAME>' SHOULD WRITE BEFORE the code.
|
||||
4. Attention2: If there is any setting, ALWAYS SET A DEFAULT VALUE, ALWAYS USE STRONG TYPE AND EXPLICIT VARIABLE.
|
||||
5. Attention3: YOU MUST FOLLOW "Data structures and interface definitions". DONT CHANGE ANY DESIGN.
|
||||
6. Think before writing: What should be implemented and provided in this document?
|
||||
7. CAREFULLY CHECK THAT YOU DONT MISS ANY NECESSARY CLASS/FUNCTION IN THIS FILE.
|
||||
Attention: Use '##' to split sections, not '#', and '## <SECTION_NAME>' SHOULD WRITE BEFORE the code and triple quote.
|
||||
|
||||
## {filename}: Write code with triple quoto. Do your best to implement THIS ONLY ONE FILE. ONLY USE EXISTING API. IF NO API, IMPLEMENT IT.
|
||||
|
||||
## Format example
|
||||
-----
|
||||
## Code: {filename}
|
||||
```python
|
||||
## {filename}
|
||||
...
|
||||
```
|
||||
-----
|
||||
"""
|
||||
|
||||
## {filename}: Please encapsulate your code within triple quotes. Focus your efforts on implementing ONLY WITHIN THIS FILE. Any class or function labeled as MISSING-DESIGN should be implemented IN THIS FILE ALONE. Do NOT make changes to any other files.
|
||||
OUTPUT_MAPPING = {
|
||||
"{filename}": (str, ...),
|
||||
}
|
||||
|
||||
|
||||
class WriteCode(Action):
|
||||
def __init__(self, name="WriteCode", context: list[Message] = None, llm=None):
|
||||
|
|
@ -43,7 +49,7 @@ class WriteCode(Action):
|
|||
def _is_invalid(self, filename):
|
||||
return any(i in filename for i in ["mp3", "wav"])
|
||||
|
||||
def _save(self, context, filename, code_rsp):
|
||||
def _save(self, context, filename, code):
|
||||
# logger.info(filename)
|
||||
# logger.info(code_rsp)
|
||||
if self._is_invalid(filename):
|
||||
|
|
@ -57,16 +63,19 @@ class WriteCode(Action):
|
|||
ws_path = ws_path / ws_name
|
||||
code_path = ws_path / filename
|
||||
code_path.parent.mkdir(parents=True, exist_ok=True)
|
||||
code = CodeParser.parse_code(block="", text=code_rsp)
|
||||
code_path.write_text(code)
|
||||
logger.info(f"Saving Code to {code_path}")
|
||||
|
||||
async def run(self, **kwargs):
|
||||
prompt = PROMPT_TEMPLATE.format(**kwargs)
|
||||
filename = kwargs['filename']
|
||||
context = kwargs['context']
|
||||
logger.info(f'Writing {filename}..')
|
||||
@retry(stop=stop_after_attempt(2), wait=wait_fixed(1))
|
||||
async def write_code(self, prompt):
|
||||
code_rsp = await self._aask(prompt)
|
||||
code = CodeParser.parse_code(block="", text=code_rsp)
|
||||
return code
|
||||
|
||||
async def run(self, context, filename):
|
||||
prompt = PROMPT_TEMPLATE.format(context=context, filename=filename)
|
||||
logger.info(f'Writing {filename}..')
|
||||
code = await self.write_code(prompt)
|
||||
# code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING)
|
||||
self._save(context, filename, code_rsp)
|
||||
return code_rsp
|
||||
# self._save(context, filename, code)
|
||||
return code
|
||||
|
|
|
|||
|
|
@ -7,38 +7,75 @@
|
|||
"""
|
||||
|
||||
from metagpt.actions.action import Action
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.common import CodeParser
|
||||
from tenacity import retry, stop_after_attempt, wait_fixed
|
||||
|
||||
PROMPT_TEMPLATE = """
|
||||
Please review the following code:
|
||||
NOTICE
|
||||
Role: You are a professional software engineer, and your main task is to review the code. You need to ensure that the code conforms to the PEP8 standards, is elegantly designed and modularized, easy to read and maintain, and is written in Python 3.9 (or in another programming language).
|
||||
ATTENTION: Use '##' to SPLIT SECTIONS, not '#'. Output format carefully referenced "Format example".
|
||||
|
||||
## Code Review: Based on the following context and code, and following the check list, Provide key, clear, concise, and specific code modification suggestions, up to 5.
|
||||
```
|
||||
1. Check 0: Is the code implemented as per the requirements?
|
||||
2. Check 1: Are there any issues with the code logic?
|
||||
3. Check 2: Does the existing code follow the "Data structures and interface definitions"?
|
||||
4. Check 3: Is there a function in the code that is omitted or not fully implemented that needs to be implemented?
|
||||
5. Check 4: Does the code have unnecessary or lack dependencies?
|
||||
```
|
||||
|
||||
## Rewrite Code: {filename} Base on "Code Review" and the source code, rewrite code with triple quotes. Do your utmost to optimize THIS SINGLE FILE.
|
||||
-----
|
||||
# Context
|
||||
{context}
|
||||
|
||||
## Code: {filename}
|
||||
```
|
||||
{code}
|
||||
```
|
||||
-----
|
||||
|
||||
The main aspects you need to focus on include but are not limited to the code structure, coding standards, possible errors, and improvement suggestions.
|
||||
## Format example
|
||||
-----
|
||||
{format_example}
|
||||
-----
|
||||
|
||||
Please write your code review:
|
||||
"""
|
||||
|
||||
FORMAT_EXAMPLE = """
|
||||
|
||||
## Code Review
|
||||
1. The code ...
|
||||
2. ...
|
||||
3. ...
|
||||
4. ...
|
||||
5. ...
|
||||
|
||||
## Rewrite Code: {filename}
|
||||
```python
|
||||
## {filename}
|
||||
...
|
||||
```
|
||||
"""
|
||||
|
||||
|
||||
class WriteCodeReview(Action):
|
||||
def __init__(self, name, context=None, llm=None):
|
||||
def __init__(self, name="WriteCodeReview", context: list[Message] = None, llm=None):
|
||||
super().__init__(name, context, llm)
|
||||
|
||||
async def run(self, code):
|
||||
"""
|
||||
Generate a code review for the given code.
|
||||
@retry(stop=stop_after_attempt(2), wait=wait_fixed(1))
|
||||
async def write_code(self, prompt):
|
||||
code_rsp = await self._aask(prompt)
|
||||
code = CodeParser.parse_code(block="", text=code_rsp)
|
||||
return code
|
||||
|
||||
:param code: The code to be reviewed.
|
||||
:type code: str
|
||||
:return: The code review.
|
||||
:rtype: str
|
||||
"""
|
||||
# Set the context for the llm model
|
||||
self.context = {"code": code}
|
||||
|
||||
# Generate the prompt
|
||||
prompt = PROMPT_TEMPLATE.format(**self.context)
|
||||
|
||||
# Generate the code review
|
||||
self.input_data = prompt
|
||||
self.output_data = await self._aask(prompt)
|
||||
|
||||
return self.output_data
|
||||
async def run(self, context, code, filename):
|
||||
format_example = FORMAT_EXAMPLE.format(filename=filename)
|
||||
prompt = PROMPT_TEMPLATE.format(context=context, code=code, filename=filename, format_example=format_example)
|
||||
logger.info(f'Code review {filename}..')
|
||||
code = await self.write_code(prompt)
|
||||
# code_rsp = await self._aask_v1(prompt, "code_rsp", OUTPUT_MAPPING)
|
||||
# self._save(context, filename, code)
|
||||
return code
|
||||
|
|
|
|||
|
|
@ -10,10 +10,10 @@ import shutil
|
|||
from collections import OrderedDict
|
||||
from pathlib import Path
|
||||
|
||||
from metagpt.actions import WriteCode, WriteDesign, WriteTasks
|
||||
from metagpt.const import WORKSPACE_ROOT
|
||||
from metagpt.logs import logger
|
||||
from metagpt.roles import Role
|
||||
from metagpt.actions import WriteCode, WriteCodeReview, WriteTasks, WriteDesign
|
||||
from metagpt.schema import Message
|
||||
from metagpt.utils.common import CodeParser
|
||||
|
||||
|
|
@ -48,9 +48,12 @@ async def gather_ordered_k(coros, k) -> list:
|
|||
class Engineer(Role):
|
||||
def __init__(self, name="Alex", profile="Engineer", goal="Write elegant, readable, extensible, efficient code",
|
||||
constraints="The code you write should conform to code standard like PEP8, be modular, easy to read and maintain",
|
||||
n_borg=1):
|
||||
n_borg=1, use_code_review=False):
|
||||
super().__init__(name, profile, goal, constraints)
|
||||
self._init_actions([WriteCode])
|
||||
self.use_code_review = use_code_review
|
||||
if self.use_code_review:
|
||||
self._init_actions([WriteCode, WriteCodeReview])
|
||||
self._watch([WriteTasks])
|
||||
self.todos = []
|
||||
self.n_borg = n_borg
|
||||
|
|
@ -76,7 +79,8 @@ class Engineer(Role):
|
|||
if not msg:
|
||||
return WORKSPACE_ROOT / 'src'
|
||||
workspace = self.parse_workspace(msg)
|
||||
return WORKSPACE_ROOT / workspace
|
||||
# Codes are written in workspace/{package_name}/{package_name}
|
||||
return WORKSPACE_ROOT / workspace / workspace
|
||||
|
||||
def recreate_workspace(self):
|
||||
workspace = self.get_workspace()
|
||||
|
|
@ -130,6 +134,7 @@ class Engineer(Role):
|
|||
# logger.info(todo)
|
||||
# logger.info(code_rsp)
|
||||
# code = self.parse_code(code_rsp)
|
||||
self.write_file(todo, code_rsp)
|
||||
msg = Message(content=code_rsp, role=self.profile, cause_by=type(self._rc.todo))
|
||||
self._rc.memory.add(msg)
|
||||
|
||||
|
|
@ -137,5 +142,46 @@ class Engineer(Role):
|
|||
msg = Message(content="all done.", role=self.profile, cause_by=type(self._rc.todo))
|
||||
return msg
|
||||
|
||||
async def _act_sp_precision(self) -> Message:
|
||||
for todo in self.todos:
|
||||
"""
|
||||
# 从历史信息中挑选必须的信息,以减少prompt长度(人工经验总结)
|
||||
1. Architect全部
|
||||
2. ProjectManager全部
|
||||
3. 是否需要其他代码(暂时需要)?
|
||||
TODO:目标是不需要。在任务拆分清楚后,根据设计思路,不需要其他代码也能够写清楚单个文件,如果不能则表示还需要在定义的更清晰,这个是代码能够写长的关键
|
||||
"""
|
||||
context = []
|
||||
msg = self._rc.memory.get_by_actions([WriteDesign, WriteTasks, WriteCode])
|
||||
for m in msg:
|
||||
context.append(m.content)
|
||||
context_str = "\n".join(context)
|
||||
# 编写code
|
||||
code = await WriteCode().run(
|
||||
context=context_str,
|
||||
filename=todo
|
||||
)
|
||||
# code review
|
||||
if self.use_code_review:
|
||||
try:
|
||||
rewrite_code = await WriteCodeReview().run(
|
||||
context=context_str,
|
||||
code=code,
|
||||
filename=todo
|
||||
)
|
||||
code = rewrite_code
|
||||
except Exception as e:
|
||||
logger.error("code review failed!", e)
|
||||
pass
|
||||
self.write_file(todo, code)
|
||||
msg = Message(content=code, role=self.profile, cause_by=WriteCode)
|
||||
self._rc.memory.add(msg)
|
||||
|
||||
logger.info(f'Done {self.get_workspace()} generating.')
|
||||
msg = Message(content="all done.", role=self.profile, cause_by=WriteCode)
|
||||
return msg
|
||||
|
||||
async def _act(self) -> Message:
|
||||
if self.use_code_review:
|
||||
return await self._act_sp_precision()
|
||||
return await self._act_sp()
|
||||
|
|
|
|||
13
startup.py
13
startup.py
|
|
@ -8,23 +8,28 @@ from metagpt.roles import Architect, Engineer, ProductManager, ProjectManager
|
|||
from metagpt.software_company import SoftwareCompany
|
||||
|
||||
|
||||
async def startup(idea: str, investment: float = 3.0, n_round: int = 5):
|
||||
async def startup(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False):
|
||||
"""Run a startup. Be a boss."""
|
||||
company = SoftwareCompany()
|
||||
company.hire([ProductManager(), Architect(), ProjectManager(), Engineer(n_borg=5)])
|
||||
company.hire([ProductManager(),
|
||||
Architect(),
|
||||
ProjectManager(),
|
||||
Engineer(n_borg=5, use_code_review=code_review)])
|
||||
company.invest(investment)
|
||||
company.start_project(idea)
|
||||
await company.run(n_round=n_round)
|
||||
|
||||
|
||||
def main(idea: str, investment: float = 3.0, n_round: int = 5):
|
||||
def main(idea: str, investment: float = 3.0, n_round: int = 5, code_review: bool = False):
|
||||
"""
|
||||
We are a software startup comprised of AI. By investing in us, you are empowering a future filled with limitless possibilities.
|
||||
:param idea: Your innovative idea, such as "Creating a snake game."
|
||||
:param investment: As an investor, you have the opportunity to contribute a certain dollar amount to this AI company.
|
||||
:param n_round:
|
||||
:param code_review: Whether to use code review.
|
||||
:return:
|
||||
"""
|
||||
asyncio.run(startup(idea, investment, n_round))
|
||||
asyncio.run(startup(idea, investment, n_round, code_review))
|
||||
|
||||
|
||||
if __name__ == '__main__':
|
||||
|
|
|
|||
|
|
@ -13,23 +13,29 @@ from tests.metagpt.actions.mock import SEARCH_CODE_SAMPLE
|
|||
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_code_review():
|
||||
async def test_write_code_review(capfd):
|
||||
code = """
|
||||
def add(a, b):
|
||||
return a + b
|
||||
return a +
|
||||
"""
|
||||
write_code_review = WriteCodeReview("write_code_review")
|
||||
# write_code_review = WriteCodeReview("write_code_review")
|
||||
|
||||
review = await write_code_review.run(code)
|
||||
code = await WriteCodeReview().run(
|
||||
context="编写一个从a加b的函数,返回a+b",
|
||||
code=code,
|
||||
filename="math.py"
|
||||
)
|
||||
|
||||
# 我们不能精确地预测生成的代码评审,但我们可以检查返回的是否为字符串
|
||||
assert isinstance(review, str)
|
||||
assert len(review) > 0
|
||||
assert isinstance(code, str)
|
||||
assert len(code) > 0
|
||||
|
||||
captured = capfd.readouterr()
|
||||
print(f"输出内容: {captured.out}")
|
||||
|
||||
@pytest.mark.asyncio
|
||||
async def test_write_code_review_directly():
|
||||
code = SEARCH_CODE_SAMPLE
|
||||
write_code_review = WriteCodeReview("write_code_review")
|
||||
review = await write_code_review.run(code)
|
||||
logger.info(review)
|
||||
# @pytest.mark.asyncio
|
||||
# async def test_write_code_review_directly():
|
||||
# code = SEARCH_CODE_SAMPLE
|
||||
# write_code_review = WriteCodeReview("write_code_review")
|
||||
# review = await write_code_review.run(code)
|
||||
# logger.info(review)
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue