From a8a0228bb607214893d7742489e61a11dcea9ebc Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=E7=8E=8B=E8=89=AF?= <841369634@qq.com> Date: Fri, 8 May 2026 15:01:25 +0800 Subject: [PATCH] feat: `Agent` can set `code` and connect the code into `tool.name` --- apps/application/flow/default_workflow.json | 1 + .../application/flow/default_workflow_en.json | 1 + .../application/flow/default_workflow_zh.json | 1 + .../flow/default_workflow_zh_Hant.json | 1 + .../0014_application_code_and_more.py | 25 +++++++++++++++++++ apps/application/models/application.py | 2 ++ apps/application/serializers/application.py | 15 +++++++++-- apps/chat/mcp/tools.py | 2 +- apps/chat/serializers/chat_authentication.py | 2 +- ui/src/api/type/application.ts | 1 + ui/src/locales/lang/en-US/common.ts | 1 + .../locales/lang/en-US/views/application.ts | 3 +++ ui/src/locales/lang/zh-CN/common.ts | 1 + .../locales/lang/zh-CN/views/application.ts | 3 +++ ui/src/locales/lang/zh-Hant/common.ts | 1 + .../locales/lang/zh-Hant/views/application.ts | 3 +++ .../views/application/ApplicationSetting.vue | 1 + .../component/CopyApplicationDialog.vue | 2 ++ .../component/CreateApplicationDialog.vue | 2 ++ ui/src/workflow/common/data.ts | 2 ++ ui/src/workflow/common/template.ts | 2 ++ ui/src/workflow/nodes/base-node/index.vue | 13 ++++++++++ 22 files changed, 81 insertions(+), 4 deletions(-) create mode 100644 apps/application/migrations/0014_application_code_and_more.py diff --git a/apps/application/flow/default_workflow.json b/apps/application/flow/default_workflow.json index 48ac23c4dc6..6aabac692b1 100644 --- a/apps/application/flow/default_workflow.json +++ b/apps/application/flow/default_workflow.json @@ -14,6 +14,7 @@ "node_data": { "desc": "", "name": "maxkbapplication", + "code": "", "prologue": "您好,我是 MaxKB 小助手,您可以向我提出 MaxKB 使用问题。\n- MaxKB 主要功能有什么?\n- MaxKB 支持哪些大语言模型?\n- MaxKB 支持哪些文档类型?" }, "input_field_list": [ diff --git a/apps/application/flow/default_workflow_en.json b/apps/application/flow/default_workflow_en.json index 17c397306b9..2b359c572f5 100644 --- a/apps/application/flow/default_workflow_en.json +++ b/apps/application/flow/default_workflow_en.json @@ -14,6 +14,7 @@ "node_data": { "desc": "", "name": "maxkbapplication", + "code": "", "prologue": "Hello, I am the MaxKB assistant. You can ask me about MaxKB usage issues.\n-What are the main functions of MaxKB?\n-What major language models does MaxKB support?\n-What document types does MaxKB support?" }, "input_field_list": [ diff --git a/apps/application/flow/default_workflow_zh.json b/apps/application/flow/default_workflow_zh.json index 48ac23c4dc6..6aabac692b1 100644 --- a/apps/application/flow/default_workflow_zh.json +++ b/apps/application/flow/default_workflow_zh.json @@ -14,6 +14,7 @@ "node_data": { "desc": "", "name": "maxkbapplication", + "code": "", "prologue": "您好,我是 MaxKB 小助手,您可以向我提出 MaxKB 使用问题。\n- MaxKB 主要功能有什么?\n- MaxKB 支持哪些大语言模型?\n- MaxKB 支持哪些文档类型?" }, "input_field_list": [ diff --git a/apps/application/flow/default_workflow_zh_Hant.json b/apps/application/flow/default_workflow_zh_Hant.json index 9cac9a54dc6..1389730de47 100644 --- a/apps/application/flow/default_workflow_zh_Hant.json +++ b/apps/application/flow/default_workflow_zh_Hant.json @@ -14,6 +14,7 @@ "node_data": { "desc": "", "name": "maxkbapplication", + "code": "", "prologue": "您好,我是 MaxKB 小助手,您可以向我提出 MaxKB 使用問題。\n- MaxKB 主要功能有哪些?\n- MaxKB 支援哪些大型語言模型?\n- MaxKB 支援哪些文件類型?" }, "input_field_list": [ diff --git a/apps/application/migrations/0014_application_code_and_more.py b/apps/application/migrations/0014_application_code_and_more.py new file mode 100644 index 00000000000..595baee4dc5 --- /dev/null +++ b/apps/application/migrations/0014_application_code_and_more.py @@ -0,0 +1,25 @@ +# Generated by Django 5.2.9 on 2026-05-08 17:03 + +from django.db import migrations, models + + +class Migration(migrations.Migration): + + dependencies = [ + ('application', '0013_application_long_term_enable_and_more'), + ] + + operations = [ + migrations.AddField( + model_name='application', + name='code', + field=models.CharField(null=True, max_length=128, verbose_name='应用代码'), + preserve_default=False, + ), + migrations.AddField( + model_name='applicationversion', + name='application_code', + field=models.CharField(null=True, max_length=128, verbose_name='应用代码'), + preserve_default=False, + ), + ] \ No newline at end of file diff --git a/apps/application/models/application.py b/apps/application/models/application.py index 34824b29eee..7989c8e2332 100644 --- a/apps/application/models/application.py +++ b/apps/application/models/application.py @@ -63,6 +63,7 @@ class Application(AppModelMixin): default='default') is_publish = models.BooleanField(verbose_name="是否发布", default=False) name = models.CharField(max_length=128, verbose_name="应用名称", db_index=True) + code = models.CharField(max_length=128, verbose_name="应用代码", blank=True, default=None, null=True) desc = models.CharField(max_length=512, verbose_name="引用描述", default="") prologue = models.CharField(max_length=40960, verbose_name="开场白", default="") dialogue_number = models.IntegerField(default=0, verbose_name="会话数量") @@ -147,6 +148,7 @@ class ApplicationVersion(AppModelMixin): publish_user_name = models.CharField(verbose_name="发布者名称", max_length=128, default="") workspace_id = models.CharField(max_length=64, verbose_name="工作空间id", default="default", db_index=True) application_name = models.CharField(max_length=128, verbose_name="应用名称") + application_code = models.CharField(max_length=128, verbose_name="应用代码", blank=True, default=None, null=True) desc = models.CharField(max_length=512, verbose_name="引用描述", default="") prologue = models.CharField(max_length=40960, verbose_name="开场白", default="") dialogue_number = models.IntegerField(default=0, verbose_name="会话数量") diff --git a/apps/application/serializers/application.py b/apps/application/serializers/application.py index 794398a0532..4ca38a199a0 100644 --- a/apps/application/serializers/application.py +++ b/apps/application/serializers/application.py @@ -199,6 +199,8 @@ class Meta: class WorkflowRequest(serializers.Serializer): name = serializers.CharField(required=True, max_length=64, min_length=1, label=_("Application Name")) + code = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=64, + label=_("Application Code")) desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=256, min_length=1, label=_("Application Description")) @@ -214,10 +216,12 @@ def to_application_model(user_id: str, workspace_id: str, application: Dict): if node.get('id') == 'base-node': node.get('properties')['node_data']['desc'] = application.get('desc') node.get('properties')['node_data']['name'] = application.get('name') + node.get('properties')['node_data']['code'] = application.get('code') node.get('properties')['node_data']['prologue'] = application.get('prologue') return Application( id=uuid.uuid7(), name=application.get('name'), + code=application.get('code'), desc=application.get('desc'), workspace_id=workspace_id, folder_id=application.get('folder_id', application.get('workspace_id')), @@ -242,6 +246,8 @@ def to_application_model(user_id: str, workspace_id: str, application: Dict): class SimplateRequest(serializers.Serializer): name = serializers.CharField(required=True, max_length=64, min_length=1, label=_("application name")) + code = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=64, + label=_("application code")) desc = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=256, min_length=1, label=_("application describe")) @@ -302,6 +308,7 @@ def to_application_model(user_id: str, workspace_id: str, application: Dict): return Application( id=uuid.uuid7(), name=application.get('name'), + code=application.get('code'), desc=application.get('desc'), workspace_id=workspace_id, prologue=application.get('prologue'), @@ -453,6 +460,8 @@ class ApplicationImportRequest(serializers.Serializer): class ApplicationEditSerializer(serializers.Serializer): name = serializers.CharField(required=False, max_length=64, min_length=1, label=_("Application Name")) + code = serializers.CharField(required=False, allow_null=True, allow_blank=True, max_length=64, + label=_("Application Code")) desc = serializers.CharField(required=False, max_length=256, min_length=1, allow_null=True, allow_blank=True, label=_("Application Description")) model_id = serializers.CharField(required=False, allow_blank=True, allow_null=True, @@ -706,6 +715,7 @@ def to_application(application, workspace_id, user_id, update_tool_map, folder_i id=uuid.uuid7(), user_id=user_id, name=application.get('name'), + code=application.get('code'), workspace_id=workspace_id, folder_id=folder_id, desc=application.get('desc'), @@ -913,7 +923,7 @@ def to_tool_dict(tool, tool_workflow_dict): @staticmethod def reset_application_version(application_version, application): update_field_dict = { - 'application_name': 'name', 'desc': 'desc', 'prologue': 'prologue', 'dialogue_number': 'dialogue_number', + 'application_name': 'name', 'application_code': 'code', 'desc': 'desc', 'prologue': 'prologue', 'dialogue_number': 'dialogue_number', 'user_id': 'user_id', 'model_id': 'model_id', 'knowledge_setting': 'knowledge_setting', 'model_setting': 'model_setting', 'model_params_setting': 'model_params_setting', 'tts_model_params_setting': 'tts_model_params_setting', @@ -955,6 +965,7 @@ def publish(self, instance, with_valid=True): node_data = base_node.get('properties').get('node_data') if node_data is not None: application.name = node_data.get('name') + application.code = node_data.get('code') application.desc = node_data.get('desc') application.prologue = node_data.get('prologue') application.work_flow = work_flow @@ -1095,7 +1106,7 @@ def edit(self, instance: Dict, with_valid=True): self.update_work_flow_model(instance) if 'mcp_servers' in instance and len(instance.get('mcp_servers', {})) > 0: ToolExecutor().validate_mcp_transport(json.dumps(instance.get('mcp_servers'))) - update_keys = ['name', 'desc', 'model_id', 'multiple_rounds_dialogue', 'prologue', 'status', + update_keys = ['name', 'code', 'desc', 'model_id', 'multiple_rounds_dialogue', 'prologue', 'status', 'knowledge_setting', 'model_setting', 'problem_optimization', 'dialogue_number', 'stt_model_id', 'tts_model_id', 'tts_model_enable', 'stt_model_enable', 'tts_type', 'tts_autoplay', 'stt_autosend', 'file_upload_enable', 'file_upload_setting', diff --git a/apps/chat/mcp/tools.py b/apps/chat/mcp/tools.py index 4a3ff972388..18e8d0be750 100644 --- a/apps/chat/mcp/tools.py +++ b/apps/chat/mcp/tools.py @@ -34,7 +34,7 @@ def list_tools(self): return { "tools": [ { - "name": f'agent_{str(self.application.id)[:8]}', + "name": f'agent_{str(self.application.id)[:8]}' + (f'_{self.application.code}' if self.application.code else ''), "description": f'{self.application.name} {self.application.desc}', "inputSchema": { "type": "object", diff --git a/apps/chat/serializers/chat_authentication.py b/apps/chat/serializers/chat_authentication.py index 08992098627..bb5e65cf49c 100644 --- a/apps/chat/serializers/chat_authentication.py +++ b/apps/chat/serializers/chat_authentication.py @@ -95,7 +95,7 @@ class ApplicationProfileSerializer(serializers.Serializer): @staticmethod def reset_application(application, application_version): update_field_dict = { - 'application_name': 'name', 'desc': 'desc', 'prologue': 'prologue', 'dialogue_number': 'dialogue_number', + 'application_name': 'name', 'application_code': 'code', 'desc': 'desc', 'prologue': 'prologue', 'dialogue_number': 'dialogue_number', 'user_id': 'user_id', 'model_id': 'model_id', 'knowledge_setting': 'knowledge_setting', 'model_setting': 'model_setting', 'model_params_setting': 'model_params_setting', 'tts_model_params_setting': 'tts_model_params_setting', diff --git a/ui/src/api/type/application.ts b/ui/src/api/type/application.ts index 89fd7f22941..8d4ef104c07 100644 --- a/ui/src/api/type/application.ts +++ b/ui/src/api/type/application.ts @@ -4,6 +4,7 @@ import bus from '@/bus' interface ApplicationFormType { name?: string + code?: string desc?: string model_id?: string dialogue_number?: number diff --git a/ui/src/locales/lang/en-US/common.ts b/ui/src/locales/lang/en-US/common.ts index d05e958f096..59c92eed488 100644 --- a/ui/src/locales/lang/en-US/common.ts +++ b/ui/src/locales/lang/en-US/common.ts @@ -37,6 +37,7 @@ export default { private: 'Private', paramSetting: 'Parameter Settings', name: 'Name', + code: 'Code', creator: 'Creator', createdIn: 'created in', author: 'Author', diff --git a/ui/src/locales/lang/en-US/views/application.ts b/ui/src/locales/lang/en-US/views/application.ts index b5ce367b8cd..6c87a831cde 100644 --- a/ui/src/locales/lang/en-US/views/application.ts +++ b/ui/src/locales/lang/en-US/views/application.ts @@ -54,6 +54,9 @@ After disabling, the long-term memory of the conversation users will be cleared, placeholder: 'Please enter the agent name', requiredMessage: 'Agent name is required', }, + appCode: { + placeholder: 'Please enter the agent code: [a-zA-Z0-9_-]', + }, appDescription: { placeholder: 'Describe the Agent scenario and use, e.g.: XXX assistant answering user questions about XXX product usage', diff --git a/ui/src/locales/lang/zh-CN/common.ts b/ui/src/locales/lang/zh-CN/common.ts index b4e4390c101..42847157333 100644 --- a/ui/src/locales/lang/zh-CN/common.ts +++ b/ui/src/locales/lang/zh-CN/common.ts @@ -39,6 +39,7 @@ export default { private: '私有', paramSetting: '参数设置', name: '名称', + code: '代码', creator: '创建者', createdIn: '创建于', author: '作者', diff --git a/ui/src/locales/lang/zh-CN/views/application.ts b/ui/src/locales/lang/zh-CN/views/application.ts index ca12ea71d6b..edc1519c840 100644 --- a/ui/src/locales/lang/zh-CN/views/application.ts +++ b/ui/src/locales/lang/zh-CN/views/application.ts @@ -51,6 +51,9 @@ export default { placeholder: '请输入智能体名称', requiredMessage: '请输入智能体名称', }, + appCode: { + placeholder: '请输入智能体代码: [a-zA-Z0-9_-]', + }, appDescription: { placeholder: '描述该智能体的应用场景及用途,如:XXX 小助手回答用户提出的 XXX 产品使用问题', }, diff --git a/ui/src/locales/lang/zh-Hant/common.ts b/ui/src/locales/lang/zh-Hant/common.ts index 008377c4daf..b9d64dc1684 100644 --- a/ui/src/locales/lang/zh-Hant/common.ts +++ b/ui/src/locales/lang/zh-Hant/common.ts @@ -37,6 +37,7 @@ export default { private: '私有', paramSetting: '參數設定', name: '名稱', + code: '代碼', creator: '建立者', createdIn: '創建於', author: '作者', diff --git a/ui/src/locales/lang/zh-Hant/views/application.ts b/ui/src/locales/lang/zh-Hant/views/application.ts index fb4287ef2ba..f4bafaf9d73 100644 --- a/ui/src/locales/lang/zh-Hant/views/application.ts +++ b/ui/src/locales/lang/zh-Hant/views/application.ts @@ -50,6 +50,9 @@ export default { placeholder: '請輸入智能體名稱', requiredMessage: '請輸入智能體名稱', }, + appCode: { + placeholder: '請輸入智能體代碼: [a-zA-Z0-9_-]', + }, appDescription: { placeholder: '描述該智能體的應用場景及用途,如:XXX 小助手回答用戶提出的 XXX 產品使用問題', }, diff --git a/ui/src/views/application/ApplicationSetting.vue b/ui/src/views/application/ApplicationSetting.vue index f09b7fb29c5..c6fd13d331e 100644 --- a/ui/src/views/application/ApplicationSetting.vue +++ b/ui/src/views/application/ApplicationSetting.vue @@ -1014,6 +1014,7 @@ const knowledgeLoading = ref(false) const applicationForm = ref({ name: '', + code: '', desc: '', model_id: '', dialogue_number: 1, diff --git a/ui/src/views/application/component/CopyApplicationDialog.vue b/ui/src/views/application/component/CopyApplicationDialog.vue index 4e22e598ba4..c7720857048 100644 --- a/ui/src/views/application/component/CopyApplicationDialog.vue +++ b/ui/src/views/application/component/CopyApplicationDialog.vue @@ -71,6 +71,7 @@ const dialogVisible = ref(false) const applicationForm = ref({ name: '', + code: '', desc: '', model_id: '', dialogue_number: 0, @@ -109,6 +110,7 @@ watch(dialogVisible, (bool) => { if (!bool) { applicationForm.value = { name: '', + code: '', desc: '', model_id: '', dialogue_number: 0, diff --git a/ui/src/views/application/component/CreateApplicationDialog.vue b/ui/src/views/application/component/CreateApplicationDialog.vue index cf8d8e7be48..c05cf92444e 100644 --- a/ui/src/views/application/component/CreateApplicationDialog.vue +++ b/ui/src/views/application/component/CreateApplicationDialog.vue @@ -130,6 +130,7 @@ const work_flow_template = ref() const applicationForm = ref({ name: '', + code: '', desc: '', model_id: undefined, dialogue_number: 1, @@ -185,6 +186,7 @@ watch(dialogVisible, (bool) => { if (!bool) { applicationForm.value = { name: '', + code: '', desc: '', model_id: undefined, dialogue_number: 1, diff --git a/ui/src/workflow/common/data.ts b/ui/src/workflow/common/data.ts index a726ae9ed5a..24a337463c4 100644 --- a/ui/src/workflow/common/data.ts +++ b/ui/src/workflow/common/data.ts @@ -47,6 +47,7 @@ export const baseNode = { input_field_list: [], node_data: { name: '', + code: '', desc: '', prologue: t('views.application.form.defaultPrologue'), tts_type: 'BROWSER', @@ -69,6 +70,7 @@ export const knowledgeBaseNode = { input_field_list: [], node_data: { name: '', + code: '', desc: '', prologue: t('views.application.form.defaultPrologue'), tts_type: 'BROWSER', diff --git a/ui/src/workflow/common/template.ts b/ui/src/workflow/common/template.ts index 38b64de2be5..2a82fd9c07a 100644 --- a/ui/src/workflow/common/template.ts +++ b/ui/src/workflow/common/template.ts @@ -19,6 +19,7 @@ export const applicationTemplate: any = { node_data: { desc: '模板', name: '知识库问答助手', + code: '', prologue: '您好,我是 XXX 小助手,您可以向我提出 XXX 使用问题。\n- XXX 主要功能有什么?\n- XXX 如何收费?\n- 需要转人工服务', tts_type: 'BROWSER', @@ -800,6 +801,7 @@ export const knowledgeTemplate: any = { node_data: { desc: '', name: '', + code: '', prologue: '\u60a8\u597d\uff0c\u6211\u662f XXX \u5c0f\u52a9\u624b\uff0c\u60a8\u53ef\u4ee5\u5411\u6211\u63d0\u51fa XXX \u4f7f\u7528\u95ee\u9898\u3002\n- XXX \u4e3b\u8981\u529f\u80fd\u6709\u4ec0\u4e48\uff1f\n- XXX \u5982\u4f55\u6536\u8d39\uff1f\n- \u9700\u8981\u8f6c\u4eba\u5de5\u670d\u52a1', tts_type: 'BROWSER', diff --git a/ui/src/workflow/nodes/base-node/index.vue b/ui/src/workflow/nodes/base-node/index.vue index cfeacd6937f..32b9792a084 100644 --- a/ui/src/workflow/nodes/base-node/index.vue +++ b/ui/src/workflow/nodes/base-node/index.vue @@ -26,6 +26,18 @@ @blur="form_data.name = form_data.name?.trim()" /> + + +