feat(evaluation): 改进RAG评估功能并优化UI布局
- 添加json-repair依赖以增强JSON解析容错能力 - 移除评估标签中的Beta标识,提升用户体验 - 增加评估问题生成的最大尝试次数 - 优化评估结果页面布局,将检索配置和评估报告并排显示 - 添加任务中心状态同步功能 - 改进指标显示样式和间距
This commit is contained in:
parent
9032848450
commit
e6cfb16429
@ -62,6 +62,7 @@ dependencies = [
|
|||||||
"aiofiles>=24.1.0",
|
"aiofiles>=24.1.0",
|
||||||
"aiohttp>=3.9.0",
|
"aiohttp>=3.9.0",
|
||||||
"deepagents>=0.2.5",
|
"deepagents>=0.2.5",
|
||||||
|
"json-repair>=0.54.0",
|
||||||
]
|
]
|
||||||
[tool.ruff]
|
[tool.ruff]
|
||||||
line-length = 120 # 代码最大行宽
|
line-length = 120 # 代码最大行宽
|
||||||
|
|||||||
@ -287,7 +287,9 @@ class EvaluationService:
|
|||||||
await context.set_progress(25, "生成样本")
|
await context.set_progress(25, "生成样本")
|
||||||
|
|
||||||
with open(data_file_path, "w", encoding="utf-8") as f:
|
with open(data_file_path, "w", encoding="utf-8") as f:
|
||||||
while generated < count and attempts < count * 2:
|
# Allow more attempts to generate enough questions
|
||||||
|
max_attempts = max(count * 5, 50)
|
||||||
|
while generated < count and attempts < max_attempts:
|
||||||
attempts += 1
|
attempts += 1
|
||||||
i0 = random.randrange(len(all_chunks))
|
i0 = random.randrange(len(all_chunks))
|
||||||
e0 = embeddings[i0]
|
e0 = embeddings[i0]
|
||||||
@ -320,20 +322,27 @@ class EvaluationService:
|
|||||||
try:
|
try:
|
||||||
resp = await asyncio.to_thread(llm.call, prompt, False)
|
resp = await asyncio.to_thread(llm.call, prompt, False)
|
||||||
content = resp.content if resp else ""
|
content = resp.content if resp else ""
|
||||||
obj = json.loads(content)
|
|
||||||
|
import json_repair
|
||||||
|
obj = json_repair.loads(content)
|
||||||
q = obj.get("query")
|
q = obj.get("query")
|
||||||
a = obj.get("gold_answer")
|
a = obj.get("gold_answer")
|
||||||
gids = obj.get("gold_chunk_ids")
|
gids = obj.get("gold_chunk_ids")
|
||||||
if not q or not a or not isinstance(gids, list):
|
if not q or not a or not isinstance(gids, list):
|
||||||
|
logger.warning(f"Generated JSON missing fields or invalid format: {obj}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
gids = [str(x) for x in gids if str(x) in allowed_ids]
|
gids = [str(x) for x in gids if str(x) in allowed_ids]
|
||||||
if not gids:
|
if not gids:
|
||||||
|
logger.warning("Generated gold_chunk_ids not found in allowed context")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
line = {"query": q, "gold_chunk_ids": gids, "gold_answer": a}
|
line = {"query": q, "gold_chunk_ids": gids, "gold_answer": a}
|
||||||
f.write(json.dumps(line, ensure_ascii=False) + "\n")
|
f.write(json.dumps(line, ensure_ascii=False) + "\n")
|
||||||
generated += 1
|
generated += 1
|
||||||
await context.set_progress(0 + int(99 * generated / max(count, 1)), f"已生成 {generated}/{count}")
|
await context.set_progress(0 + int(99 * generated / max(count, 1)), f"已生成 {generated}/{count}")
|
||||||
except Exception:
|
except Exception as e:
|
||||||
|
logger.warning(f"Benchmark generation failed for one item: {e}")
|
||||||
continue
|
continue
|
||||||
|
|
||||||
meta = {
|
meta = {
|
||||||
|
|||||||
@ -9,7 +9,7 @@
|
|||||||
<a-select
|
<a-select
|
||||||
v-model:value="selectedBenchmarkId"
|
v-model:value="selectedBenchmarkId"
|
||||||
placeholder="请选择评估基准"
|
placeholder="请选择评估基准"
|
||||||
style="width: 180px"
|
style="width: 240px"
|
||||||
@change="onBenchmarkChanged"
|
@change="onBenchmarkChanged"
|
||||||
:loading="benchmarksLoading"
|
:loading="benchmarksLoading"
|
||||||
>
|
>
|
||||||
@ -202,48 +202,53 @@
|
|||||||
</a-descriptions-item>
|
</a-descriptions-item>
|
||||||
</a-descriptions>
|
</a-descriptions>
|
||||||
|
|
||||||
<!-- 检索配置 -->
|
<!-- 检索配置和整体评估报告 -->
|
||||||
<a-card size="small" title="检索配置" style="margin-bottom: 20px" v-if="selectedResult.retrieval_config">
|
<a-row :gutter="16" style="margin-bottom: 20px">
|
||||||
<div class="json-viewer-container">
|
<!-- 检索配置 -->
|
||||||
<pre class="json-viewer">{{ JSON.stringify(selectedResult.retrieval_config, null, 2) }}</pre>
|
<a-col :span="12" v-if="selectedResult.retrieval_config">
|
||||||
</div>
|
<a-card size="small" title="检索配置">
|
||||||
</a-card>
|
<div class="json-viewer-container">
|
||||||
|
<pre class="json-viewer">{{ JSON.stringify(selectedResult.retrieval_config, null, 2) }}</pre>
|
||||||
|
</div>
|
||||||
|
</a-card>
|
||||||
|
</a-col>
|
||||||
|
|
||||||
<!-- 整体评估报告 -->
|
<!-- 整体评估报告 -->
|
||||||
<div class="evaluation-report">
|
<a-col :span="selectedResult.retrieval_config ? 12 : 24">
|
||||||
<h4 style="margin-bottom: 16px">整体评估报告</h4>
|
<a-card size="small" title="整体评估报告">
|
||||||
<a-row :gutter="[16, 16]">
|
<!-- 检索指标 -->
|
||||||
<a-col :span="12">
|
<div style="margin-bottom: 20px;">
|
||||||
<a-card size="small" title="检索指标">
|
<h5 style="margin-bottom: 12px; font-size: 14px; font-weight: 500;">检索指标</h5>
|
||||||
<div v-if="Object.keys(evaluationStats.retrievalMetrics || {}).length > 0">
|
<div v-if="Object.keys(evaluationStats.retrievalMetrics || {}).length > 0">
|
||||||
<div v-for="(value, key) in evaluationStats.retrievalMetrics" :key="key" class="report-metric">
|
<div v-for="(value, key) in evaluationStats.retrievalMetrics" :key="key" class="report-metric">
|
||||||
<span class="metric-label">{{ getMetricTitle(key) }}</span>
|
<span class="metric-label">{{ getMetricTitle(key) }}:</span>
|
||||||
<span class="metric-value" :style="{ color: getScoreColor(value) }">
|
<span class="metric-value" :style="{ color: getScoreColor(value) }">
|
||||||
{{ formatMetricValue(value) }}
|
{{ formatMetricValue(value) }}
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
<span v-else class="no-metrics">-</span>
|
<span v-else class="no-metrics">-</span>
|
||||||
</a-card>
|
</div>
|
||||||
</a-col>
|
|
||||||
<a-col :span="12">
|
<!-- 答案准确性 -->
|
||||||
<a-card size="small" title="答案准确性">
|
<div>
|
||||||
|
<h5 style="margin-bottom: 12px; font-size: 14px; font-weight: 500;">答案准确性</h5>
|
||||||
<div class="accuracy-stats">
|
<div class="accuracy-stats">
|
||||||
<div class="accuracy-item">
|
<div class="accuracy-item">
|
||||||
<span class="accuracy-label">正确答案数</span>
|
<span class="accuracy-label">正确答案数:</span>
|
||||||
<span class="accuracy-value">{{ evaluationStats.correctAnswers || 0 }} / {{ evaluationStats.totalQuestions || 0 }}</span>
|
<span class="accuracy-value">{{ evaluationStats.correctAnswers || 0 }} / {{ evaluationStats.totalQuestions || 0 }}</span>
|
||||||
</div>
|
</div>
|
||||||
<div class="accuracy-item">
|
<div class="accuracy-item">
|
||||||
<span class="accuracy-label">准确率</span>
|
<span class="accuracy-label">准确率:</span>
|
||||||
<span class="accuracy-value" :style="{ color: getScoreColor(evaluationStats.answerAccuracy) }">
|
<span class="accuracy-value" :style="{ color: getScoreColor(evaluationStats.answerAccuracy) }">
|
||||||
{{ (evaluationStats.answerAccuracy * 100).toFixed(1) }}%
|
{{ (evaluationStats.answerAccuracy * 100).toFixed(1) }}%
|
||||||
</span>
|
</span>
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
</a-card>
|
</div>
|
||||||
</a-col>
|
</a-card>
|
||||||
</a-row>
|
</a-col>
|
||||||
</div>
|
</a-row>
|
||||||
|
|
||||||
<!-- 详细结果表格 -->
|
<!-- 详细结果表格 -->
|
||||||
<h4 style="margin-bottom: 16px">详细评估结果</h4>
|
<h4 style="margin-bottom: 16px">详细评估结果</h4>
|
||||||
@ -317,6 +322,7 @@ import { evaluationApi } from '@/apis/knowledge_api';
|
|||||||
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
|
||||||
import SearchConfigModal from './SearchConfigModal.vue';
|
import SearchConfigModal from './SearchConfigModal.vue';
|
||||||
import { SettingOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
import { SettingOutlined, ReloadOutlined } from '@ant-design/icons-vue';
|
||||||
|
import { useTaskerStore } from '@/stores/tasker';
|
||||||
|
|
||||||
const props = defineProps({
|
const props = defineProps({
|
||||||
databaseId: {
|
databaseId: {
|
||||||
@ -327,6 +333,9 @@ const props = defineProps({
|
|||||||
|
|
||||||
const emit = defineEmits(['switch-to-benchmarks']);
|
const emit = defineEmits(['switch-to-benchmarks']);
|
||||||
|
|
||||||
|
// 使用任务中心 store
|
||||||
|
const taskerStore = useTaskerStore();
|
||||||
|
|
||||||
// 状态
|
// 状态
|
||||||
const selectedBenchmarkId = ref(null);
|
const selectedBenchmarkId = ref(null);
|
||||||
const selectedBenchmark = ref(null);
|
const selectedBenchmark = ref(null);
|
||||||
@ -539,6 +548,8 @@ const startEvaluation = async () => {
|
|||||||
if (response.message === 'success') {
|
if (response.message === 'success') {
|
||||||
message.success('评估任务已开始');
|
message.success('评估任务已开始');
|
||||||
loadEvaluationHistory();
|
loadEvaluationHistory();
|
||||||
|
// 刷新任务中心的任务列表
|
||||||
|
taskerStore.loadTasks();
|
||||||
} else {
|
} else {
|
||||||
message.error(response.message || '启动评估失败');
|
message.error(response.message || '启动评估失败');
|
||||||
}
|
}
|
||||||
@ -1178,6 +1189,7 @@ onMounted(() => {
|
|||||||
|
|
||||||
.metric-label {
|
.metric-label {
|
||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
|
padding-right: 18px;
|
||||||
color: var(--gray-700);
|
color: var(--gray-700);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -42,14 +42,14 @@
|
|||||||
ref="mindmapSectionRef"
|
ref="mindmapSectionRef"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="evaluation" tab="RAG评估(Beta)">
|
<a-tab-pane key="evaluation" tab="RAG评估">
|
||||||
<RAGEvaluationTab
|
<RAGEvaluationTab
|
||||||
v-if="databaseId"
|
v-if="databaseId"
|
||||||
:database-id="databaseId"
|
:database-id="databaseId"
|
||||||
@switch-to-benchmarks="activeTab = 'benchmarks'"
|
@switch-to-benchmarks="activeTab = 'benchmarks'"
|
||||||
/>
|
/>
|
||||||
</a-tab-pane>
|
</a-tab-pane>
|
||||||
<a-tab-pane key="benchmarks" tab="评估基准管理(Beta)">
|
<a-tab-pane key="benchmarks" tab="评估基准">
|
||||||
<div class="benchmark-management-container">
|
<div class="benchmark-management-container">
|
||||||
<div class="benchmark-content">
|
<div class="benchmark-content">
|
||||||
<EvaluationBenchmarks
|
<EvaluationBenchmarks
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user