feat(knowledge): 实现知识库命中测试功能

- 新增 hitTest 函数以调用命中测试接口
- 创建 HitTest 组件以展示命中测试结果
- 在知识库详情页面添加命中测试按钮和相关功能
- 优化知识库详情页面布局和样式
This commit is contained in:
Huangzhe
2025-04-28 15:43:02 +08:00
parent 41fd5e0ad9
commit 46b240e847
4 changed files with 104 additions and 186 deletions

View File

@@ -33,6 +33,16 @@ export function datasetCreate(data) {
data data
}) })
} }
// 命中测试
export function hitTest(data) {
return request({
url: getUrl('/datasetsEx/hit/test'),
method: 'post',
data
})
}
// 知识库删除 // 知识库删除
export function datasetDelete(data) { export function datasetDelete(data) {
return request({ return request({

View File

@@ -91,7 +91,7 @@ export default {
}, },
handleClickAgent(agent) { handleClickAgent(agent) {
this.agentConfig.agent = agent this.agentConfig.agent = agent
this.agentConfig.title = '智能体详情' this.agentConfig.title = agent.name + ' - 智能体详情'
this.agentConfig.visible = true this.agentConfig.visible = true
}, },
@@ -152,7 +152,6 @@ export default {
<div class="folder-content"> <div class="folder-content">
<div class="folder"> <div class="folder">
<!--listItem.name.获取字符串第一个--> <!--listItem.name.获取字符串第一个-->
{{ listItem.imageId ? listItem.imageId : listItem.name[0] }} {{ listItem.imageId ? listItem.imageId : listItem.name[0] }}
</div> </div>
</div> </div>
@@ -204,6 +203,7 @@ export default {
</r-dialog> </r-dialog>
<el-drawer <el-drawer
v-if="agentConfig.visible"
:visible.sync="agentConfig.visible" :visible.sync="agentConfig.visible"
:title="agentConfig.title" :title="agentConfig.title"
size="90%" size="90%"

View File

@@ -1,8 +1,11 @@
<script> <script>
import { hitTest } from '@/api/generatedApi'
export default { export default {
name: 'hitTest', name: 'hitTest',
data() { data() {
return { return {
datasetId: void 0,
paramsConfig: { paramsConfig: {
visible: false, visible: false,
searchMode: 'vector', // 默认选择向量检索 searchMode: 'vector', // 默认选择向量检索
@@ -11,96 +14,33 @@ export default {
}, },
inputMessage: '', inputMessage: '',
loading: false, loading: false,
messages: [ messages: []
{
id: 1,
type: 'question',
content: '黄金运用方式有哪些?',
time: new Date()
},
{
id: 2,
type: 'answer',
content: '根据您的问题,我找到了以下相关信息:',
references: [
{
id: 1,
title: '-',
content:
'黄金可以作为投资者的资产配置1把黄金作为储备资产的投资者2被保险人投资在黄金的抵押品或非标资产债券的担保3或者是投资者的避险资产。',
score: 0.709,
file: '北京人寿的准备金样表版_有关项.pdf'
},
{
id: 2,
title: '新增分说明一下',
content: '保险责任',
score: 0.705,
file: 'Sheet1'
},
{
id: 3,
title: '北京人寿承诺服务各项指标',
content:
'第二代保人寿保险营业执照人及行政管理人天津后勤保障处5.第三代保人寿保险公司承诺为公司客户方案服力及运营提供客户服务力6...',
score: 0.703,
file: '北京人寿承诺服务各项指标有效版102026.pdf'
},
{
id: 4,
title: '-',
content: '# 黄金的分:年金',
score: 0.7,
file: 'loma.md'
},
{
id: 5,
title: '第12讲年金 152',
content: '一、年金基础 153',
score: 0.698,
file: 'loma.md'
}
],
time: new Date()
}
]
} }
}, },
created() {
// 从 路由参数中获取datasetId
this.datasetId = this.$route.query.datasetId
},
methods: { methods: {
sendMessage() { sendMessage() {
if (!this.inputMessage.trim()) return if (!this.inputMessage.trim()) return
// 添加用户问题到消息列表 // 显示骨架屏
this.messages.push({
id: this.messages.length + 1,
type: 'question',
content: this.inputMessage,
time: new Date()
})
// 清空输入框
this.inputMessage = ''
// 模拟加载状态
this.loading = true this.loading = true
// 模拟API请求延迟 // 发送请求
setTimeout(() => { hitTest({
datasetsId: this.datasetId,
query: this.inputMessage
}).then(res => {
const { content } = res.content
this.messages.push(content)
this.loading = false this.loading = false
// 这里可以添加实际的API调用逻辑 })
}, 1000) // 清空输入框
}, this.inputMessage = ''
getTableData() {
// 根据之前的记忆修复getTableData函数确保正确返回Promise
return this.$api
.getDocByPage(this.queryParams)
.then(res => {
return res.data
})
.catch(err => {
console.error('获取数据失败:', err)
return []
})
}, },
formatScore(score) { formatScore(score) {
return score.toFixed(3) return score.toFixed(3)
@@ -117,62 +57,50 @@ export default {
<el-image alt="当前会话尚无记录"></el-image> <el-image alt="当前会话尚无记录"></el-image>
<p>这里会显示命中测试的记录</p> <p>这里会显示命中测试的记录</p>
</div> </div>
<div class="messages" v-else> <div
<div class="messages mb10"
v-for="message in messages" v-else
:key="message.id" v-for="content in messages"
class="message-item" :key="content"
:class="message.type" >
> <!-- 对话区域 -->
<!-- 问题消息 --> <el-card>
<div v-if="message.type === 'question'" class="question-message"> <!-- 头像 会话内容 -->
<div class="message-header"> <div class="flex align-items-c mb10">
<el-avatar size="small" icon="el-icon-user"></el-avatar> <el-avatar :size="40" class="user-avatar" shape="square">
<div class="message-info"> admin
<div class="message-title">用户提问</div> </el-avatar>
</div>
</div>
<div class="message-content">{{ message.content }}</div>
</div>
<!-- 回答消息 --> <div style="margin: 0 10px;">
<div v-else-if="message.type === 'answer'" class="answer-message"> 根据您的问题我们为你提供以下建议
<div class="message-header">
<el-avatar size="small" icon="el-icon-s-custom"></el-avatar>
<div class="message-info">
<div class="message-title">AI 回复</div>
</div>
</div>
<div class="message-content">{{ message.content }}</div>
<!-- 引用内容 -->
<div
class="references"
v-if="message.references && message.references.length"
>
<div
v-for="(ref, index) in message.references"
:key="index"
class="reference-item"
>
<div class="reference-header">
<div class="reference-index">{{ index + 1 }}</div>
<div class="reference-title">{{ ref.title }}</div>
<div class="reference-score">
{{ formatScore(ref.score) }}
</div>
</div>
<div class="reference-content">{{ ref.content }}</div>
<div class="reference-footer">
<el-tag size="mini" type="info">
<i class="el-icon-document"></i> {{ ref.file }}
</el-tag>
</div>
</div>
</div> </div>
</div> </div>
</div>
<el-card
v-for="(contentItem, index) in content"
:key="index"
shadow="hover"
body-style="padding: 10px;backgroundColor: f2f2f2"
class="mb10"
>
<div class="flex justify-content-b">
<!-- 内容详情 -->
<section>
<p class="mb10">{{ contentItem.segment.content }}</p>
<el-tag
type="success"
size="small"
v-for="(keyword, index) in contentItem.segment.keywords"
:key="index"
>
{{ keyword }}
</el-tag>
</section>
<!-- score -->
<span class="score">{{ contentItem.score.toFixed(3) }}</span>
</div>
</el-card>
</el-card>
<!-- 加载状态 --> <!-- 加载状态 -->
<div v-if="loading" class="loading-message"> <div v-if="loading" class="loading-message">
<el-skeleton style="width: 100%" animated> <el-skeleton style="width: 100%" animated>
@@ -205,8 +133,8 @@ export default {
:class="{ active: paramsConfig.searchMode === 'vector' }" :class="{ active: paramsConfig.searchMode === 'vector' }"
> >
<el-radio v-model="paramsConfig.searchMode" label="vector" <el-radio v-model="paramsConfig.searchMode" label="vector"
>向量检索</el-radio >向量检索
> </el-radio>
<p class="option-desc"> <p class="option-desc">
向量检索是一种基于向量相似度的检索方式适用于知识库中的大数据量场景 向量检索是一种基于向量相似度的检索方式适用于知识库中的大数据量场景
</p> </p>
@@ -217,8 +145,8 @@ export default {
:class="{ active: paramsConfig.searchMode === 'fulltext' }" :class="{ active: paramsConfig.searchMode === 'fulltext' }"
> >
<el-radio v-model="paramsConfig.searchMode" label="fulltext" <el-radio v-model="paramsConfig.searchMode" label="fulltext"
>全文检索</el-radio >全文检索
> </el-radio>
<p class="option-desc"> <p class="option-desc">
全文检索是一种基于文本相似度的检索方式适用于知识库中的小数据量场景 全文检索是一种基于文本相似度的检索方式适用于知识库中的小数据量场景
</p> </p>
@@ -229,8 +157,8 @@ export default {
:class="{ active: paramsConfig.searchMode === 'hybrid' }" :class="{ active: paramsConfig.searchMode === 'hybrid' }"
> >
<el-radio v-model="paramsConfig.searchMode" label="hybrid" <el-radio v-model="paramsConfig.searchMode" label="hybrid"
>混合检索</el-radio >混合检索
> </el-radio>
<p class="option-desc"> <p class="option-desc">
混合检索是一种基于向量和文本相似度的检索方式适用于知识库中的中等数据量场景 混合检索是一种基于向量和文本相似度的检索方式适用于知识库中的中等数据量场景
</p> </p>
@@ -245,7 +173,6 @@ export default {
:precision="3" :precision="3"
:step="0.001" :step="0.001"
:min="0" :min="0"
:max="1"
size="small" size="small"
controls-position="right" controls-position="right"
></el-input-number> ></el-input-number>
@@ -265,19 +192,23 @@ export default {
<div class="action-buttons"> <div class="action-buttons">
<el-button size="small" @click="paramsConfig.visible = false" <el-button size="small" @click="paramsConfig.visible = false"
>取消</el-button >取消
> </el-button>
<el-button <el-button
type="primary" type="primary"
size="small" size="small"
@click="paramsConfig.visible = false" @click="paramsConfig.visible = false"
>确定</el-button >确定
> </el-button>
</div> </div>
</div> </div>
<el-button icon="el-icon-setting" size="small" slot="reference" <el-button
>参数设置</el-button icon="el-icon-setting"
> size="small"
slot="reference"
v-if="false"
>参数设置
</el-button>
</el-popover> </el-popover>
<el-input <el-input

View File

@@ -46,38 +46,19 @@
<div> <div>
<!-- <el-button type="primary" size="medium" class="normal-button" @click="jumpEditKnowledge">修改知识库</el-button>--> <!-- <el-button type="primary" size="medium" class="normal-button" @click="jumpEditKnowledge">修改知识库</el-button>-->
<el-button type="primary" size="medium" icon="el-icon-edit-outline" class="primary-button"
@click="jumpEditKnowledge">修改知识库
</el-button>
<el-button type="primary" size="medium" icon="el-icon-plus" class="primary-button"
@click="jumpAddKnowledge">上传知识
</el-button>
<el-button type="primary" icon="el-icon-s-promotion" size="medium" class="line-button"
@click="handleHitTestClick">命中测试
</el-button>
<el-button <el-button
type="primary" type="primary" icon="el-icon-edit-outline" size="medium" class="line-button"
size="medium" @click="handleMetaData">元数据
icon="el-icon-edit-outline" </el-button>
class="primary-button"
@click="jumpEditKnowledge"
>修改知识库</el-button
>
<el-button
type="primary"
size="medium"
icon="el-icon-plus"
class="primary-button"
@click="jumpAddKnowledge"
>上传知识</el-button
>
<!-- <el-button-->
<!-- type="primary"-->
<!-- icon="el-icon-s-promotion"-->
<!-- size="medium"-->
<!-- class="line-button"-->
<!-- @click="handleHitTestClick"-->
<!-- >命中测试</el-button-->
<!-- >-->
<el-button
type="primary"
icon="el-icon-edit-outline"
size="medium"
class="line-button"
@click="handleMetaData"
>元数据</el-button
>
</div> </div>
</div> </div>
<div class="mt20 card-body"> <div class="mt20 card-body">
@@ -195,11 +176,7 @@
</el-drawer> </el-drawer>
<!--元数据--> <!--元数据-->
<el-drawer <el-drawer :title="metaDataDrawer.title" :visible.sync="metaDataDrawer.visible" size="30%">
:title="metaDataDrawer.title"
:visible.sync="metaDataDrawer.visible"
size="30%"
>
<meta-data ref="metaData" :datasetId="$route.query.datasetId"></meta-data> <meta-data ref="metaData" :datasetId="$route.query.datasetId"></meta-data>
</el-drawer> </el-drawer>
</div> </div>
@@ -212,7 +189,7 @@ import {
datasetsExPages, datasetsExPages,
datasetUpdate, datasetUpdate,
getDatasetById getDatasetById
} from '@/api/generatedApi/index' } from '@/api/generatedApi'
import { getUserList } from '@/api/generatedApi/system' import { getUserList } from '@/api/generatedApi/system'
import { import {
documentSourceOptions, documentSourceOptions,