165 lines
4.7 KiB
Vue
165 lines
4.7 KiB
Vue
|
|
<template>
|
|||
|
|
<div class="rag-lab-container">
|
|||
|
|
<el-row :gutter="20">
|
|||
|
|
<!-- 左侧:调试输入 [AC-ASA-05] -->
|
|||
|
|
<el-col :span="10">
|
|||
|
|
<el-card header="调试输入">
|
|||
|
|
<el-form label-position="top">
|
|||
|
|
<el-form-item label="查询 Query">
|
|||
|
|
<el-input
|
|||
|
|
v-model="queryParams.query"
|
|||
|
|
type="textarea"
|
|||
|
|
:rows="4"
|
|||
|
|
placeholder="输入测试问题..."
|
|||
|
|
/>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="知识库范围">
|
|||
|
|
<el-select
|
|||
|
|
v-model="queryParams.kbIds"
|
|||
|
|
multiple
|
|||
|
|
placeholder="请选择知识库"
|
|||
|
|
style="width: 100%"
|
|||
|
|
>
|
|||
|
|
<el-option label="默认知识库" value="default" />
|
|||
|
|
</el-select>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-form-item label="参数配置">
|
|||
|
|
<div class="param-item">
|
|||
|
|
<span class="label">Top-K</span>
|
|||
|
|
<el-input-number v-model="queryParams.params.topK" :min="1" :max="10" />
|
|||
|
|
</div>
|
|||
|
|
<div class="param-item">
|
|||
|
|
<span class="label">Score Threshold</span>
|
|||
|
|
<el-slider
|
|||
|
|
v-model="queryParams.params.threshold"
|
|||
|
|
:min="0"
|
|||
|
|
:max="1"
|
|||
|
|
:step="0.1"
|
|||
|
|
show-input
|
|||
|
|
/>
|
|||
|
|
</div>
|
|||
|
|
</el-form-item>
|
|||
|
|
<el-button type="primary" block @click="handleRun" :loading="loading">
|
|||
|
|
运行实验
|
|||
|
|
</el-button>
|
|||
|
|
</el-form>
|
|||
|
|
</el-card>
|
|||
|
|
</el-col>
|
|||
|
|
|
|||
|
|
<!-- 右侧:实验结果 [AC-ASA-05] -->
|
|||
|
|
<el-col :span="14">
|
|||
|
|
<el-tabs v-model="activeTab" type="border-card">
|
|||
|
|
<el-tab-pane label="召回片段" name="retrieval">
|
|||
|
|
<div v-if="results.retrievalResults.length === 0" class="placeholder-text">
|
|||
|
|
暂无实验数据
|
|||
|
|
</div>
|
|||
|
|
<div v-else class="result-list">
|
|||
|
|
<el-card
|
|||
|
|
v-for="(item, index) in results.retrievalResults"
|
|||
|
|
:key="index"
|
|||
|
|
class="result-card"
|
|||
|
|
shadow="never"
|
|||
|
|
>
|
|||
|
|
<div class="result-header">
|
|||
|
|
<el-tag size="small">Score: {{ item.score.toFixed(4) }}</el-tag>
|
|||
|
|
<span class="source">来源: {{ item.source }}</span>
|
|||
|
|
</div>
|
|||
|
|
<div class="result-content">{{ item.content }}</div>
|
|||
|
|
</el-card>
|
|||
|
|
</div>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
<el-tab-pane label="最终 Prompt" name="prompt">
|
|||
|
|
<div v-if="!results.finalPrompt" class="placeholder-text">
|
|||
|
|
等待实验运行...
|
|||
|
|
</div>
|
|||
|
|
<div v-else class="prompt-view">
|
|||
|
|
<pre><code>{{ results.finalPrompt }}</code></pre>
|
|||
|
|
</div>
|
|||
|
|
</el-tab-pane>
|
|||
|
|
</el-tabs>
|
|||
|
|
</el-col>
|
|||
|
|
</el-row>
|
|||
|
|
</div>
|
|||
|
|
</template>
|
|||
|
|
|
|||
|
|
<script setup lang="ts">
|
|||
|
|
import { ref, reactive } from 'vue'
|
|||
|
|
import { ElMessage } from 'element-plus'
|
|||
|
|
import { runRagExperiment } from '@/api/rag'
|
|||
|
|
|
|||
|
|
const loading = ref(false)
|
|||
|
|
const activeTab = ref('retrieval')
|
|||
|
|
|
|||
|
|
const queryParams = reactive({
|
|||
|
|
query: '',
|
|||
|
|
kbIds: [],
|
|||
|
|
params: {
|
|||
|
|
topK: 3,
|
|||
|
|
threshold: 0.5
|
|||
|
|
}
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
const results = reactive({
|
|||
|
|
retrievalResults: [],
|
|||
|
|
finalPrompt: ''
|
|||
|
|
})
|
|||
|
|
|
|||
|
|
/** 运行实验 [AC-ASA-05] */
|
|||
|
|
const handleRun = async () => {
|
|||
|
|
if (!queryParams.query.trim()) {
|
|||
|
|
ElMessage.warning('请输入查询 Query')
|
|||
|
|
return
|
|||
|
|
}
|
|||
|
|
|
|||
|
|
loading.value = true
|
|||
|
|
try {
|
|||
|
|
const res: any = await runRagExperiment(queryParams)
|
|||
|
|
results.retrievalResults = res.retrievalResults || []
|
|||
|
|
results.finalPrompt = res.finalPrompt || ''
|
|||
|
|
activeTab.value = 'retrieval'
|
|||
|
|
ElMessage.success('实验运行成功')
|
|||
|
|
} catch (err) {
|
|||
|
|
console.error(err)
|
|||
|
|
} finally {
|
|||
|
|
loading.value = false
|
|||
|
|
}
|
|||
|
|
}
|
|||
|
|
</script>
|
|||
|
|
|
|||
|
|
<style scoped>
|
|||
|
|
.rag-lab-container { padding: 20px; }
|
|||
|
|
.param-item {
|
|||
|
|
display: flex;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
.param-item .label {
|
|||
|
|
width: 120px;
|
|||
|
|
font-size: 14px;
|
|||
|
|
}
|
|||
|
|
.placeholder-text { color: #909399; text-align: center; padding: 50px 0; }
|
|||
|
|
.result-list { height: 600px; overflow-y: auto; }
|
|||
|
|
.result-card { margin-bottom: 15px; }
|
|||
|
|
.result-header {
|
|||
|
|
display: flex;
|
|||
|
|
justify-content: space-between;
|
|||
|
|
align-items: center;
|
|||
|
|
margin-bottom: 10px;
|
|||
|
|
}
|
|||
|
|
.source { font-size: 12px; color: #909399; }
|
|||
|
|
.result-content { font-size: 14px; line-height: 1.6; color: #303133; }
|
|||
|
|
.prompt-view {
|
|||
|
|
background-color: #f5f7fa;
|
|||
|
|
padding: 15px;
|
|||
|
|
border-radius: 4px;
|
|||
|
|
max-height: 600px;
|
|||
|
|
overflow-y: auto;
|
|||
|
|
}
|
|||
|
|
.prompt-view pre {
|
|||
|
|
margin: 0;
|
|||
|
|
white-space: pre-wrap;
|
|||
|
|
word-wrap: break-word;
|
|||
|
|
font-family: monospace;
|
|||
|
|
}
|
|||
|
|
</style>
|