mirror of
https://gitee.com/myxzgzs/boyue-ui-admin-vue3
synced 2025-08-08 16:32:43 +08:00
【功能新增】AI:增加召回测试的实现
This commit is contained in:
parent
1958c2bb9d
commit
45ceacdcac
@ -45,5 +45,13 @@ export const KnowledgeSegmentApi = {
|
|||||||
url: `/ai/knowledge/segment/get-process-list`,
|
url: `/ai/knowledge/segment/get-process-list`,
|
||||||
params: { documentIds: documentIds.join(',') }
|
params: { documentIds: documentIds.join(',') }
|
||||||
})
|
})
|
||||||
|
},
|
||||||
|
|
||||||
|
// 搜索知识库分片
|
||||||
|
searchKnowledgeSegment: async (params: any) => {
|
||||||
|
return await request.get({
|
||||||
|
url: `/ai/knowledge/segment/search`,
|
||||||
|
params
|
||||||
|
})
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -622,17 +622,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'console/knowledge/document',
|
path: 'knowledge/document',
|
||||||
component: () => import('@/views/ai/knowledge/document/index.vue'),
|
component: () => import('@/views/ai/knowledge/document/index.vue'),
|
||||||
name: 'AiKnowledgeDocument',
|
name: 'AiKnowledgeDocument',
|
||||||
meta: {
|
meta: {
|
||||||
title: '知识库文档',
|
title: '知识库文档',
|
||||||
icon: 'ep:document',
|
icon: 'ep:document',
|
||||||
noCache: false
|
noCache: false,
|
||||||
|
activeMenu: '/ai/knowledge'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'console/knowledge/document/create',
|
path: 'knowledge/document/create',
|
||||||
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
||||||
name: 'AiKnowledgeDocumentCreate',
|
name: 'AiKnowledgeDocumentCreate',
|
||||||
meta: {
|
meta: {
|
||||||
@ -640,11 +641,11 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
icon: 'ep:plus',
|
icon: 'ep:plus',
|
||||||
noCache: true,
|
noCache: true,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
activeMenu: '/ai/console/knowledge/document'
|
activeMenu: '/ai/knowledge'
|
||||||
}
|
}
|
||||||
},
|
},
|
||||||
{
|
{
|
||||||
path: 'console/knowledge/document/update',
|
path: 'knowledge/document/update',
|
||||||
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
|
||||||
name: 'AiKnowledgeDocumentUpdate',
|
name: 'AiKnowledgeDocumentUpdate',
|
||||||
meta: {
|
meta: {
|
||||||
@ -652,7 +653,19 @@ const remainingRouter: AppRouteRecordRaw[] = [
|
|||||||
icon: 'ep:edit',
|
icon: 'ep:edit',
|
||||||
noCache: true,
|
noCache: true,
|
||||||
hidden: true,
|
hidden: true,
|
||||||
activeMenu: '/ai/console/knowledge/document'
|
activeMenu: '/ai/knowledge'
|
||||||
|
}
|
||||||
|
},
|
||||||
|
{
|
||||||
|
path: 'knowledge/retrieval',
|
||||||
|
component: () => import('@/views/ai/knowledge/knowledge/retrieval/index.vue'),
|
||||||
|
name: 'AiKnowledgeRetrieval',
|
||||||
|
meta: {
|
||||||
|
title: '文档召回测试',
|
||||||
|
icon: 'ep:search',
|
||||||
|
noCache: true,
|
||||||
|
hidden: true,
|
||||||
|
activeMenu: '/ai/knowledge'
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
]
|
]
|
||||||
|
@ -145,7 +145,6 @@ const splitContent = async (file: any) => {
|
|||||||
)
|
)
|
||||||
} catch (error) {
|
} catch (error) {
|
||||||
console.error('获取分段内容失败:', file, error)
|
console.error('获取分段内容失败:', file, error)
|
||||||
message.error('获取分段内容失败')
|
|
||||||
} finally {
|
} finally {
|
||||||
splitLoading.value = false
|
splitLoading.value = false
|
||||||
}
|
}
|
||||||
@ -214,7 +213,6 @@ const handleSave = async () => {
|
|||||||
}
|
}
|
||||||
} catch (error: any) {
|
} catch (error: any) {
|
||||||
console.error('保存失败:', modelData.value, error)
|
console.error('保存失败:', modelData.value, error)
|
||||||
message.error(error.message)
|
|
||||||
} finally {
|
} finally {
|
||||||
// 关闭按钮加载状态
|
// 关闭按钮加载状态
|
||||||
submitLoading.value = false
|
submitLoading.value = false
|
||||||
|
@ -95,6 +95,14 @@
|
|||||||
>
|
>
|
||||||
文档
|
文档
|
||||||
</el-button>
|
</el-button>
|
||||||
|
<el-button
|
||||||
|
link
|
||||||
|
type="primary"
|
||||||
|
@click="handleRetrieval(scope.row.id)"
|
||||||
|
v-hasPermi="['ai:knowledge:query']"
|
||||||
|
>
|
||||||
|
召回测试
|
||||||
|
</el-button>
|
||||||
<el-button
|
<el-button
|
||||||
link
|
link
|
||||||
type="danger"
|
type="danger"
|
||||||
@ -191,11 +199,19 @@ const handleDelete = async (id: number) => {
|
|||||||
const router = useRouter()
|
const router = useRouter()
|
||||||
const handleDocument = (id: number) => {
|
const handleDocument = (id: number) => {
|
||||||
router.push({
|
router.push({
|
||||||
path: '/ai/console/knowledge/document',
|
name: 'AiKnowledgeDocument',
|
||||||
query: { knowledgeId: id }
|
query: { knowledgeId: id }
|
||||||
})
|
})
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** 跳转到文档召回测试页面 */
|
||||||
|
const handleRetrieval = (id: number) => {
|
||||||
|
router.push({
|
||||||
|
name: 'AiKnowledgeRetrieval',
|
||||||
|
query: { id }
|
||||||
|
})
|
||||||
|
}
|
||||||
|
|
||||||
/** 初始化 **/
|
/** 初始化 **/
|
||||||
onMounted(() => {
|
onMounted(() => {
|
||||||
getList()
|
getList()
|
||||||
|
163
src/views/ai/knowledge/knowledge/retrieval/index.vue
Normal file
163
src/views/ai/knowledge/knowledge/retrieval/index.vue
Normal file
@ -0,0 +1,163 @@
|
|||||||
|
<template>
|
||||||
|
<div class="flex gap-20px w-full">
|
||||||
|
<!-- 左侧输入区域 -->
|
||||||
|
<ContentWrap class="flex-1 min-w-300px">
|
||||||
|
<div class="mb-15px">
|
||||||
|
<h3 class="m-0 mb-5px">召回测试</h3>
|
||||||
|
<div class="text-gray-500 text-14px">根据给定的查询文本测试召回效果。</div>
|
||||||
|
</div>
|
||||||
|
<div>
|
||||||
|
<div class="relative mb-10px">
|
||||||
|
<el-input
|
||||||
|
v-model="queryParams.content"
|
||||||
|
type="textarea"
|
||||||
|
:rows="8"
|
||||||
|
placeholder="请输入文本"
|
||||||
|
/>
|
||||||
|
<div class="absolute bottom-10px right-10px text-gray-400 text-12px">
|
||||||
|
{{ queryParams.content?.length }} / 200
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-10px">
|
||||||
|
<span class="w-60px text-gray-500">topK:</span>
|
||||||
|
<el-input-number v-model="queryParams.topK" :min="1" :max="20" />
|
||||||
|
</div>
|
||||||
|
<div class="flex items-center mb-15px">
|
||||||
|
<span class="w-60px text-gray-500">相似度:</span>
|
||||||
|
<el-input-number
|
||||||
|
v-model="queryParams.similarityThreshold"
|
||||||
|
:min="0"
|
||||||
|
:max="1"
|
||||||
|
:precision="2"
|
||||||
|
:step="0.01"
|
||||||
|
/>
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-end">
|
||||||
|
<el-button type="primary" @click="getRetrievalResult" :loading="loading">测试</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContentWrap>
|
||||||
|
|
||||||
|
<!-- 右侧召回结果区域 -->
|
||||||
|
<ContentWrap class="flex-1 min-w-300px">
|
||||||
|
<el-empty v-if="loading" description="正在检索中..." />
|
||||||
|
<div v-else-if="segments.length > 0" class="font-bold mb-15px">
|
||||||
|
{{ segments.length }} 个召回段落
|
||||||
|
</div>
|
||||||
|
<el-empty v-else description="暂无召回结果" />
|
||||||
|
<div>
|
||||||
|
<div
|
||||||
|
v-for="(segment, index) in segments"
|
||||||
|
:key="index"
|
||||||
|
class="mb-20px border border-solid border-gray-200 rounded p-15px"
|
||||||
|
>
|
||||||
|
<div class="flex justify-between text-12px text-gray-500 mb-5px">
|
||||||
|
<span>
|
||||||
|
分段({{ segment.id }}) · {{ segment.contentLength }} 字符数 ·
|
||||||
|
{{ segment.tokens }} Token
|
||||||
|
</span>
|
||||||
|
<span class="px-8px py-4px bg-blue-50 text-blue-500 rounded-full text-12px font-bold">
|
||||||
|
score: {{ segment.score }}
|
||||||
|
</span>
|
||||||
|
</div>
|
||||||
|
<div
|
||||||
|
class="bg-gray-50 p-10px rounded mb-10px whitespace-pre-wrap overflow-hidden transition-all duration-100 text-13px"
|
||||||
|
:class="{
|
||||||
|
'line-clamp-2 max-h-50px': !segment.expanded,
|
||||||
|
'max-h-500px': segment.expanded
|
||||||
|
}"
|
||||||
|
>
|
||||||
|
{{ segment.content }}
|
||||||
|
</div>
|
||||||
|
<div class="flex justify-between items-center">
|
||||||
|
<div class="flex items-center text-gray-500 text-13px">
|
||||||
|
<Icon icon="ep:document" class="mr-5px" />
|
||||||
|
<span>{{ segment.documentName || '未知文档' }}</span>
|
||||||
|
</div>
|
||||||
|
<el-button size="small" @click="toggleExpand(segment)">
|
||||||
|
{{ segment.expanded ? '收起' : '展开' }}
|
||||||
|
<Icon :icon="segment.expanded ? 'ep:arrow-up' : 'ep:arrow-down'" />
|
||||||
|
</el-button>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</div>
|
||||||
|
</ContentWrap>
|
||||||
|
</div>
|
||||||
|
</template>
|
||||||
|
|
||||||
|
<script setup lang="ts">
|
||||||
|
import { useMessage } from '@/hooks/web/useMessage'
|
||||||
|
import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment'
|
||||||
|
import { KnowledgeApi } from '@/api/ai/knowledge/knowledge'
|
||||||
|
/** 文档召回测试 */
|
||||||
|
defineOptions({ name: 'KnowledgeDocumentRetrieval' })
|
||||||
|
|
||||||
|
const message = useMessage() // 消息弹窗
|
||||||
|
const route = useRoute() // 路由
|
||||||
|
const router = useRouter() // 路由
|
||||||
|
|
||||||
|
const loading = ref(false) // 加载状态
|
||||||
|
const segments = ref<any[]>([]) // 召回结果
|
||||||
|
const queryParams = reactive({
|
||||||
|
id: undefined,
|
||||||
|
content: '',
|
||||||
|
topK: 10,
|
||||||
|
similarityThreshold: 0.5
|
||||||
|
})
|
||||||
|
|
||||||
|
/** 调用文档召回测试接口 */
|
||||||
|
const getRetrievalResult = async () => {
|
||||||
|
if (!queryParams.content) {
|
||||||
|
message.warning('请输入查询文本')
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
loading.value = true
|
||||||
|
segments.value = []
|
||||||
|
|
||||||
|
try {
|
||||||
|
const data = await KnowledgeSegmentApi.searchKnowledgeSegment({
|
||||||
|
knowledgeId: queryParams.id,
|
||||||
|
content: queryParams.content,
|
||||||
|
topK: queryParams.topK,
|
||||||
|
similarityThreshold: queryParams.similarityThreshold
|
||||||
|
})
|
||||||
|
segments.value = data || []
|
||||||
|
} catch (error) {
|
||||||
|
console.error(error)
|
||||||
|
} finally {
|
||||||
|
loading.value = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 展开/收起段落内容 */
|
||||||
|
const toggleExpand = (segment: any) => {
|
||||||
|
segment.expanded = !segment.expanded
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 获取知识库信息 */
|
||||||
|
const getKnowledgeInfo = async (id: number) => {
|
||||||
|
try {
|
||||||
|
const knowledge = await KnowledgeApi.getKnowledge(id)
|
||||||
|
if (knowledge) {
|
||||||
|
queryParams.topK = knowledge.topK || queryParams.topK
|
||||||
|
queryParams.similarityThreshold =
|
||||||
|
knowledge.similarityThreshold || queryParams.similarityThreshold
|
||||||
|
}
|
||||||
|
} catch (error) {}
|
||||||
|
}
|
||||||
|
|
||||||
|
/** 初始化 **/
|
||||||
|
onMounted(() => {
|
||||||
|
// 如果知识库 ID 不存在,显示错误提示并关闭页面
|
||||||
|
if (!route.query.id) {
|
||||||
|
message.error('知识库 ID 不存在,无法进行召回测试')
|
||||||
|
router.back()
|
||||||
|
return
|
||||||
|
}
|
||||||
|
queryParams.id = route.query.id as any
|
||||||
|
|
||||||
|
// 获取知识库信息并设置默认值
|
||||||
|
getKnowledgeInfo(queryParams.id as any)
|
||||||
|
})
|
||||||
|
</script>
|
Loading…
x
Reference in New Issue
Block a user