647 lines
24 KiB
Markdown
647 lines
24 KiB
Markdown
# RAG 评测框架设计文档
|
||
|
||
> 版本:v1.0
|
||
> 日期:2026-04-13
|
||
> 背景:为 dagent agent 平台设计的独立 RAG 评测框架
|
||
|
||
---
|
||
|
||
## 一、背景与目标
|
||
|
||
### 为什么做成独立框架
|
||
|
||
dagent 平台已具备完整的 RAG 能力(知识库切片、向量检索、ReAct Agent),但缺乏系统性的评测手段。将评测能力做成**独立框架**而非嵌入现有 backend,原因如下:
|
||
|
||
- **平台无关**:通过标准化 Adapter 接口,可评测任何 RAG 系统,不只是 dagent
|
||
- **独立部署**:不影响生产服务,可单独扩缩容,评测任务不占用业务资源
|
||
- **技术栈自由**:可选最适合评测场景的工具和模型
|
||
- **可复用**:其他项目也能接入使用
|
||
|
||
### 目标
|
||
|
||
1. 提供**检索层**和**生成层**的完整评测指标体系
|
||
2. 支持通过 **Python SDK** 集成到 CI/CD 流程
|
||
3. 提供 **Web UI** 供非技术人员操作和查看报告
|
||
4. 对接 dagent 平台,同时保持对其他平台的扩展能力
|
||
|
||
---
|
||
|
||
## 二、评测指标体系
|
||
|
||
### 2.1 检索层评测(Retrieval Evaluation)
|
||
|
||
评测知识库切片的召回质量,**不依赖 LLM**,纯计算指标。
|
||
|
||
| 指标 | 全称 | 说明 | 计算方式 |
|
||
|------|------|------|----------|
|
||
| **Hit Rate@K** | 命中率 | Top-K 结果中是否包含至少一个相关切片 | 二值判断,对所有样本取均值 |
|
||
| **MRR@K** | Mean Reciprocal Rank | 第一个相关切片排名的倒数均值 | `MRR = mean(1 / rank_i)`,rank_i 为第一个相关切片的位置 |
|
||
| **NDCG@K** | Normalized Discounted Cumulative Gain | 考虑排名权重的相关性得分,最全面的检索指标 | `NDCG = DCG / IDCG`,DCG 对高排名相关结果给予更高权重 |
|
||
| **Context Precision** | 上下文精确率 | 召回的切片中有多少是真正相关的(信噪比) | LLM-as-judge 判断每个召回切片是否相关 |
|
||
| **Context Recall** | 上下文召回率 | 回答所需信息有多少被召回覆盖 | LLM 将参考答案分解为原子声明,检查每条声明是否被召回内容覆盖 |
|
||
|
||
**指标公式**
|
||
|
||
```
|
||
Hit Rate@K = (1/|Q|) * Σ 1[∃ relevant chunk in top-K results]
|
||
|
||
MRR@K = (1/|Q|) * Σ (1 / rank_i)
|
||
rank_i = position of first relevant chunk for query i
|
||
|
||
DCG@K = Σ_{i=1}^{K} rel_i / log2(i+1)
|
||
NDCG@K = DCG@K / IDCG@K
|
||
IDCG = DCG of ideal (perfect) ranking
|
||
|
||
Context Precision = |relevant ∩ retrieved| / |retrieved|
|
||
Context Recall = |ground truth claims covered by context| / |total ground truth claims|
|
||
```
|
||
|
||
### 2.2 生成层评测(Generation Evaluation)
|
||
|
||
评测 Agent 基于召回内容的回复质量,**依赖 LLM Judge**。
|
||
|
||
| 指标 | 说明 | 计算方式 | 是否需要参考答案 |
|
||
|------|------|----------|-----------------|
|
||
| **Faithfulness(忠实度)** | 回答中每个声明是否都有召回内容支撑,无幻觉 | LLM 分解答案为原子声明 → 逐条判断是否可从 context 推导 → 支持数/总数 | 否 |
|
||
| **Answer Relevance(答案相关性)** | 回答是否切题,有没有答非所问 | LLM 从答案反向生成问题 → 与原问题做 Embedding 相似度 | 否 |
|
||
| **Answer Correctness(答案正确性)** | 回答与标准答案的事实一致程度 | LLM judge 评分 + Embedding 相似度加权 | 是 |
|
||
| **Groundedness(可溯源性)** | 回答中每个声明是否可追溯到具体切片 | LLM-as-judge,带 chain-of-thought | 否 |
|
||
|
||
**Faithfulness 计算原理(最重要的指标)**
|
||
|
||
```
|
||
1. LLM 将 answer 分解为原子声明列表
|
||
例:"答案:北京是中国首都,人口约2200万"
|
||
→ ["北京是中国首都", "北京人口约2200万"]
|
||
|
||
2. 对每条声明,LLM 判断:能否从 retrieved context 中推导出来?
|
||
→ [True, False] (第二条无法从 context 推导 = 幻觉)
|
||
|
||
3. Faithfulness = 支持的声明数 / 总声明数 = 1/2 = 0.5
|
||
```
|
||
|
||
### 2.3 端到端综合指标
|
||
|
||
| 指标 | 计算方式 | 说明 |
|
||
|------|----------|------|
|
||
| **RAG Score** | 调和均值(Faithfulness, Answer Relevance, Context Precision, Context Recall) | 综合评分,任一短板都会拉低总分 |
|
||
| **Hallucination Rate** | 含幻觉样本数 / 总样本数(Faithfulness < 阈值) | 幻觉发生率 |
|
||
|
||
---
|
||
|
||
## 三、系统架构
|
||
|
||
### 3.1 整体架构
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ RAG Eval Framework │
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────────┐ ┌───────────────┐ │
|
||
│ │ Python SDK │ │ FastAPI Server │ │ React Web UI │ │
|
||
│ │ (核心逻辑) │ ←→ │ (REST API) │ ←→ │ (可视化报告) │ │
|
||
│ │ CLI 支持 │ │ 任务队列 │ │ 测试集管理 │ │
|
||
│ └──────────────┘ └──────────────────┘ └───────────────┘ │
|
||
│ ↓ ↓ │
|
||
│ ┌──────────────────────────────────────────────────────────┐ │
|
||
│ │ 核心模块 │ │
|
||
│ │ Adapters │ Evaluators │ LLM Judge │ Dataset Gen │ │
|
||
│ └──────────────────────────────────────────────────────────┘ │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
↕ HTTP API(标准化 Adapter 接口)
|
||
┌─────────────────────────────────────────────────────────────────┐
|
||
│ dagent platform / 任何其他 RAG 系统 │
|
||
└─────────────────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
### 3.2 数据流
|
||
|
||
```
|
||
测试集 (question + relevant_chunk_ids + reference_answer)
|
||
↓
|
||
EvalRunner.run(dataset, agent_id, knowledge_hub_id)
|
||
↓
|
||
┌────────────────────────────────────────────────────┐
|
||
│ for each sample: │
|
||
│ │
|
||
│ Step 1: adapter.retrieve(question) │
|
||
│ → 获取 Top-K 召回切片 │
|
||
│ → 计算 Hit Rate / MRR / NDCG(与标注对比) │
|
||
│ │
|
||
│ Step 2: adapter.chat(question) │
|
||
│ → 获取 Agent 回复 + 引用切片 │
|
||
│ → judge.score_faithfulness(answer, context) │
|
||
│ → judge.score_relevance(question, answer) │
|
||
│ → judge.score_correctness(answer, reference) │
|
||
└────────────────────────────────────────────────────┘
|
||
↓
|
||
EvalReport(每条样本详情 + 汇总统计 + 趋势对比)
|
||
```
|
||
|
||
---
|
||
|
||
## 四、项目结构
|
||
|
||
```
|
||
rag-eval/
|
||
├── sdk/ # Python SDK(核心)
|
||
│ ├── rag_eval/
|
||
│ │ ├── __init__.py
|
||
│ │ ├── runner.py # 评测任务执行器(入口)
|
||
│ │ ├── adapters/ # 平台适配器
|
||
│ │ │ ├── base.py # 抽象接口定义
|
||
│ │ │ └── dagent.py # dagent 适配器实现
|
||
│ │ ├── evaluators/ # 评测器
|
||
│ │ │ ├── retrieval.py # 检索层:Hit Rate / MRR / NDCG
|
||
│ │ │ └── generation.py # 生成层:Faithfulness / Relevance / Correctness
|
||
│ │ ├── judge/ # LLM Judge
|
||
│ │ │ ├── base.py # 抽象接口
|
||
│ │ │ └── openai_compatible.py # 兼容 DeepSeek / Qwen / OpenAI
|
||
│ │ ├── dataset/ # 测试集管理
|
||
│ │ │ ├── schema.py # 数据结构定义(Pydantic)
|
||
│ │ │ └── generator.py # LLM 自动生成测试集
|
||
│ │ └── report.py # 报告生成与格式化
|
||
│ ├── pyproject.toml
|
||
│ └── README.md
|
||
│
|
||
├── server/ # FastAPI 后端
|
||
│ ├── main.py
|
||
│ ├── api/
|
||
│ │ ├── dataset.py # 测试集 CRUD
|
||
│ │ ├── task.py # 评测任务管理
|
||
│ │ ├── report.py # 报告查询
|
||
│ │ └── config.py # 平台连接 & Judge 配置
|
||
│ ├── service/
|
||
│ │ ├── task_service.py
|
||
│ │ └── report_service.py
|
||
│ ├── models/ # 数据库模型(SQLite / PostgreSQL)
|
||
│ │ └── schema.sql
|
||
│ └── requirements.txt
|
||
│
|
||
├── frontend/ # React 前端
|
||
│ ├── src/
|
||
│ │ ├── pages/
|
||
│ │ │ ├── Dataset/ # 测试集管理(上传/生成/标注)
|
||
│ │ │ ├── Task/ # 评测任务(配置/提交/进度)
|
||
│ │ │ └── Report/ # 报告 & 可视化(雷达图/趋势图)
|
||
│ │ └── components/
|
||
│ └── package.json
|
||
│
|
||
└── docker-compose.yml # 一键部署
|
||
```
|
||
|
||
---
|
||
|
||
## 五、核心接口设计
|
||
|
||
### 5.1 Adapter 抽象接口
|
||
|
||
```python
|
||
# sdk/rag_eval/adapters/base.py
|
||
|
||
from abc import ABC, abstractmethod
|
||
from dataclasses import dataclass
|
||
|
||
@dataclass
|
||
class RetrievedChunk:
|
||
chunk_id: str
|
||
content: str
|
||
score: float # 相似度分数
|
||
headers: str # 所属章节标题
|
||
file_id: str
|
||
|
||
@dataclass
|
||
class AgentResponse:
|
||
answer: str
|
||
retrieved_chunks: list[RetrievedChunk] # Agent 实际使用的切片
|
||
latency_ms: int
|
||
|
||
class RAGAdapter(ABC):
|
||
"""
|
||
任何 RAG 平台都需要实现这两个方法。
|
||
框架通过此接口与平台交互,不依赖平台内部实现。
|
||
"""
|
||
|
||
@abstractmethod
|
||
async def retrieve(
|
||
self,
|
||
query: str,
|
||
knowledge_hub_id: str,
|
||
top_k: int = 10,
|
||
**kwargs
|
||
) -> list[RetrievedChunk]:
|
||
"""调用平台检索接口,返回召回的切片列表"""
|
||
...
|
||
|
||
@abstractmethod
|
||
async def chat(
|
||
self,
|
||
query: str,
|
||
agent_id: str,
|
||
**kwargs
|
||
) -> AgentResponse:
|
||
"""调用平台 Agent 对话接口,返回回复和引用的切片"""
|
||
...
|
||
```
|
||
|
||
### 5.2 dagent 适配器
|
||
|
||
```python
|
||
# sdk/rag_eval/adapters/dagent.py
|
||
|
||
class DagentAdapter(RAGAdapter):
|
||
"""
|
||
对接 dagent 平台的适配器。
|
||
通过 HTTP API 调用,不依赖 dagent 内部代码。
|
||
"""
|
||
|
||
def __init__(self, base_url: str, org_id: str, token: str):
|
||
self.base_url = base_url
|
||
self.org_id = org_id
|
||
self.headers = {"Authorization": f"Bearer {token}"}
|
||
|
||
async def retrieve(self, query, knowledge_hub_id, top_k=10, **kwargs):
|
||
# 调用 dagent 知识库检索接口
|
||
# POST /dagent/knowledge/retrieve
|
||
async with aiohttp.ClientSession() as session:
|
||
resp = await session.post(
|
||
f"{self.base_url}/dagent/knowledge/retrieve",
|
||
json={"query": query, "knowledge_hub_id": knowledge_hub_id,
|
||
"top_k": top_k, "org_id": self.org_id},
|
||
headers=self.headers
|
||
)
|
||
data = await resp.json()
|
||
return [RetrievedChunk(**chunk) for chunk in data["chunks"]]
|
||
|
||
async def chat(self, query, agent_id, **kwargs):
|
||
# 调用 dagent Agent 对话接口(SSE 流式,解析完整回复)
|
||
# POST /dagent/agent/chat
|
||
...
|
||
```
|
||
|
||
### 5.3 LLM Judge
|
||
|
||
```python
|
||
# sdk/rag_eval/judge/openai_compatible.py
|
||
|
||
class OpenAICompatibleJudge(LLMJudge):
|
||
"""
|
||
兼容所有 OpenAI 协议的模型:DeepSeek / Qwen / OpenAI / Azure OpenAI
|
||
评判逻辑使用中文 prompt,适合中文 RAG 场景
|
||
"""
|
||
|
||
def __init__(self, base_url: str, api_key: str, model: str):
|
||
self.client = AsyncOpenAI(base_url=base_url, api_key=api_key)
|
||
self.model = model
|
||
|
||
async def score_faithfulness(self, answer: str, context: list[str]) -> float:
|
||
"""
|
||
原理:
|
||
1. 让 LLM 把 answer 分解为原子声明列表
|
||
2. 对每条声明,判断是否可从 context 推导
|
||
3. 返回 支持声明数 / 总声明数
|
||
"""
|
||
context_text = "\n\n".join(context)
|
||
|
||
# Step 1: 分解为原子声明
|
||
decompose_prompt = f"""
|
||
请将以下回答分解为独立的原子声明列表,每条声明是一个不可再分的事实陈述。
|
||
回答:{answer}
|
||
输出格式:JSON 数组,如 ["声明1", "声明2", ...]
|
||
"""
|
||
claims = await self._call_json(decompose_prompt)
|
||
|
||
# Step 2: 逐条判断是否有 context 支撑
|
||
supported = 0
|
||
for claim in claims:
|
||
verify_prompt = f"""
|
||
参考资料:
|
||
{context_text}
|
||
|
||
声明:{claim}
|
||
|
||
问题:上述声明是否可以从参考资料中推导出来?
|
||
只回答 yes 或 no。
|
||
"""
|
||
result = await self._call(verify_prompt)
|
||
if "yes" in result.lower():
|
||
supported += 1
|
||
|
||
return supported / len(claims) if claims else 0.0
|
||
|
||
async def score_relevance(self, question: str, answer: str) -> float:
|
||
"""
|
||
原理:
|
||
1. 让 LLM 从 answer 反向生成 N 个问题
|
||
2. 计算这些问题与原 question 的 Embedding 相似度
|
||
3. 返回均值
|
||
"""
|
||
...
|
||
|
||
async def score_correctness(self, answer: str, reference: str) -> float:
|
||
"""
|
||
原理:LLM 对比 answer 和 reference,给出 0-1 分数
|
||
"""
|
||
prompt = f"""
|
||
请评估以下回答与参考答案的事实一致程度,给出 0 到 1 之间的分数。
|
||
1.0 = 完全一致,0.0 = 完全错误或无关。
|
||
|
||
参考答案:{reference}
|
||
待评估回答:{answer}
|
||
|
||
只输出一个 0 到 1 之间的小数。
|
||
"""
|
||
result = await self._call(prompt)
|
||
return float(result.strip())
|
||
```
|
||
|
||
### 5.4 测试集数据结构
|
||
|
||
```python
|
||
# sdk/rag_eval/dataset/schema.py
|
||
|
||
@dataclass
|
||
class EvalSample:
|
||
id: str
|
||
question: str # 测试问题
|
||
reference_answer: str # 标准参考答案
|
||
relevant_chunk_ids: list[str] # 标注的相关切片 ID(用于检索层评测)
|
||
knowledge_hub_id: str # 所属知识库
|
||
source_file_id: str | None = None # 来源文件(可选)
|
||
metadata: dict = field(default_factory=dict)
|
||
|
||
@dataclass
|
||
class EvalDataset:
|
||
id: str
|
||
name: str
|
||
description: str
|
||
samples: list[EvalSample]
|
||
created_at: datetime
|
||
```
|
||
|
||
### 5.5 SDK 使用示例
|
||
|
||
```python
|
||
from rag_eval import EvalRunner
|
||
from rag_eval.adapters import DagentAdapter
|
||
from rag_eval.judge import OpenAICompatibleJudge
|
||
|
||
# 配置适配器(对接 dagent)
|
||
adapter = DagentAdapter(
|
||
base_url="http://dagent-backend:8000",
|
||
org_id="org_xxx",
|
||
token="your-token"
|
||
)
|
||
|
||
# 配置 LLM Judge(独立于 dagent,使用 DeepSeek)
|
||
judge = OpenAICompatibleJudge(
|
||
base_url="https://api.deepseek.com/v1",
|
||
api_key="sk-xxx",
|
||
model="deepseek-chat"
|
||
)
|
||
|
||
# 运行评测
|
||
runner = EvalRunner(adapter=adapter, judge=judge)
|
||
report = await runner.run(
|
||
dataset="./my_dataset.json",
|
||
agent_id="agent_xxx",
|
||
knowledge_hub_id="hub_xxx",
|
||
top_k=10,
|
||
)
|
||
|
||
# 查看结果
|
||
print(report.summary())
|
||
# ┌─────────────────────────────────────────┐
|
||
# │ 评测报告摘要 │
|
||
# ├──────────────────────┬──────────────────┤
|
||
# │ 样本数 │ 200 │
|
||
# │ Hit Rate@10 │ 0.87 │
|
||
# │ MRR@10 │ 0.72 │
|
||
# │ NDCG@10 │ 0.81 │
|
||
# │ Context Precision │ 0.76 │
|
||
# │ Context Recall │ 0.83 │
|
||
# │ Faithfulness │ 0.91 │
|
||
# │ Answer Relevance │ 0.88 │
|
||
# │ Answer Correctness │ 0.79 │
|
||
# │ RAG Score │ 0.84 │
|
||
# │ Hallucination Rate │ 4.5% │
|
||
# └──────────────────────┴──────────────────┘
|
||
|
||
report.save("./eval_report_20260413.json")
|
||
```
|
||
|
||
---
|
||
|
||
## 六、测试集构建方案
|
||
|
||
### 6.1 数据结构
|
||
|
||
每条测试样本:
|
||
```json
|
||
{
|
||
"id": "sample_001",
|
||
"question": "什么是向量数据库?",
|
||
"reference_answer": "向量数据库是专门存储和检索高维向量的数据库系统...",
|
||
"relevant_chunk_ids": ["chunk_abc123", "chunk_def456"],
|
||
"knowledge_hub_id": "hub_xxx",
|
||
"source_file_id": "file_yyy"
|
||
}
|
||
```
|
||
|
||
### 6.2 构建方式
|
||
|
||
**方式 A:LLM 自动生成(推荐先用)**
|
||
|
||
```python
|
||
from rag_eval.dataset import DatasetGenerator
|
||
|
||
generator = DatasetGenerator(judge=judge, adapter=adapter)
|
||
dataset = await generator.generate(
|
||
knowledge_hub_id="hub_xxx",
|
||
questions_per_chunk=2,
|
||
question_types=["factual", "reasoning", "comparison", "unanswerable"]
|
||
)
|
||
# 自动生成问题 + 参考答案 + 标注 relevant_chunk_ids
|
||
```
|
||
|
||
原理:
|
||
1. 遍历知识库中所有切片
|
||
2. 对每个切片,用 LLM 生成 2-3 个不同类型的问题
|
||
3. 用 LLM 基于切片内容生成参考答案
|
||
4. 自动标注 `relevant_chunk_ids`(生成来源切片)
|
||
5. 建议人工抽检 10-20% 过滤低质量样本
|
||
|
||
**方式 B:人工标注(质量最高)**
|
||
|
||
通过 Web UI 提供标注界面:
|
||
- 输入问题
|
||
- 搜索并标注相关切片
|
||
- 填写参考答案
|
||
|
||
**问题类型覆盖建议**
|
||
|
||
| 类型 | 示例 | 占比建议 |
|
||
|------|------|----------|
|
||
| 事实查询 | "X 是什么?" | 40% |
|
||
| 多跳推理 | "X 和 Y 的关系是?" | 20% |
|
||
| 比较 | "X 和 Y 有什么区别?" | 20% |
|
||
| 不可回答 | 文档中不存在的信息 | 10% |
|
||
| 摘要 | "总结 X 的主要内容" | 10% |
|
||
|
||
推荐测试集规模:**200-500 条**,低于 100 条统计意义不足。
|
||
|
||
---
|
||
|
||
## 七、Web 端功能规划
|
||
|
||
| 页面 | 核心功能 |
|
||
|------|----------|
|
||
| **测试集管理** | 上传 JSON 测试集、LLM 自动生成、人工标注界面、样本预览 |
|
||
| **评测任务** | 配置 Adapter(平台连接)、配置 Judge 模型、提交任务、实时进度 |
|
||
| **评测报告** | 各指标得分雷达图、样本级别明细表、多次评测趋势对比、问题样本下钻 |
|
||
| **配置管理** | 平台连接配置(URL/Token)、Judge 模型配置(API Key/Model)|
|
||
|
||
---
|
||
|
||
## 八、数据库设计(Server 端)
|
||
|
||
```sql
|
||
-- 平台连接配置
|
||
CREATE TABLE platform_config (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
type TEXT NOT NULL, -- 'dagent' | 'custom'
|
||
base_url TEXT NOT NULL,
|
||
org_id TEXT,
|
||
token TEXT,
|
||
created_at DATETIME
|
||
);
|
||
|
||
-- Judge 模型配置
|
||
CREATE TABLE judge_config (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
base_url TEXT NOT NULL,
|
||
api_key TEXT NOT NULL,
|
||
model TEXT NOT NULL,
|
||
created_at DATETIME
|
||
);
|
||
|
||
-- 测试集
|
||
CREATE TABLE eval_dataset (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT NOT NULL,
|
||
description TEXT,
|
||
sample_count INTEGER,
|
||
created_at DATETIME
|
||
);
|
||
|
||
-- 测试样本
|
||
CREATE TABLE eval_sample (
|
||
id TEXT PRIMARY KEY,
|
||
dataset_id TEXT NOT NULL,
|
||
question TEXT NOT NULL,
|
||
reference_answer TEXT NOT NULL,
|
||
relevant_chunk_ids TEXT NOT NULL, -- JSON array
|
||
knowledge_hub_id TEXT NOT NULL,
|
||
source_file_id TEXT,
|
||
metadata TEXT -- JSON
|
||
);
|
||
|
||
-- 评测任务
|
||
CREATE TABLE eval_task (
|
||
id TEXT PRIMARY KEY,
|
||
name TEXT,
|
||
dataset_id TEXT NOT NULL,
|
||
platform_config_id TEXT NOT NULL,
|
||
judge_config_id TEXT NOT NULL,
|
||
agent_id TEXT NOT NULL,
|
||
knowledge_hub_id TEXT NOT NULL,
|
||
top_k INTEGER DEFAULT 10,
|
||
status TEXT NOT NULL, -- pending | running | done | failed
|
||
progress INTEGER DEFAULT 0,
|
||
created_at DATETIME,
|
||
finished_at DATETIME
|
||
);
|
||
|
||
-- 样本级评测结果
|
||
CREATE TABLE eval_result (
|
||
id TEXT PRIMARY KEY,
|
||
task_id TEXT NOT NULL,
|
||
sample_id TEXT NOT NULL,
|
||
retrieved_chunks TEXT, -- JSON
|
||
agent_answer TEXT,
|
||
hit_rate REAL,
|
||
mrr REAL,
|
||
ndcg REAL,
|
||
context_precision REAL,
|
||
context_recall REAL,
|
||
faithfulness REAL,
|
||
answer_relevance REAL,
|
||
answer_correctness REAL,
|
||
judge_detail TEXT -- JSON,LLM judge 的推理过程
|
||
);
|
||
|
||
-- 评测汇总报告
|
||
CREATE TABLE eval_report (
|
||
id TEXT PRIMARY KEY,
|
||
task_id TEXT NOT NULL UNIQUE,
|
||
sample_count INTEGER,
|
||
avg_hit_rate REAL,
|
||
avg_mrr REAL,
|
||
avg_ndcg REAL,
|
||
avg_context_precision REAL,
|
||
avg_context_recall REAL,
|
||
avg_faithfulness REAL,
|
||
avg_answer_relevance REAL,
|
||
avg_answer_correctness REAL,
|
||
rag_score REAL,
|
||
hallucination_rate REAL,
|
||
created_at DATETIME
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
## 九、开发优先级
|
||
|
||
| 阶段 | 内容 | 说明 |
|
||
|------|------|------|
|
||
| **Phase 1** | SDK 核心:Adapter 接口 + 检索评测器 | 无 LLM 依赖,最快验证,Hit Rate/MRR/NDCG |
|
||
| **Phase 2** | dagent Adapter 实现 | 对接现有平台 HTTP API |
|
||
| **Phase 3** | LLM Judge 模块 | Faithfulness / Relevance / Correctness |
|
||
| **Phase 4** | 测试集自动生成器 | 降低标注成本 |
|
||
| **Phase 5** | FastAPI Server | 把 SDK 包成 Web 服务,支持异步任务 |
|
||
| **Phase 6** | React 前端 | 报告可视化、测试集管理 |
|
||
|
||
---
|
||
|
||
## 十、技术选型
|
||
|
||
| 模块 | 技术 | 理由 |
|
||
|------|------|------|
|
||
| SDK | Python 3.10+, asyncio, Pydantic | 与 dagent 保持一致,异步支持并发评测 |
|
||
| Server | FastAPI + SQLite(开发)/ PostgreSQL(生产) | 轻量,易部署 |
|
||
| 任务队列 | asyncio.Queue(轻量)/ Celery(生产) | 评测任务耗时长,需异步执行 |
|
||
| Frontend | React + TypeScript + Ant Design | 与 dagent 前端技术栈一致 |
|
||
| LLM Judge | OpenAI SDK(兼容 DeepSeek/Qwen) | 统一接口,灵活切换模型 |
|
||
| 部署 | Docker Compose | 一键启动 server + frontend |
|
||
|
||
---
|
||
|
||
## 十一、与 dagent 平台的集成方式
|
||
|
||
框架通过 **HTTP API** 调用 dagent,不依赖 dagent 内部代码。
|
||
|
||
dagent 需要提供(或框架调用现有接口):
|
||
|
||
1. **检索接口**:`POST /dagent/knowledge/retrieve`
|
||
- 输入:query, knowledge_hub_id, top_k, org_id
|
||
- 输出:切片列表(chunk_id, content, score, headers, file_id)
|
||
|
||
2. **对话接口**:`POST /dagent/agent/chat`(现有 SSE 接口)
|
||
- 输入:question, agent_id, org_id
|
||
- 输出:回复文本 + 引用切片信息
|
||
|
||
如果 dagent 现有接口不完全满足,可在 dagent 侧新增一个**评测专用接口**,返回更详细的检索过程信息(如每个切片的 cosine distance、rerank score 等)。
|