From 3e69cd2bd7f8a765c2315b6903ab9f22fa294a66 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E8=8E=98=E6=9D=83=20=E9=A9=AC?= Date: Fri, 29 Mar 2024 22:16:21 +0800 Subject: [PATCH] feat: +software_development_intent_detect --- metagpt/actions/intent_detect.py | 44 +++++++++++++++++++-- metagpt/tools/libs/dialog.py | 43 +++++++++++++++----- tests/metagpt/actions/test_intent_detect.py | 3 ++ tests/metagpt/tools/libs/test_dialog.py | 4 +- 4 files changed, 78 insertions(+), 16 deletions(-) diff --git a/metagpt/actions/intent_detect.py b/metagpt/actions/intent_detect.py index 5204cff76..0104d079d 100644 --- a/metagpt/actions/intent_detect.py +++ b/metagpt/actions/intent_detect.py @@ -292,8 +292,6 @@ class IntentDetect(Action): class LightIntentDetect(IntentDetect): - result: List[SOPItem] = None - async def run(self, with_messages: List[Message] = None, **kwargs) -> Message: """ Runs the intention detection action. @@ -305,9 +303,8 @@ class LightIntentDetect(IntentDetect): msg_markdown = self._message_to_markdown(with_messages) await self._get_intentions(msg_markdown) await self._get_sops() + await self._merge() - distinct = {i.sop_index - 1: SOP_CONFIG[i.sop_index - 1] for i in self._intent_to_sops if i.sop_index > 0} - self.result = list(distinct.values()) return Message(content="", role="assistant", cause_by=self) async def _get_sops(self): @@ -336,3 +333,42 @@ class LightIntentDetect(IntentDetect): json_blocks = parse_json_code_block(rsp) vv = json.loads(json_blocks[0]) self._intent_to_sops = [self.IntentSOP.model_validate(i) for i in vv] + + async def _merge(self): + self.result = IntentDetectResult(clarifications=[]) + sops = {i.description: i for i in SOP_CONFIG} + intent_to_sops = {i.intent: i.sop for i in self._intent_to_sops if i.sop != ""} + + distinct = {} + for i in self._intent_to_sops: + if i.sop_index == 0: # 1-based index + ref = self._get_intent_ref(i.intent) + item = IntentDetectIntentionSOP(intention=IntentDetectIntentionRef(intent=i.intent, refs=[ref])) + self.result.intentions.append(item) + continue + distinct[i.sop_index] = [i.intent] + distinct.get(i.sop_index, []) + + merge_intents = {} + for sop_index, intents in distinct.items(): + if len(intents) > 1: + merge_intents[sop_index] = intents + continue + ref = self._get_intent_ref(intents[0]) + item = IntentDetectIntentionSOP(intention=IntentDetectIntentionRef(intent=intents[0], refs=[ref])) + key = intent_to_sops.get(intents[0]) + if key: + item.sop = sops.get(key) + self.result.intentions.append(item) + + for sop_index, intents in merge_intents.items(): + intent_ref = IntentDetectIntentionRef(intent="\n".join(intents), refs=[]) + for i in intents: + ref = self._get_intent_ref(i) + intent_ref.refs.append(ref) + item = IntentDetectIntentionSOP(intention=intent_ref) + item.sop = SOP_CONFIG[sop_index - 1] # 1-based index + self.result.intentions.append(item) + + def _get_intent_ref(self, intent: str) -> str: + mappings = {i.intent: i.ref for i in self._dialog_intentions.intentions} + return mappings[intent] diff --git a/metagpt/tools/libs/dialog.py b/metagpt/tools/libs/dialog.py index 603805b60..6cc7b7b3b 100644 --- a/metagpt/tools/libs/dialog.py +++ b/metagpt/tools/libs/dialog.py @@ -81,7 +81,7 @@ async def intent_detect(messages: List[Message]) -> IntentDetectResult: @register_tool(tags=["dialog", "software development intent detect"]) -async def software_development_intent_detect(messages: List[Message]) -> List[str]: +async def software_development_intent_detect(messages: List[Message]) -> IntentDetectResult: """Detects software development intent from a list of dialog messages. Args: @@ -101,16 +101,39 @@ async def software_development_intent_detect(messages: List[Message]) -> List[st >>> messages = [Message.model_validate(i) for i in dialog] >>> result = await software_development_intent_detect(messages) >>> print(result) - [ - "Writes a PRD based on software requirements.", - "Writes a design to the project repository, based on the PRD of the project.", - "Writes a project plan to the project repository, based on the design of the project.", - "Writes codes to the project repository, based on the project plan of the project.", - "Run QA test on the project repository.", - "Stage and commit changes for the project repository using Git." - ] + { + "clarifications": [], + "intentions": [ + { + "intention": { + "intent": "Request to build a service that can receive text messages and ...", + "refs": [ + "Can you build TextToSummarize which is a SMS number that I can text ..." + ] + }, + "sop": { + "description": "Intentions related to or including software development, such as ...", + "sop": [ + "Writes a PRD based on software requirements.", + "Writes a design to the project repository, based on the PRD of the project.", + "Writes a project plan to the project repository, based on the design of the project.", + "Writes codes to the project repository, based on the project plan of the project.", + "Run QA test on the project repository.", + "Stage and commit changes for the project repository using Git." + ] + } + }, + { + "intention": { + "intent": "Request for a phone number to send text messages for the summarization service", + "refs": [] + }, + "sop": null + } + ] + } """ ctx = Context() action = LightIntentDetect(context=ctx) await action.run(messages) - return action.result[0].sop if action.result else [] + return action.result diff --git a/tests/metagpt/actions/test_intent_detect.py b/tests/metagpt/actions/test_intent_detect.py index 9ee32339d..95efc16d8 100644 --- a/tests/metagpt/actions/test_intent_detect.py +++ b/tests/metagpt/actions/test_intent_detect.py @@ -70,6 +70,9 @@ async def test_light_intent_detect(content: str, context): messages = [Message.model_validate(i) for i in json.loads(content)] rsp = await action.run(messages) assert isinstance(rsp, Message) + assert action._dialog_intentions + assert action._intent_to_sops + assert action.result if __name__ == "__main__": diff --git a/tests/metagpt/tools/libs/test_dialog.py b/tests/metagpt/tools/libs/test_dialog.py index eab5c4f2d..39052f885 100644 --- a/tests/metagpt/tools/libs/test_dialog.py +++ b/tests/metagpt/tools/libs/test_dialog.py @@ -24,9 +24,9 @@ async def test_intent_detect(): async def test_software_develop_intent_detect(): messages = [Message.model_validate(i) for i in DEMO_CONTENT] result = await software_development_intent_detect(messages) - assert isinstance(result, list) + assert isinstance(result, IntentDetectResult) assert result - logger.info(f"dialog:{DEMO_CONTENT}\nresult:{result}") + logger.info(f"dialog:{DEMO_CONTENT}\nresult:{result.model_dump_json()}") if __name__ == "__main__":