1685 lines
61 KiB
Markdown
1685 lines
61 KiB
Markdown
# RAG Eval 平台技术规格说明书
|
||
|
||
| 属性 | 内容 |
|
||
|------|------|
|
||
| 文档版本 | v1.0 |
|
||
| 适用代码库 | `rag-eval/` |
|
||
| 最后更新 | 2026-05-18 |
|
||
| 读者对象 | 后端/前端开发、算法工程师、测试与运维 |
|
||
| 关联文档 | [框架设计稿](./rag-eval-framework-design.md)、[14 组分批规则](./循环测试_14组分批规则.md) |
|
||
|
||
---
|
||
|
||
## 目录
|
||
|
||
1. [概述](#1-概述)
|
||
2. [系统架构](#2-系统架构)
|
||
3. [技术栈与代码组织](#3-技术栈与代码组织)
|
||
4. [核心模块设计](#4-核心模块设计)
|
||
5. [数据模型](#5-数据模型)
|
||
6. [业务流程与时序图](#6-业务流程与时序图)
|
||
7. [评测指标体系](#7-评测指标体系)
|
||
8. [REST API 概览](#8-rest-api-概览)
|
||
9. [前端架构](#9-前端架构)
|
||
10. [部署、运维与安全](#10-部署运维与安全)
|
||
11. [扩展开发指南](#11-扩展开发指南)
|
||
|
||
---
|
||
|
||
## 1. 概述
|
||
|
||
### 1.1 背景与定位
|
||
|
||
RAG Eval(RAG Evaluation Framework)是一套**独立于业务 RAG 服务**的评测平台,最初为 **dagent** 知识库与 Agent 能力构建,但通过 `RAGAdapter` 抽象层保持**平台无关**:任何能通过 HTTP 提供「语义检索」与「Agent 对话」能力的系统均可接入。
|
||
|
||
将评测从生产服务中剥离的设计动机包括:
|
||
|
||
- **资源隔离**:大批量评测(数万条问答、多轮循环)不占用线上推理配额;
|
||
- **技术选型自由**:Judge 模型、Embedding 服务、存储均可独立升级;
|
||
- **可复用**:同一套指标与 UI 可对比不同版本 dagent、不同知识库切片策略或竞品 RAG;
|
||
- **可自动化**:SDK/CLI 适合接入 CI,在发版前做回归门禁。
|
||
|
||
### 1.2 设计目标
|
||
|
||
| 目标 | 实现方式 |
|
||
|------|----------|
|
||
| 检索 + 生成全链路评测 | `EvalRunner` 编排 retrieve → 规则指标 → chat → LLM Judge |
|
||
| 低标注成本 | LLM 自动出题、Faithfulness 等无需 gold chunk |
|
||
| 知识库专项能力 | 单跳/多跳召回、循环压测、MD 问答集解析 |
|
||
| 人机协作 | Web UI 审核出题、查看 Judge 推理明细 |
|
||
| 可观测 | 任务进度、分章节报告、AI 文字解读 |
|
||
|
||
### 1.3 能力矩阵
|
||
|
||
平台在「评测深度」与「使用场景」两个维度上提供六类能力:
|
||
|
||
| 能力 | 典型用户 | 是否依赖 LLM Judge | 主要产出 |
|
||
|------|----------|---------------------|----------|
|
||
| 综合评测(eval_task) | 算法/质量 | 是(部分指标) | `eval_report`、雷达图 |
|
||
| 测试集 LLM 生成 | 数据标注 | 是 | `eval_sample` |
|
||
| 单跳召回测试 | 检索工程师 | 否 | 命中率、余弦相似度 |
|
||
| 多跳召回测试 | 检索工程师 | 可选 | 分跳命中、全链路命中 |
|
||
| QA 生成(qa_gen) | 数据生产 | 是 | `qa_gen_question` |
|
||
| 循环测试(loop) | 大规模压测 | 是 | 多轮问答 + 召回验证 |
|
||
|
||
### 1.4 术语表
|
||
|
||
| 术语 | 含义 |
|
||
|------|------|
|
||
| **切片(Chunk)** | 知识库中文档经切分后的最小检索单元,含 `chunk_id`、headers、正文 |
|
||
| **单跳(Single-hop)** | 一个问题对应一次检索即可回答 |
|
||
| **多跳(Multi-hop)** | 需按跳次(hop)依次检索不同章节/文件 |
|
||
| **Judge** | 使用 LLM(及 Embedding)对回答/上下文打分的组件 |
|
||
| **Adapter** | 对接外部 RAG 平台的 HTTP 客户端封装 |
|
||
| **循环任务(Loop)** | 多轮「生成问答 → 去重 → 单跳验证」的自动化流水线 |
|
||
| **任务组 / 批次** | 大规模循环测试时,按每 100 切片一批、每 3 批一组的规划单位 |
|
||
|
||
---
|
||
|
||
## 2. 系统架构
|
||
|
||
### 2.1 逻辑分层架构
|
||
|
||
系统采用经典三层架构,**评测核心逻辑集中在 Python SDK**,Server 负责持久化、任务调度与 API 暴露,Frontend 负责交互与可视化。
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph Presentation["表现层"]
|
||
UI["React Web UI<br/>Ant Design + ECharts"]
|
||
CLI["rag-eval CLI"]
|
||
end
|
||
|
||
subgraph Application["应用层 (server/)"]
|
||
API["FastAPI Routers<br/>11 模块"]
|
||
SVC["Services<br/>task_service / loop_engine / dedup"]
|
||
DB[("SQLite<br/>aiosqlite + WAL")]
|
||
end
|
||
|
||
subgraph Domain["领域层 (sdk/rag_eval/)"]
|
||
RUN["EvalRunner"]
|
||
ADP["RAGAdapter"]
|
||
JUD["LLMJudge"]
|
||
EVA["Retrieval Evaluators"]
|
||
SJ["single_jump / multi_hop"]
|
||
GEN["DatasetGenerator"]
|
||
end
|
||
|
||
subgraph External["外部系统"]
|
||
DAG["dagent Platform<br/>semantic_search / agent/chat"]
|
||
LLM["OpenAI 兼容 API<br/>DeepSeek / GPT / Qwen"]
|
||
EMB["Embedding API"]
|
||
end
|
||
|
||
UI -->|REST JSON| API
|
||
CLI --> RUN
|
||
API --> SVC
|
||
SVC --> RUN
|
||
SVC --> SJ
|
||
SVC --> DB
|
||
RUN --> ADP
|
||
RUN --> JUD
|
||
RUN --> EVA
|
||
ADP --> DAG
|
||
JUD --> LLM
|
||
JUD --> EMB
|
||
SJ --> DAG
|
||
```
|
||
|
||
**关键设计决策:**
|
||
|
||
1. **Server 通过 `sys.path.insert` 引用 SDK**,而非将 SDK 发布为独立 wheel 后再依赖——开发迭代快,但部署时需保证 `sdk/` 与 `server/` 目录相对位置固定。
|
||
2. **长任务采用「提交后立即返回 + 后台 asyncio 协程」**,状态写入 SQLite,前端轮询进度。
|
||
3. **循环任务暂停/恢复**通过进程内 `_loop_controls` 字典 + `asyncio.Event` 实现,多 Worker 部署时需注意状态不共享(当前为单进程假设)。
|
||
|
||
### 2.2 部署架构
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
subgraph Dev["开发环境"]
|
||
Vite["Vite Dev :5173"]
|
||
Uvicorn["uvicorn :8021"]
|
||
Vite -->|proxy /api| Uvicorn
|
||
end
|
||
|
||
subgraph Prod["生产 / Docker"]
|
||
Nginx["nginx :80"]
|
||
FE["frontend/dist 静态资源"]
|
||
BE["uvicorn server:app"]
|
||
SQL[("rag_eval.db")]
|
||
Nginx --> FE
|
||
Nginx -->|/api| BE
|
||
BE --> SQL
|
||
BE -->|挂载| FE2["StaticFiles 可选单端口"]
|
||
end
|
||
|
||
BE --> DAGENT["dagent 远程集群"]
|
||
BE --> JUDGE["Judge API"]
|
||
```
|
||
|
||
| 组件 | 默认端口 | 说明 |
|
||
|------|----------|------|
|
||
| FastAPI | 8021(`main.py`)/ 8003(文档示例) | 开发时常用 8021 |
|
||
| Vite | 5173 | 仅开发,`vite.config` 代理 API |
|
||
| SQLite 文件 | `server/data/rag_eval.db` | WAL 模式,`busy_timeout=30s` |
|
||
|
||
### 2.3 与 dagent 的集成边界
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
RE["RAG Eval"]
|
||
RE -->|POST /dagent/knowledge/hub/semantic_search_knowledge/detail| SRCH["语义检索"]
|
||
RE -->|POST /dagent/agent/chat SSE| CHAT["Agent 对话"]
|
||
RE -->|知识库文件列表等| META["元数据 API<br/>qa_gen_dagent 使用"]
|
||
|
||
SRCH --> CHUNKS["RetrievedChunk[]"]
|
||
CHAT --> ANSWER["AgentResponse"]
|
||
```
|
||
|
||
`DagentAdapter` **不 import dagent 内部 Python 包**,仅通过 HTTP 契约交互,保证评测框架可独立发版。
|
||
|
||
---
|
||
|
||
## 3. 技术栈与代码组织
|
||
|
||
### 3.1 技术栈
|
||
|
||
| 层级 | 技术 | 版本策略 |
|
||
|------|------|----------|
|
||
| 语言 | Python 3.10+ | Server + SDK |
|
||
| Web 框架 | FastAPI | 异步路由、OpenAPI |
|
||
| 数据库 | SQLite + aiosqlite | 嵌入式,适合单机大规模评测 |
|
||
| HTTP 客户端 | aiohttp(Adapter)、httpx/openai(Judge) | 全异步 |
|
||
| 前端 | React 18、TypeScript、Vite、Ant Design | SPA |
|
||
| 图表 | ECharts(报告雷达图) | — |
|
||
| 包管理 | pip(server)、npm(frontend)、Poetry 可选(sdk) | — |
|
||
|
||
### 3.2 仓库目录详解
|
||
|
||
```
|
||
rag-eval/
|
||
├── docs/ # 文档与数据资产
|
||
│ ├── RAG-Eval平台技术规格说明书.md # 本文档
|
||
│ ├── task_groups_plan.json # 14 组 × 42 批次机器可读规划
|
||
│ ├── exports/ # 全量问答导出
|
||
│ └── …
|
||
├── sdk/rag_eval/
|
||
│ ├── adapters/ base.py, dagent.py
|
||
│ ├── judge/ base.py, openai_compatible.py
|
||
│ ├── evaluators/ retrieval.py(Hit/MRR/NDCG)
|
||
│ ├── dataset/ schema.py, generator.py
|
||
│ ├── single_jump/ parser, mapper, tester, report
|
||
│ ├── multi_hop/ parser, tester, report
|
||
│ ├── runner.py 综合评测编排
|
||
│ ├── report.py EvalReport / SampleResult
|
||
│ └── cli.py run / generate 子命令
|
||
├── server/
|
||
│ ├── main.py 应用入口、路由注册、静态资源
|
||
│ ├── api/ 11 个 APIRouter 模块
|
||
│ ├── service/ 后台任务与循环引擎
|
||
│ └── models/ db.py, schema.sql, 轻量迁移
|
||
└── frontend/src/
|
||
├── pages/ Config, Dataset, Task, Report, SingleJump, MultiHop, QaGen
|
||
└── services/api.ts REST 封装
|
||
```
|
||
|
||
### 3.3 进程内依赖关系
|
||
|
||
```mermaid
|
||
graph TD
|
||
main["server/main.py"] --> api_mod["api/*"]
|
||
main --> loop_eng["service/loop_engine.py"]
|
||
api_mod --> task_svc["service/task_service.py"]
|
||
task_svc --> runner["sdk: EvalRunner"]
|
||
task_svc --> dagent["sdk: DagentAdapter"]
|
||
loop_eng --> qa_api["api/qa_gen 逻辑"]
|
||
loop_eng --> sj_api["api/single_jump 逻辑"]
|
||
runner --> judge["sdk: OpenAICompatibleJudge"]
|
||
```
|
||
|
||
---
|
||
|
||
## 4. 核心模块设计
|
||
|
||
### 4.1 RAGAdapter 抽象层
|
||
|
||
**职责**:屏蔽各 RAG 平台 API 差异,向上提供统一的 `retrieve` 与 `chat`。
|
||
|
||
```python
|
||
# sdk/rag_eval/adapters/base.py(概念接口)
|
||
class RAGAdapter(ABC):
|
||
async def retrieve(query, knowledge_hub_id, top_k=10, **kwargs) -> list[RetrievedChunk]
|
||
async def chat(query, agent_id, **kwargs) -> AgentResponse
|
||
```
|
||
|
||
**`RetrievedChunk` 字段:**
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| `chunk_id` | 切片唯一 ID(dagent 为 `knowledge_md_header_split_id`) |
|
||
| `content` | 正文,用于 Judge 与展示 |
|
||
| `score` | 相似度得分(dagent 由 `1 - cosine_distance` 推导) |
|
||
| `headers` | 切片标题路径 |
|
||
| `file_id` | 所属文件 |
|
||
|
||
**`DagentAdapter.retrieve` 实现要点:**
|
||
|
||
- 请求体:`query`, `org_id`, `top_k`, 可选 `knowledge_hub_id`, `file_id_list`
|
||
- 合并 `standard_answer_results` 与 `related_knowledge_rerank_results_top` 两路结果后截断至 `top_k`
|
||
|
||
**`DagentAdapter.chat` 实现要点:**
|
||
|
||
- SSE 流式解析 `data:` 行,拼接 `message_type` 为 answer 的文本块
|
||
- 记录端到端 `latency_ms`
|
||
|
||
### 4.2 EvalRunner 综合评测编排器
|
||
|
||
`EvalRunner.run()` 对数据集中每个 `EvalSample`:
|
||
|
||
1. 受 `asyncio.Semaphore(concurrency)` 限制并发;
|
||
2. 按 `RunConfig.should_eval()` 决定计算哪些指标;
|
||
3. 检索与生成可独立开关,也支持 `selected_metrics` 精细选择。
|
||
|
||
**单样本流水线(逻辑顺序):**
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[EvalSample] --> B{need_retrieval?}
|
||
B -->|是| C[adapter.retrieve]
|
||
C --> D{有 relevant_chunk_ids?}
|
||
D -->|是| E[hit_rate / mrr / ndcg]
|
||
C --> F{有 reference_answer?}
|
||
F -->|是| G[Judge: context_precision / recall]
|
||
B -->|否| H{need_generation?}
|
||
E --> H
|
||
G --> H
|
||
H -->|是| I[adapter.chat]
|
||
I --> J{无 retrieved_chunks?}
|
||
J -->|是| K[补一次 retrieve]
|
||
J --> L[Judge: faithfulness / relevance / groundedness / correctness]
|
||
K --> L
|
||
L --> M[SampleResult]
|
||
```
|
||
|
||
**报告聚合:**
|
||
|
||
- 各指标算术平均(忽略 `None`);
|
||
- **RAG Score** = Faithfulness、Answer Relevance、Context Precision、Context Recall 四者的**调和均值**(任一项偏低则显著拉低总分);
|
||
- **Hallucination Rate** = Faithfulness < `faithfulness_threshold`(默认 0.7)的样本占比。
|
||
|
||
### 4.3 LLMJudge(OpenAI 兼容实现)
|
||
|
||
**设计原则**:Prompt 为中文;输出要求 JSON;失败时返回 `(0.0, raw_detail)` 便于排查。
|
||
|
||
| 方法 | 算法概要 | 输出范围 |
|
||
|------|----------|----------|
|
||
| `score_faithfulness` | 回答拆声明 → 逐条验证是否可由 context 推出 | 支持声明占比 |
|
||
| `score_relevance` | 由回答反推 3 个问题 → 与原始问题 Embedding 相似度均值 | 0–1 |
|
||
| `score_correctness` | 与 reference 的事实一致性 JSON 评分 | 0–1 |
|
||
| `score_groundedness` | 声明级标注来源切片编号 | 有源声明占比 |
|
||
| `score_context_precision` | 逐 chunk 判定是否对答题有用 | useful/(total) |
|
||
| `score_context_recall` | 参考答案拆陈述 → 是否在检索文中被支持 | supported/total |
|
||
|
||
Faithfulness 两步法降低单次长上下文 Judge 的不稳定性,但会增加 LLM 调用次数(约 1 + N 次 claim 验证)。
|
||
|
||
### 4.4 单跳召回模块(single_jump)
|
||
|
||
| 子模块 | 职责 |
|
||
|--------|------|
|
||
| `parser.py` | 解析 MD:`# 章` → `## section_path` → `Qn` / `**An:**` |
|
||
| `mapper.py` | `section_path` 模糊匹配知识库 `file_id`(exact/contains/fuzzy) |
|
||
| `tester.py` | 并发调用 dagent 检索 API,写 `RecallResult` |
|
||
| `report.py` | 汇总召回率、文件命中率、章节匹配率 |
|
||
|
||
**命中判定**:在 `hit_top_k` 范围内检查 `expected_chunk_id` 或 `file_id` 是否出现在召回列表。
|
||
|
||
### 4.5 多跳召回模块(multi_hop)
|
||
|
||
解析带 `type`、`hops[]` 的多跳问答 MD;对每一跳构造检索 query,分别召回并合并去重;支持 Agent 最终回答与分跳贡献分析。详见 [multi-hop-example.md](./multi-hop-example.md)。
|
||
|
||
### 4.6 循环引擎(loop_engine)
|
||
|
||
**目标**:对指定 `file_ids` 列表,在多轮中持续生成不重复问答并用单跳召回验证质量,直至达到 `max_rounds` / `max_questions` 或连续空轮。
|
||
|
||
**单轮阶段:**
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> FetchExisting: 新一轮开始
|
||
FetchExisting --> QaGen: 拉取历史已批准题
|
||
QaGen --> Dedup: LLM 出题 + 质量分
|
||
Dedup --> SingleJump: Embedding 向量去重
|
||
SingleJump --> WaitComplete: 创建单跳任务
|
||
WaitComplete --> UpdateStats: 轮询完成
|
||
UpdateStats --> CheckTerminate: 更新 loop_round
|
||
CheckTerminate --> FetchExisting: 未终止
|
||
CheckTerminate --> [*]: 达到上限或 stop
|
||
```
|
||
|
||
**控制面:**
|
||
|
||
| 操作 | 机制 |
|
||
|------|------|
|
||
| 暂停 | `pause_event.clear()` + DB `status=paused` |
|
||
| 恢复 | `pause_event.set()` |
|
||
| 停止 | `stop=True` + DB `status=stopped` |
|
||
| 孤儿恢复 | 启动时 `recover_orphaned_loops()` 将异常退出时的 `running` 改为 `paused` |
|
||
|
||
### 4.7 去重服务(dedup)
|
||
|
||
循环任务中默认使用 **Embedding 余弦相似度**(非 LLM)在 section 内及全局(`global_dedup`)检测重复题,阈值可配。历史题目从 `qa_gen_question` 表加载,支持跨任务全局去重以应对 14 组 42 批次大规模跑数。
|
||
|
||
---
|
||
|
||
## 5. 数据模型
|
||
|
||
### 5.1 ER 关系概览
|
||
|
||
```mermaid
|
||
erDiagram
|
||
platform_config ||--o{ eval_task : uses
|
||
judge_config ||--o{ eval_task : uses
|
||
eval_dataset ||--o{ eval_sample : contains
|
||
eval_dataset ||--o{ eval_task : ""
|
||
eval_task ||--o{ eval_result : produces
|
||
eval_task ||--|| eval_report : summarizes
|
||
|
||
loop_task ||--o{ loop_round : has
|
||
loop_round }o--|| qa_gen_task : links
|
||
loop_round }o--|| single_jump_task : links
|
||
qa_gen_task ||--o{ qa_gen_question : contains
|
||
single_jump_task ||--o{ single_jump_result : contains
|
||
|
||
multi_hop_task ||--o{ multi_hop_result : contains
|
||
multi_hop_gen_task ||--o{ multi_hop_gen_question : contains
|
||
prompt_template ||--o{ qa_gen_task : optional
|
||
```
|
||
|
||
### 5.2 核心表说明
|
||
|
||
| 表名 | 用途 | 关键字段 |
|
||
|------|------|----------|
|
||
| `platform_config` | dagent 连接信息 | `base_url`, `org_id`, `token` |
|
||
| `judge_config` | Judge + Embedding | `model`, `embed_model` |
|
||
| `eval_dataset` / `eval_sample` | 综合评测数据集 | `relevant_chunk_ids` JSON |
|
||
| `eval_task` / `eval_result` / `eval_report` | 综合评测任务与结果 | `status`, `progress`, 各指标均值 |
|
||
| `single_jump_task` / `single_jump_result` | 单跳召回 | `retrieved` JSON, `is_file_hit` |
|
||
| `multi_hop_task` / `multi_hop_result` | 多跳召回 | `hops`, `actual_hops` JSON |
|
||
| `qa_gen_task` / `qa_gen_question` | 出题与审核 | `status`, `embedding`, `chunk_id` |
|
||
| `loop_task` / `loop_round` | 循环压测 | `current_round`, 累计统计字段 |
|
||
| `multi_hop_gen_task` | 多跳 QA 生成 | `source` file/dagent |
|
||
| `prompt_template` | Prompt 管理 | `content` |
|
||
|
||
### 5.3 任务状态机(综合评测)
|
||
|
||
```mermaid
|
||
stateDiagram-v2
|
||
[*] --> pending: 创建任务
|
||
pending --> running: 后台开始
|
||
running --> done: EvalRunner 成功
|
||
running --> failed: 异常
|
||
done --> [*]
|
||
failed --> [*]
|
||
```
|
||
|
||
### 5.4 迁移策略
|
||
|
||
`models/db.py` 中 `_run_migrations()` 对已有库做**向前兼容**列追加(`PRAGMA table_info` 检测),避免破坏已有 `rag_eval.db`。新环境执行 `schema.sql` 全量建表。
|
||
|
||
---
|
||
|
||
## 6. 业务流程与时序图
|
||
|
||
### 6.1 综合评测任务(Web → Server → SDK)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
autonumber
|
||
actor User as 用户
|
||
participant UI as React UI
|
||
participant API as task.py
|
||
participant SVC as task_service
|
||
participant DB as SQLite
|
||
participant RUN as EvalRunner
|
||
participant ADP as DagentAdapter
|
||
participant JUD as LLMJudge
|
||
participant DG as dagent
|
||
|
||
User->>UI: 新建评测任务
|
||
UI->>API: POST /api/task/run
|
||
API->>DB: INSERT eval_task (pending)
|
||
API-->>UI: task_id
|
||
API->>SVC: asyncio.create_task(run_eval_task)
|
||
|
||
SVC->>DB: status=running
|
||
SVC->>DB: 加载 dataset / configs
|
||
SVC->>RUN: run(dataset, RunConfig)
|
||
|
||
loop 每个 EvalSample(并发 N)
|
||
RUN->>ADP: retrieve(question)
|
||
ADP->>DG: semantic_search
|
||
DG-->>ADP: chunks
|
||
RUN->>JUD: context_precision / recall
|
||
JUD-->>RUN: scores
|
||
RUN->>ADP: chat(question)
|
||
ADP->>DG: agent/chat SSE
|
||
DG-->>ADP: answer stream
|
||
RUN->>JUD: faithfulness / relevance / ...
|
||
RUN->>DB: INSERT eval_result(批量或逐条)
|
||
end
|
||
|
||
RUN-->>SVC: EvalReport
|
||
SVC->>JUD: 生成 interpretation 文案
|
||
SVC->>DB: INSERT eval_report, status=done
|
||
UI->>API: GET /api/report/{task_id}
|
||
API-->>UI: 雷达图 + 明细
|
||
```
|
||
|
||
### 6.2 Faithfulness 评判(两步法)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant RUN as EvalRunner
|
||
participant JUD as OpenAICompatibleJudge
|
||
participant LLM as Judge LLM
|
||
|
||
RUN->>JUD: score_faithfulness(answer, contexts)
|
||
JUD->>LLM: Prompt: 分解为原子声明 JSON
|
||
LLM-->>JUD: ["声明1", "声明2", ...]
|
||
loop 每条声明
|
||
JUD->>LLM: Prompt: 声明是否可由资料推出 yes/no
|
||
LLM-->>JUD: yes | no
|
||
end
|
||
JUD-->>RUN: score = supported_count / total_claims
|
||
```
|
||
|
||
### 6.3 单跳召回测试
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
actor User as 用户
|
||
participant API as single_jump.py
|
||
participant P as MD Parser
|
||
participant M as FileMapper
|
||
participant T as SingleJumpTester
|
||
participant DG as dagent
|
||
|
||
User->>API: POST /api/single-jump/task (multipart MD)
|
||
API->>P: parse(md_content)
|
||
P-->>API: sections[] + qa_pairs[]
|
||
API->>M: map(section_path → file_id)
|
||
M->>DG: 文件列表 / 路径匹配
|
||
API->>DB: INSERT task + results (pending)
|
||
|
||
loop 每条 QA(并发)
|
||
T->>DG: semantic_search(question)
|
||
DG-->>T: retrieved[]
|
||
T->>T: 计算 is_file_hit, chunk_hit, cosine_sim
|
||
T->>DB: UPDATE single_jump_result
|
||
end
|
||
|
||
API->>DB: task status=done
|
||
User->>API: GET summary / results
|
||
```
|
||
|
||
### 6.4 循环测试(单轮)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant LE as loop_engine
|
||
participant DB as SQLite
|
||
participant QG as qa_gen 流程
|
||
participant DD as dedup
|
||
participant SJ as single_jump 流程
|
||
participant DG as dagent
|
||
|
||
LE->>DB: 读取 loop_task / 历史轮次
|
||
LE->>DB: 汇总已批准问题(可选 global_dedup)
|
||
LE->>QG: 按 file_ids 拉切片 → LLM 出题
|
||
QG->>DB: qa_gen_question (pending)
|
||
LE->>DD: Embedding 相似度过滤
|
||
DD->>DB: status=approved | rejected
|
||
LE->>SJ: 生成 MD → 创建 single_jump_task
|
||
SJ->>DG: 批量检索
|
||
SJ->>DB: single_jump_result
|
||
LE->>DB: 更新 loop_round 统计
|
||
LE->>LE: 检查 max_rounds / max_questions / stop
|
||
```
|
||
|
||
### 6.5 LLM 自动出题(测试集 / qa_gen)
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant API as dataset.py / qa_gen.py
|
||
participant GEN as DatasetGenerator
|
||
participant ADP as DagentAdapter
|
||
participant JUD as LLMJudge
|
||
participant DG as dagent
|
||
|
||
API->>ADP: 获取文件切片列表
|
||
ADP->>DG: 知识库 API
|
||
loop 每个切片
|
||
GEN->>JUD: 根据 chunk 内容生成 Q&A
|
||
JUD-->>GEN: question + answer
|
||
GEN->>JUD: quality_score 评估
|
||
end
|
||
GEN->>API: EvalSample[] 或 qa_gen_question
|
||
API->>DB: 持久化
|
||
```
|
||
|
||
### 6.6 配置管理流程
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
actor User as 用户
|
||
participant UI as Config 页面
|
||
participant API as config.py
|
||
participant DB as SQLite
|
||
|
||
User->>UI: 添加平台配置 / Judge 配置
|
||
UI->>API: POST /api/config/platform | /judge
|
||
API->>DB: INSERT(API Key 明文存库,需注意权限)
|
||
API-->>UI: config_id
|
||
Note over UI,DB: 后续所有任务通过 config_id 引用
|
||
```
|
||
|
||
---
|
||
|
||
## 7. 评测指标体系
|
||
|
||
### 7.1 检索层(规则指标)
|
||
|
||
设召回序列为 $[c_1, \ldots, c_K]$,相关集合为 $R$。
|
||
|
||
| 指标 | 公式 / 定义 | 需要标注 |
|
||
|------|-------------|----------|
|
||
| **Hit Rate@K** | 若 $R \cap \{c_1..c_K\} \neq \emptyset$ 则为 1,否则 0 | `relevant_chunk_ids` |
|
||
| **MRR@K** | $\frac{1}{\text{rank}}$,rank 为第一个相关 chunk 的位置 | 同上 |
|
||
| **NDCG@K** | 按相关度折损的 DCG 归一化 | 同上,实现见 `evaluators/retrieval.py` |
|
||
|
||
### 7.2 检索层(LLM 指标)
|
||
|
||
| 指标 | 含义 | 典型问题 |
|
||
|------|------|----------|
|
||
| **Context Precision** | 召回切片中有多少比例对回答真正有用 | 噪声切片多 → 低 |
|
||
| **Context Recall** | 参考答案中的事实有多少能在召回中找到 | 切片不全 → 低 |
|
||
|
||
### 7.3 生成层
|
||
|
||
| 指标 | 含义 | 低分常见原因 |
|
||
|------|------|--------------|
|
||
| **Faithfulness** | 回答是否可仅从检索内容推出 | 幻觉、过度推断 |
|
||
| **Answer Relevance** | 回答是否切题 | 答非所问、冗长 |
|
||
| **Answer Correctness** | 与参考答案事实一致性 | 错误细节 |
|
||
| **Groundedness** | 声明是否有明确切片来源 | 无出处断言 |
|
||
|
||
### 7.4 综合指标
|
||
|
||
**RAG Score(调和均值):**
|
||
|
||
$$
|
||
\text{RAG Score} = \frac{n}{\sum_{i=1}^{n} \frac{1}{s_i}}
|
||
$$
|
||
|
||
其中 $s_i$ 为 Faithfulness、Answer Relevance、Context Precision、Context Recall 中非空且 $>0$ 的项。
|
||
|
||
**Hallucination Rate:**
|
||
|
||
$$
|
||
\text{Hallucination Rate} = \frac{|\{i : \text{faithfulness}_i < \tau\}|}{n}, \quad \tau = 0.7
|
||
$$
|
||
|
||
### 7.5 单跳召回专用指标
|
||
|
||
| 指标 | 计算 |
|
||
|------|------|
|
||
| 召回率 | 有检索结果的问题数 / 总问题数 |
|
||
| 文件命中率 | `is_file_hit=1` / 有检索结果数 |
|
||
| 切片命中率 | `expected_chunk_id` 出现在 top-K |
|
||
| 平均余弦相似度 | 召回列表 $1 - \text{cosine\_distance}$ 的统计 |
|
||
| 章节匹配率 | 成功映射 file_id 的 section 数 / 总 section 数 |
|
||
|
||
### 7.6 指标解读阈值(建议)
|
||
|
||
| 指标 | 优秀 | 良好 | 需关注 |
|
||
|------|------|------|--------|
|
||
| Hit Rate | > 0.90 | 0.70–0.90 | < 0.70 |
|
||
| MRR | > 0.80 | 0.60–0.80 | < 0.60 |
|
||
| Faithfulness | > 0.85 | 0.70–0.85 | < 0.70 |
|
||
| RAG Score | > 0.80 | 0.65–0.80 | < 0.65 |
|
||
| Hallucination Rate | < 5% | 5–15% | > 15% |
|
||
|
||
---
|
||
|
||
## 8. REST API 概览
|
||
|
||
所有路由前缀为 `/api`。下表为模块级索引,完整参数见 Swagger `/docs`。
|
||
|
||
| 前缀 | 模块文件 | 职责 |
|
||
|------|----------|------|
|
||
| `/api/config` | `config.py` | 平台 / Judge CRUD |
|
||
| `/api/dataset` | `dataset.py` | 数据集、样本、导入、LLM 生成 |
|
||
| `/api/task` | `task.py` | 综合评测任务 |
|
||
| `/api/report` | `report.py` | 报告与样本明细 |
|
||
| `/api/single-jump` | `single_jump.py` | 单跳任务与结果 |
|
||
| `/api/multi-hop` | `multi_hop.py` | 多跳任务 |
|
||
| `/api/qa-gen` | `qa_gen.py` + `qa_gen_dagent.py` | 出题(文件 / dagent 数据源) |
|
||
| `/api/multi-hop-gen` | `multi_hop_gen.py` | 多跳 QA 生成 |
|
||
| `/api/loop` | `loop.py` | 循环任务、暂停/恢复、导出 |
|
||
| `/api/prompt-template` | `prompt_template.py` | Prompt CRUD |
|
||
| `/api/health` | `main.py` | 健康检查 |
|
||
|
||
**异步任务通用约定:**
|
||
|
||
- 创建接口返回 `task_id`;
|
||
- `GET .../task/{id}` 含 `status`, `progress`, `total`, `error_message`;
|
||
- 完成后通过 report 或 export 接口拉取结果。
|
||
|
||
---
|
||
|
||
## 9. 前端架构
|
||
|
||
### 9.1 路由与页面
|
||
|
||
| 路径 | 页面 | 功能 |
|
||
|------|------|------|
|
||
| `/config` | Config | 平台 / Judge 配置 |
|
||
| `/dataset` | Dataset | 列表、详情、导入、生成 |
|
||
| `/task` | Task | 创建综合评测 |
|
||
| `/report/:taskId` | Report | 雷达图、明细、Judge 下钻 |
|
||
| `/single-jump` | SingleJump | 上传 MD、进度、分章节报告 |
|
||
| `/multi-hop` | MultiHop | 多跳测试 |
|
||
| `/qa-gen` | QaGen | 出题任务与审核 |
|
||
|
||
### 9.2 数据流
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
Pages["pages/*"] --> API["services/api.ts"]
|
||
API --> HTTP["services/http.ts<br/>axios baseURL=/api"]
|
||
HTTP --> BE["FastAPI"]
|
||
```
|
||
|
||
开发模式 Vite 将 `/api` 代理至 `http://127.0.0.1:8021`;生产构建后由同源 FastAPI 或 nginx 转发。
|
||
|
||
### 9.3 报告可视化
|
||
|
||
`Report` 页使用 ECharts 雷达图展示各维度均值;表格列出 `eval_result` 逐条指标;Modal 展示 `judge_detail` JSON 供人工复核 LLM 评判理由。
|
||
|
||
---
|
||
|
||
## 10. 部署、运维与安全
|
||
|
||
### 10.1 Docker Compose
|
||
|
||
`docker-compose.yml` 定义 `server` 与 `frontend` 服务;`Dockerfile.server` 安装 Python 依赖并启动 uvicorn;`Dockerfile.frontend` 多阶段构建静态资源。
|
||
|
||
### 10.2 数据备份
|
||
|
||
- **核心资产**:`server/data/rag_eval.db`(循环测试后可达数 GB);
|
||
- **导出副本**:`docs/exports/` 下 MD/JSON;
|
||
- **规划文件**:`docs/task_groups_plan.json`。
|
||
|
||
建议定期备份 DB 与 exports,避免仅依赖单机 SQLite。
|
||
|
||
### 10.3 性能调优
|
||
|
||
| 旋钮 | 位置 | 说明 |
|
||
|------|------|------|
|
||
| `concurrency` | `RunConfig` / 任务表单 | 增大可加速,受 dagent/Judge 限流约束 |
|
||
| `top_k` | 检索配置 | 影响检索耗时与 Context 指标 |
|
||
| SQLite WAL | `db.py` | 已启用,避免长事务锁库 |
|
||
| 循环 `global_dedup` | loop 创建参数 | 真时跨任务查重 SQL 较重 |
|
||
|
||
### 10.4 安全注意
|
||
|
||
- `judge_config.api_key` 存于 SQLite **明文**,需限制 DB 文件权限;
|
||
- CORS 当前为 `allow_origins=["*"]`,公网部署应收紧;
|
||
- 导出脚本与 API 可能含完整问答与切片内容,注意数据分级。
|
||
|
||
### 10.5 运维脚本
|
||
|
||
| 脚本 | 用途 |
|
||
|------|------|
|
||
| `server/scripts/export_loop_all_groups.py` | 从 DB 导出 14 组全量问答 |
|
||
| `server/scripts/batch_create_tasks.py` | 批量创建循环任务 |
|
||
| `server/scripts/export_loop_batches_recall_md.py` | 导出召回 MD |
|
||
|
||
---
|
||
|
||
## 11. 扩展开发指南
|
||
|
||
### 11.1 新增 RAG 平台 Adapter
|
||
|
||
1. 在 `sdk/rag_eval/adapters/` 新建 `my_platform.py`;
|
||
2. 实现 `retrieve` / `chat`,映射为 `RetrievedChunk` / `AgentResponse`;
|
||
3. 在 `platform_config.type` 增加枚举,Server 侧 `task_service` 按 type 实例化。
|
||
|
||
### 11.2 新增评测指标
|
||
|
||
1. 在 `LLMJudge` 增加 `score_xxx` 方法;
|
||
2. `EvalRunner._eval_sample` 中调用;
|
||
3. `SampleResult` / `eval_result` 表增加列 + 迁移;
|
||
4. 前端 Report 增加展示卡片。
|
||
|
||
### 11.3 新增 API 模块
|
||
|
||
1. `server/api/` 新建路由文件;
|
||
2. `main.py` `include_router`;
|
||
3. `frontend/services/api.ts` 增加客户端方法。
|
||
|
||
### 11.4 测试建议
|
||
|
||
- **单元测试**:`evaluators/retrieval.py` 规则指标边界;`parser.py` MD 样例;
|
||
- **集成测试**:Mock dagent HTTP,跑通 `EvalRunner` 单样本;
|
||
- **回归**:固定小数据集 + Judge 模型版本记录在报告中。
|
||
|
||
---
|
||
|
||
## 12. 数据库字段字典(详表)
|
||
|
||
本章列出主要表的字段语义,便于写 SQL 报表、对接 BI 或二次开发。JSON 列在 SQLite 中以 `TEXT` 存储,应用层 `json.loads` 解析。
|
||
|
||
### 12.1 platform_config
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| id | TEXT PK | UUID hex |
|
||
| name | TEXT | 展示名称,如「dagent 生产」 |
|
||
| type | TEXT | 默认 `dagent`,预留其他 Adapter |
|
||
| base_url | TEXT | 如 `https://dagent.d-robotics.cc` |
|
||
| org_id | TEXT | 组织 ID,检索与对话必传 |
|
||
| token | TEXT | 可选 Bearer Token |
|
||
| created_at | TEXT | ISO 时间戳 |
|
||
|
||
### 12.2 judge_config
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| embed_base_url | TEXT | 为空则回退 base_url |
|
||
| embed_api_key | TEXT | 为空则回退 api_key |
|
||
| embed_model | TEXT | 去重与 Answer Relevance 使用 |
|
||
|
||
### 12.3 eval_sample
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| relevant_chunk_ids | TEXT | JSON 数组,Hit/MRR/NDCG 必需 |
|
||
| knowledge_hub_id | TEXT | 样本级 hub,可与任务级叠加 |
|
||
| source_file_id | TEXT | 可选,追溯出题文件 |
|
||
| metadata | TEXT | 扩展 JSON |
|
||
|
||
### 12.4 eval_task
|
||
|
||
| 字段 | 类型 | 说明 |
|
||
|------|------|------|
|
||
| eval_retrieval | INTEGER | 1/0,与 selected_metrics 二选一逻辑在 RunConfig |
|
||
| eval_generation | INTEGER | 1/0 |
|
||
| selected_metrics | TEXT | JSON 数组,非空时覆盖上述开关 |
|
||
| file_id_list | TEXT | 检索时限制文件范围 |
|
||
| concurrency | INTEGER | 默认 3 |
|
||
| progress / total | INTEGER | 已完成样本数 / 总样本数 |
|
||
|
||
### 12.5 eval_result
|
||
|
||
逐样本存储完整评判:`retrieved_chunks` 为正文数组 JSON;`judge_detail` 为各 LLM 指标原始返回,供 UI 下钻。
|
||
|
||
### 12.6 single_jump_result
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| retrieved | 原始 dagent 返回 JSON 数组,保留 `cosine_distance_1` |
|
||
| is_file_hit | 预期 file_id 是否出现在召回列表 |
|
||
| is_chunk_hit | expected_chunk_id 是否在 hit_top_k 内 |
|
||
| chunk_hit_rank | 命中时排名,从 1 起 |
|
||
| raw_chunk_headers | 用于与 qa_gen 的 chunk_headers 对齐展示 |
|
||
|
||
### 12.7 qa_gen_question
|
||
|
||
| 字段 | 说明 |
|
||
|------|------|
|
||
| status | pending / approved / rejected |
|
||
| dup_of | 若重复,指向主题库中已有题 id |
|
||
| dup_similarity | 向量相似度 |
|
||
| embedding | 存 JSON 向量,去重用 |
|
||
| chunk_content_preview | 前 500 字,审核时免查库 |
|
||
|
||
### 12.8 loop_task
|
||
|
||
累计字段 `total_generated`、`total_approved`、`total_recalled`、`total_file_hit` 等由引擎每轮刷新;`expected_chunk_count` 与分批规划对齐,用于校验从 dagent 拉取的切片数量是否完整。`global_dedup=1` 时去重范围扩大到全部已批准题目。
|
||
|
||
### 12.9 loop_round
|
||
|
||
每轮关联 `qa_gen_task_id` 与 `single_jump_task_id`,`status` 跟踪本轮是进行中还是已完成,便于服务重启后从断点阶段继续。
|
||
|
||
---
|
||
|
||
## 13. REST API 端点详解
|
||
|
||
以下按模块列出常用端点;HTTP 方法后的路径均相对于 `/api`。
|
||
|
||
### 13.1 配置 `/config`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/config/platform` | 列表 |
|
||
| POST | `/config/platform` | 创建,body: name, base_url, org_id, token? |
|
||
| DELETE | `/config/platform/{id}` | 删除 |
|
||
| GET | `/config/judge` | Judge 列表 |
|
||
| POST | `/config/judge` | 创建 Judge + Embedding 配置 |
|
||
|
||
### 13.2 数据集 `/dataset`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| GET | `/dataset/list` | 所有数据集 |
|
||
| GET | `/dataset/{id}` | 含样本列表 |
|
||
| POST | `/dataset/create` | name, description |
|
||
| POST | `/dataset/sample/add` | 单条样本 |
|
||
| POST | `/dataset/import` | multipart JSON 文件 |
|
||
| POST | `/dataset/generate` | 触发 LLM 生成,返回 generate_task_id |
|
||
| GET | `/dataset/generate/{id}` | 生成进度 |
|
||
| GET | `/dataset/chunks-preview` | 预览 hub 下切片规模 |
|
||
|
||
### 13.3 评测任务 `/task` 与报告 `/report`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | `/task/run` | 创建并异步执行 eval_task |
|
||
| GET | `/task/list` | 任务列表含 status、progress |
|
||
| GET | `/report/{task_id}` | 聚合指标 + interpretation |
|
||
| GET | `/report/{task_id}/items` | 分页样本明细 |
|
||
|
||
### 13.4 单跳 `/single-jump`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | `/single-jump/task` | Form: env_url, org_id, md_file, top_k, agent_id… |
|
||
| POST | `/single-jump/task/batch` | 批量文件 |
|
||
| GET | `/single-jump/task/{id}/summary` | 汇总指标 |
|
||
| GET | `/single-jump/task/{id}/sections` | 章节树 |
|
||
| GET | `/single-jump/task/{id}/results` | ?section= 过滤 |
|
||
| GET | `/single-jump/task/{id}/export-failed-md` | 导出失败题为 MD |
|
||
|
||
### 13.5 循环 `/loop`
|
||
|
||
| 方法 | 路径 | 说明 |
|
||
|------|------|------|
|
||
| POST | `/loop/task` | Form 创建循环任务 |
|
||
| POST | `/loop/task/{id}/pause` | 暂停 |
|
||
| POST | `/loop/task/{id}/resume` | 恢复 |
|
||
| POST | `/loop/task/{id}/stop` | 停止 |
|
||
| GET | `/loop/task/{id}/export` | ?format=md\|json&category=all\|hit\|… |
|
||
|
||
### 13.6 问题生成 `/qa-gen`
|
||
|
||
`qa_gen.py` 与 `qa_gen_dagent.py` **共享前缀** `/api/qa-gen`:前者上传 MD/文件列表出题,后者从 dagent 拉取目录树与切片。注意路由注册顺序避免路径冲突。
|
||
|
||
| 能力 | 端点示例 |
|
||
|------|----------|
|
||
| dagent 文件树 | GET `/qa-gen/dagent/tree?org_id=` |
|
||
| 创建出题任务 | POST `/qa-gen/task/from-dagent` |
|
||
| 审核 | POST `/qa-gen/question/{id}/approve` |
|
||
|
||
---
|
||
|
||
## 14. 循环引擎深度解析
|
||
|
||
### 14.1 启动与断点续跑
|
||
|
||
`run_loop_task` 被 `asyncio.create_task` 调用后立即返回。引擎启动时查询 `loop_round` 最后一条记录:
|
||
|
||
- 若上一轮 `single_jump` 未完成,则继续等待或重试;
|
||
- 若已完成且未达终止条件,则 `current_round += 1` 并新建 `loop_round` 行。
|
||
|
||
服务崩溃后,`recover_orphaned_loops` 将 `status=running` 改为 `paused`,人工确认后调 resume。
|
||
|
||
### 14.2 单轮各阶段耗时分布
|
||
|
||
```mermaid
|
||
gantt
|
||
title 典型单轮耗时构成(示意)
|
||
dateFormat X
|
||
axisFormat %s
|
||
section 出题
|
||
拉切片与LLM生成 :a1, 0, 40
|
||
section 去重
|
||
Embedding批量比对 :a2, after a1, 15
|
||
section 召回
|
||
单跳并发检索 :a3, after a2, 30
|
||
```
|
||
|
||
实际占比取决于 `concurrency`、切片数、Judge 模型速度及 dagent 集群负载。组 1 批次 1 在远程环境上常见数千条 approved 题,单轮可能持续数十分钟。
|
||
|
||
### 14.3 终止条件
|
||
|
||
| 条件 | 行为 |
|
||
|------|------|
|
||
| `max_rounds > 0` 且达到 | 正常结束 |
|
||
| `max_questions > 0` 且累计 approved 达到 | 正常结束 |
|
||
| 连续若干轮无新 approved | 可配置空轮退出(引擎内 consecutive_empty_rounds) |
|
||
| 用户 stop | 立即写 stopped |
|
||
| 异常 | status=failed,error_message 记录栈信息 |
|
||
|
||
### 14.4 与单跳模块的协作
|
||
|
||
循环引擎不直接调用 `SingleJumpTester` 类,而是通过内部函数或 API 层复用「生成 MD → 创建 task → 轮询完成」流程,保证与手动单跳使用同一套解析与命中逻辑,避免统计口径不一致。
|
||
|
||
---
|
||
|
||
## 15. 单跳 MD 格式与 FileMapper
|
||
|
||
### 15.1 MD 结构约定
|
||
|
||
```markdown
|
||
# 第1章 章节展示标题
|
||
|
||
## path/to/file.md / doc_name
|
||
> 原始切片标题: 完整 headers 路径
|
||
# 1. doc_name_Document
|
||
> 由 LLM 自动生成的问答对
|
||
|
||
## Q1: 问题正文?
|
||
|
||
**A1:** 参考答案正文
|
||
|
||
> chunk_id: abc123...
|
||
```
|
||
|
||
- `# 第N章`:人类可读章节名;
|
||
- `##` 行:与 `FileMapper` 键一致,通常为 `file_name / doc_name`;
|
||
- `Q`/`A` 编号:解析器正则提取;
|
||
- 可选 `chunk_id` 行:用于切片级命中。
|
||
|
||
`loop_recall_md.py` 与 `single_jump/parser.py` 共用同一套生成规则,保证「循环内导出 → 再导入单跳」不丢字段。
|
||
|
||
### 15.2 FileMapper 匹配策略
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
SP[section_path 来自 MD] --> E{精确匹配 file_path?}
|
||
E -->|是| OK[file_id]
|
||
E -->|否| C{路径包含?}
|
||
C -->|是| OK
|
||
C -->|否| F[模糊相似度]
|
||
F -->|高于阈值| OK
|
||
F -->|否| MISS[unmatched]
|
||
```
|
||
|
||
未匹配章节仍参与召回测试,但 `is_file_hit` 统计为未命中文件;报告中的「章节匹配率」帮助发现路径规范问题。
|
||
|
||
---
|
||
|
||
## 16. LLM Judge 调用链与成本
|
||
|
||
### 16.1 单样本 Judge 调用次数估算
|
||
|
||
设回答被拆为 $N$ 条声明,检索上下文分为 $K$ 个 chunk:
|
||
|
||
| 指标 | 约 LLM 次数 |
|
||
|------|-------------|
|
||
| Faithfulness | $1 + N$ |
|
||
| Groundedness | 1 |
|
||
| Context Precision | 1 |
|
||
| Context Recall | 1 |
|
||
| Answer Correctness | 1 |
|
||
| Answer Relevance | 1 + 3 次 embedding |
|
||
|
||
综合评测全开时,单样本可达 **10+ 次** LLM 调用。批量任务应合理设置 `concurrency` 与 `selected_metrics`,避免 Judge API 限流。
|
||
|
||
### 16.2 Prompt 语言与 JSON 解析
|
||
|
||
所有 Prompt 为中文指令,要求模型**仅输出 JSON**。`OpenAICompatibleJudge` 对返回做 `json.loads`,失败时捕获异常并将 raw 文本写入 `judge_detail` 便于排查。生产环境建议选用 JSON 遵循能力较强的模型(如 GPT-4o、DeepSeek-V3)。
|
||
|
||
### 16.3 Embedding 用途
|
||
|
||
- **Answer Relevance**:回答 → 生成 3 个假设问题 → 与原始问题做 cosine;
|
||
- **循环去重**:approved 题的 question 字段向量与新区块比对;
|
||
- 二者共用 `judge_config` 中的 embed 三元组,可与 chat 模型异构部署。
|
||
|
||
---
|
||
|
||
## 17. 前端页面交互说明
|
||
|
||
### 17.1 测试集页
|
||
|
||
支持三种来源并列:表格手动新增、上传 JSON、侧栏触发「LLM 生成」并轮询 `generate_task`。详情页展示样本字段,可编辑 `relevant_chunk_ids`(JSON 文本框)。
|
||
|
||
### 17.2 评测任务页
|
||
|
||
表单字段与 `eval_task` 表一一对应;提交后列表展示进度条 `progress/total`;完成后跳转 Report。删除任务不级联删除 report 时需确认实现(当前以实现为准)。
|
||
|
||
### 17.3 单跳报告页
|
||
|
||
左侧章节树,右侧问题列表;支持按 section 筛选、查看每条 retrieved JSON、导出未命中 MD。颜色区分 file_hit / chunk_hit / 空召回。
|
||
|
||
### 17.4 问题生成页
|
||
|
||
Tab 区分「文件上传」与「dagent 数据源」;dagent 模式先加载文件树勾选 file_ids;问题表支持 approve/reject、编辑 Q/A;embedding 去重结果展示 dup_similarity。
|
||
|
||
---
|
||
|
||
## 18. 故障排查手册
|
||
|
||
| 现象 | 可能原因 | 处理 |
|
||
|------|----------|------|
|
||
| 任务长期 running | 后台协程异常退出未写库 | 查 server 日志;手动改 status |
|
||
| Judge 全 0 分 | API Key 无效或 JSON 解析失败 | 查 judge_detail 原始返回 |
|
||
| Hit Rate 全空 | 未填 relevant_chunk_ids | 补标注或关闭规则指标 |
|
||
| 单跳章节全 unmatched | file 路径与知识库不一致 | 检查 MD `##` 行与 dagent 路径 |
|
||
| 循环任务暂停无法恢复 | 进程重启后 _loop_controls 丢失 | 仅支持同进程 resume;否则新起任务 |
|
||
| SQLite database locked | 并发写冲突 | 已设 busy_timeout;避免多进程写同一库 |
|
||
| 导出 OOM | 一次加载百万级 JSON | 使用 export_loop_all_groups 按批次导出 |
|
||
| dagent 429 | 并发过高 | 降低 concurrency |
|
||
|
||
### 18.1 日志位置
|
||
|
||
开发时 uvicorn 标准输出;历史曾写入根目录 `server*.log`(已清理)。生产建议配置结构化日志到文件或 ELK。
|
||
|
||
---
|
||
|
||
## 19. dagent 远程集成实践
|
||
|
||
### 19.1 环境划分
|
||
|
||
| 环境 | 用途 |
|
||
|------|------|
|
||
| dagent-dev | 功能联调 |
|
||
| dagent.d-robotics.cc | 14 组循环生产数据所在 |
|
||
|
||
`platform_config` 与 `loop_task.env_url` 应指向被测环境,避免 dev 数据写入生产库统计。
|
||
|
||
### 19.2 跨切片模式(cross_chunk)
|
||
|
||
当前部分 dagent 版本不支持检索 API 的 `file_id_list` 过滤,单跳与循环建议 **cross_chunk=true**,在更大池中检索后再用 `is_file_hit` 判断文件是否正确。这与「限定文件内检索」的理想实验设置不同,报告解读时需注明。
|
||
|
||
### 19.3 大规模跑数建议
|
||
|
||
1. 按 [task_groups_plan.json](./task_groups_plan.json) 分批,每批 100 切片;
|
||
2. 组间并行度受 Judge 与 dagent 配额限制,建议组内顺序、组间适度并行;
|
||
3. 每批完成后执行 `export_loop_all_groups.py` 或 API export 做冷备;
|
||
4. `rag_eval.db` 体积膨胀后 VACUUM 需停机维护。
|
||
|
||
### 19.4 问答集资产用途
|
||
|
||
`docs/exports/` 下 23 万+ 题可用于:检索离线评测、训练数据构造、Bad Case 分析、版本回归对比。JSON 行内含 `chunk_id` 可回连 dagent 切片。
|
||
|
||
---
|
||
|
||
## 20. SDK CLI 与配置参考
|
||
|
||
### 20.1 命令行
|
||
|
||
```bash
|
||
rag-eval generate --config config.yaml --output dataset.json
|
||
rag-eval run --config config.yaml --dataset dataset.json --output report.json
|
||
```
|
||
|
||
`config.yaml` 结构见 `docs/config.example.yaml`:含 `platform`、`judge`、`agent_id`、`knowledge_hub_id`、`top_k`、`concurrency` 等。
|
||
|
||
### 20.2 编程式调用
|
||
|
||
Server 与 CLI 均依赖同一 `EvalRunner`,保证指标口径一致。自定义 `progress_cb` 可在 CI 中打印进度条。
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant CI as CI Pipeline
|
||
participant CLI as rag-eval CLI
|
||
participant RUN as EvalRunner
|
||
participant ADP as Adapter
|
||
|
||
CI->>CLI: run dataset.json
|
||
CLI->>RUN: run(config)
|
||
RUN->>ADP: retrieve + chat
|
||
RUN-->>CLI: report.json
|
||
CI->>CI: 阈值门禁 RAG Score > 0.75
|
||
```
|
||
|
||
---
|
||
|
||
## 21. 多跳召回与多跳出题(技术说明)
|
||
|
||
### 21.1 多跳问题模型
|
||
|
||
多跳样本除 `question`、`answer` 外,核心结构为 `hops[]`:每一跳描述一个子问题、期望检索的 `section_path` / `file_id`,以及该跳对最终答案的贡献权重。评测时引擎按跳次顺序调用检索 API,记录每跳 top-K 结果,再合并为 `retrieved` 全集用于兼容旧版统计。
|
||
|
||
**全链路命中(full_hit)**:所有期望跳的文件或切片均命中;**部分命中(partial_hit)**:至少一跳命中。与单跳相比,多跳更考察知识库跨文档关联能力。
|
||
|
||
### 21.2 多跳与 Agent 回答
|
||
|
||
若配置 `agent_id` 与 `judge_config_id`,在分跳检索完成后可调用 Agent 生成最终答案,并由 Judge 评判与标准答案的一致性。该路径同时消耗检索与对话配额,适合端到端 RAG 演示而非纯检索压测。
|
||
|
||
### 21.3 multi_hop_gen
|
||
|
||
`multi_hop_gen_task` 支持 `source=file`(上传结构化工单)或 `source=dagent`(按 org 拉目录)。`hops_per_question` 控制每题跳数,`questions_per_group` 控制每组生成数量。生成结果存入 `multi_hop_gen_question`,经人工审核后可导出为多跳 MD 再回流多跳召回测试。
|
||
|
||
```mermaid
|
||
flowchart LR
|
||
GEN[multi_hop_gen] --> MD[多跳 MD]
|
||
MD --> MH[multi_hop 测试]
|
||
MH --> RPT[分跳命中报告]
|
||
```
|
||
|
||
---
|
||
|
||
## 22. DatasetGenerator 与测试集自动生成
|
||
|
||
### 22.1 流程
|
||
|
||
`DatasetGenerator` 接收 `file_id_list`,通过 Adapter 拉取每个文件的切片列表。对每个切片:
|
||
|
||
1. 将切片正文(及可选图片多模态描述)填入出题 Prompt;
|
||
2. 调用 Judge LLM 生成「问题 + 参考答案」;
|
||
3. 二次调用质量评估 Prompt,低于 `quality_threshold` 的丢弃;
|
||
4. 写入 `eval_sample` 或仅返回内存对象。
|
||
|
||
### 22.2 与综合评测的衔接
|
||
|
||
Web UI 的「LLM 自动生成」创建 `generate_task` 异步任务,完成后样本落入指定 `eval_dataset`。用户可人工删改再启动 `eval_task`,形成「半自动」评测流水线。
|
||
|
||
### 22.3 标注成本对比
|
||
|
||
| 方式 | 人工成本 | 适合场景 |
|
||
|------|----------|----------|
|
||
| 全手动 | 高 | 黄金集、合规场景 |
|
||
| LLM 生成 + 人工抽检 | 中 | 快速扩容 |
|
||
| 循环测试自动生成 | 低 | 覆盖度优先、允许噪声 |
|
||
|
||
---
|
||
|
||
## 23. 报告对象模型(EvalReport)
|
||
|
||
`EvalReport` 由 `EvalRunner._build_report` 构造,包含:
|
||
|
||
- 任务级聚合:各 `avg_*` 字段、`rag_score`、`hallucination_rate`;
|
||
- 样本级列表:`List[SampleResult]`,每条含指标分数字段与 `judge_detail` 字典;
|
||
- 序列化:`report.save(path)` 输出 JSON,CLI 与 Server 共用。
|
||
|
||
Server 额外调用 Judge 生成自然语言 `interpretation`,写入 `eval_report` 表,前端直接展示而无需客户端再调 LLM。
|
||
|
||
**SampleResult 字段与 UI 映射:**
|
||
|
||
| 字段 | 报告页展示 |
|
||
|------|------------|
|
||
| hit_rate, mrr, ndcg | 检索卡片 / 雷达图轴 |
|
||
| faithfulness 等 | 生成卡片 |
|
||
| judge_detail | Modal JSON |
|
||
| error | 红色错误行 |
|
||
|
||
---
|
||
|
||
## 24. 并发模型与资源约束
|
||
|
||
### 24.1 asyncio 语义
|
||
|
||
Server 主线程运行在 uvicorn 事件循环上。`run_eval_task`、`run_loop_task`、单跳后台任务均为 `asyncio.create_task` 调度的协程,共享同一线程。CPU 密集操作(如大 JSON 解析)应避免阻塞过久,必要时用 `asyncio.to_thread`。
|
||
|
||
### 24.2 Semaphore
|
||
|
||
`EvalRunner` 与 `SingleJumpTester` 均使用 `asyncio.Semaphore(concurrency)` 限制同时在飞的 HTTP 请求数。过大并发会导致:
|
||
|
||
- dagent 返回 429 或超时;
|
||
- Judge API 账单激增;
|
||
- SQLite 写锁等待。
|
||
|
||
推荐生产从 3–5 起步,循环大规模任务可提到 10–20 并监控错误率。
|
||
|
||
### 24.3 连接池
|
||
|
||
`DagentAdapter` 每次调用新建 `aiohttp.ClientSession`,简单但高并发下开销大。若需优化可改为 Session 复用(注意线程/协程安全)。
|
||
|
||
---
|
||
|
||
## 25. 类图:SDK 核心类型
|
||
|
||
```mermaid
|
||
classDiagram
|
||
class RAGAdapter {
|
||
<<abstract>>
|
||
+retrieve()
|
||
+chat()
|
||
}
|
||
class DagentAdapter {
|
||
+base_url
|
||
+org_id
|
||
}
|
||
class LLMJudge {
|
||
<<abstract>>
|
||
+score_faithfulness()
|
||
+score_relevance()
|
||
}
|
||
class OpenAICompatibleJudge {
|
||
+client AsyncOpenAI
|
||
}
|
||
class EvalRunner {
|
||
+adapter
|
||
+judge
|
||
+run()
|
||
}
|
||
class EvalSample {
|
||
+question
|
||
+reference_answer
|
||
+relevant_chunk_ids
|
||
}
|
||
class EvalReport {
|
||
+rag_score
|
||
+results
|
||
}
|
||
RAGAdapter <|-- DagentAdapter
|
||
LLMJudge <|-- OpenAICompatibleJudge
|
||
EvalRunner --> RAGAdapter
|
||
EvalRunner --> LLMJudge
|
||
EvalRunner --> EvalReport
|
||
EvalRunner --> EvalSample
|
||
```
|
||
|
||
---
|
||
|
||
## 26. 安全、合规与权限(建议)
|
||
|
||
当前版本为**内网工具**假设:无用户登录、无 RBAC。若对外暴露:
|
||
|
||
1. 增加 OAuth2 / 公司 SSO,API 带 Bearer;
|
||
2. `judge_config.api_key` 改为 KMS 引用或环境变量注入,库内不存明文;
|
||
3. 导出接口加审计日志;
|
||
4. 按 org 隔离 `platform_config`,防止误测他人知识库。
|
||
|
||
---
|
||
|
||
## 27. 版本演进与已知限制
|
||
|
||
| 限制 | 说明 | 规避 |
|
||
|------|------|------|
|
||
| 单进程循环控制 | pause/resume 不能跨 uvicorn worker | 单 worker 或 Redis 协调 |
|
||
| qa_gen 路由共用前缀 | 两模块同 prefix | 注册顺序、集成测试 |
|
||
| README 端口 8003 vs 代码 8021 | 历史文档差异 | 以实际启动参数为准 |
|
||
| 超大 SQLite | 循环后 DB 数 GB | 归档 exports、分库 |
|
||
| file_id 过滤 | 部分 dagent 不支持 | cross_chunk |
|
||
|
||
---
|
||
|
||
## 28. 典型使用场景 walkthrough
|
||
|
||
### 28.1 场景 A:发版前回归
|
||
|
||
1. 维护固定 `eval_dataset`(200 条黄金集);
|
||
2. CI 中 `rag-eval run`,`selected_metrics` 仅开 faithfulness + hit_rate;
|
||
3. 对比上一版本 `report.json`,RAG Score 下降超过 5% 则失败。
|
||
|
||
### 28.2 场景 B:新知识库冷启动
|
||
|
||
1. `qa_gen` 从 dagent 选目录出题;
|
||
2. 人工审核 10% 样本;
|
||
3. 单跳全库 MD 导出召回报告;
|
||
4. 针对 file_miss 章节调整切片策略。
|
||
|
||
### 28.3 场景 C:远程 4140 切片覆盖
|
||
|
||
1. 按 `task_groups_plan` 建 42 个 `loop_task`;
|
||
2. 每组跑满 `max_rounds=5` 或直至收益递减;
|
||
3. `export_loop_all_groups.py` 汇总;
|
||
4. 用 exports JSON 训练或分析高频未命中 chunk。
|
||
|
||
### 28.4 场景 D:多跳文档联调
|
||
|
||
1. `multi_hop_gen` 生成推理链问题;
|
||
2. 导出 MD → `multi_hop` 任务;
|
||
3. 查看 `hop_hit_count` 与 `full_hit` 比例,定位薄弱跳次。
|
||
|
||
---
|
||
|
||
## 29. 与早期设计文档的关系
|
||
|
||
[rag-eval-framework-design.md](./rag-eval-framework-design.md) 撰写于框架立项阶段,侧重指标定义与分层理念。本文档(技术规格说明书)对齐**当前代码实现**,补充了循环测试、多跳、qa_gen、SQLite 表结构及运维细节。若两处冲突,以本规格书与源码为准。
|
||
|
||
---
|
||
|
||
## 30. 名词索引(中英对照)
|
||
|
||
| 中文 | 英文 | 代码中的关键符号 |
|
||
|------|------|------------------|
|
||
| 评测运行器 | Eval Runner | `EvalRunner` |
|
||
| 平台适配器 | RAG Adapter | `RAGAdapter` |
|
||
| 评判器 | LLM Judge | `LLMJudge` |
|
||
| 单跳 | Single-hop | `single_jump` |
|
||
| 多跳 | Multi-hop | `multi_hop` |
|
||
| 循环任务 | Loop Task | `loop_task` |
|
||
| 调和均值 | Harmonic Mean | `rag_score` 计算 |
|
||
| 幻觉率 | Hallucination Rate | faithfulness < threshold |
|
||
| 切片 | Chunk | `RetrievedChunk` |
|
||
| 知识库 | Knowledge Hub | `knowledge_hub_id` |
|
||
|
||
---
|
||
|
||
## 31. 检索规则指标实现细节
|
||
|
||
本节说明 `sdk/rag_eval/evaluators/retrieval.py` 中各函数的行为边界,便于复现论文指标或对接外部评测框架。
|
||
|
||
### 31.1 Hit Rate@K
|
||
|
||
对单个问题,若在召回列表前 K 个位置中**至少出现一个** `chunk_id` 属于标注集合 `relevant_chunk_ids`,则该样本 hit=1,否则为 0。数据集层面 Hit Rate 为所有样本 hit 的算术平均。该指标对「只要召回到一个相关文档即可」的场景敏感,无法区分相关结果排名优劣。
|
||
|
||
### 31.2 MRR@K
|
||
|
||
取第一个相关 chunk 的排名 rank(从 1 开始),贡献 $1/\text{rank}$;若无相关则贡献 0。数据集 MRR 为样本 MRR 的平均。相比 Hit Rate,MRR 对「相关结果是否靠前」更敏感,适合排序质量评估。
|
||
|
||
### 31.3 NDCG@K
|
||
|
||
实现采用二元相关度:相关 chunk 为 1,否则为 0。计算 DCG@K 时使用 $\sum_i \frac{2^{rel_i}-1}{\log_2(i+1)}$,再以理想 DCG 归一化。若标注集中有多个相关 chunk,全部命中可获得更高 NDCG。K 默认与 `RunConfig.top_k` 一致。
|
||
|
||
### 31.4 与 LLM 检索指标的关系
|
||
|
||
Hit/MRR/NDCG 依赖**离散的 chunk_id 标注**;Context Precision/Recall 依赖**自然语言参考答案**,可捕捉语义等价但 chunk_id 未标注的情况。生产评测建议两套指标并行:规则指标看排序,LLM 指标看语义覆盖。
|
||
|
||
---
|
||
|
||
## 32. Windows 与跨平台注意事项
|
||
|
||
`server/main.py` 在 `win32` 平台将 stdout/stderr 重定向为 UTF-8,避免控制台 GBK 打印特殊 Unicode(如数学符号)时崩溃。`single_jump/tester.py` 与 `loop_engine.py` 同样调用 `reconfigure(encoding='utf-8')`。
|
||
|
||
路径上 Server 使用 `Path(__file__)` 定位 `sdk` 与 `frontend/dist`,避免硬编码盘符。运维脚本应避免写死 `d:/project/...`,改用相对路径。
|
||
|
||
SQLite 在 Windows 上注意:
|
||
|
||
- 勿将 `rag_eval.db` 放在同步盘(OneDrive)上,易导致锁异常;
|
||
- 杀毒软件实时扫描大库文件会拖慢写入,建议排除 `server/data/`。
|
||
|
||
---
|
||
|
||
## 33. Docker 与 nginx 配置说明
|
||
|
||
`docker-compose.yml` 通常定义两个 service:`server` 暴露 API 端口,`frontend` 构建静态资源或由 nginx 托管。`nginx.conf` 将 `/api` 反向代理至 uvicorn,其余路径走静态文件,并配置 `try_files` 支持 React Router 前端路由。
|
||
|
||
`Dockerfile.server` 安装 `requirements.txt`,复制 `server/` 与 `sdk/`,工作目录设为 `server`,CMD 为 uvicorn。构建上下文应为 `rag-eval` 根目录而非 `server/`,否则找不到 sdk。
|
||
|
||
镜像内数据库路径需挂载 volume,例如 `- ./server/data:/app/server/data`,防止容器销毁丢数。
|
||
|
||
---
|
||
|
||
## 34. 提示词模板(prompt_template)机制
|
||
|
||
`prompt_template` 表允许用户保存可复用的出题或评判 Prompt 正文。创建 `qa_gen_task` 时可指定模板 id,生成逻辑将基础 Prompt 与模板合并,实现「同一套切片、不同题型」的 A/B。模板 CRUD 通过 `/api/prompt-template` 完成,前端可在问题生成页下拉选择。
|
||
|
||
设计意图是**将 Prompt 版本从代码中解耦**,使领域专家可在 UI 调整措辞而无需发版 Server。若模板为空则回退代码内默认 Prompt(见 `dataset/generator.py` 与 qa_gen 路由内联字符串)。
|
||
|
||
---
|
||
|
||
## 35. 数据导出格式说明
|
||
|
||
### 35.1 循环导出 JSON 结构
|
||
|
||
`export_loop_all_groups.py` 产出顶层字段:
|
||
|
||
- `exported_at`、`environment`、`org_id`;
|
||
- `task_groups[]`:每组含 `batches[]`;
|
||
- 每 batch 含 `questions[]`,单题含 `question`、`reference_answer`、`chunk_id`、`file_name`、`round`、`quality_score` 等。
|
||
|
||
该格式适合用 jq/Python 做二次聚合,例如按 `file_name` 统计每文件出题数。
|
||
|
||
### 35.2 循环导出 MD 结构
|
||
|
||
与单跳解析器兼容,可直接作为下一轮 `single-jump` 上传文件,形成「生成 → 验证 → 修正 → 再验证」闭环。
|
||
|
||
### 35.3 综合评测 report.json
|
||
|
||
CLI `rag-eval run --output report.json` 输出 `EvalReport` 全量序列化,含 `results` 数组。可用于离线绘图或与历史版本 diff。
|
||
|
||
---
|
||
|
||
## 36. 质量保障建议(组织级)
|
||
|
||
1. **黄金集**:维护 100–500 条人工审核样本,任何检索模型变更必跑;
|
||
2. **Judge 模型版本冻结**:报告中记录 `judge_config.model`,避免不可比;
|
||
3. **定期冷备**:`rag_eval.db` + `docs/exports/` 双轨;
|
||
4. **指标看板**:从 `eval_report` 表 ETL 到 Grafana,按日趋势监控 RAG Score;
|
||
5. **Bad Case 例会**:单跳 `export-failed-md` 导出未命中集,分配给切片优化负责人。
|
||
|
||
---
|
||
|
||
## 37. 读者常见问题(FAQ)
|
||
|
||
**问:评测任务为何一直 pending?**
|
||
答:若未触发 `create_task` 或进程未启动后台协程,会停留 pending。检查 uvicorn 是否运行、API 是否返回 task_id。
|
||
|
||
**问:能否只评检索不评生成?**
|
||
答:可以。`eval_retrieval=1`、`eval_generation=0`,或 `selected_metrics` 仅列检索项。
|
||
|
||
**问:循环任务与 qa_gen 区别?**
|
||
答:qa_gen 单轮出题+审核;循环在多轮中自动串联出题、去重、单跳验证并累计统计。
|
||
|
||
**问:23 万题导出是否含重复?**
|
||
答:去重后 approved 题;不同轮次可能对同一切片有不同问法,是否语义重复需用 embedding 再滤。
|
||
|
||
**问:如何对接非 dagent 平台?**
|
||
答:实现 `RAGAdapter`,在 Server 实例化处按 `platform_config.type` 分支。
|
||
|
||
**问:RAG Score 很低但 Faithfulness 很高?**
|
||
答:调和均值受 Context Precision/Recall 拖累,说明检索上下文质量差而非生成幻觉。
|
||
|
||
---
|
||
|
||
## 38. 文档贡献与维护
|
||
|
||
修改核心流程(如新增指标、表结构变更)时,请同步更新:
|
||
|
||
1. 本技术规格说明书对应章节;
|
||
2. 根目录 `README.md` 功能表;
|
||
3. `docs/README.md` 索引;
|
||
4. OpenAPI 注解(如有)。
|
||
|
||
Pull Request 模板可要求勾选「已更新技术文档」。
|
||
|
||
---
|
||
|
||
## 39. Server 启动生命周期
|
||
|
||
```mermaid
|
||
sequenceDiagram
|
||
participant U as uvicorn
|
||
participant M as main.py
|
||
participant DB as init_db
|
||
participant R as recover_orphaned_loops
|
||
|
||
U->>M: import app
|
||
M->>M: sys.path sdk
|
||
M->>M: include_router × 11
|
||
Note over M: lifespan start
|
||
M->>DB: executeschema + migrations
|
||
M->>R: running loop → paused
|
||
M-->>U: yield app ready
|
||
Note over M: 处理 HTTP 请求
|
||
Note over M: lifespan end
|
||
```
|
||
|
||
应用启动时 `init_db` 执行 `schema.sql` 中 `CREATE TABLE IF NOT EXISTS`,随后 `_run_migrations` 对老库补列。任何 `running` 状态的 `loop_task` 在崩溃后被标记 `paused`,防止 UI 显示「运行中」却无法控制。
|
||
|
||
静态资源:若存在 `frontend/dist`,`app.mount("/", StaticFiles)` 将 SPA 挂在根路径,API 仍在 `/api` 下,需注意路由匹配顺序(API 路由先注册)。
|
||
|
||
---
|
||
|
||
## 40. 单跳 tester 请求载荷说明
|
||
|
||
`SingleJumpTester` 向 dagent 发起的语义检索 POST 体核心字段:
|
||
|
||
- `query`:问题正文;
|
||
- `org_id`:组织;
|
||
- `top_k`:召回数量(展示用,命中判断可能用更小的 `hit_top_k`);
|
||
- `knowledge_hub_id`:若任务指定;
|
||
- 可选 `file_id_list`:非 cross_chunk 时限制范围。
|
||
|
||
响应解析保留每条结果的 `file_id`、`knowledge_md_header_split_id`、`cosine_distance_1`、`headers` 等原始字段写入 `retrieved` JSON,以便报告页展示完整证据链。
|
||
|
||
错误处理:单条 QA 失败写入 `error` 字段,不中断整任务;summary 统计时排除或单独计数 error 行。
|
||
|
||
---
|
||
|
||
## 41. 去重算法(dedup)参数
|
||
|
||
`service/dedup.py` 对候选题计算 embedding,与库中已批准题做余弦相似度。超过阈值则标记 `dup_of` 指向最相似题 id,并可选自动 reject。
|
||
|
||
| 参数 | 典型值 | 影响 |
|
||
|------|--------|------|
|
||
| 相似度阈值 | 0.85–0.92 | 越高越宽松 |
|
||
| global_dedup | true/false | 跨 loop_task 比对 |
|
||
| 比对字段 | question | 仅问题文本,不含答案 |
|
||
|
||
全局去重在 14 组大规模跑数时显著增加 SQL 与 embedding 调用,但能有效降低「同一文档重复问法」占比。
|
||
|
||
---
|
||
|
||
## 42. 前端 http 客户端与错误处理
|
||
|
||
`services/http.ts` 基于 axios,`baseURL` 设为 `/api`。响应拦截器统一处理 4xx/5xx,Ant Design `message.error` 提示。文件上传使用 `FormData`,不手动设 Content-Type 以保留 boundary。
|
||
|
||
长轮询:任务列表页 `setInterval` 刷新 progress,间隔约 2–5 秒;任务完成后清除定时器。大结果集分页:`qa_gen` questions 接口支持 `page`/`page_size`。
|
||
|
||
---
|
||
|
||
## 43. 评测指标体系与 RAGAS 对照
|
||
|
||
本平台指标设计参考 RAGAS、TruLens 等框架的常见定义,但实现为独立中文 Prompt,**数值不可与 RAGAS 官方实现直接划等号**。对照关系如下:
|
||
|
||
| 本平台 | 相近概念 | 差异 |
|
||
|--------|----------|------|
|
||
| Faithfulness | faithfulness | 两步 claim 验证 |
|
||
| Answer Relevance | answer_relevancy | 反推问题 + embedding |
|
||
| Context Precision | context_precision | LLM 判 useful |
|
||
| Context Recall | context_recall | 陈述级支持 |
|
||
| Groundedness | 部分 attribution | 显式 chunk index |
|
||
|
||
对外汇报时建议注明「内部评测框架」及 Judge 模型版本。
|
||
|
||
---
|
||
|
||
## 44. 性能基准(经验值,非承诺)
|
||
|
||
在「Judge=gpt-4o、dagent 内网、concurrency=3、样本 100 条、全开指标」条件下,单次 eval_task 约 15–40 分钟,主要耗时在 Faithfulness 与 Context 类 LLM 调用。单跳 1000 题、top_k=10、concurrency=10,约 10–25 分钟,主要受 dagent 检索延迟制约。
|
||
|
||
循环任务单批 100 切片、5 轮、每切片 5 题量级,总 LLM 调用可达数万次,应安排在非高峰时段并监控配额。
|
||
|
||
---
|
||
|
||
## 45. 代码阅读路径(新人 onboarding)
|
||
|
||
1. `sdk/rag_eval/runner.py` — 理解主流程;
|
||
2. `sdk/rag_eval/adapters/dagent.py` — 理解外部依赖;
|
||
3. `server/service/task_service.py` — 理解持久化;
|
||
4. `server/api/task.py` + `frontend/pages/Task` — 理解端到端;
|
||
5. `server/service/loop_engine.py` — 理解最复杂编排;
|
||
6. `server/models/schema.sql` — 理解数据全貌。
|
||
|
||
每模块配有本规格书第 4、6 章交叉引用。
|
||
|
||
---
|
||
|
||
## 46. eval_task 创建请求体字段说明
|
||
|
||
Web UI 提交 `POST /api/task/run` 时,JSON 或表单字段与数据库列映射如下。理解该映射有助于用脚本批量建任务。
|
||
|
||
| 请求字段 | 必填 | 说明 |
|
||
|----------|------|------|
|
||
| dataset_id | 是 | 已存在的数据集 |
|
||
| platform_config_id | 是 | dagent 连接 |
|
||
| judge_config_id | 是 | LLM 评判 |
|
||
| agent_id | 是 | 生成层对话用 |
|
||
| knowledge_hub_id | 是 | 检索范围 |
|
||
| name | 否 | 任务展示名 |
|
||
| top_k | 否 | 默认 10 |
|
||
| eval_retrieval / eval_generation | 否 | 布尔 |
|
||
| selected_metrics | 否 | JSON 数组,覆盖上述布尔 |
|
||
| file_id_list | 否 | 限制检索文件 |
|
||
| concurrency | 否 | 默认 3 |
|
||
|
||
创建成功后 Server 立即 `asyncio.create_task(run_eval_task(task_id))`,HTTP 响应不等待评测结束。
|
||
|
||
---
|
||
|
||
## 47. 切片质量与评测结果的因果关系链
|
||
|
||
评测低分往往并非单一环节失败,建议按下列因果链排查:
|
||
|
||
```mermaid
|
||
flowchart TD
|
||
A[切片过长/过短] --> B[检索噪声大]
|
||
B --> C[Context Precision 低]
|
||
A --> D[关键信息未切片]
|
||
D --> E[Context Recall 低]
|
||
E --> F[Agent 缺上下文]
|
||
F --> G[Faithfulness 低或胡编]
|
||
H[Agent Prompt 不当] --> G
|
||
I[Embedding 模型不适配中文] --> B
|
||
```
|
||
|
||
平台提供单跳与循环工具定位 B 段;综合评测定位 C–G;需与知识库工程、Agent 配置协同优化。
|
||
|
||
---
|
||
|
||
## 48. 未来架构演进方向(规划,非承诺)
|
||
|
||
1. **任务队列外置**:Redis + Celery/RQ,支持多 Worker 与任务取消;
|
||
2. **PostgreSQL 选项**:替代 SQLite 以支撑多实例写入;
|
||
3. **Judge 缓存**:相同 (question, chunks) 哈希复用评判结果;
|
||
4. **插件化指标**:`Metric` 接口注册,UI 动态勾选;
|
||
5. **权限与租户**:org 级隔离与审计;
|
||
6. **标准数据集格式**:导入 RAGAS/HuggingFace 格式。
|
||
|
||
上述方向来自实际运维痛点,实施优先级由产品决定。
|
||
|
||
---
|
||
|
||
## 49. 缩略语表
|
||
|
||
| 缩略语 | 全称 |
|
||
|--------|------|
|
||
| RAG | Retrieval-Augmented Generation |
|
||
| LLM | Large Language Model |
|
||
| SSE | Server-Sent Events |
|
||
| WAL | Write-Ahead Logging |
|
||
| API | Application Programming Interface |
|
||
| SPA | Single Page Application |
|
||
| CRUD | Create Read Update Delete |
|
||
| ER | Entity-Relationship |
|
||
| CI/CD | Continuous Integration / Delivery |
|
||
| EVB | 项目内知识库代号 |
|
||
| org_id | dagent 组织标识 |
|
||
|
||
---
|
||
|
||
## 50. 结语
|
||
|
||
RAG Eval 平台将**可复现的指标体系**、**贴近 dagent 的工程工具链**与**大规模数据生产能力**整合在同一仓库中。本文档从架构、数据、流程、API、运维五方面描述当前实现,并配有二十余张 Mermaid 图辅助理解。随着代码迭代,请持续更新本文档与根目录 README,保持「文档 — 代码 — 数据导出」三者一致,以便团队在任何时间点都能追溯评测结论的依据。
|
||
|
||
---
|
||
|
||
## 附录 A:14 组循环测试与数据资产
|
||
|
||
大规模远程 dagent 循环测试将 4140 切片划分为 **42 批次 × 14 组**,详见 [循环测试_14组分批规则.md](./循环测试_14组分批规则.md)。全量导出(235,347 题)位于 [exports/](./exports/)。
|
||
|
||
```mermaid
|
||
flowchart TB
|
||
subgraph Chunks["4140 切片 / 207 文件"]
|
||
B1["批次 1-3<br/>组1"]
|
||
B2["批次 4-6<br/>组2"]
|
||
BDOT["…"]
|
||
B14["批次 40-42<br/>组14"]
|
||
end
|
||
|
||
Chunks --> LT["loop_task × 42"]
|
||
LT --> QR["qa_gen_question"]
|
||
LT --> SR["single_jump_result"]
|
||
QR --> EXP["docs/exports/ 汇总"]
|
||
SR --> EXP
|
||
```
|
||
|
||
---
|
||
|
||
## 附录 B:文档修订记录
|
||
|
||
| 版本 | 日期 | 说明 |
|
||
|------|------|------|
|
||
| v1.0 | 2026-05-18 | 首版:架构、时序图、数据模型、指标、API、运维与字段字典 |
|
||
| v1.1 | 2026-05-18 | 扩充第 12–20 章,满足万字技术规格要求 |
|
||
|
||
---
|
||
|
||
*本文档随代码演进更新;若与实现不一致,以仓库源码为准。*
|