diff --git a/metagpt/actions/search_enhanced_qa.py b/metagpt/actions/search_enhanced_qa.py index b1810f06a..1427f9b19 100644 --- a/metagpt/actions/search_enhanced_qa.py +++ b/metagpt/actions/search_enhanced_qa.py @@ -209,7 +209,7 @@ async def _search_citations(self, query: str) -> list[str]: list[str]: Summaries of relevant web content. """ - relevant_urls = await self.collect_relevant_links(query) + relevant_urls = await self._collect_relevant_links(query) await self._reporter.async_report({"type": "search", "stage": "searching", "urls": relevant_urls}) if not relevant_urls: logger.warning(f"No relevant URLs found for query: {query}") @@ -226,7 +226,7 @@ async def _search_citations(self, query: str) -> list[str]: return citations - async def collect_relevant_links(self, query: str) -> list[str]: + async def _collect_relevant_links(self, query: str) -> list[str]: """Search and rank URLs relevant to the query. Args: diff --git a/metagpt/prompts/product_manager.py b/metagpt/prompts/product_manager.py index b77c9051b..1bc210740 100644 --- a/metagpt/prompts/product_manager.py +++ b/metagpt/prompts/product_manager.py @@ -32,7 +32,7 @@ - UI Design Draft: Basic layout and functionality - Open Questions: Unclear aspects needing clarification -#### Mermaid Diagram Rules +#### Mermaid Diagram Rules 1. Use mermaid quadrantChart syntax. Distribute scores evenly between 0 and 1 2. Example: ```mermaid @@ -67,7 +67,7 @@ Must follow this strict information gathering process: 1. Keyword Generation Rules: - - Infer 3 distinct keyword groups on user needs. + - Infer 3 distinct keyword groups on user needs(Infer directly instead of using tools). - Each group must be a space-separated phrase containing: * Target industry/product name (REQUIRED) * Specific aspect or metric @@ -80,7 +80,7 @@ 2. Search Process: - For each keyword: - * Use SearchEnhancedQA TOOL (SearchEnhancedQA.collect_relevant_links) collect top 3 search results + * Use SearchEnhancedQA TOOL (SearchEnhancedQA.run) collect top 3 search results * Remove duplicate URLs 3. Information Analysis: diff --git a/metagpt/roles/product_manager.py b/metagpt/roles/product_manager.py index e91ad86e7..334f36f52 100644 --- a/metagpt/roles/product_manager.py +++ b/metagpt/roles/product_manager.py @@ -6,12 +6,16 @@ @File : product_manager.py @Modified By: liushaojie, 2024/10/17. """ +from metagpt.actions import UserRequirement, WritePRD +from metagpt.actions.prepare_documents import PrepareDocuments from metagpt.actions.search_enhanced_qa import SearchEnhancedQA from metagpt.prompts.product_manager import PRODUCT_MANAGER_INSTRUCTION from metagpt.roles.di.role_zero import RoleZero +from metagpt.roles.role import RoleReactMode from metagpt.tools.libs.browser import Browser from metagpt.tools.libs.editor import Editor -from metagpt.utils.common import tool2name +from metagpt.utils.common import any_to_name, any_to_str, tool2name +from metagpt.utils.git_repository import GitRepository class ProductManager(RoleZero): @@ -33,12 +37,29 @@ class ProductManager(RoleZero): max_react_loop: int = 50 tools: list[str] = ["RoleZero", Browser.__name__, Editor.__name__, SearchEnhancedQA.__name__] + todo_action: str = any_to_name(WritePRD) + def __init__(self, **kwargs) -> None: super().__init__(**kwargs) - self.enable_memory = False + if self.use_fixed_sop: + self.enable_memory = False + self.set_actions([PrepareDocuments(send_to=any_to_str(self)), WritePRD]) + self._watch([UserRequirement, PrepareDocuments]) + self.rc.react_mode = RoleReactMode.BY_ORDER def _update_tool_execution(self): - se_qa = SearchEnhancedQA() - self.tool_execution_map.update( - tool2name(SearchEnhancedQA, ["collect_relevant_links"], se_qa.collect_relevant_links) - ) + wp = WritePRD() + self.tool_execution_map.update(tool2name(WritePRD, ["run"], wp.run)) + + async def _think(self) -> bool: + """Decide what to do""" + if not self.use_fixed_sop: + return await super()._think() + + if GitRepository.is_git_dir(self.config.project_path) and not self.config.git_reinit: + self._set_state(1) + else: + self._set_state(0) + self.config.git_reinit = False + self.todo_action = any_to_name(WritePRD) + return bool(self.rc.todo) diff --git a/metagpt/schema.py b/metagpt/schema.py index 24ca5b14f..70933d9bc 100644 --- a/metagpt/schema.py +++ b/metagpt/schema.py @@ -694,7 +694,7 @@ def append_task( dependent_task_ids (list[str]): The task ids that the new task depends on instruction (str): The instruction of the new task assignee (str): The assignee of the new task - task_type (str): The type of the new task, default is empty string + task_type (str, optional): The type of the new task, default is empty string """ new_task = Task( task_id=task_id, diff --git a/tests/metagpt/roles/di/run_product_manager.py b/tests/metagpt/roles/di/run_product_manager.py index 42aa7840a..bb230b7d9 100644 --- a/tests/metagpt/roles/di/run_product_manager.py +++ b/tests/metagpt/roles/di/run_product_manager.py @@ -4,16 +4,10 @@ from metagpt.logs import logger from metagpt.roles 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?""" - -CASE1_GREEDY_SNAKE = "设计一个贪吃蛇游戏,root path: '/Users/seeker/Projects/sdfz/mg/mgx_ops/workspace/temp'" +CASE0_WRITE_2048 = """Write a PRD for a cli 2048 game""" +CASE1_GREEDY_SNAKE = "设计一个贪吃蛇游戏" CASE2_SMART_HOME = "搜索并分析米家、华为智能家居和海尔智家在智能家居市场中的功能、用户需求和市场定位" CASE3_BEST_SELLING_REFRIGERATOR = "调研当前市场上最畅销的智能冰箱的五个关键特性" - OLD_PRD = """ Language en_us @@ -62,33 +56,32 @@ Currently, all aspects of the project are clear. """ CASE4_MUSIC_STREAMING_MEDIA = f"""We have received feedback from users regarding the current music streaming service, stating that they need better personalized recommendations. Please readjust the content of PRD {OLD_PRD} based on these feedback.""" - CASE5_SMART_BIG_SCREEN = """分析2024年上半年中国家庭智能大屏行业的发展情况并输出市场分析报告""" - -# CASE6 = "我想要生产一个电子烟产品,请帮我完成市场调研分析报告" +CASE6_ELECTRONIC_CIGARETTE = """我想要生产一个电子烟产品,请帮我完成市场调研分析报告""" -async def main(requirement): - product_manager = ProductManager() - await product_manager.run(requirement) - - -if __name__ == "__main__": +def main(): cases = [ + # CASE0_WRITE_2048, # CASE1_GREEDY_SNAKE, # CASE2_SMART_HOME, - CASE3_BEST_SELLING_REFRIGERATOR, + # CASE3_BEST_SELLING_REFRIGERATOR, # CASE4_MUSIC_STREAMING_MEDIA, - # CASE5_SMART_BIG_SCREEN, - # CASE6, - # WRITE_2048, + CASE5_SMART_BIG_SCREEN, + # CASE6_ELECTRONIC_CIGARETTE, ] + root_path = "/tmp" logger.remove() - # logger.add(sys.stderr, level="DEBUG") logger.add(sys.stderr, level="INFO") for case in cases: + case += f"\nroot path: '{root_path}'" logger.info(f"user requirement:\n{case}") try: - asyncio.run(main(case)) + product_manager = ProductManager() + asyncio.run(product_manager.run(case)) except Exception as e: print(e) + + +if __name__ == "__main__": + main()