feat: enhance search functionality with dynamic scope selection and improved UI

This commit is contained in:
CaptainB 2025-10-17 11:11:04 +08:00
parent 3ba1a0be7b
commit 947152ed33
5 changed files with 135 additions and 14 deletions

View File

@ -7,6 +7,8 @@ from django.db.models import QuerySet
from application.flow.i_step_node import NodeResult
from application.flow.step_node.search_document_node.i_search_document_node import ISearchDocumentStepNode
from common.constants.permission_constants import RoleConstants
from common.database_model_manage.database_model_manage import DatabaseModelManage
from knowledge.models import Document, DocumentTag, Knowledge
@ -39,6 +41,23 @@ class BaseSearchDocumentNode(ISearchDocumentStepNode):
knowledge_id__in=self.get_reference_content(search_scope_reference)
).values_list('id', flat=True)
# 权限过滤
get_knowledge_list_of_authorized = DatabaseModelManage.get_model('get_knowledge_list_of_authorized')
chat_user_type = self.workflow_manage.get_body().get('chat_user_type')
if get_knowledge_list_of_authorized is not None and RoleConstants.CHAT_USER.value.name == chat_user_type:
# 获取授权的知识库ID列表
authorized_knowledge_ids = get_knowledge_list_of_authorized(
self.workflow_manage.get_body().get('chat_user_id'),
knowledge_id_list
)
# 过滤出授权知识库下的文档
document_id_list = QuerySet(Document).filter(
id__in=document_id_list,
knowledge_id__in=authorized_knowledge_ids
).values_list('id', flat=True)
if search_mode == 'auto': # 通过问题自动检索
matched_doc_ids = self.handle_auto_tags(document_id_list, question_reference)

View File

