Skip to content
Toggle navigation
P
Projects
G
Groups
S
Snippets
Help
phsl
/
admin
This project
Loading...
Sign in
Toggle navigation
Go to a project
Project
Repository
Issues
0
Merge Requests
0
Pipelines
Wiki
Snippets
Members
Activity
Graph
Charts
Create a new issue
Jobs
Commits
Issue Boards
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Charts
Commit
45ceacdc
authored
Mar 02, 2025
by
YunaiV
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
【功能新增】AI:增加召回测试的实现
parent
1958c2bb
Hide whitespace changes
Inline
Side-by-side
Showing
5 changed files
with
215 additions
and
17 deletions
+215
-17
src/api/ai/knowledge/segment/index.ts
+16
-8
src/router/modules/remaining.ts
+19
-6
src/views/ai/knowledge/document/form/SplitStep.vue
+0
-2
src/views/ai/knowledge/knowledge/index.vue
+17
-1
src/views/ai/knowledge/knowledge/retrieval/index.vue
+163
-0
No files found.
src/api/ai/knowledge/segment/index.ts
View file @
45ceacdc
...
@@ -30,20 +30,28 @@ export const KnowledgeSegmentApi = {
...
@@ -30,20 +30,28 @@ export const KnowledgeSegmentApi = {
deleteKnowledgeSegment
:
async
(
id
:
number
)
=>
{
deleteKnowledgeSegment
:
async
(
id
:
number
)
=>
{
return
await
request
.
delete
({
url
:
`/ai/knowledge/segment/delete?id=`
+
id
})
return
await
request
.
delete
({
url
:
`/ai/knowledge/segment/delete?id=`
+
id
})
},
},
// 切片内容
// 切片内容
splitContent
:
async
(
url
:
string
,
segmentMaxTokens
:
number
)
=>
{
splitContent
:
async
(
url
:
string
,
segmentMaxTokens
:
number
)
=>
{
return
await
request
.
get
({
return
await
request
.
get
({
url
:
`/ai/knowledge/segment/split`
,
url
:
`/ai/knowledge/segment/split`
,
params
:
{
url
,
segmentMaxTokens
}
params
:
{
url
,
segmentMaxTokens
}
})
})
},
},
// 获取文档处理列表
// 获取文档处理列表
getKnowledgeSegmentProcessList
:
async
(
documentIds
:
number
[])
=>
{
getKnowledgeSegmentProcessList
:
async
(
documentIds
:
number
[])
=>
{
return
await
request
.
get
({
return
await
request
.
get
({
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
})
})
}
}
}
}
src/router/modules/remaining.ts
View file @
45ceacdc
...
@@ -622,17 +622,18 @@ const remainingRouter: AppRouteRecordRaw[] = [
...
@@ -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[] = [
...
@@ -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[] = [
...
@@ -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'
}
}
}
}
]
]
...
...
src/views/ai/knowledge/document/form/SplitStep.vue
View file @
45ceacdc
...
@@ -145,7 +145,6 @@ const splitContent = async (file: any) => {
...
@@ -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 () => {
...
@@ -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
...
...
src/views/ai/knowledge/knowledge/index.vue
View file @
45ceacdc
...
@@ -97,6 +97,14 @@
...
@@ -97,6 +97,14 @@
</el-button>
</el-button>
<el-button
<el-button
link
link
type=
"primary"
@
click=
"handleRetrieval(scope.row.id)"
v-hasPermi=
"['ai:knowledge:query']"
>
召回测试
</el-button>
<el-button
link
type=
"danger"
type=
"danger"
@
click=
"handleDelete(scope.row.id)"
@
click=
"handleDelete(scope.row.id)"
v-hasPermi=
"['ai:knowledge:delete']"
v-hasPermi=
"['ai:knowledge:delete']"
...
@@ -191,11 +199,19 @@ const handleDelete = async (id: number) => {
...
@@ -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/d
ocument'
,
name
:
'AiKnowledgeD
ocument'
,
query
:
{
knowledgeId
:
id
}
query
:
{
knowledgeId
:
id
}
})
})
}
}
/** 跳转到文档召回测试页面 */
const
handleRetrieval
=
(
id
:
number
)
=>
{
router
.
push
({
name
:
'AiKnowledgeRetrieval'
,
query
:
{
id
}
})
}
/** 初始化 **/
/** 初始化 **/
onMounted
(()
=>
{
onMounted
(()
=>
{
getList
()
getList
()
...
...
src/views/ai/knowledge/knowledge/retrieval/index.vue
0 → 100644
View file @
45ceacdc
<
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
>
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment