mirror of
https://github.com/FoundationAgents/MetaGPT.git
synced 2026-06-08 15:05:17 +02:00
refactor: write prd & system design
This commit is contained in:
parent
9339eab20c
commit
e8131652de
3 changed files with 123 additions and 35 deletions
|
|
@ -5,13 +5,21 @@
|
|||
@Author : alexanderwu
|
||||
@File : design_api.py
|
||||
"""
|
||||
import json
|
||||
import shutil
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions import Action, ActionOutput
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import PRDS_FILE_REPO, SYSTEM_DESIGN_FILE_REPO, WORKSPACE_ROOT
|
||||
from metagpt.const import (
|
||||
DATA_API_DESIGN_FILE_REPO,
|
||||
PRDS_FILE_REPO,
|
||||
SEQ_FLOW_FILE_REPO,
|
||||
SYSTEM_DESIGN_FILE_REPO,
|
||||
SYSTEM_DESIGN_PDF_FILE_REPO,
|
||||
WORKSPACE_ROOT,
|
||||
)
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Document, Documents
|
||||
from metagpt.utils.common import CodeParser
|
||||
|
|
@ -214,40 +222,29 @@ class WriteDesign(Action):
|
|||
# 对于那些发生变动的PRD和设计文档,重新生成设计内容;
|
||||
changed_files = Documents()
|
||||
for filename in changed_prds.keys():
|
||||
prd = await prds_file_repo.get(filename)
|
||||
old_system_design_doc = await system_design_file_repo.get(filename)
|
||||
if not old_system_design_doc:
|
||||
system_design = await self._run(context=prd.content)
|
||||
doc = Document(
|
||||
root_path=SYSTEM_DESIGN_FILE_REPO, filename=filename, content=system_design.instruct_content.json()
|
||||
)
|
||||
else:
|
||||
doc = await self._merge(prd_doc=prd, system_design_doc=old_system_design_doc)
|
||||
await system_design_file_repo.save(
|
||||
filename=filename, content=doc.content, dependencies={prd.root_relative_path}
|
||||
doc = await self._update_system_design(
|
||||
filename=filename, prds_file_repo=prds_file_repo, system_design_file_repo=system_design_file_repo
|
||||
)
|
||||
changed_files.docs[filename] = doc
|
||||
|
||||
for filename in changed_system_designs.keys():
|
||||
if filename in changed_files.docs:
|
||||
continue
|
||||
prd_doc = await prds_file_repo.get(filename=filename)
|
||||
old_system_design_doc = await system_design_file_repo.get(filename)
|
||||
new_system_design_doc = await self._merge(prd_doc, old_system_design_doc)
|
||||
await system_design_file_repo.save(
|
||||
filename=filename, content=new_system_design_doc.content, dependencies={prd_doc.root_relative_path}
|
||||
doc = await self._update_system_design(
|
||||
filename=filename, prds_file_repo=prds_file_repo, system_design_file_repo=system_design_file_repo
|
||||
)
|
||||
changed_files.docs[filename] = new_system_design_doc
|
||||
changed_files.docs[filename] = doc
|
||||
|
||||
# 等docs/system_designs/下所有文件都处理完才发publish message,给后续做全局优化留空间。
|
||||
return ActionOutput(content=changed_files.json(), instruct_content=changed_files)
|
||||
|
||||
async def _run(self, context, format=CONFIG.prompt_format):
|
||||
async def _new_system_design(self, context, format=CONFIG.prompt_format):
|
||||
prompt_template, format_example = get_template(templates, format)
|
||||
prompt = prompt_template.format(context=context, format_example=format_example)
|
||||
# system_design = await self._aask(prompt)
|
||||
system_design = await self._aask_v1(prompt, "system_design", OUTPUT_MAPPING, format=format)
|
||||
# fix Python package name, we can't system_design.instruct_content.python_package_name = "xxx" since "Python package name" contain space, have to use setattr
|
||||
# fix Python package name, we can't system_design.instruct_content.python_package_name = "xxx" since "Python
|
||||
# package name" contain space, have to use setattr
|
||||
setattr(
|
||||
system_design.instruct_content,
|
||||
"Python package name",
|
||||
|
|
@ -268,3 +265,49 @@ class WriteDesign(Action):
|
|||
else:
|
||||
ws_name = CodeParser.parse_str(block="Python package name", text=system_design)
|
||||
CONFIG.git_repo.rename_root(ws_name)
|
||||
|
||||
async def _update_system_design(self, filename, prds_file_repo, system_design_file_repo) -> Document:
|
||||
prd = await prds_file_repo.get(filename)
|
||||
old_system_design_doc = await system_design_file_repo.get(filename)
|
||||
if not old_system_design_doc:
|
||||
system_design = await self._new_system_design(context=prd.content)
|
||||
doc = Document(
|
||||
root_path=SYSTEM_DESIGN_FILE_REPO, filename=filename, content=system_design.instruct_content.json()
|
||||
)
|
||||
else:
|
||||
doc = await self._merge(prd_doc=prd, system_design_doc=old_system_design_doc)
|
||||
await system_design_file_repo.save(
|
||||
filename=filename, content=doc.content, dependencies={prd.root_relative_path}
|
||||
)
|
||||
await self._save_data_api_design(doc)
|
||||
await self._save_seq_flow(doc)
|
||||
await self._save_pdf(doc)
|
||||
return doc
|
||||
|
||||
@staticmethod
|
||||
async def _save_data_api_design(design_doc):
|
||||
m = json.loads(design_doc.content)
|
||||
data_api_design = m.get("Data structures and interface definitions")
|
||||
if not data_api_design:
|
||||
return
|
||||
path = CONFIG.git_repo.workdir / DATA_API_DESIGN_FILE_REPO
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exists_ok=True)
|
||||
await mermaid_to_file(data_api_design, path / Path(design_doc).with_suffix(".mmd"))
|
||||
|
||||
@staticmethod
|
||||
async def _save_seq_flow(design_doc):
|
||||
m = json.loads(design_doc.content)
|
||||
seq_flow = m.get("Program call flow")
|
||||
if not seq_flow:
|
||||
return
|
||||
path = CONFIG.git_repo.workdir / SEQ_FLOW_FILE_REPO
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exists_ok=True)
|
||||
await mermaid_to_file(seq_flow, path / Path(design_doc).with_suffix(".mmd"))
|
||||
|
||||
@staticmethod
|
||||
async def _save_pdf(design_doc):
|
||||
m = json.loads(design_doc.content)
|
||||
file_repo = CONFIG.git_repo.new_file_repository(SYSTEM_DESIGN_PDF_FILE_REPO)
|
||||
await file_repo.save(filename=design_doc.filename, content=json_to_markdown(m))
|
||||
|
|
|
|||
|
|
@ -5,16 +5,28 @@
|
|||
@Author : alexanderwu
|
||||
@File : write_prd.py
|
||||
"""
|
||||
from __future__ import annotations
|
||||
|
||||
import json
|
||||
from pathlib import Path
|
||||
from typing import List
|
||||
|
||||
from metagpt.actions import Action, ActionOutput
|
||||
from metagpt.actions.search_and_summarize import SearchAndSummarize
|
||||
from metagpt.config import CONFIG
|
||||
from metagpt.const import DOCS_FILE_REPO, PRDS_FILE_REPO, REQUIREMENT_FILENAME
|
||||
from metagpt.const import (
|
||||
COMPETITIVE_ANALYSIS_FILE_REPO,
|
||||
DOCS_FILE_REPO,
|
||||
PRD_PDF_FILE_REPO,
|
||||
PRDS_FILE_REPO,
|
||||
REQUIREMENT_FILENAME,
|
||||
)
|
||||
from metagpt.logs import logger
|
||||
from metagpt.schema import Document, Documents
|
||||
from metagpt.utils.file_repository import FileRepository
|
||||
from metagpt.utils.get_template import get_template
|
||||
from metagpt.utils.json_to_markdown import json_to_markdown
|
||||
from metagpt.utils.mermaid import mermaid_to_file
|
||||
|
||||
templates = {
|
||||
"json": {
|
||||
|
|
@ -233,22 +245,15 @@ class WritePRD(Action):
|
|||
prd_docs = await prds_file_repo.get_all()
|
||||
change_files = Documents()
|
||||
for prd_doc in prd_docs:
|
||||
if await self._is_relative_to(requirement_doc, prd_doc):
|
||||
prd_doc = await self._merge(requirement_doc, prd_doc)
|
||||
await prds_file_repo.save(filename=prd_doc.filename, content=prd_doc.content)
|
||||
change_files.docs[prd_doc.filename] = prd_doc
|
||||
prd_doc = await self._update_prd(requirement_doc, prd_doc, prds_file_repo, *args, **kwargs)
|
||||
if not prd_doc:
|
||||
continue
|
||||
change_files.docs[prd_doc.filename] = prd_doc
|
||||
# 如果没有任何PRD,就使用docs/requirement.txt生成一个prd
|
||||
if not change_files.docs:
|
||||
prd = await self._run_new_requirement(
|
||||
requirements=[requirement_doc.content], format=format, *args, **kwargs
|
||||
)
|
||||
doc = Document(
|
||||
root_path=PRDS_FILE_REPO,
|
||||
filename=FileRepository.new_file_name() + ".json",
|
||||
content=prd.instruct_content.json(),
|
||||
)
|
||||
await prds_file_repo.save(filename=doc.filename, content=doc.content)
|
||||
change_files.docs[doc.filename] = doc
|
||||
prd_doc = await self._update_prd(requirement_doc, None, prds_file_repo)
|
||||
if prd_doc:
|
||||
change_files.docs[prd_doc.filename] = prd_doc
|
||||
# 等docs/prds/下所有文件都与新增需求对比完后,再触发publish message让工作流跳转到下一环节。如此设计是为了给后续做全局优化留空间。
|
||||
return ActionOutput(content=change_files.json(), instruct_content=change_files)
|
||||
|
||||
|
|
@ -275,3 +280,38 @@ class WritePRD(Action):
|
|||
|
||||
async def _merge(self, doc1, doc2) -> Document:
|
||||
pass
|
||||
|
||||
async def _update_prd(self, requirement_doc, prd_doc, prds_file_repo, *args, **kwargs) -> Document | None:
|
||||
if not prd_doc:
|
||||
prd = await self._run_new_requirement(
|
||||
requirements=[requirement_doc.content], format=format, *args, **kwargs
|
||||
)
|
||||
new_prd_doc = Document(
|
||||
root_path=PRDS_FILE_REPO,
|
||||
filename=FileRepository.new_file_name() + ".json",
|
||||
content=prd.instruct_content.json(),
|
||||
)
|
||||
elif await self._is_relative_to(requirement_doc, prd_doc):
|
||||
new_prd_doc = await self._merge(requirement_doc, prd_doc)
|
||||
else:
|
||||
return None
|
||||
await prds_file_repo.save(filename=new_prd_doc.filename, content=new_prd_doc.content)
|
||||
await self._save_competitive_analysis(new_prd_doc)
|
||||
await self._save_pdf(new_prd_doc)
|
||||
|
||||
@staticmethod
|
||||
async def _save_competitive_analysis(prd_doc):
|
||||
m = json.loads(prd_doc.content)
|
||||
quadrant_chart = m.get("Competitive Quadrant Chart")
|
||||
if not quadrant_chart:
|
||||
return
|
||||
path = CONFIG.git_repo.workdir / COMPETITIVE_ANALYSIS_FILE_REPO
|
||||
if not path.exists():
|
||||
path.mkdir(parents=True, exists_ok=True)
|
||||
await mermaid_to_file(quadrant_chart, path / Path(prd_doc).with_suffix(".mmd"))
|
||||
|
||||
@staticmethod
|
||||
async def _save_pdf(prd_doc):
|
||||
m = json.loads(prd_doc.content)
|
||||
file_repo = CONFIG.git_repo.new_file_repository(PRD_PDF_FILE_REPO)
|
||||
await file_repo.save(filename=prd_doc.filename, content=json_to_markdown(m))
|
||||
|
|
|
|||
|
|
@ -55,3 +55,8 @@ DOCS_FILE_REPO = "docs"
|
|||
PRDS_FILE_REPO = "docs/prds"
|
||||
SYSTEM_DESIGN_FILE_REPO = "docs/system_design"
|
||||
TASK_FILE_REPO = "docs/tasks"
|
||||
COMPETITIVE_ANALYSIS_FILE_REPO = "resources/competitive_analysis"
|
||||
DATA_API_DESIGN_FILE_REPO = "resources/data_api_design"
|
||||
SEQ_FLOW_FILE_REPO = "resources/seq_flow"
|
||||
SYSTEM_DESIGN_PDF_FILE_REPO = "resources/system_design"
|
||||
PRD_PDF_FILE_REPO = "resources/prd"
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue