diff --git a/apps/routers/appcenter.py b/apps/routers/appcenter.py index 3e05516445649b98a90a3e71b9f0c4ce89957e94..646cd8a12f469f503bc9ae061a63631cc7f2f514 100644 --- a/apps/routers/appcenter.py +++ b/apps/routers/appcenter.py @@ -259,7 +259,6 @@ async def get_application(appId: Annotated[uuid.UUID, Path()]) -> JSONResponse: published=app_data.published, name=app_data.name, description=app_data.description, - icon=app_data.icon, links=app_data.links, recommendedQuestions=app_data.first_questions, dialogRounds=app_data.history_len, @@ -289,7 +288,6 @@ async def get_application(appId: Annotated[uuid.UUID, Path()]) -> JSONResponse: published=app_data.published, name=app_data.name, description=app_data.description, - icon=app_data.icon, links=[], recommendedQuestions=[], dialogRounds=app_data.history_len, diff --git a/apps/scheduler/call/cmd/__init__.py b/apps/scheduler/call/cmd/__init__.py deleted file mode 100644 index ae0a99e47f7d13c02f056b035ebcb3e174750614..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/__init__.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""命令生成工具""" diff --git a/apps/scheduler/call/cmd/assembler.py b/apps/scheduler/call/cmd/assembler.py deleted file mode 100644 index 6ac19edb0f3751b36c028f0d4f52682c6ba1248b..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/assembler.py +++ /dev/null @@ -1,69 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""命令行组装器""" - -import string -from typing import Any, Literal, Optional - - -class CommandlineAssembler: - """命令行组装器""" - - @staticmethod - def convert_dict_to_cmdline(args_dict: dict[str, Any], usage: str) -> str: - """将字典转换为命令行""" - opts_result = "" - for key, val in args_dict["opts"].items(): - if isinstance(val, bool) and val: - opts_result += f" {key}" - continue - - opts_result += f" {key} {val}" - # opts_result = opts_result.lstrip(" ") + " ${OPTS}" - opts_result = opts_result.lstrip(" ") - - result = string.Template(usage) - return result.safe_substitute(OPTS=opts_result, **args_dict["args"]) - - @staticmethod - def get_command(instruction: str, collection_name: str) -> str: - """获取命令行""" - collection = VectorDB.get_collection(collection_name) - return VectorDB.get_docs(collection, instruction, {"type": "binary"}, 1)[0].metadata["name"] - - @staticmethod - def _documents_to_choices(docs: list[DocumentWrapper]) -> list[dict[str, Any]]: - return [{ - "name": doc.metadata["name"], - "description": doc.data, - } for doc in docs] - - @staticmethod - def get_data( - query_type: Literal["subcommand", "global_option", "option", "argument"], - instruction: str, collection_name: str, binary_name: str, subcmd_name: Optional[str] = None, num: int = 5, - ) -> list[dict[str, Any]]: - collection = VectorDB.get_collection(collection_name) - if collection is None: - err = f"Collection {collection_name} not found" - raise ValueError(err) - - # Query certain type - requirements = { - "$and": [ - {"type": query_type}, - {"binary": binary_name}, - ], - } - if subcmd_name is not None: - requirements["$and"].append({"subcmd": subcmd_name}) - - result_list = VectorDB.get_docs(collection, instruction, requirements, num) - - return CommandlineAssembler._documents_to_choices(result_list) - - @staticmethod - async def select_option(instruction: str, choices: list[dict[str, Any]]) -> tuple[str, str]: - """选择当前最合适的命令行选项""" - top_option = await Select().generate(choices, instruction=instruction) - top_option_description = [choice["description"] for choice in choices if choice["name"] == top_option] - return top_option, top_option_description[0] diff --git a/apps/scheduler/call/cmd/cmd.py b/apps/scheduler/call/cmd/cmd.py deleted file mode 100644 index c4585a5509ea74e6384af9642ad7743c70abcf76..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/cmd.py +++ /dev/null @@ -1,33 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""自然语言生成命令""" - -from collections.abc import AsyncGenerator -from typing import Any - -from pydantic import Field - -from apps.models import LanguageType -from apps.scheduler.call.core import CoreCall -from apps.schemas.scheduler import CallInfo, CallOutputChunk - - -class Cmd(CoreCall): - """Cmd工具。用于根据BTDL描述文件,生成命令。""" - - exec_name: str | None = Field(default=None, description="命令中可执行文件的名称,可选") - args: list[str] = Field(default=[], description="命令中可执行文件的参数(例如 `--help`),可选") - - @classmethod - def info(cls, language: LanguageType = LanguageType.CHINESE) -> CallInfo: - """返回Call的名称和描述""" - i18n_info = { - LanguageType.CHINESE: CallInfo(name="Cmd", description="根据BTDL描述文件,生成命令。"), - LanguageType.ENGLISH: CallInfo( - name="Cmd", description="Generate commands based on BTDL description files.", - ), - } - return i18n_info[language] - - async def _exec(self, _slot_data: dict[str, Any]) -> AsyncGenerator[CallOutputChunk, None]: - """调用Cmd工具""" - diff --git a/apps/scheduler/call/cmd/prompt.py b/apps/scheduler/call/cmd/prompt.py deleted file mode 100644 index 6172fbd40b71ab306c0dcdc762f1527b1493283c..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/prompt.py +++ /dev/null @@ -1,219 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""命令行生成器相关提示词""" - -from textwrap import dedent - -from apps.models import LanguageType - -CREATE: dict[LanguageType, str] = { - LanguageType.CHINESE: dedent(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 - - - - - 任务:{{instruction}} - 前缀:`{{binary_name}} {{subcmd_name}}` - 用法:`{{subcmd_usage}}`。这是一个Python模板字符串。OPTS是所有标志的占位符。参数必须是 {{argument_list}} \ -其中之一。 - 前缀描述:二进制程序`{{binary_name}}`的描述为"{{binary_description}}",`{{subcmd_name}}`子命令的描述为\ - "{{subcmd_description}}"。 - - 现在生成相应的计划: - """), - LanguageType.ENGLISH: dedent(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. - - - - 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[Mounthost /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 - - - - 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}}". - - Generate the corresponding plan now: - """), -} - -EVALUATE: dict[LanguageType, str] = { - LanguageType.CHINESE: dedent(r""" - - - 你是一个计划评估器。你的任务是评估给定的计划是否合理和完整。 - - 一个好的计划应该: - 1. 涵盖原始任务的所有要求 - 2. 使用适当的工具收集必要的信息 - 3. 具有清晰和逻辑的步骤 - 4. 没有冗余或不必要的步骤 - - 对于计划中的每个步骤,评估: - 1. 工具选择是否适当 - 2. 输入参数是否清晰和充分 - 3. 该步骤是否有助于实现最终目标 - - 请回复: - "VALID" - 如果计划良好且完整 - "INVALID: <原因>" - 如果计划有问题,请解释原因 - - - - 任务:{{instruction}} - 计划:{{plan}} - - 现在评估计划,并回复"VALID"或"INVALID: <原因>": - """), - LanguageType.ENGLISH: dedent(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 - - - - Task: {{instruction}} - Original Plan: {{plan}} - Evaluation: {{evaluation}} - - Now evaluate the plan and reply "VALID" or "INVALID: ": - """), -} - -REPLAN: dict[LanguageType, str] = { - LanguageType.CHINESE: dedent(r""" - - - 你是一个计划重新规划器。当计划被评估为无效时,你需要生成一个新的、改进的计划。 - - 新计划应该: - 1. 解决评估中提到的所有问题 - 2. 保持与原始计划相同的格式 - 3. 更加精确和完整 - 4. 为每个步骤使用适当的工具 - - 遵循与原始计划相同的格式: - - 每个步骤应以"Plan: "开头 - - 包含带有适当参数的工具使用 - - 保持步骤简洁和重点突出 - - 以"Final"步骤结束 - - - - 任务:{{instruction}} - 原始计划:{{plan}} - 评估:{{evaluation}} - - 生成一个新的、改进的计划,解决评估中提到的所有问题: - """), - LanguageType.ENGLISH: dedent(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 - - - - Task: {{instruction}} - Original Plan: {{plan}} - Evaluation: {{evaluation}} - - Now, generate a new, improved plan that solves all problems mentioned in the evaluation: - """), -} diff --git a/apps/scheduler/call/cmd/schema.py b/apps/scheduler/call/cmd/schema.py deleted file mode 100644 index 107032df956569e32f5529b3d21309da8452637a..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/schema.py +++ /dev/null @@ -1,2 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""命令行生成工具 数据结构""" diff --git a/apps/scheduler/call/cmd/solver.py b/apps/scheduler/call/cmd/solver.py deleted file mode 100644 index 49cdd1714a6a3ee4814dc076b7b8f1f2ad32652a..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/cmd/solver.py +++ /dev/null @@ -1,125 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""命令行解析器""" - -import copy -import re -from typing import Any - -from apps.scheduler.call.cmd.assembler import CommandlineAssembler - - -class Solver: - """解析命令行生成器""" - - @staticmethod - async def _get_option(agent_input: str, collection_name: str, binary_name: str, subcmd_name: str, spec: dict[str, Any]) -> tuple[str, str]: - """选择最匹配的命令行参数""" - # 选择最匹配的Global Options - global_options = CommandlineAssembler.get_data("global_option", agent_input, collection_name, binary_name, num=2) - # 选择最匹配的Options - options = CommandlineAssembler.get_data("option", agent_input, collection_name, binary_name, subcmd_name, 3) - # 判断哪个更符合标准 - choices = options + global_options - result, result_desc = await CommandlineAssembler.select_option(agent_input, choices) - - option_type = "" - # 从BTDL里面拿出JSON Schema - if not option_type: - for opt in global_options: - if result == opt["name"]: - option_type = "global_option" - break - if not option_type: - for opt in options: - if result == opt["name"]: - option_type = "option" - break - - if option_type == "global_option": - spec = spec[binary_name][1][result] - elif option_type == "option": - spec = spec[binary_name][2][subcmd_name][2][result] - else: - err = "No option found." - raise ValueError(err) - - # 返回参数名字、描述 - return result, spec, result_desc - - @staticmethod - async def _get_value(question: str, description: str, spec: dict[str, Any]) -> dict[str, Any]: - """根据用户目标和JSON Schema,生成命令行参数""" - gen_input = f""" - 用户的目标为: [[{question}]] - - 依照JSON Schema,生成下列参数: - {description} - - 严格按照JSON Schema格式输出,不要添加或编造字段。""".format(objective=question, description=description) - return await Json().generate(question=gen_input, background="Empty.", spec=spec) - - - @staticmethod - async def process_output(output: str, question: str, collection_name: str, binary_name: str, subcmd_name: str, spec: dict[str, Any]) -> tuple[str, str]: # noqa: PLR0913 - """对规划器输出的evidence进行解析,生成命令行参数""" - spec_template = { - "type": "object", - "properties": {}, - } - opt_spec = copy.deepcopy(spec_template) - full_opt_desc = "" - arg_spec = copy.deepcopy(spec_template) - full_arg_desc = "" - - lines = output.split("\n") - for line in lines: - if not line.startswith("Plan:"): - continue - - evidence = re.search(r"#E.*", line) - if not evidence: - continue - evidence = evidence.group(0) - - if "Option" in evidence: - action_input = re.search(r"\[.*\]", evidence) - if not action_input: - continue - action_input = action_input.group(0) - action_input = action_input.rstrip("]").lstrip("[") - opt_name, single_opt_spec, opt_desc = await Solver._get_option(action_input, collection_name, binary_name, subcmd_name, spec) - - opt_spec["properties"].update({opt_name: single_opt_spec}) - full_opt_desc += f"- {opt_name}: {opt_desc}\n" - - elif "Argument" in evidence: - name = re.search(r"\[.*\]", evidence) - if not name: - continue - name = name.group(0) - name = name.rstrip("]").lstrip("[") - name = name.lower() - - if name not in spec[binary_name][2][subcmd_name][3]: - continue - - value = re.search(r"<.*>", evidence) - if not value: - continue - value = value.group(0) - value = value.rstrip(">").lstrip("<") - - arg_spec["properties"].update({name: spec[binary_name][2][subcmd_name][3][name]}) - arg_desc = spec[binary_name][2][subcmd_name][3][name]["description"] - full_arg_desc += f"- {name}: {arg_desc}. 可能的值: {value}.\n" - - result_dict = { - "opts": {}, - "args": {}, - } - result_dict["opts"].update(await Solver._get_value(question, full_opt_desc, opt_spec)) - result_dict["args"].update(await Solver._get_value(question, full_arg_desc, arg_spec)) - - result_cmd = CommandlineAssembler.convert_dict_to_cmdline(result_dict, spec[binary_name][2][subcmd_name][1]) - full_description = "各命令行标志的描述为:\n" + full_opt_desc + "\n\n各参数的描述为:\n" + full_arg_desc - return result_cmd, full_description diff --git a/apps/scheduler/call/core.py b/apps/scheduler/call/core.py index 8628af9fd05ba7ed451a7eed3f07cb78cbee5c43..b350b9bf635296d4996d331c9ab25fc3922b0140 100644 --- a/apps/scheduler/call/core.py +++ b/apps/scheduler/call/core.py @@ -128,6 +128,7 @@ class CoreCall(BaseModel): step_order=history_order, background=executor.background, thinking=executor.task.runtime.reasoning, + app_metadata=executor.app_metadata, ) diff --git a/apps/scheduler/call/llm/llm.py b/apps/scheduler/call/llm/llm.py index a3dc92b890bdb04520b565eb40b090be55e559f6..8ec8ad56cddbf1ceb9f9a1155ee41de11a5bddd7 100644 --- a/apps/scheduler/call/llm/llm.py +++ b/apps/scheduler/call/llm/llm.py @@ -21,7 +21,6 @@ from apps.schemas.scheduler import ( CallVars, ) -from .prompt import LLM_CONTEXT_PROMPT, LLM_DEFAULT_PROMPT from .schema import LLMInput, LLMOutput if TYPE_CHECKING: @@ -38,9 +37,9 @@ class LLM(CoreCall, input_model=LLMInput, output_model=LLMOutput): # 大模型参数 temperature: float = Field(description="大模型温度(随机化程度)", default=0.7) step_history_size: int = Field(description="上下文信息中包含的步骤历史数量", default=3, ge=0, le=10) - history_length: int = Field(description="历史对话记录数量", default=0, ge=0) + history_length: int | None = Field(description="历史对话记录数量", default=None, ge=0) system_prompt: str = Field(description="大模型系统提示词", default="You are a helpful assistant.") - user_prompt: str = Field(description="大模型用户提示词", default=LLM_DEFAULT_PROMPT) + user_prompt: str | None = Field(description="大模型用户提示词", default=None) @classmethod @@ -84,20 +83,15 @@ class LLM(CoreCall, input_model=LLMInput, output_model=LLMOutput): else: context_prompt = "无背景信息。" - # 历史对话记录 history_messages = [] - if self.history_length > 0: - # 从 conversation 中提取历史记录 + history_len = self.history_length + if history_len is None and call_vars.app_metadata is not None: + history_len = call_vars.app_metadata.history_len + + if history_len is not None and history_len > 0: conversation = self._sys_vars.background.conversation - # 取最后 history_length 条记录 - recent_conversation = conversation[-self.history_length:] - # 将历史记录转换为消息格式 - for item in recent_conversation: - if "question" in item and "answer" in item: - history_messages.extend([ - {"role": "user", "content": item["question"]}, - {"role": "assistant", "content": item["answer"]}, - ]) + # 每对消息包含 2 条记录(user + assistant) + history_messages = conversation[-history_len * 2:] # 参数 time = datetime.now(tz=pytz.timezone("Asia/Shanghai")).strftime("%Y-%m-%d %H:%M:%S") @@ -114,18 +108,16 @@ class LLM(CoreCall, input_model=LLMInput, output_model=LLMOutput): system_input = system_tmpl.render(**formatter) # 准备用户提示词 - user_tmpl = env.from_string(self.user_prompt) + user_prompt = self.user_prompt if self.user_prompt is not None else self._load_prompt("llm") + user_tmpl = env.from_string(user_prompt) user_input = user_tmpl.render(**formatter) except Exception as e: raise CallError(message=f"用户提示词渲染失败:{e!s}", data={}) from e # 构建消息列表,将历史消息放在前面 - messages = [] + messages = [{"role": "system", "content": system_input}] messages.extend(history_messages) - messages.extend([ - {"role": "system", "content": system_input}, - {"role": "user", "content": user_input}, - ]) + messages.append({"role": "user", "content": user_input}) return messages diff --git a/apps/scheduler/call/llm/prompt.py b/apps/scheduler/call/llm/prompt.py deleted file mode 100644 index 0d4ccaa95775d049d5ef20383e335c1298c77481..0000000000000000000000000000000000000000 --- a/apps/scheduler/call/llm/prompt.py +++ /dev/null @@ -1,71 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""大模型工具的提示词""" - -from textwrap import dedent - -from apps.models import LanguageType - -LLM_CONTEXT_PROMPT: dict[LanguageType, str] = { - LanguageType.CHINESE: dedent( - r""" - 以下是AI处理用户指令时所做的思考,在中给出: - - {{ reasoning }} - - - 你作为AI,在完成用户指令前,需要获取必要的信息。为此,你调用了一些工具,并获得了它们的输出: - 工具的输出数据将在中给出, 其中为工具的名称,为工具的输出数据。 - - {% for tool in context_data %} - - {{ tool.step_name }} - {{ tool.step_description }} - {{ tool.output_data }} - - {% endfor %} - - """, - ).strip("\n"), - LanguageType.ENGLISH: dedent( - r""" - The following is the thinking of the AI when processing the user's instruction, given in : - - {{ reasoning }} - - - As an AI, before completing the user's instruction, you need to obtain necessary information. For this \ -purpose, you have called some tools and obtained their outputs: - The output data of the tools will be given in , where is the name of the tool and \ - is the output data of the tool. - - {% for tool in context_data %} - - {{ tool.step_name }} - {{ tool.step_description }} - {{ tool.output_data }} - - {% endfor %} - - """, - ).strip("\n"), -} -LLM_DEFAULT_PROMPT: str = dedent( - r""" - - 你是一个乐于助人的智能助手。请结合给出的背景信息, 回答用户的提问。 - 当前时间:{{ time }},可以作为时间参照。 - 用户的问题将在中给出,上下文背景信息将在中给出。 - 注意:输出不要包含任何XML标签,不要编造任何信息。若你认为用户提问与背景信息无关,请忽略背景信息直接作答。 - - - - {{ question }} - - - - {{ context }} - - - 现在,输出你的回答: - """, -).strip("\n") diff --git a/apps/scheduler/call/suggest/suggest.py b/apps/scheduler/call/suggest/suggest.py index 24a86e6ba9eed4f337a59f1f2d95d1d5ed201339..40ba6206b9dc07fe0b5ef9ee2ef44405c38c3a0d 100644 --- a/apps/scheduler/call/suggest/suggest.py +++ b/apps/scheduler/call/suggest/suggest.py @@ -142,7 +142,7 @@ class Suggestion(CoreCall, input_model=SuggestionInput, output_model=SuggestionO """通过LLM生成问题""" prompt = prompt_tpl.render( history=self._history_questions, - generated=list(generated_questions) if generated_questions else None, + generated=list(generated_questions) if generated_questions else [], tool=tool_info, preference=user_domain, target_num=target_num, diff --git a/apps/scheduler/executor/base.py b/apps/scheduler/executor/base.py index a5ccdf19a1dad51e5f578df0c43f1ad6344eaa61..66f2cc9f398d726c7e3df8b783fc00d6577a0e95 100644 --- a/apps/scheduler/executor/base.py +++ b/apps/scheduler/executor/base.py @@ -9,11 +9,10 @@ from typing import Any from pydantic import BaseModel, ConfigDict from apps.common.queue import MessageQueue -from apps.common.security import Security from apps.llm import LLM from apps.schemas.enum_var import EventType +from apps.schemas.flow import AgentAppMetadata, FlowAppMetadata from apps.schemas.message import TextAddContent -from apps.schemas.record import RecordContent from apps.schemas.scheduler import ExecutorBackground from apps.schemas.task import TaskData from apps.services.record import RecordManager @@ -27,6 +26,7 @@ class BaseExecutor(BaseModel, ABC): task: TaskData msg_queue: MessageQueue llm: LLM + app_metadata: FlowAppMetadata | AgentAppMetadata | None = None question: str @@ -43,7 +43,6 @@ class BaseExecutor(BaseModel, ABC): async def _load_history(self, n: int = 3) -> None: """加载历史记录""" - # 不存在conversationId,为首个问题 if not self.task.metadata.conversationId: self.background = ExecutorBackground( conversation=[], @@ -52,27 +51,28 @@ class BaseExecutor(BaseModel, ABC): num=n, ) return - # 获取最后n+10条Record records = await RecordManager.query_record_by_conversation_id( self.task.metadata.userId, self.task.metadata.conversationId, n + 10, ) - # 组装问答、事实和历史问题 context = [] facts = [] history_questions = [] for i, record in enumerate(records): - record_data = RecordContent.model_validate_json(Security.decrypt(record.content, record.key)) - # context 取最后 n 组 - if i >= len(records) - n: - context.append({ - "question": record_data.question, - "answer": record_data.answer, - }) - # facts 取最后 n+5 组 - if i >= len(records) - (n + 5): - facts.extend(record_data.facts) - # history_questions 取全部(n+10组) - history_questions.append(record_data.question) + if i < n: + # 因为要reverse,所以这里要先answer后question + context.extend([ + { + "role": "user", + "content": record.content.question, + }, + { + "role": "assistant", + "content": record.content.answer, + }, + ]) + if i < n + 5: + facts.extend(record.content.facts) + history_questions.append(record.content.question) self.background = ExecutorBackground( conversation=context, facts=facts, diff --git a/apps/scheduler/pool/loader/btdl.py b/apps/scheduler/pool/loader/btdl.py deleted file mode 100644 index c4fac922d3ff0ba0d4d05b37a49afd8fc1a24a0f..0000000000000000000000000000000000000000 --- a/apps/scheduler/pool/loader/btdl.py +++ /dev/null @@ -1,237 +0,0 @@ -# Copyright (c) Huawei Technologies Co., Ltd. 2023-2025. All rights reserved. -"""BTDL文档加载器""" - -import hashlib -from typing import Any - -import yaml - -btdl_spec = [] - - -""" -基本的载入形态: - -{"docker": ("描述", [{全局options}], {"cmd1名字": ("cmd1描述", "cmd1用法", [{cmd1选项}], [{cmd1参数}], "cmd1例子")})} -""" -class BTDLLoader: - """二进制描述文件 加载器""" - - @staticmethod - # 循环检查每一个参数,确定为合法JSON Schema - def _check_single_argument(argument: dict[str, Any], *, strict: bool = True) -> None: - """检查单个参数的JSON Schema是否正确""" - if strict and "name" not in argument: - err = "argument must have a name" - raise ValueError(err) - if strict and "description" not in argument: - err = f"argument {argument['name']} must have a description" - raise ValueError(err) - if "type" not in argument: - err = f"argument {argument['name']} must have a type" - raise ValueError(err) - if argument["type"] not in ["string", "integer", "number", "boolean", "array", "object"]: - err = f"argument {argument['name']} type not supported" - raise ValueError(err) - if argument["type"] == "array": - if "items" not in argument: - err = f"argument {argument['name']}: array type must have items" - raise ValueError(err) - BTDLLoader._check_single_argument(argument["items"], strict=False) - if argument["type"] == "object": - if "properties" not in argument: - err = f"argument {argument['name']}: object type must have properties" - raise ValueError(err) - for value in argument["properties"].values(): - BTDLLoader._check_single_argument(value, strict=False) - - def _load_single_subcmd( - self, - binary_name: str, - subcmd_spec: dict[str, Any], - ) -> dict[str, tuple[str, str, dict[str, Any], dict[str, Any], str]]: - """加载单个子命令""" - if "name" not in subcmd_spec: - err = "subcommand must have a name" - raise ValueError(err) - name = subcmd_spec["name"] - - if "description" not in subcmd_spec: - err = f"subcommand {name} must have a description" - raise ValueError(err) - description = subcmd_spec["description"] - - if "usage" not in subcmd_spec: - # OPTS和ARGS算保留字 - usage = "{OPTS} {ARGS}" - else: - if not isinstance(subcmd_spec["usage"], str): - err = f"subcommand {name}: usage must be a string" - raise ValueError(err) - usage = subcmd_spec["usage"] - - options = {} - option_docs = [] - if "options" in subcmd_spec: - if not isinstance(subcmd_spec["options"], list): - err = f"subcommand {name}: options must be a list" - raise ValueError(err) - - for item in subcmd_spec["options"]: - BTDLLoader._check_single_argument(item) - - new_item = item - if "required" not in item: - new_item.update({"required": False}) - - option_name = new_item["name"] - new_item.pop("name") - options.update({option_name: new_item}) - - id = hashlib.md5(f"o_{binary_name}_sub_{name}_{option_name}".encode()).hexdigest() - option_docs.append(DocumentWrapper( - id=id, - data=new_item["description"], - metadata={ - "binary": binary_name, - "subcmd": name, - "type": "option", - "name": option_name, - }, - )) - - VectorDB.add_docs(self.vec_collection, option_docs) - - arguments = {} - arguments_docs = [] - if "arguments" in subcmd_spec: - if not isinstance(subcmd_spec["arguments"], list): - err = f"subcommand {name}: arguments must be a list" - raise ValueError(err) - - for item in subcmd_spec["arguments"]: - BTDLLoader._check_single_argument(item) - - new_item = item - if "required" not in item: - new_item.update({"required": False}) - if "multiple" not in item: - new_item.update({"multiple": False}) - - argument_name = new_item["name"] - new_item.pop("name") - arguments.update({argument_name: new_item}) - - id = hashlib.md5(f"a_{binary_name}_sub_{name}_{argument_name}".encode()).hexdigest() - arguments_docs.append(DocumentWrapper( - id=id, - data=new_item["description"], - metadata={ - "binary": binary_name, - "subcmd": name, - "type": "argument", - "name": argument_name, - }, - )) - - VectorDB.add_docs(self.vec_collection, arguments_docs) - - if "examples" in subcmd_spec: - if not isinstance(subcmd_spec["examples"], list): - err = f"subcommand {name}: examples must be a list" - raise ValueError(err) - - examples = "以下是几组命令行,以及它的作用的示例:\n" - for items in subcmd_spec["examples"]: - examples += "`{}`: {}\n".format(items["command"], items["description"]) - else: - examples = "" - - # 组装结果 - return {name: (description, usage, options, arguments, examples)} - - def _load_global_options(self, binary_name: str, cmd_spec: dict[str, Any]) -> dict[str, Any]: - if "global_options" not in cmd_spec: - return {} - - if not isinstance(cmd_spec["global_options"], list): - err = "global_options must be a list" - raise TypeError(err) - - result = {} - result_doc = [] - for item in cmd_spec["global_options"]: - try: - BTDLLoader._check_single_argument(item) - - new_item = item - if "required" not in item: - new_item.update({"required": False}) - name = new_item["name"] - new_item.pop("name") - result.update({name: new_item}) - - id = hashlib.md5(f"g_{binary_name}_{name}".encode()).hexdigest() - result_doc.append(DocumentWrapper( - id=id, - data=new_item["description"], - metadata={ - "binary": binary_name, - "type": "global_option", - "name": name, - }, - )) - except ValueError as e: # noqa: PERF203 - err = f"Value error in global_options: {e!s}" - raise ValueError(err) from e - - VectorDB.add_docs(self.vec_collection, result_doc) - return result - - def load_btdl(self, filename: str) -> dict[str, Any]: - # Load single btdl.yaml - try: - yaml_data = yaml.safe_load(open(filename, "r", encoding="utf-8")) - except FileNotFoundError as e: - err = "BTDLLoader: file not found." - raise FileNotFoundError(err) from e - - result = {} - result_doc = [] - for item in yaml_data["cmd"]: - # 依序处理每一个命令 - key = item["name"] - description = item["description"] - - cmd_spec = yaml_data[item["name"]] - global_options = self._load_global_options(key, cmd_spec) - - sub_cmds = {} - sub_cmds_doc = [] - for sub_cmd in cmd_spec["commands"]: - sub_cmds.update(self._load_single_subcmd(key, sub_cmd)) - id = hashlib.md5(f"s_{key}_{sub_cmd['name']}".encode()).hexdigest() - sub_cmds_doc.append(DocumentWrapper( - id=id, - data=sub_cmd["description"], - metadata={ - "binary": key, - "type": "subcommand", - "name": sub_cmd["name"], - }, - )) - result.update({key: (description, global_options, sub_cmds)}) - VectorDB.add_docs(self.vec_collection, sub_cmds_doc) - - id = hashlib.md5(f"b_{key}".encode()).hexdigest() - result_doc.append(DocumentWrapper( - id=id, - data=description, - metadata={ - "name": key, - "type": "binary", - }, - )) - - VectorDB.add_docs(self.vec_collection, result_doc) - return result diff --git a/apps/scheduler/scheduler/data.py b/apps/scheduler/scheduler/data.py index b5990606f02174085b722cb5aa32e983b1decad6..122a3444c0a3ca4df8b60df74c16e8c530e110c5 100644 --- a/apps/scheduler/scheduler/data.py +++ b/apps/scheduler/scheduler/data.py @@ -60,6 +60,10 @@ class DataMixin: user_id = task.metadata.userId record_id = task.metadata.id + if task.metadata.conversationId is None: + msg = "conversationId cannot be None" + raise ValueError(msg) + pg_record = PgRecord( id=record_id, conversationId=task.metadata.conversationId, diff --git a/apps/scheduler/scheduler/executor.py b/apps/scheduler/scheduler/executor.py index 2becbedc17082e7f66052ef876248064c570f9ac..6bde045d805b0bdbc29535f1e4ce325b6f698eac 100644 --- a/apps/scheduler/scheduler/executor.py +++ b/apps/scheduler/scheduler/executor.py @@ -12,6 +12,7 @@ from apps.scheduler.executor.agent import MCPAgentExecutor from apps.scheduler.executor.flow import FlowExecutor from apps.scheduler.executor.qa import QAExecutor from apps.scheduler.pool.pool import pool +from apps.schemas.flow import AgentAppMetadata, FlowAppMetadata from apps.schemas.request_data import RequestData from apps.schemas.task import TaskData from apps.services.appcenter import AppCenterManager @@ -26,6 +27,7 @@ class ExecutorMixin: post_body: RequestData queue: MessageQueue llm: LLM + app_metadata: FlowAppMetadata | AgentAppMetadata | None async def get_top_flow(self) -> str: """获取Top1 Flow (由FlowMixin实现)""" @@ -35,6 +37,7 @@ class ExecutorMixin: """根据 app_id 创建对应的执行器任务""" if final_app_id is None: _logger.info("[Scheduler] 运行智能问答模式") + self.app_metadata = None return asyncio.create_task(self._run_qa()) try: @@ -44,6 +47,7 @@ class ExecutorMixin: await self.queue.close() return None + self.app_metadata = app_data context_num = app_data.history_len _logger.info("[Scheduler] App上下文窗口: %d", context_num) @@ -84,6 +88,7 @@ class ExecutorMixin: msg_queue=self.queue, question=self.post_body.question, llm=self.llm, + app_metadata=self.app_metadata, ) _logger.info("[Scheduler] 开始智能问答") await qa_executor.init() @@ -123,6 +128,7 @@ class ExecutorMixin: question=self.post_body.question, post_body_app=self.post_body.app, llm=self.llm, + app_metadata=self.app_metadata, ) _logger.info("[Scheduler] 运行工作流执行器") @@ -143,6 +149,7 @@ class ExecutorMixin: agent_id=self.post_body.app.app_id, params=self.post_body.app.params, llm=self.llm, + app_metadata=self.app_metadata, ) _logger.info("[Scheduler] 运行MCP执行器") await agent_exec.init() diff --git a/apps/schemas/appcenter.py b/apps/schemas/appcenter.py index ac295a8db8fbb7c457609d6aca89f19cbccdb673..ea974ba1a40c833e57e11040092f850981ea8938 100644 --- a/apps/schemas/appcenter.py +++ b/apps/schemas/appcenter.py @@ -15,7 +15,6 @@ class AppCenterCardItem(BaseModel): app_id: uuid.UUID = Field(..., alias="appId", description="应用ID") app_type: AppType = Field(..., alias="appType", description="应用类型") - icon: str = Field(..., description="应用图标") name: str = Field(..., description="应用名称") description: str = Field(..., description="应用简介") author: str = Field(..., description="应用作者") @@ -58,7 +57,6 @@ class AppData(BaseModel): """应用信息数据结构""" app_type: AppType = Field(..., alias="appType", description="应用类型") - icon: str = Field(default="", description="图标") name: str = Field(..., max_length=20, description="应用名称") description: str = Field(..., max_length=150, description="应用简介") links: list[AppLink] = Field(default=[], description="相关链接", max_length=5) diff --git a/apps/schemas/scheduler.py b/apps/schemas/scheduler.py index cf3e62dce770e65bc349fc4e04fcc4db205a2d35..dd212e73aec3df4db802a2549e020334bc554866 100644 --- a/apps/schemas/scheduler.py +++ b/apps/schemas/scheduler.py @@ -48,6 +48,7 @@ class CallVars(BaseModel): background: ExecutorBackground = Field(description="Executor的背景信息") ids: CallIds = Field(description="Call的ID") language: LanguageType = Field(description="语言", default=LanguageType.CHINESE) + app_metadata: Any = Field(description="应用元数据", default=None) class CallError(Exception): diff --git a/apps/services/appcenter.py b/apps/services/appcenter.py index d7089a5c0547265e90b7ba114679b95d52c60814..a6e631c0c54386ac3c96f5240dd1f44b07161535 100644 --- a/apps/services/appcenter.py +++ b/apps/services/appcenter.py @@ -189,7 +189,6 @@ class AppCenterManager: app_cards += [AppCenterCardItem( appId=row.id, appType=row.appType, - icon=row.icon, name=row.name, description=row.description, author=author_names[row.authorId], @@ -548,7 +547,6 @@ class AppCenterManager: "type": MetadataType.APP, "id": app_id, "author": user_id, - "icon": source.icon, "name": source.name, "description": source.description, "history_len": data.history_len if data else APP_DEFAULT_HISTORY_LEN, diff --git a/data/prompts/call/llm.en.md b/data/prompts/call/llm.en.md new file mode 100644 index 0000000000000000000000000000000000000000..3a191421e4324b972b7a7b7cc4aaa74db316bed8 --- /dev/null +++ b/data/prompts/call/llm.en.md @@ -0,0 +1,24 @@ +# Generate Question Response + +## Role + +You are a helpful and intelligent assistant. + +## Task Description + +Please answer the user's question based on the context provided. + +Note: + +- Stay honest and do not fabricate any information +- If you believe the user's question is unrelated to the context, ignore the context and answer directly + +**Current time:** {{ time }}, which can be used as a time reference. + +## User Question + +{{ question }} + +--- + +Now, output your answer: diff --git a/data/prompts/call/llm.zh.md b/data/prompts/call/llm.zh.md new file mode 100644 index 0000000000000000000000000000000000000000..51681f730fdeac6e3eb69d05d98421ac480538bc --- /dev/null +++ b/data/prompts/call/llm.zh.md @@ -0,0 +1,24 @@ +# 生成问题回复 + +## 角色 + +你是一个乐于助人的智能助手。 + +## 任务说明 + +请结合上下文,回答用户的提问。 + +注意: + +- 保持诚实,不要编造任何信息 +- 若你认为用户提问与上下文无关,请忽略上下文直接作答 + +**当前时间:** {{ time }},可以作为时间参照。 + +## 用户问题 + +{{ question }} + +--- + +现在,输出你的回答: diff --git a/data/prompts/call/llm_step_memory.en.md b/data/prompts/call/llm_step_memory.en.md new file mode 100644 index 0000000000000000000000000000000000000000..2646fa4d809d158ad40913251720b106b1cfdbb8 --- /dev/null +++ b/data/prompts/call/llm_step_memory.en.md @@ -0,0 +1,39 @@ +{% if role == "user" %} +# Task Execution Record - Step {{ step_index }} + +## Task Goal + +{{ step_goal }} + +## Tool Invocation + +To complete this step, we invoked the tool `{{ step_name }}`. + +## Input Parameters + +The parameters provided to the tool are: + +```json +{{ input_data | tojson }} +``` + +{% elif role == "assistant" %} +# Task Execution Result - Step {{ step_index }} + +## Execution Status + +Step {{ step_index }} has been completed. + +Execution status: {{ step_status }} + +## Output Data + +The data obtained from the tool execution is: + +```json +{{ output_data | tojson }} +``` + +This data will be used as input or reference information for subsequent steps. + +{% endif %} diff --git a/data/prompts/call/llm_step_memory.zh.md b/data/prompts/call/llm_step_memory.zh.md new file mode 100644 index 0000000000000000000000000000000000000000..d11588746f88b9478e89716bd7cfb6331af0c5f1 --- /dev/null +++ b/data/prompts/call/llm_step_memory.zh.md @@ -0,0 +1,39 @@ +{% if role == "user" %} +# 任务执行记录 - 第{{ step_index }}步 + +## 任务目标 + +{{ step_goal }} + +## 调用工具 + +为了完成这一步骤,我们调用了工具 `{{ step_name }}`。 + +## 输入参数 + +提供给工具的参数为: + +```json +{{ input_data | tojson }} +``` + +{% elif role == "assistant" %} +# 任务执行结果 - 第{{ step_index }}步 + +## 执行状态 + +第{{ step_index }}步已执行完成。 + +执行状态:{{ step_status }} + +## 输出数据 + +工具执行后得到的数据为: + +```json +{{ output_data | tojson }} +``` + +这些数据将作为后续步骤的输入或参考信息使用。 + +{% endif %} diff --git a/data/prompts/system/mcp/get_missing_params.en.md b/data/prompts/system/mcp/get_missing_params.en.md index f57dfda2db489402e5199dc918482770c0046457..b7b880f71df0a465a5a0e89c54a6ba65c4f9c05a 100644 --- a/data/prompts/system/mcp/get_missing_params.en.md +++ b/data/prompts/system/mcp/get_missing_params.en.md @@ -1,22 +1,69 @@ -Based on tool execution errors, identify missing or incorrect parameters, set them to null, and retain correct parameter values. -**Please call the `get_missing_parameters` tool to return the result**. +# Parameter Error Analysis Task -## Task Requirements -- **Analyze error messages**: Identify which parameters caused the error -- **Mark problematic parameters**: Set missing or incorrect parameter values to `null` -- **Retain valid values**: Keep original values for parameters that didn't cause errors -- **Call tool to return**: Must call the `get_missing_parameters` tool with the parameter JSON as input +## Role + +You are a professional parameter analysis assistant capable of identifying missing or incorrect parameters based on tool execution errors and setting them to null for users to provide again. + +Your main responsibilities are: analyze error messages during tool execution, accurately identify which parameters caused the error, set these missing or incorrect parameter values to `null`, while preserving the original values of parameters that didn't cause errors. After completing the analysis, you must call the `get_missing_parameters` tool, passing the parameter JSON with null markers as the tool input. + +## User Instructions + +**Current Goal**: Analyze tool execution errors and identify parameters that need to be re-obtained + +**Overall Goal (for reference)**: {{goal}} + +## Tools + +You can call these tools to respond to user instructions. + +{% raw %}{% if use_xml_format %}{% endraw %} +When calling tools, use XML-style tags for formatting. The format specification is as follows: + +```xml + + tool_name + + Parameters with null markers, must be in JSON format + + +``` + +Format example (for reference): Marking missing parameters + +```xml + + get_missing_parameters + + { + "host": "192.0.0.1", + "port": 3306, + "username": null, + "password": null + } + + +``` + +{% raw %}{% endif %}{% endraw %} + +### get_missing_parameters + +Description: Based on tool execution errors, identify missing or incorrect parameters and set them to null. Preserve the values of correct parameters. + +JSON Schema: Dynamically generated (based on the original tool's parameter Schema) ## Example **Tool**: `mysql_analyzer` - Analyze MySQL database performance **Current Input**: + ```json {"host": "192.0.0.1", "port": 3306, "username": "root", "password": "password"} ``` **Parameter Schema**: + ```json { "properties": { @@ -32,8 +79,19 @@ Based on tool execution errors, identify missing or incorrect parameters, set th **Error**: `password is not correct` **Should call tool**: -``` -get_missing_parameters({"host": "192.0.0.1", "port": 3306, "username": null, "password": null}) + +```xml + + get_missing_parameters + + { + "host": "192.0.0.1", + "port": 3306, + "username": null, + "password": null + } + + ``` > Analysis: Error indicates incorrect password, so set `password` to `null`; also set `username` to `null` for user to provide credentials again @@ -45,15 +103,19 @@ get_missing_parameters({"host": "192.0.0.1", "port": 3306, "username": null, "pa **Tool**: `{{tool_name}}` - {{tool_description}} **Current Input**: + ```json {{input_param}} ``` **Parameter Schema**: + ```json {{input_schema}} ``` **Error**: {{error_message}} -**Please call the `get_missing_parameters` tool to return the analysis result**. +--- + +Now begin responding to user instructions: diff --git a/data/semantics/app/13754910-336e-45a9-a9ba-5d3c1b9bf20c/metadata.yaml b/data/semantics/app/13754910-336e-45a9-a9ba-5d3c1b9bf20c/metadata.yaml index b8ddd83fd6f588449dc41723539b0cc82faa0a16..b9ad745a26301d2141fc49a1f43ff62ecbc70108 100644 --- a/data/semantics/app/13754910-336e-45a9-a9ba-5d3c1b9bf20c/metadata.yaml +++ b/data/semantics/app/13754910-336e-45a9-a9ba-5d3c1b9bf20c/metadata.yaml @@ -2,8 +2,6 @@ type: app # 应用ID id: 13754910-336e-45a9-a9ba-5d3c1b9bf20c -# 应用图标(前端访问URI) -icon: /static/apps/13754910-336e-45a9-a9ba-5d3c1b9bf20c.png # 应用种类 app_type: flow # 应用的名称(展示用) diff --git a/design/services/appcenter.md b/design/services/appcenter.md index c6f0ac9b50354342f12bd87f62c56058cf8f2252..c7d9047c1865f296ae1c9be524b26ac73481fe50 100644 --- a/design/services/appcenter.md +++ b/design/services/appcenter.md @@ -24,7 +24,6 @@ { "appId": "550e8400-e29b-41d4-a716-446655440000", "appType": "flow", - "icon": "/icons/app-icon.png", "name": "文档处理助手", "description": "智能化文档分析与处理工具", "author": "user123", @@ -44,7 +43,6 @@ "published": true, "name": "文档处理助手", "description": "智能化文档分析与处理工具", - "icon": "/icons/app-icon.png", "links": [ { "title": "使用文档", @@ -81,7 +79,6 @@ "published": false, "name": "代码审查助手", "description": "自动化代码审查与建议", - "icon": "/icons/code-review.png", "links": [], "recommendedQuestions": [], "dialogRounds": 3, @@ -152,7 +149,6 @@ GET /api/app?keyword=文档&appType=flow&page=1 { "appId": "550e8400-e29b-41d4-a716-446655440000", "appType": "flow", - "icon": "/icons/app-icon.png", "name": "文档处理助手", "description": "智能化文档分析与处理工具", "author": "user123", @@ -175,7 +171,6 @@ GET /api/app?keyword=文档&appType=flow&page=1 "appType": "flow", "name": "新应用", "description": "这是一个新创建的应用", - "icon": "/icons/new-app.png", "links": [ { "title": "帮助文档", @@ -198,7 +193,6 @@ GET /api/app?keyword=文档&appType=flow&page=1 "appType": "flow", "name": "更新后的应用名称", "description": "更新后的描述", - "icon": "/icons/updated-icon.png", "links": [], "recommendedQuestions": [], "dialogRounds": 5, @@ -707,7 +701,7 @@ stateDiagram-v2 - **功能描述**: 创建应用元数据对象 - **验证步骤**: 验证是否提供了必要的数据源(新建数据或现有数据) -- **通用参数**: 构建参数集合,包括ID、作者、图标、名称、描述、对话轮次和权限配置 +- **通用参数**: 构建参数集合,包括ID、作者、名称、描述、对话轮次和权限配置 - **类型分派**: - 工作流应用 → 调用`_create_flow_metadata` - 智能体应用 → 调用`_create_agent_metadata` @@ -776,7 +770,6 @@ erDiagram string author "FK" enum appType datetime updatedAt - string icon boolean isPublished enum permission } diff --git a/documents/design/openapi/openapi3_1.yaml b/documents/design/openapi/openapi3_1.yaml index 4380112a5fcb046f693df8d2489bf8d38a31f725..04e6b47f847e6bbb5e9f447eb42584d90e9ad4ea 100644 --- a/documents/design/openapi/openapi3_1.yaml +++ b/documents/design/openapi/openapi3_1.yaml @@ -1607,10 +1607,6 @@ components: type: string title: Appid description: 应用ID - icon: - type: string - title: Icon - description: 应用图标 name: type: string title: Name @@ -1635,7 +1631,6 @@ components: type: object required: - appId - - icon - name - description - author @@ -1955,11 +1950,6 @@ components: description: GET /api/conversation 返回数据结构 CreateAppRequest: properties: - icon: - type: string - title: Icon - description: 图标 - default: '' name: type: string maxLength: 20 @@ -2333,11 +2323,6 @@ components: description: GET /api/app 返回数据结构 GetAppPropertyMsg: properties: - icon: - type: string - title: Icon - description: 图标 - default: '' name: type: string maxLength: 20 @@ -3413,10 +3398,6 @@ components: type: string title: Description description: 服务简介 - icon: - type: string - title: Icon - description: 服务图标 author: type: string title: Author @@ -3430,7 +3411,6 @@ components: - serviceId - name - description - - icon - author - favorited title: ServiceCardItem