fix(backend): 落实 PR #9 评审意见
- 检索调优参数(rrf_k/rerank/rerank_candidates/score_threshold)透传到引擎实际执行 - Recall 去重,避免同一 Note 多 Block 重复导致 Recall 超 1 - RAG 运行改为后台异步执行:创建即 queued + 202,支持取消与 SSE 实时事件 - 数据集元数据校验,坏文件隔离跳过;citation_required 语义修正 - modes 空/重复校验;配置快照记录模型版本与索引元信息 Co-Authored-By: Claude Code <noreply@anthropic.com>
This commit is contained in:
@@ -11,7 +11,7 @@ import json
|
||||
from dataclasses import dataclass, field
|
||||
from pathlib import Path
|
||||
|
||||
from pydantic import ValidationError
|
||||
from pydantic import BaseModel, Field, ValidationError
|
||||
|
||||
from app.config import get_settings
|
||||
from app.contracts import (
|
||||
@@ -34,6 +34,20 @@ class RAGDataset:
|
||||
content_hash: str = ""
|
||||
|
||||
|
||||
class _DatasetMeta(BaseModel):
|
||||
"""Dataset 元数据的最小校验模型。
|
||||
|
||||
list_datasets 用它逐文件校验元信息字段结构,把「合法 JSON 但字段类型错误」
|
||||
(如 cases: 42)这类损坏文件隔离掉,而不是让 len() 抛 TypeError 拖垮整个列表。
|
||||
"""
|
||||
|
||||
dataset_id: str = Field(min_length=1)
|
||||
kind: str = ""
|
||||
version: str = ""
|
||||
description: str = ""
|
||||
cases: list = Field(default_factory=list)
|
||||
|
||||
|
||||
def _datasets_dir() -> Path:
|
||||
return get_settings().benchmark_datasets_path
|
||||
|
||||
@@ -109,6 +123,14 @@ def _dataset_from_raw(raw: dict, raw_bytes: bytes, kind: BenchmarkKind) -> RAGDa
|
||||
f"Dataset case '{parsed.case_id}' must declare expected_note_ids or expected_block_ids.",
|
||||
{"dataset_id": dataset_id, "case_id": parsed.case_id},
|
||||
)
|
||||
# citation_required=true 时必须声明 expected_block_ids,否则无法计算 Citation Hit Rate
|
||||
if parsed.citation_required and not parsed.expected_block_ids:
|
||||
raise ApiError(
|
||||
422,
|
||||
"BENCHMARK_DATASET_INVALID",
|
||||
f"Dataset case '{parsed.case_id}' requires expected_block_ids when citation_required is true.",
|
||||
{"dataset_id": dataset_id, "case_id": parsed.case_id},
|
||||
)
|
||||
cases.append(parsed)
|
||||
|
||||
return RAGDataset(
|
||||
@@ -124,23 +146,25 @@ def _dataset_from_raw(raw: dict, raw_bytes: bytes, kind: BenchmarkKind) -> RAGDa
|
||||
def list_datasets(kind: BenchmarkKind) -> list[BenchmarkDatasetInfo]:
|
||||
"""枚举受控目录下指定 kind 的数据集元信息(不含 Case 内容)。
|
||||
|
||||
个别文件损坏时跳过而非整体失败,保证列表接口健壮;损坏细节由 load_dataset 抛出。
|
||||
逐文件用 _DatasetMeta 校验元信息字段结构,单个损坏文件隔离跳过而非整体失败,
|
||||
保证列表接口健壮;损坏细节由 load_dataset 抛出。
|
||||
"""
|
||||
infos: list[BenchmarkDatasetInfo] = []
|
||||
for path in _dataset_files():
|
||||
try:
|
||||
raw, raw_bytes = _read_json(path)
|
||||
except ApiError:
|
||||
meta = _DatasetMeta.model_validate(raw)
|
||||
except (ApiError, ValidationError):
|
||||
continue
|
||||
if raw.get("kind", kind.value) != kind.value:
|
||||
if meta.kind not in ("", kind.value):
|
||||
continue
|
||||
infos.append(
|
||||
BenchmarkDatasetInfo(
|
||||
dataset_id=raw.get("dataset_id", path.stem),
|
||||
dataset_id=meta.dataset_id,
|
||||
kind=kind,
|
||||
version=str(raw.get("version", "")),
|
||||
description=str(raw.get("description", "")),
|
||||
case_count=len(raw.get("cases", [])),
|
||||
version=meta.version,
|
||||
description=meta.description,
|
||||
case_count=len(meta.cases),
|
||||
content_hash=_content_hash(raw_bytes),
|
||||
)
|
||||
)
|
||||
|
||||
Reference in New Issue
Block a user