From 0a7a8e6a46522315b0c32a1e589c49224cbc7859 Mon Sep 17 00:00:00 2001 From: zxstty Date: Mon, 18 Aug 2025 12:05:54 +0800 Subject: [PATCH 1/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8D=E5=A4=A7=E6=A8=A1?= =?UTF-8?q?=E5=9E=8Bcore=20prompt=E5=85=B1=E4=BA=AB=E7=9A=84=E9=97=AE?= =?UTF-8?q?=E9=A2=98?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/llm/patterns/core.py | 7 ++++--- 1 file changed, 4 insertions(+), 3 deletions(-) diff --git a/apps/llm/patterns/core.py b/apps/llm/patterns/core.py index f80c275dc..7c35247a7 100644 --- a/apps/llm/patterns/core.py +++ b/apps/llm/patterns/core.py @@ -9,9 +9,6 @@ from apps.schemas.enum_var import LanguageType class CorePattern(ABC): """基础大模型范式抽象类""" - system_prompt: dict[LanguageType, str] = {} - """系统提示词""" - user_prompt: dict[LanguageType, str] = {} """用户提示词""" input_tokens: int = 0 """输入Token数量""" @@ -31,9 +28,13 @@ class CorePattern(ABC): """ if system_prompt is not None: self.system_prompt = system_prompt + else: + self.system_prompt = {} if user_prompt is not None: self.user_prompt = user_prompt + else: + self.user_prompt = {} if not self.user_prompt: err = "必须设置用户提示词!" -- Gitee From 5fafea05b99ef9de3b93b828595c2ecf5f8091e4 Mon Sep 17 00:00:00 2001 From: zxstty Date: Mon, 18 Aug 2025 15:44:52 +0800 Subject: [PATCH 2/4] =?UTF-8?q?=E4=BF=AE=E5=A4=8Dmcp=E5=AE=89=E8=A3=85?= =?UTF-8?q?=E7=9A=84bug?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/llm/patterns/core.py | 18 +++++++++--------- apps/llm/patterns/facts.py | 6 +++++- apps/scheduler/pool/loader/mcp.py | 8 ++++++++ apps/services/mcp_service.py | 2 +- 4 files changed, 23 insertions(+), 11 deletions(-) diff --git a/apps/llm/patterns/core.py b/apps/llm/patterns/core.py index 7c35247a7..2dfba6c10 100644 --- a/apps/llm/patterns/core.py +++ b/apps/llm/patterns/core.py @@ -3,6 +3,7 @@ from abc import ABC, abstractmethod from textwrap import dedent +from pydantic import BaseModel, Field from apps.schemas.enum_var import LanguageType @@ -14,6 +15,14 @@ class CorePattern(ABC): """输入Token数量""" output_tokens: int = 0 """输出Token数量""" + system_prompt: dict[LanguageType, str] = Field( + default={}, + description="系统提示词,f-string格式", + ) + user_prompt: dict[LanguageType, str] = Field( + default={}, + description="用户提示词,f-string格式", + ) def __init__( self, @@ -26,15 +35,6 @@ class CorePattern(ABC): :param system_prompt: 系统提示词,f-string格式 :param user_prompt: 用户提示词,f-string格式 """ - if system_prompt is not None: - self.system_prompt = system_prompt - else: - self.system_prompt = {} - - if user_prompt is not None: - self.user_prompt = user_prompt - else: - self.user_prompt = {} if not self.user_prompt: err = "必须设置用户提示词!" diff --git a/apps/llm/patterns/facts.py b/apps/llm/patterns/facts.py index ac833f515..6ea41eae1 100644 --- a/apps/llm/patterns/facts.py +++ b/apps/llm/patterns/facts.py @@ -133,8 +133,12 @@ class Facts(CorePattern): self.output_tokens = llm.output_tokens messages += [{"role": "assistant", "content": result}] + if language == LanguageType.CHINESE: + query = "根据给定的背景信息,提取事实条目" + else: + query = "Extract fact entries based on the given background information" json_gen = JsonGenerator( - query="根据给定的背景信息,提取事实条目", + query=query, conversation=messages, schema=FactsResult.model_json_schema(), ) diff --git a/apps/scheduler/pool/loader/mcp.py b/apps/scheduler/pool/loader/mcp.py index 1a3f04910..49c3b0bec 100644 --- a/apps/scheduler/pool/loader/mcp.py +++ b/apps/scheduler/pool/loader/mcp.py @@ -139,6 +139,7 @@ class MCPLoader(metaclass=SingletonMeta): db_service_list = await mcp_collection.find( {"_id": {"$in": mcp_ids}, "status": {"$in": [MCPInstallStatus.READY, MCPInstallStatus.FAILED]}}, ).to_list(None) + print(f"[MCPLoader] 清除状态为ready或failed的MCP安装任务: {db_service_list}") # noqa: T201 for db_service in db_service_list: try: item = MCPCollection.model_validate(db_service) @@ -163,6 +164,7 @@ class MCPLoader(metaclass=SingletonMeta): template_path = MCP_PATH / "template" / mcp_id await Path.mkdir(template_path, parents=True, exist_ok=True) # 安装MCP模板 + ProcessHandler.remove_task(mcp_id) if not ProcessHandler.add_task(mcp_id, MCPLoader._install_template_task, mcp_id, config): err = f"安装任务无法执行,请稍后重试: {mcp_id}" logger.error(err) @@ -266,6 +268,12 @@ class MCPLoader(metaclass=SingletonMeta): # 基本信息插入数据库 mcp_collection = MongoDB().get_collection("mcp") + # 清空当前工具列表 + await mcp_collection.update_one( + {"_id": mcp_id}, + {"$set": {"tools": []}}, + upsert=True, + ) await mcp_collection.update_one( {"_id": mcp_id}, { diff --git a/apps/services/mcp_service.py b/apps/services/mcp_service.py index 220ec1f83..c42bedb1e 100644 --- a/apps/services/mcp_service.py +++ b/apps/services/mcp_service.py @@ -421,7 +421,7 @@ class MCPServiceManager: db_service = await service_collection.find_one({"_id": service_id, "author": user_sub}) db_service = MCPCollection.model_validate(db_service) if install: - if db_service.status == MCPInstallStatus.INSTALLING or db_service.status == MCPInstallStatus.READY: + if db_service.status == MCPInstallStatus.INSTALLING: err = "[MCPServiceManager] MCP服务已处于安装中或已准备就绪" raise Exception(err) await service_collection.update_one( -- Gitee From 9d6019a243e862b42533bd1d3e8b820b07c6791b Mon Sep 17 00:00:00 2001 From: zxstty Date: Mon, 18 Aug 2025 16:49:02 +0800 Subject: [PATCH 3/4] =?UTF-8?q?=E5=AE=8C=E5=96=84mcp=E7=9B=B8=E5=85=B3?= =?UTF-8?q?=E5=86=85=E5=AE=B9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/llm/patterns/core.py | 26 ++++++++++++++------------ apps/llm/patterns/facts.py | 6 +----- apps/scheduler/pool/loader/mcp.py | 1 - apps/services/flow.py | 2 +- apps/services/mcp_service.py | 2 +- 5 files changed, 17 insertions(+), 20 deletions(-) diff --git a/apps/llm/patterns/core.py b/apps/llm/patterns/core.py index 2dfba6c10..648773f9c 100644 --- a/apps/llm/patterns/core.py +++ b/apps/llm/patterns/core.py @@ -15,14 +15,6 @@ class CorePattern(ABC): """输入Token数量""" output_tokens: int = 0 """输出Token数量""" - system_prompt: dict[LanguageType, str] = Field( - default={}, - description="系统提示词,f-string格式", - ) - user_prompt: dict[LanguageType, str] = Field( - default={}, - description="用户提示词,f-string格式", - ) def __init__( self, @@ -35,10 +27,20 @@ class CorePattern(ABC): :param system_prompt: 系统提示词,f-string格式 :param user_prompt: 用户提示词,f-string格式 """ - - if not self.user_prompt: - err = "必须设置用户提示词!" - raise ValueError(err) + if system_prompt is not None: + self.system_prompt = system_prompt + else: + self.system_prompt = { + LanguageType.CHINESE: "", + LanguageType.ENGLISH: "", + } + if user_prompt is not None: + self.user_prompt = user_prompt + else: + self.user_prompt = { + LanguageType.CHINESE: "", + LanguageType.ENGLISH: "", + } self.system_prompt = {lang: dedent(prompt).strip("\n") for lang, prompt in self.system_prompt.items()} diff --git a/apps/llm/patterns/facts.py b/apps/llm/patterns/facts.py index 6ea41eae1..080ecc0ea 100644 --- a/apps/llm/patterns/facts.py +++ b/apps/llm/patterns/facts.py @@ -133,12 +133,8 @@ class Facts(CorePattern): self.output_tokens = llm.output_tokens messages += [{"role": "assistant", "content": result}] - if language == LanguageType.CHINESE: - query = "根据给定的背景信息,提取事实条目" - else: - query = "Extract fact entries based on the given background information" json_gen = JsonGenerator( - query=query, + query="Extract fact entries based on the given background information", conversation=messages, schema=FactsResult.model_json_schema(), ) diff --git a/apps/scheduler/pool/loader/mcp.py b/apps/scheduler/pool/loader/mcp.py index 49c3b0bec..79e7bc381 100644 --- a/apps/scheduler/pool/loader/mcp.py +++ b/apps/scheduler/pool/loader/mcp.py @@ -139,7 +139,6 @@ class MCPLoader(metaclass=SingletonMeta): db_service_list = await mcp_collection.find( {"_id": {"$in": mcp_ids}, "status": {"$in": [MCPInstallStatus.READY, MCPInstallStatus.FAILED]}}, ).to_list(None) - print(f"[MCPLoader] 清除状态为ready或failed的MCP安装任务: {db_service_list}") # noqa: T201 for db_service in db_service_list: try: item = MCPCollection.model_validate(db_service) diff --git a/apps/services/flow.py b/apps/services/flow.py index f694cd72e..5f6c29c8a 100644 --- a/apps/services/flow.py +++ b/apps/services/flow.py @@ -2,7 +2,7 @@ """flow Manager""" import logging - +from pydantic import BaseModel, Field from pymongo import ASCENDING from apps.common.mongo import MongoDB diff --git a/apps/services/mcp_service.py b/apps/services/mcp_service.py index c42bedb1e..0b72e0748 100644 --- a/apps/services/mcp_service.py +++ b/apps/services/mcp_service.py @@ -422,7 +422,7 @@ class MCPServiceManager: db_service = MCPCollection.model_validate(db_service) if install: if db_service.status == MCPInstallStatus.INSTALLING: - err = "[MCPServiceManager] MCP服务已处于安装中或已准备就绪" + err = "[MCPServiceManager] MCP服务已处于安装中" raise Exception(err) await service_collection.update_one( {"_id": service_id}, -- Gitee From 043b61b2c90f7f5c9ba0f586dc22e462b7ec9b6f Mon Sep 17 00:00:00 2001 From: zxstty Date: Mon, 18 Aug 2025 17:17:18 +0800 Subject: [PATCH 4/4] =?UTF-8?q?=E5=AE=8C=E5=96=84=E4=B8=AD=E8=8B=B1?= =?UTF-8?q?=E6=96=87prompt=20name=20description=E7=9A=84=E8=BF=94=E5=9B=9E?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- apps/llm/patterns/core.py | 20 +- apps/llm/patterns/executor.py | 224 ++++++------- apps/llm/patterns/facts.py | 165 +++++----- apps/llm/patterns/rewoo.py | 422 +++++++++++++------------ apps/llm/patterns/rewrite.py | 232 +++++++------- apps/llm/patterns/select.py | 217 ++++++------- apps/scheduler/call/choice/choice.py | 11 - apps/scheduler/call/core.py | 16 +- apps/scheduler/call/facts/facts.py | 10 - apps/scheduler/call/graph/graph.py | 11 - apps/scheduler/call/graph/style.py | 143 ++++++--- apps/scheduler/call/llm/llm.py | 11 - apps/scheduler/call/mcp/mcp.py | 11 - apps/scheduler/call/rag/rag.py | 11 - apps/scheduler/call/search/search.py | 11 - apps/scheduler/call/slot/slot.py | 11 - apps/scheduler/call/sql/sql.py | 11 - apps/scheduler/call/suggest/suggest.py | 11 - apps/scheduler/call/summary/summary.py | 11 - apps/services/flow.py | 3 +- 20 files changed, 755 insertions(+), 807 deletions(-) diff --git a/apps/llm/patterns/core.py b/apps/llm/patterns/core.py index 648773f9c..c4a58364e 100644 --- a/apps/llm/patterns/core.py +++ b/apps/llm/patterns/core.py @@ -16,6 +16,15 @@ class CorePattern(ABC): output_tokens: int = 0 """输出Token数量""" + def get_default_prompt(self) -> dict[LanguageType, str]: + """ + 获取默认的用户提示词 + + :return: 默认的用户提示词 + :rtype: dict[LanguageType, str] + """ + return {}, {} + def __init__( self, system_prompt: dict[LanguageType, str] | None = None, @@ -27,20 +36,15 @@ class CorePattern(ABC): :param system_prompt: 系统提示词,f-string格式 :param user_prompt: 用户提示词,f-string格式 """ + default_system_prompt, default_user_prompt = self.get_default_prompt() if system_prompt is not None: self.system_prompt = system_prompt else: - self.system_prompt = { - LanguageType.CHINESE: "", - LanguageType.ENGLISH: "", - } + self.system_prompt = default_system_prompt if user_prompt is not None: self.user_prompt = user_prompt else: - self.user_prompt = { - LanguageType.CHINESE: "", - LanguageType.ENGLISH: "", - } + self.user_prompt = default_user_prompt self.system_prompt = {lang: dedent(prompt).strip("\n") for lang, prompt in self.system_prompt.items()} diff --git a/apps/llm/patterns/executor.py b/apps/llm/patterns/executor.py index 05225ad54..e2153487a 100644 --- a/apps/llm/patterns/executor.py +++ b/apps/llm/patterns/executor.py @@ -14,71 +14,73 @@ if TYPE_CHECKING: class ExecutorThought(CorePattern): """通过大模型生成Executor的思考内容""" - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - - - 你是一个可以使用工具的智能助手。 - 在回答用户的问题时,你为了获取更多的信息,使用了一个工具。 - 请简明扼要地总结工具的使用过程,提供你的见解,并给出下一步的行动。 - - 注意: - 工具的相关信息在标签中给出。 - 为了使你更好的理解发生了什么,你之前的思考过程在标签中给出。 - 输出时请不要包含XML标签,输出时请保持简明和清晰。 - - - - - {tool_name} - {tool_description} - {tool_output} - - - - {last_thought} - - - - 你当前需要解决的问题是: - {user_question} - - - 请综合以上信息,再次一步一步地进行思考,并给出见解和行动: - """, - LanguageType.ENGLISH: r""" - - - You are an intelligent assistant who can use tools. - When answering user questions, you use a tool to get more information. - Please summarize the process of using the tool briefly, provide your insights, and give the next action. - - Note: - The information about the tool is given in the tag. - To help you better understand what happened, your previous thought process is given in the tag. - Do not include XML tags in the output, and keep the output brief and clear. - - - - - {tool_name} - {tool_description} - {tool_output} - - - - {last_thought} - - - - The question you need to solve is: - {user_question} - - - Please integrate the above information, think step by step again, provide insights, and give actions: - """, - } - """用户提示词""" + def get_default_prompt(self) -> dict[LanguageType, str]: + user_prompt = { + LanguageType.CHINESE: r""" + + + 你是一个可以使用工具的智能助手。 + 在回答用户的问题时,你为了获取更多的信息,使用了一个工具。 + 请简明扼要地总结工具的使用过程,提供你的见解,并给出下一步的行动。 + + 注意: + 工具的相关信息在标签中给出。 + 为了使你更好的理解发生了什么,你之前的思考过程在标签中给出。 + 输出时请不要包含XML标签,输出时请保持简明和清晰。 + + + + + {tool_name} + {tool_description} + {tool_output} + + + + {last_thought} + + + + 你当前需要解决的问题是: + {user_question} + + + 请综合以上信息,再次一步一步地进行思考,并给出见解和行动: + """, + LanguageType.ENGLISH: r""" + + + You are an intelligent assistant who can use tools. + When answering user questions, you use a tool to get more information. + Please summarize the process of using the tool briefly, provide your insights, and give the next action. + + Note: + The information about the tool is given in the tag. + To help you better understand what happened, your previous thought process is given in the tag. + Do not include XML tags in the output, and keep the output brief and clear. + + + + + {tool_name} + {tool_description} + {tool_output} + + + + {last_thought} + + + + The question you need to solve is: + {user_question} + + + Please integrate the above information, think step by step again, provide insights, and give actions: + """, + } + """用户提示词""" + return {}, user_prompt def __init__( self, @@ -126,51 +128,53 @@ class ExecutorThought(CorePattern): class ExecutorSummary(CorePattern): """使用大模型进行生成Executor初始背景""" - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - - 根据给定的对话记录和关键事实,生成一个三句话背景总结。这个总结将用于后续对话的上下文理解。 - - 生成总结的要求如下: - 1. 突出重要信息点,例如时间、地点、人物、事件等。 - 2. “关键事实”中的内容可在生成总结时作为已知信息。 - 3. 输出时请不要包含XML标签,确保信息准确性,不得编造信息。 - 4. 总结应少于3句话,应少于300个字。 - - 对话记录将在标签中给出,关键事实将在标签中给出。 - - - {conversation} - - - {facts} - - - 现在,请开始生成背景总结: - """, - LanguageType.ENGLISH: r""" - - Based on the given conversation records and key facts, generate a three-sentence background summary. This summary will be used for context understanding in subsequent conversations. - - The requirements for generating the summary are as follows: - 1. Highlight important information points, such as time, location, people, events, etc. - 2. The content in the "key facts" can be used as known information when generating the summary. - 3. Do not include XML tags in the output, ensure the accuracy of the information, and do not make up information. - 4. The summary should be less than 3 sentences and less than 300 words. - - The conversation records will be given in the tag, and the key facts will be given in the tag. - - - {conversation} - - - {facts} - - - Now, please start generating the background summary: - """, - } - """用户提示词""" + def get_default_prompt(self) -> dict[LanguageType, str]: + user_prompt = { + LanguageType.CHINESE: r""" + + 根据给定的对话记录和关键事实,生成一个三句话背景总结。这个总结将用于后续对话的上下文理解。 + + 生成总结的要求如下: + 1. 突出重要信息点,例如时间、地点、人物、事件等。 + 2. “关键事实”中的内容可在生成总结时作为已知信息。 + 3. 输出时请不要包含XML标签,确保信息准确性,不得编造信息。 + 4. 总结应少于3句话,应少于300个字。 + + 对话记录将在标签中给出,关键事实将在标签中给出。 + + + {conversation} + + + {facts} + + + 现在,请开始生成背景总结: + """, + LanguageType.ENGLISH: r""" + + Based on the given conversation records and key facts, generate a three-sentence background summary. This summary will be used for context understanding in subsequent conversations. + + The requirements for generating the summary are as follows: + 1. Highlight important information points, such as time, location, people, events, etc. + 2. The content in the "key facts" can be used as known information when generating the summary. + 3. Do not include XML tags in the output, ensure the accuracy of the information, and do not make up information. + 4. The summary should be less than 3 sentences and less than 300 words. + + The conversation records will be given in the tag, and the key facts will be given in the tag. + + + {conversation} + + + {facts} + + + Now, please start generating the background summary: + """, + } + """用户提示词""" + return {}, user_prompt def __init__( self, diff --git a/apps/llm/patterns/facts.py b/apps/llm/patterns/facts.py index 080ecc0ea..46707d115 100644 --- a/apps/llm/patterns/facts.py +++ b/apps/llm/patterns/facts.py @@ -23,90 +23,93 @@ class FactsResult(BaseModel): class Facts(CorePattern): """事实提取""" - system_prompt: str = "You are a helpful assistant." - """系统提示词(暂不使用)""" - - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - - - 从对话中提取关键信息,并将它们组织成独一无二的、易于理解的事实,包含用户偏好、关系、实体等有用信息。 - 以下是需要关注的信息类型以及有关如何处理输入数据的详细说明。 - - **你需要关注的信息类型** - 1. 实体:对话中涉及到的实体。例如:姓名、地点、组织、事件等。 - 2. 偏好:对待实体的态度。例如喜欢、讨厌等。 - 3. 关系:用户与实体之间,或两个实体之间的关系。例如包含、并列、互斥等。 - 4. 动作:对实体产生影响的具体动作。例如查询、搜索、浏览、点击等。 - - **要求** - 1. 事实必须准确,只能从对话中提取。不要将样例中的信息体现在输出中。 - 2. 事实必须清晰、简洁、易于理解。必须少于30个字。 - 3. 必须按照以下JSON格式输出: - - {{ - "facts": ["事实1", "事实2", "事实3"] - }} - - - - - 杭州西湖有哪些景点? - 杭州西湖是中国浙江省杭州市的一个著名景点,以其美丽的自然风光和丰富的文化遗产而闻名。西湖周围有许多著名的景点,包括著名的苏堤、白堤、断桥、三潭印月等。西湖以其清澈的湖水和周围的山脉而著名,是中国最著名的湖泊之一。 - - - + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: "你是一个有用的助手。", + LanguageType.ENGLISH: "You are a helpful assistant." + } + user_prompt = { + LanguageType.CHINESE: r""" + + + 从对话中提取关键信息,并将它们组织成独一无二的、易于理解的事实,包含用户偏好、关系、实体等有用信息。 + 以下是需要关注的信息类型以及有关如何处理输入数据的详细说明。 + + **你需要关注的信息类型** + 1. 实体:对话中涉及到的实体。例如:姓名、地点、组织、事件等。 + 2. 偏好:对待实体的态度。例如喜欢、讨厌等。 + 3. 关系:用户与实体之间,或两个实体之间的关系。例如包含、并列、互斥等。 + 4. 动作:对实体产生影响的具体动作。例如查询、搜索、浏览、点击等。 + + **要求** + 1. 事实必须准确,只能从对话中提取。不要将样例中的信息体现在输出中。 + 2. 事实必须清晰、简洁、易于理解。必须少于30个字。 + 3. 必须按照以下JSON格式输出: + {{ - "facts": ["杭州西湖有苏堤、白堤、断桥、三潭印月等景点"] + "facts": ["事实1", "事实2", "事实3"] }} - - - - - {conversation} - - """, - LanguageType.ENGLISH: r""" - - - Extract key information from the conversation and organize it into unique, easily understandable facts that include user preferences, relationships, entities, etc. - The following are the types of information to be paid attention to and detailed instructions on how to handle the input data. - - **Types of information to be paid attention to** - 1. Entities: Entities involved in the conversation. For example: names, locations, organizations, events, etc. - 2. Preferences: Attitudes towards entities. For example: like, dislike, etc. - 3. Relationships: Relationships between the user and entities, or between two entities. For example: include, parallel, exclusive, etc. - 4. Actions: Specific actions that affect entities. For example: query, search, browse, click, etc. - - **Requirements** - 1. Facts must be accurate and can only be extracted from the conversation. Do not include information from the sample in the output. - 2. Facts must be clear, concise, and easy to understand. Must be less than 30 words. - 3. Output in the following JSON format: - - {{ - "facts": ["fact1", "fact2", "fact3"] - }} - - - - - What are the attractions in West Lake, Hangzhou? - West Lake in Hangzhou is a famous scenic spot in Hangzhou, Zhejiang Province, China, famous for its beautiful natural scenery and rich cultural heritage. There are many famous attractions around West Lake, including the famous Su Causeway, Bai Causeway, Broken Bridge, Three Pools Mirroring the Moon, etc. West Lake is famous for its clear water and surrounding mountains, and is one of the most famous lakes in China. - - - + + + + + 杭州西湖有哪些景点? + 杭州西湖是中国浙江省杭州市的一个著名景点,以其美丽的自然风光和丰富的文化遗产而闻名。西湖周围有许多著名的景点,包括著名的苏堤、白堤、断桥、三潭印月等。西湖以其清澈的湖水和周围的山脉而著名,是中国最著名的湖泊之一。 + + + + {{ + "facts": ["杭州西湖有苏堤、白堤、断桥、三潭印月等景点"] + }} + + + + + {conversation} + + """, + LanguageType.ENGLISH: r""" + + + Extract key information from the conversation and organize it into unique, easily understandable facts that include user preferences, relationships, entities, etc. + The following are the types of information to be paid attention to and detailed instructions on how to handle the input data. + + **Types of information to be paid attention to** + 1. Entities: Entities involved in the conversation. For example: names, locations, organizations, events, etc. + 2. Preferences: Attitudes towards entities. For example: like, dislike, etc. + 3. Relationships: Relationships between the user and entities, or between two entities. For example: include, parallel, exclusive, etc. + 4. Actions: Specific actions that affect entities. For example: query, search, browse, click, etc. + + **Requirements** + 1. Facts must be accurate and can only be extracted from the conversation. Do not include information from the sample in the output. + 2. Facts must be clear, concise, and easy to understand. Must be less than 30 words. + 3. Output in the following JSON format: + {{ - "facts": ["West Lake has the famous attractions of Suzhou Embankment, Bai Embankment, Qiantang Bridge, San Tang Yin Yue, etc."] + "facts": ["fact1", "fact2", "fact3"] }} - - - - - {conversation} - - """, - } - """用户提示词""" + + + + + What are the attractions in West Lake, Hangzhou? + West Lake in Hangzhou is a famous scenic spot in Hangzhou, Zhejiang Province, China, famous for its beautiful natural scenery and rich cultural heritage. There are many famous attractions around West Lake, including the famous Su Causeway, Bai Causeway, Broken Bridge, Three Pools Mirroring the Moon, etc. West Lake is famous for its clear water and surrounding mountains, and is one of the most famous lakes in China. + + + + {{ + "facts": ["West Lake has the famous attractions of Suzhou Embankment, Bai Embankment, Qiantang Bridge, San Tang Yin Yue, etc."] + }} + + + + + {conversation} + + """, + } + """用户提示词""" + return system_prompt, user_prompt def __init__( self, @@ -122,7 +125,7 @@ class Facts(CorePattern): language = kwargs.get("language", LanguageType.CHINESE) messages = [ - {"role": "system", "content": self.system_prompt}, + {"role": "system", "content": self.system_prompt[language]}, {"role": "user", "content": self.user_prompt[language].format(conversation=conversation)}, ] result = "" diff --git a/apps/llm/patterns/rewoo.py b/apps/llm/patterns/rewoo.py index cc4a44d79..66c3c6511 100644 --- a/apps/llm/patterns/rewoo.py +++ b/apps/llm/patterns/rewoo.py @@ -9,108 +9,110 @@ from apps.schemas.enum_var import LanguageType class InitPlan(CorePattern): """规划生成命令行""" - system_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 你是一个计划生成器。对于给定的目标,**制定一个简单的计划**,该计划可以逐步生成合适的命令行参数和标志。 - - 你会收到一个"命令前缀",这是已经确定和生成的命令部分。你需要基于这个前缀使用标志和参数来完成命令。 - - 在每一步中,指明使用哪个外部工具以及工具输入来获取证据。 - - 工具可以是以下之一: - (1) Option["指令"]:查询最相似的命令行标志。只接受一个输入参数,"指令"必须是搜索字符串。搜索字符串应该详细且包含必要的数据。 - (2) Argument[名称]<值>:将任务中的数据放置到命令行的特定位置。接受两个输入参数。 - - 所有步骤必须以"Plan: "开头,且少于150个单词。 - 不要添加任何多余的步骤。 - 确保每个步骤都包含所需的所有信息 - 不要跳过步骤。 - 不要在证据后面添加任何额外数据。 - - 开始示例 - - 任务:在后台运行一个新的alpine:latest容器,将主机/root文件夹挂载至/data,并执行top命令。 - 前缀:`docker run` - 用法:`docker run ${OPTS} ${image} ${command}`。这是一个Python模板字符串。OPTS是所有标志的占位符。参数必须是 \ - ["image", "command"] 其中之一。 - 前缀描述:二进制程序`docker`的描述为"Docker容器平台",`run`子命令的描述为"从镜像创建并运行一个新的容器"。 - - Plan: 我需要一个标志使容器在后台运行。 #E1 = Option[在后台运行单个容器] - Plan: 我需要一个标志,将主机/root目录挂载至容器内/data目录。 #E2 = Option[挂载主机/root目录至/data目录] - Plan: 我需要从任务中解析出镜像名称。 #E3 = Argument[image] - Plan: 我需要指定容器中运行的命令。 #E4 = Argument[command] - Final: 组装上述线索,生成最终命令。 #F - - 示例结束 - - 让我们开始! - """, - LanguageType.ENGLISH: r""" - You are a plan generator. For a given goal, **draft a simple plan** that can step-by-step generate the \ - appropriate command line arguments and flags. - - You will receive a "command prefix", which is the already determined and generated command part. You need to \ - use the flags and arguments based on this prefix to complete the command. - - In each step, specify which external tool to use and the tool input to get the evidence. - - The tool can be one of the following: - (1) Option["instruction"]: Query the most similar command line flag. Only accepts one input parameter, \ - "instruction" must be a search string. The search string should be detailed and contain necessary data. - (2) Argument["name"]: Place the data from the task into a specific position in the command line. \ - Accepts two input parameters. - - All steps must start with "Plan: " and be less than 150 words. - Do not add any extra steps. - Ensure each step contains all the required information - do not skip steps. - Do not add any extra data after the evidence. - - Start example - - Task: Run a new alpine:latest container in the background, mount the host /root folder to /data, and execute \ - the top command. - Prefix: `docker run` - Usage: `docker run ${OPTS} ${image} ${command}`. This is a Python template string. OPTS is a placeholder for all \ - flags. The arguments must be one of ["image", "command"]. - Prefix description: The description of binary program `docker` is "Docker container platform", and the \ - description of `run` subcommand is "Create and run a new container from an image". - - Plan: I need a flag to make the container run in the background. #E1 = Option[Run a single container in the \ - background] - Plan: I need a flag to mount the host /root directory to /data directory in the container. #E2 = Option[Mount \ - host /root directory to /data directory] - Plan: I need to parse the image name from the task. #E3 = Argument[image] - Plan: I need to specify the command to be run in the container. #E4 = Argument[command] - Final: Assemble the above clues to generate the final command. #F - - End example - - Let's get started! - """, - } - """系统提示词""" - - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 任务:{instruction} - 前缀:`{binary_name} {subcmd_name}` - 用法:`{subcmd_usage}`。这是一个Python模板字符串。OPTS是所有标志的占位符。参数必须是 {argument_list} 其中之一。 - 前缀描述:二进制程序`{binary_name}`的描述为"{binary_description}",`{subcmd_name}`子命令的描述为\ - "{subcmd_description}"。 - - 请生成相应的计划。 - """, - LanguageType.ENGLISH: r""" - Task: {instruction} - Prefix: `{binary_name} {subcmd_name}` - Usage: `{subcmd_usage}`. This is a Python template string. OPTS is a placeholder for all flags. The arguments \ - must be one of {argument_list}. - Prefix description: The description of binary program `{binary_name}` is "{binary_description}", and the \ - description of `{subcmd_name}` subcommand is "{subcmd_description}". - - Please generate the corresponding plan. - """, - } - """用户提示词""" + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: r""" + 你是一个计划生成器。对于给定的目标,**制定一个简单的计划**,该计划可以逐步生成合适的命令行参数和标志。 + + 你会收到一个"命令前缀",这是已经确定和生成的命令部分。你需要基于这个前缀使用标志和参数来完成命令。 + + 在每一步中,指明使用哪个外部工具以及工具输入来获取证据。 + + 工具可以是以下之一: + (1) Option["指令"]:查询最相似的命令行标志。只接受一个输入参数,"指令"必须是搜索字符串。搜索字符串应该详细且包含必要的数据。 + (2) Argument[名称]<值>:将任务中的数据放置到命令行的特定位置。接受两个输入参数。 + + 所有步骤必须以"Plan: "开头,且少于150个单词。 + 不要添加任何多余的步骤。 + 确保每个步骤都包含所需的所有信息 - 不要跳过步骤。 + 不要在证据后面添加任何额外数据。 + + 开始示例 + + 任务:在后台运行一个新的alpine:latest容器,将主机/root文件夹挂载至/data,并执行top命令。 + 前缀:`docker run` + 用法:`docker run ${OPTS} ${image} ${command}`。这是一个Python模板字符串。OPTS是所有标志的占位符。参数必须是 \ + ["image", "command"] 其中之一。 + 前缀描述:二进制程序`docker`的描述为"Docker容器平台",`run`子命令的描述为"从镜像创建并运行一个新的容器"。 + + Plan: 我需要一个标志使容器在后台运行。 #E1 = Option[在后台运行单个容器] + Plan: 我需要一个标志,将主机/root目录挂载至容器内/data目录。 #E2 = Option[挂载主机/root目录至/data目录] + Plan: 我需要从任务中解析出镜像名称。 #E3 = Argument[image] + Plan: 我需要指定容器中运行的命令。 #E4 = Argument[command] + Final: 组装上述线索,生成最终命令。 #F + + 示例结束 + + 让我们开始! + """, + LanguageType.ENGLISH: r""" + You are a plan generator. For a given goal, **draft a simple plan** that can step-by-step generate the \ + appropriate command line arguments and flags. + + You will receive a "command prefix", which is the already determined and generated command part. You need to \ + use the flags and arguments based on this prefix to complete the command. + + In each step, specify which external tool to use and the tool input to get the evidence. + + The tool can be one of the following: + (1) Option["instruction"]: Query the most similar command line flag. Only accepts one input parameter, \ + "instruction" must be a search string. The search string should be detailed and contain necessary data. + (2) Argument["name"]: Place the data from the task into a specific position in the command line. \ + Accepts two input parameters. + + All steps must start with "Plan: " and be less than 150 words. + Do not add any extra steps. + Ensure each step contains all the required information - do not skip steps. + Do not add any extra data after the evidence. + + Start example + + Task: Run a new alpine:latest container in the background, mount the host /root folder to /data, and execute \ + the top command. + Prefix: `docker run` + Usage: `docker run ${OPTS} ${image} ${command}`. This is a Python template string. OPTS is a placeholder for all \ + flags. The arguments must be one of ["image", "command"]. + Prefix description: The description of binary program `docker` is "Docker container platform", and the \ + description of `run` subcommand is "Create and run a new container from an image". + + Plan: I need a flag to make the container run in the background. #E1 = Option[Run a single container in the \ + background] + Plan: I need a flag to mount the host /root directory to /data directory in the container. #E2 = Option[Mount \ + host /root directory to /data directory] + Plan: I need to parse the image name from the task. #E3 = Argument[image] + Plan: I need to specify the command to be run in the container. #E4 = Argument[command] + Final: Assemble the above clues to generate the final command. #F + + End example + + Let's get started! + """, + } + """系统提示词""" + + user_prompt = { + LanguageType.CHINESE: r""" + 任务:{instruction} + 前缀:`{binary_name} {subcmd_name}` + 用法:`{subcmd_usage}`。这是一个Python模板字符串。OPTS是所有标志的占位符。参数必须是 {argument_list} 其中之一。 + 前缀描述:二进制程序`{binary_name}`的描述为"{binary_description}",`{subcmd_name}`子命令的描述为\ + "{subcmd_description}"。 + + 请生成相应的计划。 + """, + LanguageType.ENGLISH: r""" + Task: {instruction} + Prefix: `{binary_name} {subcmd_name}` + Usage: `{subcmd_usage}`. This is a Python template string. OPTS is a placeholder for all flags. The arguments \ + must be one of {argument_list}. + Prefix description: The description of binary program `{binary_name}` is "{binary_description}", and the \ + description of `{subcmd_name}` subcommand is "{subcmd_description}". + + Please generate the corresponding plan. + """, + } + """用户提示词""" + return system_prompt, user_prompt def __init__( self, @@ -163,61 +165,63 @@ class InitPlan(CorePattern): class PlanEvaluator(CorePattern): """计划评估器""" - system_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 你是一个计划评估器。你的任务是评估给定的计划是否合理和完整。 - - 一个好的计划应该: - 1. 涵盖原始任务的所有要求 - 2. 使用适当的工具收集必要的信息 - 3. 具有清晰和逻辑的步骤 - 4. 没有冗余或不必要的步骤 - - 对于计划中的每个步骤,评估: - 1. 工具选择是否适当 - 2. 输入参数是否清晰和充分 - 3. 该步骤是否有助于实现最终目标 - - 请回复: - "VALID" - 如果计划良好且完整 - "INVALID: <原因>" - 如果计划有问题,请解释原因 - """, - LanguageType.ENGLISH: r""" - You are a plan evaluator. Your task is to evaluate whether the given plan is reasonable and complete. - - A good plan should: - 1. Cover all requirements of the original task - 2. Use appropriate tools to collect necessary information - 3. Have clear and logical steps - 4. Have no redundant or unnecessary steps - - For each step in the plan, evaluate: - 1. Whether the tool selection is appropriate - 2. Whether the input parameters are clear and sufficient - 3. Whether this step helps achieve the final goal - - Please reply: - "VALID" - If the plan is good and complete - "INVALID: <原因>" - If the plan has problems, please explain the reason - """, - } - """系统提示词""" - - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 任务:{instruction} - 计划:{plan} - - 评估计划并回复"VALID"或"INVALID: <原因>"。 - """, - LanguageType.ENGLISH: r""" - Task: {instruction} - Plan: {plan} - - Evaluate the plan and reply with "VALID" or "INVALID: <原因>". - """, - } - """用户提示词""" + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: r""" + 你是一个计划评估器。你的任务是评估给定的计划是否合理和完整。 + + 一个好的计划应该: + 1. 涵盖原始任务的所有要求 + 2. 使用适当的工具收集必要的信息 + 3. 具有清晰和逻辑的步骤 + 4. 没有冗余或不必要的步骤 + + 对于计划中的每个步骤,评估: + 1. 工具选择是否适当 + 2. 输入参数是否清晰和充分 + 3. 该步骤是否有助于实现最终目标 + + 请回复: + "VALID" - 如果计划良好且完整 + "INVALID: <原因>" - 如果计划有问题,请解释原因 + """, + LanguageType.ENGLISH: r""" + You are a plan evaluator. Your task is to evaluate whether the given plan is reasonable and complete. + + A good plan should: + 1. Cover all requirements of the original task + 2. Use appropriate tools to collect necessary information + 3. Have clear and logical steps + 4. Have no redundant or unnecessary steps + + For each step in the plan, evaluate: + 1. Whether the tool selection is appropriate + 2. Whether the input parameters are clear and sufficient + 3. Whether this step helps achieve the final goal + + Please reply: + "VALID" - If the plan is good and complete + "INVALID: <原因>" - If the plan has problems, please explain the reason + """, + } + """系统提示词""" + + user_prompt = { + LanguageType.CHINESE: r""" + 任务:{instruction} + 计划:{plan} + + 评估计划并回复"VALID"或"INVALID: <原因>"。 + """, + LanguageType.ENGLISH: r""" + Task: {instruction} + Plan: {plan} + + Evaluate the plan and reply with "VALID" or "INVALID: <原因>". + """, + } + """用户提示词""" + return system_prompt, user_prompt def __init__( self, @@ -254,57 +258,59 @@ class PlanEvaluator(CorePattern): class RePlanner(CorePattern): """重新规划器""" - system_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 你是一个计划重新规划器。当计划被评估为无效时,你需要生成一个新的、改进的计划。 - - 新计划应该: - 1. 解决评估中提到的所有问题 - 2. 保持与原始计划相同的格式 - 3. 更加精确和完整 - 4. 为每个步骤使用适当的工具 - - 遵循与原始计划相同的格式: - - 每个步骤应以"Plan: "开头 - - 包含带有适当参数的工具使用 - - 保持步骤简洁和重点突出 - - 以"Final"步骤结束 - """, - LanguageType.ENGLISH: r""" - You are a plan replanner. When the plan is evaluated as invalid, you need to generate a new, improved plan. - - The new plan should: - 1. Solve all problems mentioned in the evaluation - 2. Keep the same format as the original plan - 3. Be more precise and complete - 4. Use appropriate tools for each step - - Follow the same format as the original plan: - - Each step should start with "Plan: " - - Include tool usage with appropriate parameters - - Keep steps concise and focused - - End with the "Final" step - """, - } - """系统提示词""" - - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - 任务:{instruction} - 原始计划:{plan} - 评估:{evaluation} - - 生成一个新的、改进的计划,解决评估中提到的所有问题。 - """, - LanguageType.ENGLISH: r""" - Task: {instruction} - Original Plan: {plan} - Evaluation: {evaluation} - - Generate a new, improved plan that solves all problems mentioned in the evaluation. - """, - } - """用户提示词""" + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: r""" + 你是一个计划重新规划器。当计划被评估为无效时,你需要生成一个新的、改进的计划。 + + 新计划应该: + 1. 解决评估中提到的所有问题 + 2. 保持与原始计划相同的格式 + 3. 更加精确和完整 + 4. 为每个步骤使用适当的工具 + + 遵循与原始计划相同的格式: + - 每个步骤应以"Plan: "开头 + - 包含带有适当参数的工具使用 + - 保持步骤简洁和重点突出 + - 以"Final"步骤结束 + """, + LanguageType.ENGLISH: r""" + You are a plan replanner. When the plan is evaluated as invalid, you need to generate a new, improved plan. + + The new plan should: + 1. Solve all problems mentioned in the evaluation + 2. Keep the same format as the original plan + 3. Be more precise and complete + 4. Use appropriate tools for each step + + Follow the same format as the original plan: + - Each step should start with "Plan: " + - Include tool usage with appropriate parameters + - Keep steps concise and focused + - End with the "Final" step + """, + } + """系统提示词""" + + user_prompt = { + LanguageType.CHINESE: r""" + 任务:{instruction} + 原始计划:{plan} + 评估:{evaluation} + + 生成一个新的、改进的计划,解决评估中提到的所有问题。 + """, + LanguageType.ENGLISH: r""" + Task: {instruction} + Original Plan: {plan} + Evaluation: {evaluation} + + Generate a new, improved plan that solves all problems mentioned in the evaluation. + """, + } + """用户提示词""" + return system_prompt, user_prompt def __init__( self, diff --git a/apps/llm/patterns/rewrite.py b/apps/llm/patterns/rewrite.py index 6cf36e47b..cbc3e8b74 100644 --- a/apps/llm/patterns/rewrite.py +++ b/apps/llm/patterns/rewrite.py @@ -23,125 +23,127 @@ class QuestionRewriteResult(BaseModel): class QuestionRewrite(CorePattern): """问题补全与重写""" - system_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: r""" + + + 根据历史对话,推断用户的实际意图并补全用户的提问内容,历史对话被包含在标签中,用户意图被包含在标签中。 + 要求: + 1. 请使用JSON格式输出,参考下面给出的样例;不要包含任何XML标签,不要包含任何解释说明; + 2. 若用户当前提问内容与对话上文不相关,或你认为用户的提问内容已足够完整,请直接输出用户的提问内容。 + 3. 补全内容必须精准、恰当,不要编造任何内容。 + 4. 请输出补全后的问题,不要输出其他内容。 + 输出格式样例: + {{ + "question": "补全后的问题" + }} + + + + + + + openEuler的特点是什么? + + + openEuler相较于其他操作系统,其特点是支持多种硬件架构,并且提供稳定、安全、高效的操作系统平台。 + + + + + openEuler的优势有哪些? + + + openEuler的优势包括开源、社区支持、以及对云计算和边缘计算的优化。 + + + + + + 详细点? + + + {{ + "question": "openEuler的特点是什么?请详细说明其优势和应用场景。" + }} + + + + + {history} + + + {question} + + """, + LanguageType.ENGLISH: r""" + + + Based on the historical dialogue, infer the user's actual intent and complete the user's question. The historical dialogue is contained within the tags, and the user's intent is contained within the tags. + Requirements: + 1. Please output in JSON format, referring to the example provided below; do not include any XML tags or any explanatory notes; + 2. If the user's current question is unrelated to the previous dialogue or you believe the user's question is already complete enough, directly output the user's question. + 3. The completed content must be precise and appropriate; do not fabricate any content. + 4. Output only the completed question; do not include any other content. + Example output format: + {{ + "question": "The completed question" + }} + + + + + + + What are the features of openEuler? + + + Compared to other operating systems, openEuler's features include support for multiple hardware architectures and providing a stable, secure, and efficient operating system platform. + + + + + What are the advantages of openEuler? + + + The advantages of openEuler include being open-source, having community support, and optimizations for cloud and edge computing. + + + + + + More details? + + + {{ + "question": "What are the features of openEuler? Please elaborate on its advantages and application scenarios." + }} + + + + + {history} + + + {question} + + """ + } + + """用户提示词""" + user_prompt = { + LanguageType.CHINESE: r""" - - 根据历史对话,推断用户的实际意图并补全用户的提问内容,历史对话被包含在标签中,用户意图被包含在标签中。 - 要求: - 1. 请使用JSON格式输出,参考下面给出的样例;不要包含任何XML标签,不要包含任何解释说明; - 2. 若用户当前提问内容与对话上文不相关,或你认为用户的提问内容已足够完整,请直接输出用户的提问内容。 - 3. 补全内容必须精准、恰当,不要编造任何内容。 - 4. 请输出补全后的问题,不要输出其他内容。 - 输出格式样例: - {{ - "question": "补全后的问题" - }} - - - - - - - openEuler的特点是什么? - - - openEuler相较于其他操作系统,其特点是支持多种硬件架构,并且提供稳定、安全、高效的操作系统平台。 - - - - - openEuler的优势有哪些? - - - openEuler的优势包括开源、社区支持、以及对云计算和边缘计算的优化。 - - - - - - 详细点? - - - {{ - "question": "openEuler的特点是什么?请详细说明其优势和应用场景。" - }} - - + 请输出补全后的问题 - - {history} - - - {question} - - """, - LanguageType.ENGLISH: r""" + """, + LanguageType.ENGLISH: r""" - - Based on the historical dialogue, infer the user's actual intent and complete the user's question. The historical dialogue is contained within the tags, and the user's intent is contained within the tags. - Requirements: - 1. Please output in JSON format, referring to the example provided below; do not include any XML tags or any explanatory notes; - 2. If the user's current question is unrelated to the previous dialogue or you believe the user's question is already complete enough, directly output the user's question. - 3. The completed content must be precise and appropriate; do not fabricate any content. - 4. Output only the completed question; do not include any other content. - Example output format: - {{ - "question": "The completed question" - }} - - - - - - - What are the features of openEuler? - - - Compared to other operating systems, openEuler's features include support for multiple hardware architectures and providing a stable, secure, and efficient operating system platform. - - - - - What are the advantages of openEuler? - - - The advantages of openEuler include being open-source, having community support, and optimizations for cloud and edge computing. - - - - - - More details? - - - {{ - "question": "What are the features of openEuler? Please elaborate on its advantages and application scenarios." - }} - - + Please output the completed question - - {history} - - - {question} - - """ - } - - """用户提示词""" - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - - 请输出补全后的问题 - - """, - LanguageType.ENGLISH: r""" - - Please output the completed question - - """} + """} + return system_prompt, user_prompt async def generate(self, **kwargs) -> str: # noqa: ANN003 """问题补全与重写""" diff --git a/apps/llm/patterns/select.py b/apps/llm/patterns/select.py index 924eeba38..4d9ab3a08 100644 --- a/apps/llm/patterns/select.py +++ b/apps/llm/patterns/select.py @@ -19,105 +19,53 @@ logger = logging.getLogger(__name__) class Select(CorePattern): """通过投票选择最佳答案""" - system_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: "你是一个有用的助手。", - LanguageType.ENGLISH: "You are a helpful assistant.", - } - """系统提示词""" - - user_prompt: dict[LanguageType, str] = { - LanguageType.CHINESE: r""" - - - 根据历史对话(包括工具调用结果)和用户问题,从给出的选项列表中,选出最符合要求的那一项。 - 在输出之前,请先思考,并使用“”标签给出思考过程。 - 结果需要使用JSON格式输出,输出格式为:{{ "choice": "选项名称" }} - - - - - 使用天气API,查询明天杭州的天气信息 - - - - API - HTTP请求,获得返回的JSON数据 - - - SQL - 查询数据库,获得数据库表中的数据 - - - - - - API 工具可以通过 API 来获取外部数据,而天气信息可能就存储在外部数据中,由于用户说明中明确提到了 \ - 天气 API 的使用,因此应该优先使用 API 工具。\ - SQL 工具用于从数据库中获取信息,考虑到天气数据的可变性和动态性,不太可能存储在数据库中,因此 \ - SQL 工具的优先级相对较低,\ - 最佳选择似乎是“API:请求特定 API,获取返回的 JSON 数据”。 - - - - {{ "choice": "API" }} - - - - - - - {question} - - - - {choice_list} - - - - - 让我们一步一步思考。 - """, - LanguageType.ENGLISH: r""" - - - Based on the historical dialogue (including tool call results) and user question, select the most \ - suitable option from the given option list. - Before outputting, please think carefully and use the "" tag to give the thinking process. - The output needs to be in JSON format, the output format is: {{ "choice": "option name" }} - - - - - Use the weather API to query the weather information of Hangzhou tomorrow - - - - API - HTTP request, get the returned JSON data - - - SQL - Query the database, get the data in the database table - - - - - - The API tool can get external data through API, and the weather information may be stored in \ - external data. Since the user clearly mentioned the use of weather API, it should be given \ - priority to the API tool.\ - The SQL tool is used to get information from the database, considering the variability and \ - dynamism of weather data, it is unlikely to be stored in the database, so the priority of \ - the SQL tool is relatively low, \ - The best choice seems to be "API: request a specific API, get the returned JSON data". - - - - {{ "choice": "API" }} - - - - + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: "你是一个有用的助手。", + LanguageType.ENGLISH: "You are a helpful assistant.", + } + """系统提示词""" + + user_prompt = { + LanguageType.CHINESE: r""" + + + 根据历史对话(包括工具调用结果)和用户问题,从给出的选项列表中,选出最符合要求的那一项。 + 在输出之前,请先思考,并使用“”标签给出思考过程。 + 结果需要使用JSON格式输出,输出格式为:{{ "choice": "选项名称" }} + + + + + 使用天气API,查询明天杭州的天气信息 + + + + API + HTTP请求,获得返回的JSON数据 + + + SQL + 查询数据库,获得数据库表中的数据 + + + + + + API 工具可以通过 API 来获取外部数据,而天气信息可能就存储在外部数据中,由于用户说明中明确提到了 \ + 天气 API 的使用,因此应该优先使用 API 工具。\ + SQL 工具用于从数据库中获取信息,考虑到天气数据的可变性和动态性,不太可能存储在数据库中,因此 \ + SQL 工具的优先级相对较低,\ + 最佳选择似乎是“API:请求特定 API,获取返回的 JSON 数据”。 + + + + {{ "choice": "API" }} + + + + + {question} @@ -128,12 +76,66 @@ class Select(CorePattern): - Let's think step by step. - - - """, - } - """用户提示词""" + 让我们一步一步思考。 + """, + LanguageType.ENGLISH: r""" + + + Based on the historical dialogue (including tool call results) and user question, select the most \ + suitable option from the given option list. + Before outputting, please think carefully and use the "" tag to give the thinking process. + The output needs to be in JSON format, the output format is: {{ "choice": "option name" }} + + + + + Use the weather API to query the weather information of Hangzhou tomorrow + + + + API + HTTP request, get the returned JSON data + + + SQL + Query the database, get the data in the database table + + + + + + The API tool can get external data through API, and the weather information may be stored in \ + external data. Since the user clearly mentioned the use of weather API, it should be given \ + priority to the API tool.\ + The SQL tool is used to get information from the database, considering the variability and \ + dynamism of weather data, it is unlikely to be stored in the database, so the priority of \ + the SQL tool is relatively low, \ + The best choice seems to be "API: request a specific API, get the returned JSON data". + + + + {{ "choice": "API" }} + + + + + + {question} + + + + {choice_list} + + + + + Let's think step by step. + + + """, + } + """用户提示词""" + return system_prompt, user_prompt slot_schema: ClassVar[dict[str, Any]] = { "type": "object", @@ -183,7 +185,6 @@ class Select(CorePattern): function_result = await json_gen.generate() return function_result["choice"] - async def generate(self, **kwargs) -> str: # noqa: ANN003 """使用大模型做出选择""" logger.info("[Select] 使用LLM选择") @@ -191,7 +192,7 @@ class Select(CorePattern): result_list = [] background = kwargs.get("background", "无背景信息。") - self.language = kwargs.get("language", LanguageType.CHINESE) + language = kwargs.get("language", LanguageType.CHINESE) data_str = json.dumps(kwargs.get("data", {}), ensure_ascii=False) choice_prompt, choices_list = choices_to_prompt(kwargs["choices"]) @@ -205,7 +206,7 @@ class Select(CorePattern): return choices_list[0] logger.info("[Select] 选项列表: %s", choice_prompt) - user_input = self.user_prompt[self.language].format( + user_input = self.user_prompt[language].format( question=kwargs["question"], background=background, data=data_str, diff --git a/apps/scheduler/call/choice/choice.py b/apps/scheduler/call/choice/choice.py index 38e9c0af4..fb2c475a3 100644 --- a/apps/scheduler/call/choice/choice.py +++ b/apps/scheduler/call/choice/choice.py @@ -50,17 +50,6 @@ class Choice(CoreCall, input_model=ChoiceInput, output_model=ChoiceOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - def _raise_value_error(self, msg: str) -> None: """统一处理 ValueError 异常抛出""" logger.warning(msg) diff --git a/apps/scheduler/call/core.py b/apps/scheduler/call/core.py index 16db72155..20f958508 100644 --- a/apps/scheduler/call/core.py +++ b/apps/scheduler/call/core.py @@ -71,11 +71,6 @@ class CoreCall(BaseModel): exclude=True, frozen=True, ) - language: ClassVar[SkipJsonSchema[LanguageType]] = Field( - description="语言", - default=LanguageType.CHINESE, - exclude=True, - ) i18n_info: ClassVar[SkipJsonSchema[dict[str, dict]]] = {} to_user: bool = Field(description="是否需要将输出返回给用户", default=False) @@ -85,6 +80,17 @@ class CoreCall(BaseModel): extra="allow", ) + @classmethod + def info(cls, language: LanguageType) -> CallInfo: + """ + 返回Call的名称和描述 + + :return: Call的名称和描述 + :rtype: CallInfo + """ + lang_info = cls.i18n_info.get(language, cls.i18n_info[LanguageType.CHINESE]) + return CallInfo(name=lang_info["name"], description=lang_info["description"]) + def __init_subclass__( cls, input_model: type[DataBase], output_model: type[DataBase], **kwargs: Any ) -> None: diff --git a/apps/scheduler/call/facts/facts.py b/apps/scheduler/call/facts/facts.py index 42c3c7f1e..b03745ae3 100644 --- a/apps/scheduler/call/facts/facts.py +++ b/apps/scheduler/call/facts/facts.py @@ -41,16 +41,6 @@ class FactsCall(CoreCall, input_model=FactsInput, output_model=FactsOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) @classmethod async def instance(cls, executor: "StepExecutor", node: NodePool | None, **kwargs: Any) -> Self: diff --git a/apps/scheduler/call/graph/graph.py b/apps/scheduler/call/graph/graph.py index c4cd6b685..84501d6b2 100644 --- a/apps/scheduler/call/graph/graph.py +++ b/apps/scheduler/call/graph/graph.py @@ -36,17 +36,6 @@ class Graph(CoreCall, input_model=RenderInput, output_model=RenderOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _init(self, call_vars: CallVars) -> RenderInput: """初始化Render Call,校验参数,读取option模板""" try: diff --git a/apps/scheduler/call/graph/style.py b/apps/scheduler/call/graph/style.py index 631ea88ac..e9fef0386 100644 --- a/apps/scheduler/call/graph/style.py +++ b/apps/scheduler/call/graph/style.py @@ -9,6 +9,7 @@ from pydantic import BaseModel, Field from apps.llm.function import JsonGenerator from apps.llm.patterns.core import CorePattern from apps.llm.reasoning import ReasoningLLM +from apps.schemas.enum_var import LanguageType logger = logging.getLogger(__name__) @@ -24,53 +25,95 @@ class RenderStyleResult(BaseModel): class RenderStyle(CorePattern): """选择图表样式""" - system_prompt = r""" - You are a helpful assistant. Help the user make style choices when drawing a chart. - Chart title should be short and less than 3 words. - - Available types: - - `bar`: Bar graph - - `pie`: Pie graph - - `line`: Line graph - - `scatter`: Scatter graph - - Available bar additional styles: - - `normal`: Normal bar graph - - `stacked`: Stacked bar graph - - Available pie additional styles: - - `normal`: Normal pie graph - - `ring`: Ring pie graph - - Available scales: - - `linear`: Linear scale - - `log`: Logarithmic scale - - EXAMPLE - ## Question - 查询数据库中的数据,并绘制堆叠柱状图。 - - ## Thought - Let's think step by step. The user requires drawing a stacked bar chart, so the chart type should be `bar`, \ - i.e. a bar chart; the chart style should be `stacked`, i.e. a stacked form. - - ## Answer - The chart type should be: bar - The chart style should be: stacked - The scale should be: linear - - END OF EXAMPLE - - Let's begin. - """ - - user_prompt = r""" - ## Question - {question} - - ## Thought - Let's think step by step. - """ + def get_default_prompt(self) -> dict[LanguageType, str]: + system_prompt = { + LanguageType.CHINESE: r""" + 你是一个有用的助手。帮助用户在绘制图表时做出样式选择。 + 图表标题应简短且少于3个字。 + 可用类型: + - `bar`: 柱状图 + - `pie`: 饼图 + - `line`: 折线图 + - `scatter`: 散点图 + 可用柱状图附加样式: + - `normal`: 普通柱状图 + - `stacked`: 堆叠柱状图 + 可用饼图附加样式: + - `normal`: 普通饼图 + - `ring`: 环形饼图 + 可用比例: + - `linear`: 线性比例 + - `log`: 对数比例 + EXAMPLE + ## 问题 + 查询数据库中的数据,并绘制堆叠柱状图。 + ## 思考 + 让我们一步步思考。用户要求绘制堆叠柱状图,因此图表类型应为 `bar`,即柱状图;图表样式 + 应为 `stacked`,即堆叠形式。 + ## 答案 + 图表类型应为:bar + 图表样式应为:stacked + 比例应为:linear + END OF EXAMPLE + + 让我们开始吧。 + """, + LanguageType.ENGLISH: r""" + You are a helpful assistant. Help the user make style choices when drawing a chart. + Chart title should be short and less than 3 words. + + Available types: + - `bar`: Bar graph + - `pie`: Pie graph + - `line`: Line graph + - `scatter`: Scatter graph + + Available bar additional styles: + - `normal`: Normal bar graph + - `stacked`: Stacked bar graph + + Available pie additional styles: + - `normal`: Normal pie graph + - `ring`: Ring pie graph + + Available scales: + - `linear`: Linear scale + - `log`: Logarithmic scale + + EXAMPLE + ## Question + 查询数据库中的数据,并绘制堆叠柱状图。 + + ## Thought + Let's think step by step. The user requires drawing a stacked bar chart, so the chart type should be `bar`, \ + i.e. a bar chart; the chart style should be `stacked`, i.e. a stacked form. + + ## Answer + The chart type should be: bar + The chart style should be: stacked + The scale should be: linear + + END OF EXAMPLE + + Let's begin. + """ + } + user_prompt = { + LanguageType.CHINESE: r""" + ## 问题 + {question} + ## 思考 + 让我们一步步思考。根据用户问题,选择合适的图表类型、样式和比例。 + """, + LanguageType.ENGLISH: r""" + ## Question + {question} + + ## Thought + Let's think step by step. + """ + } + return system_prompt, user_prompt def __init__(self, system_prompt: str | None = None, user_prompt: str | None = None) -> None: """初始化RenderStyle Prompt""" @@ -79,11 +122,11 @@ class RenderStyle(CorePattern): async def generate(self, **kwargs) -> dict[str, Any]: # noqa: ANN003 """使用LLM选择图表样式""" question = kwargs["question"] - + language = kwargs.get("language", LanguageType.CHINESE) # 使用Reasoning模型进行推理 messages = [ - {"role": "system", "content": self.system_prompt}, - {"role": "user", "content": self.user_prompt.format(question=question)}, + {"role": "system", "content": self.system_prompt[language]}, + {"role": "user", "content": self.user_prompt[language].format(question=question)}, ] result = "" llm = ReasoningLLM() diff --git a/apps/scheduler/call/llm/llm.py b/apps/scheduler/call/llm/llm.py index e1766674e..74c5f2f5b 100644 --- a/apps/scheduler/call/llm/llm.py +++ b/apps/scheduler/call/llm/llm.py @@ -49,17 +49,6 @@ class LLM(CoreCall, input_model=LLMInput, output_model=LLMOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _prepare_message(self, call_vars: CallVars) -> list[dict[str, Any]]: """准备消息""" # 创建共享的 Environment 实例 diff --git a/apps/scheduler/call/mcp/mcp.py b/apps/scheduler/call/mcp/mcp.py index 07d23aaf6..c0930ae0e 100644 --- a/apps/scheduler/call/mcp/mcp.py +++ b/apps/scheduler/call/mcp/mcp.py @@ -68,17 +68,6 @@ class MCP(CoreCall, input_model=MCPInput, output_model=MCPOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _init(self, call_vars: CallVars) -> MCPInput: """初始化MCP""" # 获取MCP交互类 diff --git a/apps/scheduler/call/rag/rag.py b/apps/scheduler/call/rag/rag.py index f1f1bf761..789a030a9 100644 --- a/apps/scheduler/call/rag/rag.py +++ b/apps/scheduler/call/rag/rag.py @@ -48,17 +48,6 @@ class RAG(CoreCall, input_model=RAGInput, output_model=RAGOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _init(self, call_vars: CallVars) -> RAGInput: """初始化RAG工具""" return RAGInput( diff --git a/apps/scheduler/call/search/search.py b/apps/scheduler/call/search/search.py index 83c2c53f1..473e7d73e 100644 --- a/apps/scheduler/call/search/search.py +++ b/apps/scheduler/call/search/search.py @@ -28,17 +28,6 @@ class Search(CoreCall, input_model=SearchInput, output_model=SearchOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _init(self, call_vars: CallVars) -> SearchInput: """初始化工具""" pass diff --git a/apps/scheduler/call/slot/slot.py b/apps/scheduler/call/slot/slot.py index cdbc54347..8dd0e7162 100644 --- a/apps/scheduler/call/slot/slot.py +++ b/apps/scheduler/call/slot/slot.py @@ -43,17 +43,6 @@ class Slot(CoreCall, input_model=SlotInput, output_model=SlotOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _llm_slot_fill( self, remaining_schema: dict[str, Any], language: LanguageType = LanguageType.CHINESE ) -> tuple[str, dict[str, Any]]: diff --git a/apps/scheduler/call/sql/sql.py b/apps/scheduler/call/sql/sql.py index ca6ed90b8..6eab84436 100644 --- a/apps/scheduler/call/sql/sql.py +++ b/apps/scheduler/call/sql/sql.py @@ -53,17 +53,6 @@ class SQL(CoreCall, input_model=SQLInput, output_model=SQLOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - async def _init(self, call_vars: CallVars) -> SQLInput: """初始化SQL工具。""" return SQLInput( diff --git a/apps/scheduler/call/suggest/suggest.py b/apps/scheduler/call/suggest/suggest.py index 4ba912e68..1e67b88bd 100644 --- a/apps/scheduler/call/suggest/suggest.py +++ b/apps/scheduler/call/suggest/suggest.py @@ -58,17 +58,6 @@ class Suggestion(CoreCall, input_model=SuggestionInput, output_model=SuggestionO }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - @classmethod async def instance(cls, executor: "StepExecutor", node: NodePool | None, **kwargs: Any) -> Self: """初始化""" diff --git a/apps/scheduler/call/summary/summary.py b/apps/scheduler/call/summary/summary.py index c634880c0..da3139a98 100644 --- a/apps/scheduler/call/summary/summary.py +++ b/apps/scheduler/call/summary/summary.py @@ -39,17 +39,6 @@ class Summary(CoreCall, input_model=DataBase, output_model=SummaryOutput): }, } - @classmethod - def info(cls) -> CallInfo: - """ - 返回Call的名称和描述 - - :return: Call的名称和描述 - :rtype: CallInfo - """ - lang_info = cls.i18n_info.get(cls.language, cls.i18n_info[LanguageType.CHINESE]) - return CallInfo(name=lang_info["name"], description=lang_info["description"]) - @classmethod async def instance(cls, executor: "StepExecutor", node: NodePool | None, **kwargs: Any) -> Self: """实例化工具""" diff --git a/apps/services/flow.py b/apps/services/flow.py index 5f6c29c8a..5d5c9a634 100644 --- a/apps/services/flow.py +++ b/apps/services/flow.py @@ -97,8 +97,7 @@ class FlowManager: if service_id == "": call_class: type[BaseModel] = await Pool().get_call(node_pool_record["_id"]) - call_class.language = language - node_name = call_class.info().name + node_name = call_class.info(language).name node_description = call_class.info().description else: node_name = node_pool_record["name"] -- Gitee