@ -10,13 +10,12 @@ import re
from typing import Type
from django.core import validators
from django.utils.translation import gettext_lazy as _
from rest_framework import serializers
from application.flow.i_step_node import INode, NodeResult
from common.utils.common import flat_map
from django.utils.translation import gettext_lazy as _
class DatasetSettingSerializer(serializers.Serializer):
# 需要查询的条数
@ -43,6 +42,17 @@ class SearchDatasetStepNodeSerializer(serializers.Serializer):
show_knowledge = serializers.BooleanField(required=True,
label=_("The results are displayed in the knowledge sources"))
search_scope_type = serializers.ChoiceField(
required=False, choices=['custom', 'referencing'], label=_("search scope type"),
allow_null=True, default='custom'
)
search_scope_source = serializers.ChoiceField(
required=False, choices=['document', 'knowledge'],
label=_("search scope variable type"), default='knowledge'
)
search_scope_reference = serializers.ListField(
required=False, label=_("search scope variable"), default=list
)
def is_valid(self, *, raise_exception=False):
super().is_valid(raise_exception=True)
@ -76,7 +86,9 @@ class ISearchKnowledgeStepNode(INode):
return self.execute(**self.node_params_serializer.data, question=str(question),
exclude_paragraph_id_list=exclude_paragraph_id_list)
def execute(self, dataset_id_list, dataset_setting, question, show_knowledge,
def execute(self, dataset_id_list, dataset_setting, question, show_knowledge, search_scope_type,
search_scope_source,
search_scope_reference,
exclude_paragraph_id_list=None,
**kwargs) -> NodeResult:
pass

View File

@ -68,11 +68,27 @@ class BaseSearchKnowledgeNode(ISearchKnowledgeStepNode):
result])[0:knowledge_setting.get('max_paragraph_char_number', 5000)]
self.context['directly_return'] = directly_return
def execute(self, knowledge_id_list, knowledge_setting, question, show_knowledge,
def get_reference_content(self, fields: List[str]):
return self.workflow_manage.get_reference_field(fields[0], fields[1:])
def execute(self, knowledge_id_list, knowledge_setting, question, show_knowledge, search_scope_type,
search_scope_source,
search_scope_reference,
exclude_paragraph_id_list=None,
**kwargs) -> NodeResult:
self.context['question'] = question
self.context['show_knowledge'] = show_knowledge
if search_scope_type == 'referencing': # 引用上一步知识库/文档
if search_scope_source == 'knowledge': # 知识库
knowledge_id_list = self.get_reference_content(search_scope_reference)
else: # 文档
knowledge_id_list = QuerySet(Document).filter(
id__in=self.get_reference_content(search_scope_reference)
).values_list(
'knowledge_id', flat=True
).distinct()
get_knowledge_list_of_authorized = DatabaseModelManage.get_model('get_knowledge_list_of_authorized')
chat_user_type = self.workflow_manage.get_body().get('chat_user_type')
if get_knowledge_list_of_authorized is not None and RoleConstants.CHAT_USER.value.name == chat_user_type:

View File

@ -17,7 +17,9 @@
{{ $t('views.applicationWorkflow.nodes.searchDocumentNode.selectKnowledge') }}
</span>
<span>
<el-button type="primary" link @click="openKnowledgeDialog">
<el-button v-if="form_data.search_scope_type === 'custom'"
type="primary" link @click="openKnowledgeDialog"
>
<AppIcon iconName="app-add-outlined"></AppIcon>
</el-button>
<el-select
@ -78,9 +80,6 @@
{{ $t('选择变量') }}
</span>
<span>
<el-button type="primary" link @click="openKnowledgeDialog">
<AppIcon iconName="app-add-outlined"></AppIcon>
</el-button>
<el-select
:teleported="false"
size="small"
@ -89,11 +88,11 @@
@change="form_data.search_scope_reference = []"
>
<el-option
:label="$t('知识库列表')"
:label="$t('views.applicationWorkflow.nodes.searchDocumentNode.knowledge_list')"
value="knowledge"
/>
<el-option
:label="$t('文档列表')"
:label="$t('views.applicationWorkflow.nodes.searchDocumentNode.document_list')"
value="document"
/>
</el-select>

View File

@ -14,12 +14,31 @@
<template #label>
<div class="flex-between">
<span>{{ $t('views.chatLog.selectKnowledge') }}</span>
<el-button type="primary" link @click="openknowledgeDialog">
<AppIcon iconName="app-add-outlined"></AppIcon>
</el-button>
<span>
<el-button v-if="form_data.search_scope_type === 'custom'"
type="primary" link @click="openknowledgeDialog"
>
<AppIcon iconName="app-add-outlined"></AppIcon>
</el-button>
<el-select
:teleported="false"
size="small"
v-model="form_data.search_scope_type"
style="width: 85px"
>
<el-option
:label="$t('views.applicationWorkflow.nodes.searchDocumentNode.custom')"
value="custom"
/>
<el-option
:label="$t('views.applicationWorkflow.variable.Referencing')"
value="referencing"
/>
</el-select>
</span>
</div>
</template>
<div class="w-full">
<div class="w-full" v-if="form_data.search_scope_type === 'custom'">
<el-text type="info" v-if="form_data.knowledge_id_list?.length === 0">
{{ $t('views.application.form.relatedKnowledge.placeholder') }}
</el-text>
@ -42,6 +61,49 @@
</div>
</template>
</div>
<div class="w-full" v-else>
<el-form-item
prop="search_scope_reference"
:rules="{
message: $t('views.applicationWorkflow.variable.placeholder'),
trigger: 'blur',
required: true,
}"
>
<template #label>
<div class="flex-between">
<span>
{{ $t('选择变量') }}
</span>
<span>
<el-select
:teleported="false"
size="small"
v-model="form_data.search_scope_source"
style="width: 95px"
@change="form_data.search_scope_reference = []"
>
<el-option
:label="$t('views.applicationWorkflow.nodes.searchDocumentNode.knowledge_list')"
value="knowledge"
/>
<el-option
:label="$t('views.applicationWorkflow.nodes.searchDocumentNode.document_list')"
value="document"
/>
</el-select>
</span>
</div>
</template>
<NodeCascader
ref="nodeCascaderRef"
:nodeModel="nodeModel"
class="w-full"
:placeholder="$t('views.applicationWorkflow.variable.placeholder')"
v-model="form_data.search_scope_reference"
/>
</el-form-item>
</div>
</el-form-item>
<el-form-item
:label="$t('views.applicationWorkflow.nodes.searchKnowledgeNode.searchParam')"
@ -142,6 +204,7 @@ import type { FormInstance } from 'element-plus'
import { ref, computed, onMounted } from 'vue'
import { relatedObject } from '@/utils/array'
import { SearchMode } from '@/enums/application'
import AppIcon from "@/components/app-icon/AppIcon.vue";
const props = defineProps<{ nodeModel: any }>()
const nodeCascaderRef = ref()
@ -155,6 +218,9 @@ const form = {
},
question_reference_address: [],
show_knowledge: false,
search_scope_type: 'custom',
search_scope_source: 'knowledge',
search_scope_reference: [],
}
const form_data = computed({
@ -221,6 +287,15 @@ onMounted(() => {
form_data.value.show_knowledge = form_data.value.show_knowledge
? form_data.value.show_knowledge
: false
form_data.value.search_scope_type = form_data.value.search_scope_type
? form_data.value.search_scope_type
: 'custom'
form_data.value.search_scope_source = form_data.value.search_scope_source
? form_data.value.search_scope_source
: 'knowledge'
form_data.value.knowledge_id_list = form_data.value.knowledge_id_list
? form_data.value.knowledge_id_list
: []
set(props.nodeModel, 'validate', validate)
})
</script>