Skip to content

Commit 45ceacd

Browse files
committed
【功能新增】AI:增加召回测试的实现
1 parent 1958c2b commit 45ceacd

File tree

5 files changed

+215
-17
lines changed

5 files changed

+215
-17
lines changed

src/api/ai/knowledge/segment/index.ts

Lines changed: 16 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -30,20 +30,28 @@ export const KnowledgeSegmentApi = {
3030
deleteKnowledgeSegment: async (id: number) => {
3131
return await request.delete({ url: `/ai/knowledge/segment/delete?id=` + id })
3232
},
33-
33+
3434
// 切片内容
3535
splitContent: async (url: string, segmentMaxTokens: number) => {
36-
return await request.get({
37-
url: `/ai/knowledge/segment/split`,
38-
params: { url, segmentMaxTokens }
36+
return await request.get({
37+
url: `/ai/knowledge/segment/split`,
38+
params: { url, segmentMaxTokens }
3939
})
4040
},
41-
41+
4242
// 获取文档处理列表
4343
getKnowledgeSegmentProcessList: async (documentIds: number[]) => {
44-
return await request.get({
45-
url: `/ai/knowledge/segment/get-process-list`,
46-
params: { documentIds: documentIds.join(',') }
44+
return await request.get({
45+
url: `/ai/knowledge/segment/get-process-list`,
46+
params: { documentIds: documentIds.join(',') }
47+
})
48+
},
49+
50+
// 搜索知识库分片
51+
searchKnowledgeSegment: async (params: any) => {
52+
return await request.get({
53+
url: `/ai/knowledge/segment/search`,
54+
params
4755
})
4856
}
4957
}

src/router/modules/remaining.ts

Lines changed: 19 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -622,37 +622,50 @@ const remainingRouter: AppRouteRecordRaw[] = [
622622
}
623623
},
624624
{
625-
path: 'console/knowledge/document',
625+
path: 'knowledge/document',
626626
component: () => import('@/views/ai/knowledge/document/index.vue'),
627627
name: 'AiKnowledgeDocument',
628628
meta: {
629629
title: '知识库文档',
630630
icon: 'ep:document',
631-
noCache: false
631+
noCache: false,
632+
activeMenu: '/ai/knowledge'
632633
}
633634
},
634635
{
635-
path: 'console/knowledge/document/create',
636+
path: 'knowledge/document/create',
636637
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
637638
name: 'AiKnowledgeDocumentCreate',
638639
meta: {
639640
title: '创建文档',
640641
icon: 'ep:plus',
641642
noCache: true,
642643
hidden: true,
643-
activeMenu: '/ai/console/knowledge/document'
644+
activeMenu: '/ai/knowledge'
644645
}
645646
},
646647
{
647-
path: 'console/knowledge/document/update',
648+
path: 'knowledge/document/update',
648649
component: () => import('@/views/ai/knowledge/document/form/index.vue'),
649650
name: 'AiKnowledgeDocumentUpdate',
650651
meta: {
651652
title: '修改文档',
652653
icon: 'ep:edit',
653654
noCache: true,
654655
hidden: true,
655-
activeMenu: '/ai/console/knowledge/document'
656+
activeMenu: '/ai/knowledge'
657+
}
658+
},
659+
{
660+
path: 'knowledge/retrieval',
661+
component: () => import('@/views/ai/knowledge/knowledge/retrieval/index.vue'),
662+
name: 'AiKnowledgeRetrieval',
663+
meta: {
664+
title: '文档召回测试',
665+
icon: 'ep:search',
666+
noCache: true,
667+
hidden: true,
668+
activeMenu: '/ai/knowledge'
656669
}
657670
}
658671
]

src/views/ai/knowledge/document/form/SplitStep.vue

Lines changed: 0 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -145,7 +145,6 @@ const splitContent = async (file: any) => {
145145
)
146146
} catch (error) {
147147
console.error('获取分段内容失败:', file, error)
148-
message.error('获取分段内容失败')
149148
} finally {
150149
splitLoading.value = false
151150
}
@@ -214,7 +213,6 @@ const handleSave = async () => {
214213
}
215214
} catch (error: any) {
216215
console.error('保存失败:', modelData.value, error)
217-
message.error(error.message)
218216
} finally {
219217
// 关闭按钮加载状态
220218
submitLoading.value = false

src/views/ai/knowledge/knowledge/index.vue

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -95,6 +95,14 @@
9595
>
9696
文档
9797
</el-button>
98+
<el-button
99+
link
100+
type="primary"
101+
@click="handleRetrieval(scope.row.id)"
102+
v-hasPermi="['ai:knowledge:query']"
103+
>
104+
召回测试
105+
</el-button>
98106
<el-button
99107
link
100108
type="danger"
@@ -191,11 +199,19 @@ const handleDelete = async (id: number) => {
191199
const router = useRouter()
192200
const handleDocument = (id: number) => {
193201
router.push({
194-
path: '/ai/console/knowledge/document',
202+
name: 'AiKnowledgeDocument',
195203
query: { knowledgeId: id }
196204
})
197205
}
198206
207+
/** 跳转到文档召回测试页面 */
208+
const handleRetrieval = (id: number) => {
209+
router.push({
210+
name: 'AiKnowledgeRetrieval',
211+
query: { id }
212+
})
213+
}
214+
199215
/** 初始化 **/
200216
onMounted(() => {
201217
getList()
Lines changed: 163 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,163 @@
1+
<template>
2+
<div class="flex gap-20px w-full">
3+
<!-- 左侧输入区域 -->
4+
<ContentWrap class="flex-1 min-w-300px">
5+
<div class="mb-15px">
6+
<h3 class="m-0 mb-5px">召回测试</h3>
7+
<div class="text-gray-500 text-14px">根据给定的查询文本测试召回效果。</div>
8+
</div>
9+
<div>
10+
<div class="relative mb-10px">
11+
<el-input
12+
v-model="queryParams.content"
13+
type="textarea"
14+
:rows="8"
15+
placeholder="请输入文本"
16+
/>
17+
<div class="absolute bottom-10px right-10px text-gray-400 text-12px">
18+
{{ queryParams.content?.length }} / 200
19+
</div>
20+
</div>
21+
<div class="flex items-center mb-10px">
22+
<span class="w-60px text-gray-500">topK:</span>
23+
<el-input-number v-model="queryParams.topK" :min="1" :max="20" />
24+
</div>
25+
<div class="flex items-center mb-15px">
26+
<span class="w-60px text-gray-500">相似度:</span>
27+
<el-input-number
28+
v-model="queryParams.similarityThreshold"
29+
:min="0"
30+
:max="1"
31+
:precision="2"
32+
:step="0.01"
33+
/>
34+
</div>
35+
<div class="flex justify-end">
36+
<el-button type="primary" @click="getRetrievalResult" :loading="loading">测试</el-button>
37+
</div>
38+
</div>
39+
</ContentWrap>
40+
41+
<!-- 右侧召回结果区域 -->
42+
<ContentWrap class="flex-1 min-w-300px">
43+
<el-empty v-if="loading" description="正在检索中..." />
44+
<div v-else-if="segments.length > 0" class="font-bold mb-15px">
45+
{{ segments.length }} 个召回段落
46+
</div>
47+
<el-empty v-else description="暂无召回结果" />
48+
<div>
49+
<div
50+
v-for="(segment, index) in segments"
51+
:key="index"
52+
class="mb-20px border border-solid border-gray-200 rounded p-15px"
53+
>
54+
<div class="flex justify-between text-12px text-gray-500 mb-5px">
55+
<span>
56+
分段({{ segment.id }}) · {{ segment.contentLength }} 字符数 ·
57+
{{ segment.tokens }} Token
58+
</span>
59+
<span class="px-8px py-4px bg-blue-50 text-blue-500 rounded-full text-12px font-bold">
60+
score: {{ segment.score }}
61+
</span>
62+
</div>
63+
<div
64+
class="bg-gray-50 p-10px rounded mb-10px whitespace-pre-wrap overflow-hidden transition-all duration-100 text-13px"
65+
:class="{
66+
'line-clamp-2 max-h-50px': !segment.expanded,
67+
'max-h-500px': segment.expanded
68+
}"
69+
>
70+
{{ segment.content }}
71+
</div>
72+
<div class="flex justify-between items-center">
73+
<div class="flex items-center text-gray-500 text-13px">
74+
<Icon icon="ep:document" class="mr-5px" />
75+
<span>{{ segment.documentName || '未知文档' }}</span>
76+
</div>
77+
<el-button size="small" @click="toggleExpand(segment)">
78+
{{ segment.expanded ? '收起' : '展开' }}
79+
<Icon :icon="segment.expanded ? 'ep:arrow-up' : 'ep:arrow-down'" />
80+
</el-button>
81+
</div>
82+
</div>
83+
</div>
84+
</ContentWrap>
85+
</div>
86+
</template>
87+
88+
<script setup lang="ts">
89+
import { useMessage } from '@/hooks/web/useMessage'
90+
import { KnowledgeSegmentApi } from '@/api/ai/knowledge/segment'
91+
import { KnowledgeApi } from '@/api/ai/knowledge/knowledge'
92+
/** 文档召回测试 */
93+
defineOptions({ name: 'KnowledgeDocumentRetrieval' })
94+
95+
const message = useMessage() // 消息弹窗
96+
const route = useRoute() // 路由
97+
const router = useRouter() // 路由
98+
99+
const loading = ref(false) // 加载状态
100+
const segments = ref<any[]>([]) // 召回结果
101+
const queryParams = reactive({
102+
id: undefined,
103+
content: '',
104+
topK: 10,
105+
similarityThreshold: 0.5
106+
})
107+
108+
/** 调用文档召回测试接口 */
109+
const getRetrievalResult = async () => {
110+
if (!queryParams.content) {
111+
message.warning('请输入查询文本')
112+
return
113+
}
114+
115+
loading.value = true
116+
segments.value = []
117+
118+
try {
119+
const data = await KnowledgeSegmentApi.searchKnowledgeSegment({
120+
knowledgeId: queryParams.id,
121+
content: queryParams.content,
122+
topK: queryParams.topK,
123+
similarityThreshold: queryParams.similarityThreshold
124+
})
125+
segments.value = data || []
126+
} catch (error) {
127+
console.error(error)
128+
} finally {
129+
loading.value = false
130+
}
131+
}
132+
133+
/** 展开/收起段落内容 */
134+
const toggleExpand = (segment: any) => {
135+
segment.expanded = !segment.expanded
136+
}
137+
138+
/** 获取知识库信息 */
139+
const getKnowledgeInfo = async (id: number) => {
140+
try {
141+
const knowledge = await KnowledgeApi.getKnowledge(id)
142+
if (knowledge) {
143+
queryParams.topK = knowledge.topK || queryParams.topK
144+
queryParams.similarityThreshold =
145+
knowledge.similarityThreshold || queryParams.similarityThreshold
146+
}
147+
} catch (error) {}
148+
}
149+
150+
/** 初始化 **/
151+
onMounted(() => {
152+
// 如果知识库 ID 不存在,显示错误提示并关闭页面
153+
if (!route.query.id) {
154+
message.error('知识库 ID 不存在,无法进行召回测试')
155+
router.back()
156+
return
157+
}
158+
queryParams.id = route.query.id as any
159+
160+
// 获取知识库信息并设置默认值
161+
getKnowledgeInfo(queryParams.id as any)
162+
})
163+
</script>

0 commit comments

Comments
 (0)