feat(evaluation): 改进RAG评估功能并优化UI布局

- 添加json-repair依赖以增强JSON解析容错能力
- 移除评估标签中的Beta标识,提升用户体验
- 增加评估问题生成的最大尝试次数
- 优化评估结果页面布局,将检索配置和评估报告并排显示
- 添加任务中心状态同步功能
- 改进指标显示样式和间距
This commit is contained in:
Wenjie Zhang 2025-12-10 23:12:30 +08:00
parent 9032848450
commit e6cfb16429
4 changed files with 51 additions and 29 deletions

View File

@ -62,6 +62,7 @@ dependencies = [
"aiofiles>=24.1.0",
"aiohttp>=3.9.0",
"deepagents>=0.2.5",
"json-repair>=0.54.0",
]
[tool.ruff]
line-length = 120 # 代码最大行宽

View File

@ -287,7 +287,9 @@ class EvaluationService:
await context.set_progress(25, "生成样本")
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
i0 = random.randrange(len(all_chunks))
e0 = embeddings[i0]
@ -320,20 +322,27 @@ class EvaluationService:
try:
resp = await asyncio.to_thread(llm.call, prompt, False)
content = resp.content if resp else ""
obj = json.loads(content)
import json_repair
obj = json_repair.loads(content)
q = obj.get("query")
a = obj.get("gold_answer")
gids = obj.get("gold_chunk_ids")
if not q or not a or not isinstance(gids, list):
logger.warning(f"Generated JSON missing fields or invalid format: {obj}")
continue
gids = [str(x) for x in gids if str(x) in allowed_ids]
if not gids:
logger.warning("Generated gold_chunk_ids not found in allowed context")
continue
line = {"query": q, "gold_chunk_ids": gids, "gold_answer": a}
f.write(json.dumps(line, ensure_ascii=False) + "\n")
generated += 1
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
meta = {

View File

@ -9,7 +9,7 @@
<a-select
v-model:value="selectedBenchmarkId"
placeholder="请选择评估基准"
style="width: 180px"
style="width: 240px"
@change="onBenchmarkChanged"
:loading="benchmarksLoading"
>
@ -202,48 +202,53 @@
</a-descriptions-item>
</a-descriptions>
<!-- 检索配置 -->
<a-card size="small" title="检索配置" style="margin-bottom: 20px" v-if="selectedResult.retrieval_config">
<div class="json-viewer-container">
<pre class="json-viewer">{{ JSON.stringify(selectedResult.retrieval_config, null, 2) }}</pre>
</div>
</a-card>
<!-- 检索配置和整体评估报告 -->
<a-row :gutter="16" style="margin-bottom: 20px">
<!-- 检索配置 -->
<a-col :span="12" v-if="selectedResult.retrieval_config">
<a-card size="small" title="检索配置">
<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">
<h4 style="margin-bottom: 16px">整体评估报告</h4>
<a-row :gutter="[16, 16]">
<a-col :span="12">
<a-card size="small" title="检索指标">
<!-- 整体评估报告 -->
<a-col :span="selectedResult.retrieval_config ? 12 : 24">
<a-card size="small" title="整体评估报告">
<!-- 检索指标 -->
<div style="margin-bottom: 20px;">
<h5 style="margin-bottom: 12px; font-size: 14px; font-weight: 500;">检索指标</h5>
<div v-if="Object.keys(evaluationStats.retrievalMetrics || {}).length > 0">
<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) }">
{{ formatMetricValue(value) }}
</span>
</div>
</div>
<span v-else class="no-metrics">-</span>
</a-card>
</a-col>
<a-col :span="12">
<a-card size="small" title="答案准确性">
</div>
<!-- 答案准确性 -->
<div>
<h5 style="margin-bottom: 12px; font-size: 14px; font-weight: 500;">答案准确性</h5>
<div class="accuracy-stats">
<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>
</div>
<div class="accuracy-item">
<span class="accuracy-label">准确率</span>
<span class="accuracy-label">准确率</span>
<span class="accuracy-value" :style="{ color: getScoreColor(evaluationStats.answerAccuracy) }">
{{ (evaluationStats.answerAccuracy * 100).toFixed(1) }}%
</span>
</div>
</div>
</a-card>
</a-col>
</a-row>
</div>
</div>
</a-card>
</a-col>
</a-row>
<!-- 详细结果表格 -->
<h4 style="margin-bottom: 16px">详细评估结果</h4>
@ -317,6 +322,7 @@ import { evaluationApi } from '@/apis/knowledge_api';
import ModelSelectorComponent from '@/components/ModelSelectorComponent.vue';
import SearchConfigModal from './SearchConfigModal.vue';
import { SettingOutlined, ReloadOutlined } from '@ant-design/icons-vue';
import { useTaskerStore } from '@/stores/tasker';
const props = defineProps({
databaseId: {
@ -327,6 +333,9 @@ const props = defineProps({
const emit = defineEmits(['switch-to-benchmarks']);
// 使 store
const taskerStore = useTaskerStore();
//
const selectedBenchmarkId = ref(null);
const selectedBenchmark = ref(null);
@ -539,6 +548,8 @@ const startEvaluation = async () => {
if (response.message === 'success') {
message.success('评估任务已开始');
loadEvaluationHistory();
//
taskerStore.loadTasks();
} else {
message.error(response.message || '启动评估失败');
}
@ -1178,6 +1189,7 @@ onMounted(() => {
.metric-label {
font-size: 14px;
padding-right: 18px;
color: var(--gray-700);
}

View File

@ -42,14 +42,14 @@
ref="mindmapSectionRef"
/>
</a-tab-pane>
<a-tab-pane key="evaluation" tab="RAG评估Beta">
<a-tab-pane key="evaluation" tab="RAG评估">
<RAGEvaluationTab
v-if="databaseId"
:database-id="databaseId"
@switch-to-benchmarks="activeTab = 'benchmarks'"
/>
</a-tab-pane>
<a-tab-pane key="benchmarks" tab="评估基准管理Beta">
<a-tab-pane key="benchmarks" tab="评估基准">
<div class="benchmark-management-container">
<div class="benchmark-content">
<EvaluationBenchmarks