first commit
This commit is contained in:
168
docs/models.json
Normal file
168
docs/models.json
Normal file
@@ -0,0 +1,168 @@
|
||||
{
|
||||
"data": [
|
||||
{
|
||||
"id": "text-embedding-v3",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3-vl-embedding",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3-vl-plus",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3-omni-flash",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deepseek-v3.2",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "glm-5",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "zhipu_4v",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3.5-plus",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "glm-5.1",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "kimi-k2.6",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deepseek-v4-pro",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "deepseek",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3.6-flash",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3.5-flash",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3-vl-flash",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3.6-max-preview",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "text-embedding-v4",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "kimi-k2.5",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "moonshot",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "qwen3.6-plus",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "custom",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
},
|
||||
{
|
||||
"id": "deepseek-v4-flash",
|
||||
"object": "model",
|
||||
"created": 1626777600,
|
||||
"owned_by": "deepseek",
|
||||
"supported_endpoint_types": [
|
||||
"openai"
|
||||
]
|
||||
}
|
||||
],
|
||||
"object": "list",
|
||||
"success": true
|
||||
}
|
||||
621
docs/pdf-to-dataset-requirement-design.md
Normal file
621
docs/pdf-to-dataset-requirement-design.md
Normal file
@@ -0,0 +1,621 @@
|
||||
# PDF 文档转在线评测题库需求设计
|
||||
|
||||
## 1. 背景与问题
|
||||
|
||||
当前仓库已经具备一条完整的评测主链路:
|
||||
|
||||
- `main.py --scenario ...`
|
||||
- 加载 dataset
|
||||
- 标准化样本
|
||||
- 调用 adapter
|
||||
- 使用 `ragas` 评分
|
||||
- 写出本地运行资产
|
||||
|
||||
但这条链路默认前提是“评测数据已经存在”。现实里,RAG 项目的评测样本往往首先来自大量原始文档,例如 PDF 规范、制度、手册、法规或技术说明。没有一条稳定的“文档 -> 题库 dataset”生成链路,平台就仍然停留在手工准备数据的阶段。
|
||||
|
||||
你给出的外部示例项目已经验证了两件关键能力:
|
||||
|
||||
- 可以用阿里云文档解析服务对 PDF 做异步解析
|
||||
- 可以把解析结果归一成结构节点、语义块和可追溯切片
|
||||
|
||||
因此,本轮需求设计的目标不是建设一个完整知识库系统,而是在当前评测平台里补上一条最小可用的数据生产链路,让原始 PDF 可以生成可复核的在线评测题库。
|
||||
|
||||
## 2. 目标
|
||||
|
||||
本需求设计的 V1 目标如下:
|
||||
|
||||
- 支持从单个 PDF 或一个 PDF 目录批量生成在线评测题库
|
||||
- 解析能力基于阿里云文档解析服务
|
||||
- 题目生成以单文档为边界,不跨文档混合
|
||||
- 输出结果是可直接接入当前 `online` 评测模式的 dataset 草稿
|
||||
- 输出结果保留页码、章节、来源 chunk 等证据链,便于人工复核
|
||||
- 复用当前仓库的本地文件优先原则,不引入数据库依赖
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
V1 明确不覆盖以下范围:
|
||||
|
||||
- 不建设向量库入库能力
|
||||
- 不建设文档上传 Web UI
|
||||
- 不建设多租户文档中心
|
||||
- 不支持跨文档综合题
|
||||
- 不支持 Office 文档、图片包、网页抓取等多格式输入
|
||||
- 不自动生成最终 gold dataset
|
||||
- 不在 V1 中产出离线评测必需的 `answer / contexts`
|
||||
- 不复用外部项目中与向量化落库、知识库写入直接耦合的域模型
|
||||
|
||||
换句话说,V1 的目标是 **先把 PDF 文档转成“可人工复核的在线评测题库草稿”**,而不是一步演进成完整 RAG 数据工厂。
|
||||
|
||||
## 4. 用户价值
|
||||
|
||||
引入这条链路后,平台可以覆盖以下工作方式:
|
||||
|
||||
1. 用户准备一批 PDF 文档
|
||||
2. 系统调用阿里云文档解析服务获取结构化版面结果
|
||||
3. 系统把版面结果归一成可追溯的 chunk
|
||||
4. 系统用 LLM 基于 chunk 生成问题与参考答案草稿
|
||||
5. 用户在导出的 dataset 上做人工复核
|
||||
6. 用户把复核后的 dataset 直接接入现有 `online` 评测场景
|
||||
|
||||
这样平台就同时具备:
|
||||
|
||||
- 数据生产能力
|
||||
- 数据复核能力
|
||||
- 在线评测接入能力
|
||||
- 结果资产沉淀能力
|
||||
|
||||
## 5. V1 核心决策
|
||||
|
||||
本轮设计固定以下产品决策:
|
||||
|
||||
- 输入文档范围:仅 `PDF`
|
||||
- 题目生成范围:仅 `单文档题`
|
||||
- dataset 目标类型:`在线题库`
|
||||
- 发布方式:`先生成草稿,再人工复核`
|
||||
- 解析服务:阿里云文档解析
|
||||
- 解析失败默认策略:`fail`
|
||||
|
||||
这些决策不再留给后续实现阶段临时判断。
|
||||
|
||||
## 6. 目标架构总览
|
||||
|
||||
在现有评测架构之外,新增一条“dataset build”链路:
|
||||
|
||||
```text
|
||||
PDF files
|
||||
-> dataset build config
|
||||
-> aliyun parser gateway
|
||||
-> layout normalization
|
||||
-> source chunks
|
||||
-> LLM question generation
|
||||
-> draft online dataset
|
||||
-> review
|
||||
-> existing online evaluation flow
|
||||
```
|
||||
|
||||
该链路与现有评测链路的关系如下:
|
||||
|
||||
- `dataset build` 负责“评测输入怎么生产”
|
||||
- 现有 `rag_eval/execution/` 负责“生产好的评测输入怎么跑分”
|
||||
|
||||
二者职责分离,不相互污染。
|
||||
|
||||
## 7. 模块边界设计
|
||||
|
||||
### 7.1 CLI 入口
|
||||
|
||||
保留现有评测入口不变:
|
||||
|
||||
```powershell
|
||||
python main.py --scenario scenarios/offline/sample-offline.yaml
|
||||
```
|
||||
|
||||
新增一个用于构建 dataset 的入口:
|
||||
|
||||
```powershell
|
||||
python main.py --dataset-build-config scenarios/dataset_build/sample-pdf-build.yaml
|
||||
```
|
||||
|
||||
两个入口互斥,避免一次命令同时承担“建题库”和“跑评测”两个职责。
|
||||
|
||||
### 7.2 新增模块目录
|
||||
|
||||
新增主包:
|
||||
|
||||
```text
|
||||
rag_eval/
|
||||
dataset_builder/
|
||||
__init__.py
|
||||
models.py
|
||||
schema.py
|
||||
runner.py
|
||||
writers.py
|
||||
sources.py
|
||||
parser/
|
||||
__init__.py
|
||||
aliyun_docmind_gateway.py
|
||||
aliyun_document_parser.py
|
||||
aliyun_layout_normalizer.py
|
||||
generator/
|
||||
__init__.py
|
||||
question_generator.py
|
||||
validators.py
|
||||
```
|
||||
|
||||
职责划分如下:
|
||||
|
||||
- `schema.py`:校验 dataset build YAML
|
||||
- `models.py`:定义 job、解析结果、source chunk、生成样本等内部模型
|
||||
- `runner.py`:串联一次完整 build job
|
||||
- `writers.py`:写出 dataset 和本地资产
|
||||
- `sources.py`:发现输入 PDF 文件
|
||||
- `parser/`:适配阿里云文档解析能力
|
||||
- `generator/`:调用 LLM 生成题目草稿并做输出校验
|
||||
|
||||
### 7.3 外部示例代码复用策略
|
||||
|
||||
允许从外部项目参考并迁移以下能力:
|
||||
|
||||
- `aliyun_docmind_gateway.py`
|
||||
- `aliyun_document_parser.py`
|
||||
- `aliyun_layout_normalizer.py`
|
||||
|
||||
但复用范围只限于:
|
||||
|
||||
- 异步解析任务提交与轮询
|
||||
- layout 拉取
|
||||
- 结构节点提取
|
||||
- 语义块合并
|
||||
- 可追溯切片构建
|
||||
|
||||
不迁移以下职责:
|
||||
|
||||
- 向量库入库
|
||||
- embedding 持久化
|
||||
- 外部知识库 chunk 域模型
|
||||
- 知识库索引流程
|
||||
|
||||
## 8. 配置设计
|
||||
|
||||
### 8.1 新增 YAML 类型
|
||||
|
||||
新增一类配置文件,例如:
|
||||
|
||||
```text
|
||||
scenarios/
|
||||
dataset_build/
|
||||
sample-pdf-build.yaml
|
||||
```
|
||||
|
||||
### 8.2 配置字段
|
||||
|
||||
V1 的 dataset build YAML 结构固定如下:
|
||||
|
||||
```yaml
|
||||
job_name: legal-pdf-question-bank
|
||||
input:
|
||||
path: ../../datasets/raw/pdfs
|
||||
glob: "*.pdf"
|
||||
parser:
|
||||
provider: aliyun_docmind
|
||||
failure_mode: fail
|
||||
generation:
|
||||
model: qwen-long
|
||||
output_type: online_question_bank
|
||||
review_mode: draft_with_manual_review
|
||||
max_questions_per_document: 10
|
||||
max_source_chunks_per_question: 3
|
||||
output:
|
||||
dataset_path: ../../datasets/raw/generated/legal_question_bank.csv
|
||||
artifact_dir: ../../outputs/dataset-builds/legal-pdf-question-bank
|
||||
runtime:
|
||||
max_documents: 20
|
||||
```
|
||||
|
||||
### 8.3 字段约束
|
||||
|
||||
- `job_name`:必填,作为本次构建任务名称
|
||||
- `input.path`:必填,支持单文件或目录
|
||||
- `input.glob`:可选,默认 `*.pdf`
|
||||
- `parser.provider`:V1 固定为 `aliyun_docmind`
|
||||
- `parser.failure_mode`:`fail | skip`,默认 `fail`
|
||||
- `generation.model`:可选,允许覆盖默认生成模型
|
||||
- `generation.output_type`:V1 固定为 `online_question_bank`
|
||||
- `generation.review_mode`:V1 固定为 `draft_with_manual_review`
|
||||
- `generation.max_questions_per_document`:正整数,默认 `10`
|
||||
- `generation.max_source_chunks_per_question`:正整数,默认 `3`
|
||||
- `output.dataset_path`:必填,最终 dataset 输出路径
|
||||
- `output.artifact_dir`:必填,运行资产根目录
|
||||
- `runtime.max_documents`:可选,用于限制一次处理文档数
|
||||
|
||||
## 9. 环境变量设计
|
||||
|
||||
### 9.1 阿里云解析配置
|
||||
|
||||
在 `rag_eval/settings.py` 中新增以下环境变量读取:
|
||||
|
||||
- `ALIBABA_ACCESS_KEY_ID`
|
||||
- `ALIBABA_ACCESS_KEY_SECRET`
|
||||
- `ALIBABA_ENDPOINT`
|
||||
- `ALIYUN_PARSE_POLL_INTERVAL_SECONDS`
|
||||
- `ALIYUN_PARSE_TIMEOUT_SECONDS`
|
||||
- `ALIYUN_PARSE_LAYOUT_STEP_SIZE`
|
||||
- `ALIYUN_LLM_ENHANCEMENT`
|
||||
- `ALIYUN_ENHANCEMENT_MODE`
|
||||
- `DOCUMENT_PARSE_ARTIFACT_PREFIX`
|
||||
- `PARSER_FAILURE_MODE`
|
||||
|
||||
### 9.2 题库生成模型配置
|
||||
|
||||
新增环境变量:
|
||||
|
||||
- `DATASET_GENERATOR_MODEL`
|
||||
|
||||
默认优先级如下:
|
||||
|
||||
1. dataset build YAML 中的 `generation.model`
|
||||
2. `.env` 中的 `DATASET_GENERATOR_MODEL`
|
||||
3. 代码默认值
|
||||
|
||||
### 9.3 密钥管理要求
|
||||
|
||||
设计文档只引用环境变量名,不在仓库文档中记录任何明文 AK/SK。当前已经暴露在会话里的密钥需要单独轮换,这属于实现前置的安全动作。
|
||||
|
||||
## 10. 核心数据模型设计
|
||||
|
||||
### 10.1 `DatasetBuildJob`
|
||||
|
||||
表示一次 PDF -> dataset 生成任务。
|
||||
|
||||
核心字段:
|
||||
|
||||
- `job_name`
|
||||
- `input_path`
|
||||
- `input_glob`
|
||||
- `parser_provider`
|
||||
- `failure_mode`
|
||||
- `generation_model`
|
||||
- `output_type`
|
||||
- `review_mode`
|
||||
- `dataset_path`
|
||||
- `artifact_dir`
|
||||
- `runtime`
|
||||
|
||||
### 10.2 `ParsedDocument`
|
||||
|
||||
表示一个 PDF 经解析和归一化后的文档。
|
||||
|
||||
核心字段:
|
||||
|
||||
- `doc_id`
|
||||
- `doc_name`
|
||||
- `raw_text`
|
||||
- `structure_nodes`
|
||||
- `semantic_blocks`
|
||||
- `source_chunks`
|
||||
- `metadata`
|
||||
|
||||
### 10.3 `SourceChunk`
|
||||
|
||||
`SourceChunk` 是 V1 最关键的证据单元,用于生成题目和支持人工复核。
|
||||
|
||||
字段固定为:
|
||||
|
||||
- `chunk_id`
|
||||
- `doc_id`
|
||||
- `doc_name`
|
||||
- `text`
|
||||
- `page_start`
|
||||
- `page_end`
|
||||
- `section_path`
|
||||
- `section_title`
|
||||
- `source_layout_ids`
|
||||
|
||||
设计原则:
|
||||
|
||||
- 每个 chunk 必须能反查来源页码
|
||||
- 每个 chunk 必须能反查章节路径
|
||||
- 每个 chunk 必须能反查原始 layout id
|
||||
- 每个 chunk 只服务题库生成和证据追溯,不承担向量化职责
|
||||
|
||||
### 10.4 `DraftQuestionSample`
|
||||
|
||||
表示一条待复核的在线评测样本草稿。
|
||||
|
||||
字段固定为:
|
||||
|
||||
- `sample_id`
|
||||
- `question`
|
||||
- `ground_truth`
|
||||
- `scenario`
|
||||
- `language`
|
||||
- `doc_id`
|
||||
- `doc_name`
|
||||
- `section_path`
|
||||
- `page_start`
|
||||
- `page_end`
|
||||
- `source_chunk_ids`
|
||||
- `question_type`
|
||||
- `difficulty`
|
||||
- `review_status`
|
||||
- `review_notes`
|
||||
|
||||
### 10.5 枚举约束
|
||||
|
||||
- `review_status`: `draft | approved | rejected | needs_edit`
|
||||
- `question_type`: `fact | summary | procedure | comparison`
|
||||
- `difficulty`: `easy | medium | hard`
|
||||
|
||||
## 11. 文档解析设计
|
||||
|
||||
### 11.1 解析输入范围
|
||||
|
||||
V1 仅接受:
|
||||
|
||||
- 单个 `.pdf` 文件
|
||||
- 或一个包含多个 `.pdf` 的目录
|
||||
|
||||
目录模式下默认按 `input.glob` 扫描,默认值为 `*.pdf`。
|
||||
|
||||
### 11.2 解析流程
|
||||
|
||||
每个 PDF 的处理过程固定为:
|
||||
|
||||
1. 发现文件
|
||||
2. 创建阿里云 Docmind client
|
||||
3. 提交异步解析任务
|
||||
4. 轮询直到成功、失败或超时
|
||||
5. 分页拉取全量 layout
|
||||
6. 归一成结构节点、语义块和 source chunk
|
||||
7. 写出中间资产
|
||||
|
||||
### 11.3 版面归一化规则
|
||||
|
||||
从外部示例代码中沿用以下核心规则:
|
||||
|
||||
- 识别标题层级
|
||||
- 跳过目录页内容
|
||||
- 合并连续段落文本
|
||||
- 抽取表格为可检索纯文本
|
||||
- 保留图注类文本
|
||||
- 按固定窗口做长文本切块
|
||||
- 为每个 chunk 注入章节头信息和页码追溯信息
|
||||
|
||||
### 11.4 错误处理
|
||||
|
||||
支持两种失败模式:
|
||||
|
||||
- `fail`:任一文档解析失败则整个 job 失败
|
||||
- `skip`:记录失败文档,继续处理其余文档
|
||||
|
||||
V1 默认策略为 `fail`。
|
||||
|
||||
## 12. 题库生成设计
|
||||
|
||||
### 12.1 生成单元
|
||||
|
||||
题目生成单元固定为“单文档内的一组 section-aware source chunks”。
|
||||
|
||||
约束如下:
|
||||
|
||||
- 一条题目只能引用同一个 `doc_id`
|
||||
- 一条题目最多引用 `3` 个 chunk
|
||||
- 不允许跨文档混合证据
|
||||
|
||||
### 12.2 生成输出
|
||||
|
||||
每个候选题必须产出:
|
||||
|
||||
- `question`
|
||||
- `ground_truth`
|
||||
- `source_chunk_ids`
|
||||
- `question_type`
|
||||
- `difficulty`
|
||||
|
||||
### 12.3 数量控制
|
||||
|
||||
V1 默认:
|
||||
|
||||
- 每个文档最多生成 `10` 条题
|
||||
- 每组 chunk 最多生成 `1` 条题
|
||||
|
||||
实现时必须做覆盖率与多样性平衡,避免所有问题只集中在文档开头章节。
|
||||
|
||||
### 12.4 复核模式
|
||||
|
||||
V1 不自动发布最终 dataset,只输出草稿。
|
||||
|
||||
草稿规则:
|
||||
|
||||
- `review_status` 初始一律写为 `draft`
|
||||
- `review_notes` 初始为空
|
||||
- 人工可在 CSV 中修订问题、答案与审核状态
|
||||
|
||||
### 12.5 自动校验
|
||||
|
||||
候选题进入最终 dataset 前必须通过以下校验:
|
||||
|
||||
- `question` 非空
|
||||
- `ground_truth` 非空
|
||||
- `source_chunk_ids` 非空
|
||||
- 引用的 chunk 必须真实存在
|
||||
- 所有引用 chunk 必须来自同一文档
|
||||
- `question_type` 和 `difficulty` 必须落在允许枚举内
|
||||
|
||||
自动校验失败的候选题不进入最终 draft CSV。
|
||||
|
||||
### 12.6 去重规则
|
||||
|
||||
同一文档内执行如下去重:
|
||||
|
||||
- 问题文本归一化后完全相同则去重
|
||||
- 引用 chunk 完全相同且参考答案语义近似的候选题只保留一条
|
||||
|
||||
V1 去重目标是控制明显重复,不追求复杂聚类算法。
|
||||
|
||||
## 13. 输出资产设计
|
||||
|
||||
### 13.1 Dataset 输出
|
||||
|
||||
最终 dataset 默认输出到:
|
||||
|
||||
```text
|
||||
datasets/raw/generated/<job_name>.csv
|
||||
```
|
||||
|
||||
允许由 YAML 的 `output.dataset_path` 覆盖。
|
||||
|
||||
### 13.2 运行资产目录
|
||||
|
||||
每次构建任务的运行资产输出到:
|
||||
|
||||
```text
|
||||
outputs/dataset-builds/<job_name>/<run_id>/
|
||||
```
|
||||
|
||||
### 13.3 必须输出的资产
|
||||
|
||||
每次运行至少写出以下文件:
|
||||
|
||||
- `documents.jsonl`
|
||||
- `semantic_blocks.jsonl`
|
||||
- `source_chunks.jsonl`
|
||||
- `dataset_draft.csv`
|
||||
- `parse_failures.csv`
|
||||
- `metadata.json`
|
||||
|
||||
含义如下:
|
||||
|
||||
- `documents.jsonl`:逐文档解析摘要
|
||||
- `semantic_blocks.jsonl`:逐语义块中间结果
|
||||
- `source_chunks.jsonl`:逐切片证据结果
|
||||
- `dataset_draft.csv`:生成后的题库草稿
|
||||
- `parse_failures.csv`:失败文档清单
|
||||
- `metadata.json`:运行元数据、配置快照、统计结果
|
||||
|
||||
## 14. 与现有评测链路的兼容性修正
|
||||
|
||||
### 14.1 当前问题
|
||||
|
||||
当前仓库的文档设计已经说明 `online` 模式往往只需要:
|
||||
|
||||
- `question`
|
||||
- `ground_truth`
|
||||
|
||||
然后由 adapter 在评测时补齐:
|
||||
|
||||
- `answer`
|
||||
- `contexts`
|
||||
|
||||
但当前 `rag_eval/datasets/normalizers.py` 仍然把 `contexts / answer / ground_truth` 统一视作必填。这与文档目标架构不一致,也会直接阻塞本需求设计生成的在线题库接入。
|
||||
|
||||
### 14.2 修正原则
|
||||
|
||||
后续实现必须把 dataset 校验改成按 mode 分流:
|
||||
|
||||
- `offline` 模式必须具备 `question / contexts / answer / ground_truth`
|
||||
- `online` 模式必须具备 `question / ground_truth`
|
||||
- `online` 模式允许 `contexts / answer` 在初始数据集中为空
|
||||
|
||||
### 14.3 设计影响
|
||||
|
||||
这个修正不是附属优化,而是本需求能够成立的前置条件。否则生成出来的在线题库无法进入当前评测主流程。
|
||||
|
||||
## 15. 流程设计
|
||||
|
||||
一次完整的 dataset build job 执行流程如下:
|
||||
|
||||
1. 读取 `--dataset-build-config`
|
||||
2. 校验 YAML 并生成 `DatasetBuildJob`
|
||||
3. 扫描输入 PDF
|
||||
4. 按顺序或受控并发处理每个文档
|
||||
5. 调用阿里云文档解析
|
||||
6. 归一化 layout,生成 `ParsedDocument`
|
||||
7. 萃取 `SourceChunk`
|
||||
8. 基于 `SourceChunk` 调用 LLM 生成题库草稿
|
||||
9. 对候选题做结构校验与去重
|
||||
10. 写出 `dataset_draft.csv`
|
||||
11. 写出中间 artifacts 和失败清单
|
||||
12. 人工复核后,将复核版本作为 `online` 评测输入
|
||||
|
||||
## 16. 测试设计
|
||||
|
||||
### 16.1 配置测试
|
||||
|
||||
需要覆盖:
|
||||
|
||||
- `--scenario` 与 `--dataset-build-config` 互斥
|
||||
- 缺失必填字段
|
||||
- 非法枚举值
|
||||
- 输入路径不存在
|
||||
- 输入目录中没有 PDF
|
||||
|
||||
### 16.2 解析测试
|
||||
|
||||
使用 mocked 阿里云响应覆盖:
|
||||
|
||||
- 提交成功
|
||||
- 状态轮询成功
|
||||
- 状态轮询超时
|
||||
- 任务失败
|
||||
- 返回空 layouts
|
||||
|
||||
同时要覆盖版面归一化规则:
|
||||
|
||||
- 目录页跳过
|
||||
- 标题层级继承
|
||||
- 表格扁平化
|
||||
- 图注抽取
|
||||
- 长文本切块
|
||||
|
||||
### 16.3 题库生成测试
|
||||
|
||||
使用 mocked LLM 输出覆盖:
|
||||
|
||||
- 正常结构化生成
|
||||
- 空题目
|
||||
- 缺失 ground truth
|
||||
- 引用不存在 chunk
|
||||
- 跨文档引用
|
||||
- 重复问题去重
|
||||
|
||||
### 16.4 端到端测试
|
||||
|
||||
需要至少有一组 mocked parser + mocked generator 的端到端流程测试,验证:
|
||||
|
||||
- 单 PDF 输入
|
||||
- 多 PDF 输入
|
||||
- `fail` 模式
|
||||
- `skip` 模式
|
||||
- 所有 artifact 均成功写出
|
||||
|
||||
### 16.5 评测回归测试
|
||||
|
||||
需要新增测试确保:
|
||||
|
||||
- 只包含 `question / ground_truth / metadata` 的在线题库能被加载
|
||||
- adapter 补齐 `answer / contexts` 后,现有 evaluator 能继续跑完指标
|
||||
|
||||
## 17. 实施顺序建议
|
||||
|
||||
为了降低风险,后续实现建议按以下顺序推进:
|
||||
|
||||
1. 先扩展 `main.py` 和配置层,增加 dataset build 命令入口
|
||||
2. 再扩展 `settings.py` 与依赖,接入阿里云解析配置
|
||||
3. 迁移 parser gateway 与 layout normalizer
|
||||
4. 落地 `dataset_builder` 的 models、runner、writers
|
||||
5. 实现 LLM 题库生成与输出校验
|
||||
6. 修正现有 online dataset 校验逻辑
|
||||
7. 补测试、样例 YAML 和文档
|
||||
|
||||
## 18. 最终结论
|
||||
|
||||
本需求设计固定了一个清晰、可落地的 V1 范围:
|
||||
|
||||
- 用阿里云解析 PDF
|
||||
- 把解析结果转成可追溯 source chunks
|
||||
- 用 LLM 基于单文档内容生成在线评测题库草稿
|
||||
- 用人工复核保证最终质量
|
||||
- 复核后的题库直接接入现有 online 评测流程
|
||||
|
||||
这个设计刻意收窄了输入格式、题型边界和自动化深度,目的不是保守,而是先确保整条链路能够在当前仓库架构中闭环,并且不给后续实现留下需要临场决策的空白。
|
||||
623
docs/rag-eval-architecture.md
Normal file
623
docs/rag-eval-architecture.md
Normal file
@@ -0,0 +1,623 @@
|
||||
# RAG 评测平台架构设计
|
||||
|
||||
## 1. 背景与问题
|
||||
|
||||
当前仓库已经有一个可运行的离线评测原型,其能力已经收敛到统一的 `main.py --scenario ...` 入口与 `rag_eval/` 分层模块中。这个原型适合验证以下问题:
|
||||
|
||||
- 离线导出的 RAG 样本能否被标准化
|
||||
- `ragas` 指标能否稳定跑通
|
||||
- 基础评分结果能否写出为 CSV
|
||||
|
||||
但当评测对象从“一个数据文件、一次评测”演进为“多个 RAG 应用、多个数据集、多轮实验、多种配置对比”时,单脚本结构会迅速暴露问题:
|
||||
|
||||
- **职责耦合过高**:参数解析、数据加载、样本标准化、模型创建、指标执行、结果持久化全部混在一个脚本里
|
||||
- **难以扩展在线模式**:当前实现默认输入已经包含 `answer` 与 `contexts`,不适合直接接入运行中的 RAG 应用
|
||||
- **难以复现实验**:输出主要是单个 CSV,缺少运行快照、元数据和标准汇总
|
||||
- **难以对比不同方案**:没有统一场景配置,不便系统化比较应用版本、模型和指标组合
|
||||
- **难以承载多应用接入**:缺少稳定的应用适配层,无法把 HTTP 服务型应用和本地 Python 应用纳入统一流程
|
||||
|
||||
因此,本仓库需要从“离线评测脚本”演进为“RAG 评测平台骨架”。
|
||||
|
||||
## 2. 设计目标
|
||||
|
||||
目标架构需要满足以下设计目标:
|
||||
|
||||
- **平台化**:从一次性脚本升级为长期可演进的工程结构
|
||||
- **可扩展**:新增应用接入方式、数据集格式和指标时,不需要重写主流程
|
||||
- **可复现**:每次运行都能保留完整配置快照和结果资产
|
||||
- **可对比**:不同应用、模型、检索策略和提示词方案可以稳定横向比较
|
||||
- **统一双模式**:离线评测与在线评测共享同一条核心数据流
|
||||
- **多对象接入**:支持多应用、多数据集、多模型和多场景组合
|
||||
- **本地优先**:第一阶段以本地文件资产为中心,不引入数据库依赖
|
||||
|
||||
## 3. 非目标
|
||||
|
||||
本轮架构设计明确不覆盖以下方向:
|
||||
|
||||
- 不建设数据库中心化评测平台
|
||||
- 不建设前端控制台或 Web UI
|
||||
- 不建设远程任务调度与分布式执行系统
|
||||
- 不引入服务端依赖作为评测执行前提
|
||||
- 不在本轮实现完整目录骨架和模块代码,只固定设计边界
|
||||
|
||||
换句话说,当前阶段的目标是 **先把本地文件驱动的平台骨架设计定案**,而不是一步做成完整产品。
|
||||
|
||||
## 4. 目标架构总览
|
||||
|
||||
目标平台按照职责分为六层:
|
||||
|
||||
### 4.1 配置层
|
||||
|
||||
负责读取、校验和标准化 YAML 场景配置,产出统一的 `Scenario` 对象。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 解析场景配置文件
|
||||
- 应用默认值
|
||||
- 校验模式、模型、指标和运行参数
|
||||
- 生成运行快照
|
||||
|
||||
### 4.2 应用接入层
|
||||
|
||||
负责连接外部 RAG 应用或本地 Python RAG 函数,把调用结果统一转换为标准输出结构。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 定义统一请求输入
|
||||
- 屏蔽不同应用协议差异
|
||||
- 返回可映射为 `answer` 和 `contexts` 的标准响应
|
||||
|
||||
### 4.3 数据集层
|
||||
|
||||
负责加载原始样本、标准化样本和在线问题集,并统一转换为平台的标准评测样本结构。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 读取 CSV、Excel、JSONL 等数据源
|
||||
- 规范化字段
|
||||
- 校验必填字段
|
||||
- 输出可进入评测核心层的标准样本集合
|
||||
|
||||
### 4.4 指标层
|
||||
|
||||
负责按场景配置装配评测指标,形成可执行的指标流水线。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 构建 judge model 与 embedding model
|
||||
- 根据场景启用指定指标
|
||||
- 屏蔽底层评测库差异
|
||||
|
||||
### 4.5 执行层
|
||||
|
||||
负责把配置、数据、应用和指标串成一次完整运行。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 数据准备
|
||||
- 在线应用调用或离线结果加载
|
||||
- 样本标准化
|
||||
- 并发执行评分
|
||||
- 错误捕获与降级
|
||||
- 结果合并
|
||||
|
||||
### 4.6 结果层
|
||||
|
||||
负责把一次运行的所有结果沉淀为本地文件资产。
|
||||
|
||||
职责包括:
|
||||
|
||||
- 写出评分明细
|
||||
- 写出无效样本
|
||||
- 写出场景快照
|
||||
- 写出汇总报告
|
||||
- 写出元数据
|
||||
|
||||
## 5. 领域模型
|
||||
|
||||
为了让后续实现边界清晰,平台核心对象固定为以下六类。
|
||||
|
||||
### 5.1 `AppAdapter`
|
||||
|
||||
表示一个可被评测框架调用的 RAG 应用适配器。
|
||||
|
||||
职责:
|
||||
|
||||
- 接收标准问题输入
|
||||
- 调用实际 RAG 应用
|
||||
- 返回标准化响应结果
|
||||
|
||||
最小抽象能力:
|
||||
|
||||
- 输入:`question` 与可选上下文参数
|
||||
- 输出:必须能映射为 `answer` 和 `contexts`
|
||||
|
||||
### 5.2 `Dataset`
|
||||
|
||||
表示一次评测所使用的数据集定义。
|
||||
|
||||
职责:
|
||||
|
||||
- 描述数据来源
|
||||
- 加载原始样本
|
||||
- 输出标准评测样本
|
||||
|
||||
需要同时兼容:
|
||||
|
||||
- 离线导入样本
|
||||
- 在线问题样本
|
||||
- 后续可能的多文件数据集或分片数据集
|
||||
|
||||
### 5.3 `Scenario`
|
||||
|
||||
表示一次完整实验的配置定义,是平台的统一实验入口。
|
||||
|
||||
职责:
|
||||
|
||||
- 声明评测模式
|
||||
- 绑定应用、数据集、模型和指标
|
||||
- 定义输出目录与运行参数
|
||||
|
||||
`Scenario` 必须由 YAML 场景配置生成,而不是在代码里零散拼装。
|
||||
|
||||
### 5.4 `Evaluator`
|
||||
|
||||
表示一次评测执行器。
|
||||
|
||||
职责:
|
||||
|
||||
- 根据场景驱动一次完整运行
|
||||
- 调用数据集、应用适配器和指标流水线
|
||||
- 产出运行结果与错误信息
|
||||
|
||||
### 5.5 `MetricPipeline`
|
||||
|
||||
表示一组按场景组合好的指标执行单元。
|
||||
|
||||
职责:
|
||||
|
||||
- 初始化 judge model 与 embedding model
|
||||
- 按统一接口执行多个指标
|
||||
- 输出结构化评分结果
|
||||
|
||||
### 5.6 `RunArtifact`
|
||||
|
||||
表示一次运行沉淀的结果资产集合。
|
||||
|
||||
职责:
|
||||
|
||||
- 固定结果文件布局
|
||||
- 保存配置快照、评分结果、异常样本、汇总报告和元数据
|
||||
- 作为后续复现、审计和对比的最小单元
|
||||
|
||||
## 6. 应用接入层设计
|
||||
|
||||
应用接入层把“如何调用 RAG 应用”从“如何做评测”中解耦。后续只保留两类一等公民适配器:`HTTP Adapter` 和 `Python Function Adapter`。
|
||||
|
||||
### 6.1 HTTP Adapter
|
||||
|
||||
适用于外部部署的 RAG 服务。
|
||||
|
||||
输入约束:
|
||||
|
||||
- 标准问题 `question`
|
||||
- 可选上下文参数,例如租户、知识库、会话配置、检索参数
|
||||
|
||||
输出约束:
|
||||
|
||||
- 必须能够映射出 `answer`
|
||||
- 必须能够映射出 `contexts`
|
||||
|
||||
推荐统一响应语义:
|
||||
|
||||
```json
|
||||
{
|
||||
"answer": "string",
|
||||
"contexts": ["context 1", "context 2"],
|
||||
"raw_response": {}
|
||||
}
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 真实 HTTP 响应可以与此不同
|
||||
- 但 Adapter 层必须把实际响应转换成上述平台内部语义
|
||||
- `raw_response` 可选保留,用于调试和审计,但不作为核心评测字段依赖
|
||||
|
||||
### 6.2 Python Function Adapter
|
||||
|
||||
适用于本地 Python 形式的 RAG 应用,例如本地函数、SDK 包装器或 Notebook 中已封装的检索问答逻辑。
|
||||
|
||||
输入输出约束与 HTTP Adapter 保持同构:
|
||||
|
||||
- 输入:`question` 与可选上下文参数
|
||||
- 输出:必须能映射为 `answer` 和 `contexts`
|
||||
|
||||
推荐函数语义:
|
||||
|
||||
```python
|
||||
def run(question: str, **kwargs) -> dict:
|
||||
return {
|
||||
"answer": "...",
|
||||
"contexts": ["...", "..."],
|
||||
"raw_response": {...},
|
||||
}
|
||||
```
|
||||
|
||||
设计原则:
|
||||
|
||||
- 上层评测执行器不应该关心底层是 HTTP 调用还是 Python 函数调用
|
||||
- 只要符合统一输入输出契约,就可以接入同一条评测管线
|
||||
|
||||
## 7. 数据集层设计
|
||||
|
||||
数据集层的目标是把不同来源的样本统一为同一标准格式。
|
||||
|
||||
### 7.1 原始样本
|
||||
|
||||
原始样本是未经平台标准化的输入,可能来自:
|
||||
|
||||
- 业务系统导出的 CSV 或 Excel
|
||||
- 在线评测时的问题集
|
||||
- 后续数据清洗脚本生成的中间文件
|
||||
|
||||
原始样本允许存在额外字段,但不应该直接进入评测核心层。
|
||||
|
||||
### 7.2 标准化评测样本
|
||||
|
||||
无论数据来源如何,进入评测核心层前都必须变成同一标准样本结构。最小核心字段固定为:
|
||||
|
||||
- `question`
|
||||
- `contexts`
|
||||
- `answer`
|
||||
- `ground_truth`
|
||||
|
||||
可选元数据字段可包括:
|
||||
|
||||
- `sample_id`
|
||||
- `scenario`
|
||||
- `language`
|
||||
- `retrieval_config`
|
||||
|
||||
约束如下:
|
||||
|
||||
- `question`:字符串
|
||||
- `contexts`:有序文本列表
|
||||
- `answer`:字符串
|
||||
- `ground_truth`:字符串
|
||||
|
||||
### 7.3 在线生成样本与离线导入样本的统一格式
|
||||
|
||||
双模式统一约束如下:
|
||||
|
||||
- **离线模式**:输入文件本身已包含 `question / contexts / answer / ground_truth`
|
||||
- **在线模式**:问题集先提供 `question` 与必要元信息,应用调用后补齐 `answer / contexts`,再与参考答案或标注结果结合形成标准样本
|
||||
|
||||
统一要求:
|
||||
|
||||
- 不允许在线模式绕过标准样本结构直接进入指标执行层
|
||||
- 不允许离线模式在结果写出前使用独立的资产格式
|
||||
|
||||
这保证了后续的指标层、执行层和结果层可以完全共享。
|
||||
|
||||
## 8. 场景配置设计
|
||||
|
||||
YAML 是未来统一实验入口。README 只给最小示例,详细字段定义在本节固定。
|
||||
|
||||
### 8.1 最小骨架字段
|
||||
|
||||
```yaml
|
||||
scenario_name: legal-assistant-offline-baseline
|
||||
mode: offline
|
||||
app_adapter: null
|
||||
dataset: datasets/normalized/legal_assistant_baseline.csv
|
||||
judge_model: deepseek-v4-flash
|
||||
embedding_model: text-embedding-v3
|
||||
metrics:
|
||||
- faithfulness
|
||||
- answer_relevancy
|
||||
- context_recall
|
||||
- context_precision
|
||||
output_dir: runs/legal-assistant-offline-baseline
|
||||
runtime:
|
||||
batch_size: 4
|
||||
```
|
||||
|
||||
### 8.2 字段说明
|
||||
|
||||
- `scenario_name`
|
||||
- 场景名称,用于标识一次实验
|
||||
- `mode: offline | online`
|
||||
- 评测模式
|
||||
- `app_adapter`
|
||||
- 应用适配器定义;离线模式可为 `null`,在线模式必须提供
|
||||
- `dataset`
|
||||
- 数据集路径或数据集定义引用
|
||||
- `judge_model`
|
||||
- 负责评分类指标推理的模型
|
||||
- `embedding_model`
|
||||
- 负责向量相关指标的模型
|
||||
- `metrics`
|
||||
- 本次启用的指标列表
|
||||
- `output_dir`
|
||||
- 本次运行结果输出目录
|
||||
- `runtime.batch_size`
|
||||
- 并发批次大小
|
||||
|
||||
### 8.3 在线模式的 `app_adapter` 形态
|
||||
|
||||
后续建议支持如下两种声明方式:
|
||||
|
||||
```yaml
|
||||
app_adapter:
|
||||
type: http
|
||||
endpoint: https://example-rag/api/ask
|
||||
method: POST
|
||||
timeout_seconds: 30
|
||||
```
|
||||
|
||||
```yaml
|
||||
app_adapter:
|
||||
type: python
|
||||
callable: apps.legal_assistant.adapter:run
|
||||
```
|
||||
|
||||
说明:
|
||||
|
||||
- 这是配置接口约束,不代表当前仓库已经具备解析实现
|
||||
- 字段可以后续扩充,但类型边界本轮即固定为 `http` 与 `python`
|
||||
|
||||
## 9. 评测执行层设计
|
||||
|
||||
评测执行层负责把一次实验变成稳定、可审计的运行流程。执行顺序固定如下。
|
||||
|
||||
### 9.1 数据准备
|
||||
|
||||
根据 `Scenario` 加载数据集定义,并读取原始样本。
|
||||
|
||||
### 9.2 应用调用或离线加载
|
||||
|
||||
- `offline` 模式:直接读取包含评测核心字段的离线样本
|
||||
- `online` 模式:读取问题集,调用 `AppAdapter` 获取 `answer` 与 `contexts`
|
||||
|
||||
### 9.3 样本标准化
|
||||
|
||||
所有样本统一映射为标准结构:
|
||||
|
||||
- `question`
|
||||
- `contexts`
|
||||
- `answer`
|
||||
- `ground_truth`
|
||||
|
||||
不合法样本在此阶段被分流为无效样本,而不是在指标执行阶段失败后再回溯处理。
|
||||
|
||||
### 9.4 指标编排
|
||||
|
||||
根据 `Scenario.metrics` 创建 `MetricPipeline`,并装配:
|
||||
|
||||
- judge model
|
||||
- embedding model
|
||||
- 指标实例
|
||||
|
||||
### 9.5 并发控制
|
||||
|
||||
执行层负责并发上限,不把并发策略散落到各指标实现中。
|
||||
|
||||
最低要求:
|
||||
|
||||
- 允许通过 `runtime.batch_size` 控制最大并发
|
||||
- 在线调用和指标评分后续可以拥有独立并发策略
|
||||
- 并发策略由执行层统一调度
|
||||
|
||||
### 9.6 错误捕获
|
||||
|
||||
错误需要按层次捕获:
|
||||
|
||||
- 数据加载错误
|
||||
- 应用调用错误
|
||||
- 指标执行错误
|
||||
- 结果写出错误
|
||||
|
||||
原则:
|
||||
|
||||
- 单条样本失败不应默认导致整批运行失败
|
||||
- 失败信息要体现在结果资产中
|
||||
- 不应只把错误打印到控制台后丢失
|
||||
|
||||
### 9.7 结果合并
|
||||
|
||||
最终输出应包含:
|
||||
|
||||
- 原始样本字段
|
||||
- 标准化样本字段
|
||||
- 指标分数
|
||||
- 错误字段
|
||||
- 运行元信息
|
||||
|
||||
## 10. 结果资产设计
|
||||
|
||||
平台统一采用“run 目录”模式,不引入数据库前置依赖。每次运行输出固定如下:
|
||||
|
||||
```text
|
||||
runs/<run_id>/
|
||||
├── scenario.snapshot.yaml
|
||||
├── scores.csv
|
||||
├── invalid.csv
|
||||
├── summary.md
|
||||
└── metadata.json
|
||||
```
|
||||
|
||||
各文件职责固定如下。
|
||||
|
||||
### 10.1 `runs/<run_id>/scenario.snapshot.yaml`
|
||||
|
||||
保存本次评测实际使用的场景快照,确保即使原始场景文件后续变化,也能复现本次运行。
|
||||
|
||||
### 10.2 `runs/<run_id>/scores.csv`
|
||||
|
||||
保存逐样本评测结果,至少包括:
|
||||
|
||||
- 标准样本字段
|
||||
- 指标分数
|
||||
- 错误信息
|
||||
- 模型与运行时间等元信息字段
|
||||
|
||||
### 10.3 `runs/<run_id>/invalid.csv`
|
||||
|
||||
保存标准化失败、关键字段缺失或格式不合法的样本,用于追踪数据质量问题。
|
||||
|
||||
### 10.4 `runs/<run_id>/summary.md`
|
||||
|
||||
保存面向人阅读的汇总结论,例如:
|
||||
|
||||
- 总样本数、有效样本数、无效样本数
|
||||
- 各指标均值
|
||||
- 分组统计
|
||||
- 低分样本观察
|
||||
|
||||
### 10.5 `runs/<run_id>/metadata.json`
|
||||
|
||||
保存机器可读元数据,例如:
|
||||
|
||||
- `run_id`
|
||||
- `scenario_name`
|
||||
- `mode`
|
||||
- `judge_model`
|
||||
- `embedding_model`
|
||||
- `started_at`
|
||||
- `finished_at`
|
||||
- 代码版本或 Git commit(后续可选)
|
||||
|
||||
统一约束:
|
||||
|
||||
- 无论数据来自在线还是离线模式,结果都统一写入本地文件资产
|
||||
- 在引入数据库前,不允许出现只写数据库不落本地文件的实现分支
|
||||
|
||||
## 11. 推荐代码目录结构
|
||||
|
||||
后续推荐代码骨架如下。该结构作为实现边界约束,供后续重构与新增模块直接遵循。
|
||||
|
||||
```text
|
||||
.
|
||||
├── apps/
|
||||
│ ├── <app_name>/
|
||||
│ │ ├── adapter.py
|
||||
│ │ └── README.md
|
||||
├── datasets/
|
||||
│ ├── raw/
|
||||
│ ├── normalized/
|
||||
│ └── samples/
|
||||
├── scenarios/
|
||||
│ ├── offline/
|
||||
│ └── online/
|
||||
├── rag_eval/
|
||||
│ ├── config/
|
||||
│ │ ├── loader.py
|
||||
│ │ ├── schema.py
|
||||
│ │ └── validators.py
|
||||
│ ├── adapters/
|
||||
│ │ ├── base.py
|
||||
│ │ ├── http.py
|
||||
│ │ └── python.py
|
||||
│ ├── datasets/
|
||||
│ │ ├── loader.py
|
||||
│ │ ├── normalizers.py
|
||||
│ │ └── validators.py
|
||||
│ ├── metrics/
|
||||
│ │ ├── factory.py
|
||||
│ │ ├── pipeline.py
|
||||
│ │ └── registry.py
|
||||
│ ├── execution/
|
||||
│ │ ├── evaluator.py
|
||||
│ │ ├── runner.py
|
||||
│ │ ├── concurrency.py
|
||||
│ │ └── errors.py
|
||||
│ ├── reporting/
|
||||
│ │ ├── artifacts.py
|
||||
│ │ ├── summary.py
|
||||
│ │ └── writers.py
|
||||
│ ├── shared/
|
||||
│ │ ├── types.py
|
||||
│ │ ├── models.py
|
||||
│ │ └── utils.py
|
||||
│ ├── compat.py
|
||||
│ └── settings.py
|
||||
├── runs/
|
||||
├── docs/
|
||||
├── tests/
|
||||
├── main.py
|
||||
└── README.md
|
||||
```
|
||||
|
||||
模块分层约束固定如下:
|
||||
|
||||
- `rag_eval/config/`:配置加载与校验
|
||||
- `rag_eval/adapters/`:HTTP / Python 应用接入
|
||||
- `rag_eval/datasets/`:加载、标准化、校验
|
||||
- `rag_eval/metrics/`:指标装配与扩展
|
||||
- `rag_eval/execution/`:运行编排、并发、错误处理
|
||||
- `rag_eval/reporting/`:结果写出、汇总、报告
|
||||
- `rag_eval/shared/`:通用类型与工具
|
||||
|
||||
后续实现不应再把这些职责重新合并回单个入口脚本。
|
||||
|
||||
## 12. 演进路线
|
||||
|
||||
当前仓库已经完成从单脚本离线评测到统一 CLI 的第一轮收敛,后续重点不再是移除旧入口,而是继续完善场景、接入、结果治理与测试能力。
|
||||
|
||||
推荐拆分路径如下:
|
||||
|
||||
### 12.1 第一阶段:抽离离线模式公共能力(已完成)
|
||||
|
||||
当前已完成以下能力下沉:
|
||||
|
||||
- 输入文件加载
|
||||
- 样本标准化
|
||||
- 指标装配
|
||||
- 结果写出
|
||||
|
||||
结果是离线模式已经摆脱“单文件全包”的结构,进入统一模块分层。
|
||||
|
||||
### 12.2 第二阶段:引入 `Scenario` 与 YAML 配置(已完成)
|
||||
|
||||
当前主入口已经改为读取 YAML 场景配置。
|
||||
|
||||
### 12.3 第三阶段:引入应用适配器(已完成第一版)
|
||||
|
||||
当前已具备在线模式的一等公民适配器:
|
||||
|
||||
- HTTP Adapter
|
||||
- Python Function Adapter
|
||||
|
||||
在线和离线模式已经共享同一条标准化与评分流程。
|
||||
|
||||
### 12.4 第四阶段:统一 CLI 入口(已完成)
|
||||
|
||||
当前统一 CLI 入口如下:
|
||||
|
||||
```powershell
|
||||
python main.py --scenario scenarios/offline/sample.yaml
|
||||
```
|
||||
|
||||
旧的兼容入口已经移除,仓库统一以 scenario 驱动的入口为准。
|
||||
|
||||
### 12.5 第五阶段:完善结果治理与测试
|
||||
|
||||
补齐:
|
||||
|
||||
- `runs/` 目录规范落地
|
||||
- 汇总报告生成
|
||||
- 在线/离线统一回归测试
|
||||
- 多应用与多场景对比样例
|
||||
|
||||
## 结论
|
||||
|
||||
本设计文档的核心决策已经固定:
|
||||
|
||||
- 统一采用 YAML 作为实验入口
|
||||
- 统一支持在线与离线双模式
|
||||
- 统一以标准样本结构进入评测核心层
|
||||
- 统一把结果写入本地 `run` 目录资产
|
||||
- 统一按照配置层、接入层、数据层、指标层、执行层、结果层拆分代码
|
||||
|
||||
后续工程实现应直接遵循这些边界推进,而不再重新讨论整体骨架。
|
||||
416
docs/rag-eval-engine-flow.md
Normal file
416
docs/rag-eval-engine-flow.md
Normal file
@@ -0,0 +1,416 @@
|
||||
# RAG 评测引擎链路说明
|
||||
|
||||
## 1. 这份文档解决什么问题
|
||||
|
||||
`docs/rag-eval-architecture.md` 主要回答“为什么这样分层、模块边界是什么”。
|
||||
这份文档回答的是另一件事:**一次评测在代码里到底是怎么跑起来的**。
|
||||
|
||||
如果你现在对下面这些问题还有点混淆,这份文档就是给你的:
|
||||
|
||||
- `rag_eval/` 到底是评测什么的
|
||||
- `apps/` 在整个架构里扮演什么角色
|
||||
- `offline` 和 `online` 模式的区别是什么
|
||||
- dataset、adapter、metrics、reporting 是怎么串起来的
|
||||
|
||||
---
|
||||
|
||||
## 2. 一句话理解整个引擎
|
||||
|
||||
这套系统本质上是一条标准化评测流水线:
|
||||
|
||||
```text
|
||||
scenario -> dataset -> normalize -> app adapter -> metrics -> reporting -> run artifacts
|
||||
```
|
||||
|
||||
也可以拆成更容易理解的话:
|
||||
|
||||
1. 先读取一份评测场景配置
|
||||
2. 再读取待评测数据
|
||||
3. 把原始数据标准化成统一样本结构
|
||||
4. 如果需要,调用你的 RAG 应用补齐 `answer` 和 `contexts`
|
||||
5. 用 `ragas` 指标计算分数
|
||||
6. 把结果写到本地 run 目录
|
||||
|
||||
---
|
||||
|
||||
## 3. 目录职责
|
||||
|
||||
### `rag_eval/`
|
||||
|
||||
这是**评测引擎本体**。它负责:
|
||||
|
||||
- 加载 scenario
|
||||
- 加载 dataset
|
||||
- 调用 app adapter
|
||||
- 执行指标评分
|
||||
- 写出结果资产
|
||||
|
||||
### `apps/`
|
||||
|
||||
这是**被评测应用的接入层**,不是评测框架本身。
|
||||
|
||||
这里放的是“你的 RAG 应用如何被框架调用”的示例或适配代码。例如:
|
||||
|
||||
```text
|
||||
apps/
|
||||
└── sample_python/
|
||||
├── adapter.py
|
||||
└── README.md
|
||||
```
|
||||
|
||||
`apps/sample_python/` 的意义不是提供评测逻辑,而是演示:
|
||||
|
||||
- 如果你的应用是本地 Python 函数
|
||||
- 它应该暴露什么接口
|
||||
- 返回值要长什么样
|
||||
|
||||
### `scenarios/`
|
||||
|
||||
这是评测配置层,用 YAML 声明:
|
||||
|
||||
- 评测模式
|
||||
- 数据集路径
|
||||
- judge / embedding 模型
|
||||
- 要跑哪些 metrics
|
||||
- 输出目录
|
||||
- 是否要调用 `http` 或 `python` adapter
|
||||
|
||||
### `datasets/`
|
||||
|
||||
这里存放评测输入数据。通常分为:
|
||||
|
||||
- `raw/`:原始输入
|
||||
- `normalized/`:整理后的标准评测样本
|
||||
|
||||
### `outputs/` 或 `runs/`
|
||||
|
||||
这里存放每次评测生成的结果资产,比如:
|
||||
|
||||
- `scores.csv`
|
||||
- `invalid.csv`
|
||||
- `summary.md`
|
||||
- `metadata.json`
|
||||
|
||||
---
|
||||
|
||||
## 4. 主入口链路
|
||||
|
||||
统一主链路最终都会走到:
|
||||
|
||||
- [rag_eval/execution/runner.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/execution/runner.py:1)
|
||||
|
||||
核心入口函数是 `run_scenario()`,它负责把所有子模块串起来。
|
||||
|
||||
简化后的执行顺序是:
|
||||
|
||||
```text
|
||||
run_scenario()
|
||||
-> load_scenario()
|
||||
-> build_adapter()
|
||||
-> build_metric_pipeline()
|
||||
-> Evaluator.evaluate()
|
||||
-> write_run_artifacts()
|
||||
```
|
||||
|
||||
你可以把它理解成整套系统的 orchestration 层。
|
||||
|
||||
---
|
||||
|
||||
## 5. Scenario 链路
|
||||
|
||||
scenario 是一次评测任务的“总配置”。
|
||||
|
||||
相关代码:
|
||||
|
||||
- [rag_eval/config/loader.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/config/loader.py:1)
|
||||
- [rag_eval/config/schema.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/config/schema.py:1)
|
||||
- [rag_eval/config/validators.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/config/validators.py:1)
|
||||
|
||||
它定义的内容包括:
|
||||
|
||||
- `mode`: `offline` 或 `online`
|
||||
- `dataset`
|
||||
- `judge_model`
|
||||
- `embedding_model`
|
||||
- `metrics`
|
||||
- `output_dir`
|
||||
- `runtime`
|
||||
- `app_adapter`
|
||||
|
||||
作用可以概括为一句话:
|
||||
**scenario 决定“这次评测要怎么跑”。**
|
||||
|
||||
---
|
||||
|
||||
## 6. Dataset 链路
|
||||
|
||||
数据进入评测引擎后,不会直接拿原始 CSV 去打分,而是要先标准化。
|
||||
|
||||
相关代码:
|
||||
|
||||
- [rag_eval/datasets/loader.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/datasets/loader.py:1)
|
||||
- [rag_eval/datasets/validators.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/datasets/validators.py:1)
|
||||
- [rag_eval/datasets/normalizers.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/datasets/normalizers.py:1)
|
||||
|
||||
它做三件事:
|
||||
|
||||
1. 读取 CSV / Excel / JSONL
|
||||
2. 校验必要字段
|
||||
3. 转成统一内部对象 `NormalizedSample`
|
||||
|
||||
统一样本结构定义在:
|
||||
|
||||
- [rag_eval/shared/models.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/shared/models.py:1)
|
||||
|
||||
最关键字段是:
|
||||
|
||||
- `question`
|
||||
- `contexts`
|
||||
- `answer`
|
||||
- `ground_truth`
|
||||
|
||||
这个统一结构是后续 metrics 和 reporting 能共用一条链路的前提。
|
||||
|
||||
---
|
||||
|
||||
## 7. Offline 和 Online 的真正区别
|
||||
|
||||
这是整个系统最关键的分叉点。
|
||||
|
||||
### Offline 模式
|
||||
|
||||
离线模式下,dataset 里已经有完整评测字段:
|
||||
|
||||
- `question`
|
||||
- `contexts`
|
||||
- `answer`
|
||||
- `ground_truth`
|
||||
|
||||
所以链路是:
|
||||
|
||||
```text
|
||||
load dataset -> normalize -> score metrics -> write artifacts
|
||||
```
|
||||
|
||||
这个模式不需要调用你的应用。
|
||||
|
||||
### Online 模式
|
||||
|
||||
在线模式下,dataset 往往只有:
|
||||
|
||||
- `question`
|
||||
- 一些 metadata
|
||||
|
||||
`answer` 和 `contexts` 需要评测时实时调用你的 RAG 应用拿回来。
|
||||
|
||||
所以链路会变成:
|
||||
|
||||
```text
|
||||
load dataset -> normalize -> call app adapter -> enrich samples -> score metrics -> write artifacts
|
||||
```
|
||||
|
||||
在线模式比离线模式多出来的核心环节,就是 **adapter 调用**。
|
||||
|
||||
---
|
||||
|
||||
## 8. `apps/sample_python/` 到底是干什么的
|
||||
|
||||
这个目录是一个 **Python adapter 示例**。
|
||||
|
||||
相关文件:
|
||||
|
||||
- [apps/sample_python/adapter.py](/C:/Users/A200477427/Learnings/ragas-template/apps/sample_python/adapter.py:1)
|
||||
- [apps/sample_python/README.md](/C:/Users/A200477427/Learnings/ragas-template/apps/sample_python/README.md:1)
|
||||
|
||||
它演示了:如果你的 RAG 应用是本地 Python 代码,那么框架期望你提供一个这样的函数:
|
||||
|
||||
```python
|
||||
def run(question: str, **kwargs) -> dict:
|
||||
return {
|
||||
"answer": "...",
|
||||
"contexts": ["...", "..."],
|
||||
"raw_response": {...},
|
||||
}
|
||||
```
|
||||
|
||||
也就是说,`apps/sample_python/` 不是评测引擎的一部分,而是“被评测应用”的一个参考接入模板。
|
||||
|
||||
它的作用是:
|
||||
|
||||
- 告诉你 Python 类型应用如何接入
|
||||
- 给 `python` adapter 一个最小可运行示例
|
||||
- 让你后续把真实 RAG 逻辑替换进去
|
||||
|
||||
---
|
||||
|
||||
## 9. Adapter 链路
|
||||
|
||||
adapter 层的目标是:**把不同类型的目标应用,统一成同一套输入输出协议。**
|
||||
|
||||
相关代码:
|
||||
|
||||
- [rag_eval/adapters/base.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/adapters/base.py:1)
|
||||
- [rag_eval/adapters/http.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/adapters/http.py:1)
|
||||
- [rag_eval/adapters/python.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/adapters/python.py:1)
|
||||
|
||||
当前支持两类 adapter:
|
||||
|
||||
### `python`
|
||||
|
||||
适用于本地 Python 应用。
|
||||
框架会根据 scenario 里的 `module:function` 动态加载函数,然后调用它。
|
||||
|
||||
### `http`
|
||||
|
||||
适用于独立 HTTP 服务。
|
||||
框架会构造请求、解析响应,并映射到统一结构。
|
||||
|
||||
无论哪种 adapter,最后都要返回统一结果:
|
||||
|
||||
- `answer`
|
||||
- `contexts`
|
||||
- `raw_response`(可选)
|
||||
|
||||
这一步很关键,因为 metrics 层不应该关心底层到底是 HTTP 服务还是 Python 函数。
|
||||
|
||||
---
|
||||
|
||||
## 10. Evaluator 链路
|
||||
|
||||
评测执行核心在:
|
||||
|
||||
- [rag_eval/execution/evaluator.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/execution/evaluator.py:1)
|
||||
|
||||
`Evaluator.evaluate()` 大致会做这些事:
|
||||
|
||||
1. 记录开始时间
|
||||
2. 加载 dataset
|
||||
3. 标准化样本
|
||||
4. 如果是 `online`,先调用 adapter 补齐样本
|
||||
5. 调用 metric pipeline 打分
|
||||
6. 合并样本字段和评分结果
|
||||
7. 返回 `EvaluationResult`
|
||||
|
||||
这里可以把 `Evaluator` 理解成:
|
||||
|
||||
**一次评测运行的总执行器**
|
||||
|
||||
---
|
||||
|
||||
## 11. Metric Pipeline 链路
|
||||
|
||||
相关代码:
|
||||
|
||||
- [rag_eval/metrics/factory.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/metrics/factory.py:1)
|
||||
- [rag_eval/metrics/pipeline.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/metrics/pipeline.py:1)
|
||||
- [rag_eval/metrics/registry.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/metrics/registry.py:1)
|
||||
|
||||
这里的职责不是“决定评什么”,而是“把要评的指标真正跑起来”。
|
||||
|
||||
具体包括:
|
||||
|
||||
- 初始化 OpenAI client
|
||||
- 创建 judge model / embedding model
|
||||
- 根据 scenario 装配对应的 ragas metrics
|
||||
- 并发执行单样本或批量评分
|
||||
|
||||
当前支持的指标包括:
|
||||
|
||||
- `faithfulness`
|
||||
- `answer_relevancy`
|
||||
- `context_recall`
|
||||
- `context_precision`
|
||||
|
||||
所以 metric pipeline 的职责可以总结为:
|
||||
|
||||
**把标准样本转换成结构化评分结果。**
|
||||
|
||||
---
|
||||
|
||||
## 12. Reporting 链路
|
||||
|
||||
相关代码:
|
||||
|
||||
- [rag_eval/reporting/artifacts.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/reporting/artifacts.py:1)
|
||||
- [rag_eval/reporting/summary.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/reporting/summary.py:1)
|
||||
- [rag_eval/reporting/writers.py](/C:/Users/A200477427/Learnings/ragas-template/rag_eval/reporting/writers.py:1)
|
||||
|
||||
当评测完成后,结果不会只打印在终端,而是会沉淀成标准资产。
|
||||
|
||||
标准输出一般包括:
|
||||
|
||||
- `scenario.snapshot.yaml`
|
||||
- `scores.csv`
|
||||
- `invalid.csv`
|
||||
- `summary.md`
|
||||
- `metadata.json`
|
||||
|
||||
这是整套架构很重要的价值点,因为它让每次 run 都具备:
|
||||
|
||||
- 可复现性
|
||||
- 可审计性
|
||||
- 可对比性
|
||||
|
||||
---
|
||||
|
||||
## 13. 两条完整链路示意
|
||||
|
||||
### Offline 完整链路
|
||||
|
||||
```text
|
||||
main.py
|
||||
-> run_scenario()
|
||||
-> load_scenario()
|
||||
-> load_dataset_records()
|
||||
-> normalize_records()
|
||||
-> build_metric_pipeline()
|
||||
-> score_samples()
|
||||
-> write_run_artifacts()
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
- 不调用被评测应用
|
||||
- 直接对现成样本评分
|
||||
|
||||
### Online 完整链路
|
||||
|
||||
```text
|
||||
main.py
|
||||
-> run_scenario()
|
||||
-> load_scenario()
|
||||
-> build_adapter()
|
||||
-> load_dataset_records()
|
||||
-> normalize_records()
|
||||
-> adapter.enrich_sample()
|
||||
-> build_metric_pipeline()
|
||||
-> score_samples()
|
||||
-> write_run_artifacts()
|
||||
```
|
||||
|
||||
特点:
|
||||
|
||||
- 会先调用目标应用
|
||||
- 再对实时生成的 `answer / contexts` 评分
|
||||
|
||||
---
|
||||
|
||||
## 14. 你应该怎么理解这套架构
|
||||
|
||||
如果只记一条心智模型,可以记这个:
|
||||
|
||||
- `rag_eval/` 负责“怎么评”
|
||||
- `apps/` 负责“被评的应用怎么接进来”
|
||||
- `datasets/` 负责“评测输入是什么”
|
||||
- `scenarios/` 负责“这次评测要怎么配置”
|
||||
- `reporting/` 负责“结果怎么沉淀”
|
||||
|
||||
从工程拆分上看,这个架构的核心价值不是“能跑一次评测”,而是:
|
||||
|
||||
- 可以反复跑
|
||||
- 可以换应用跑
|
||||
- 可以换数据跑
|
||||
- 可以换模型跑
|
||||
- 可以把每次实验的资产稳定留住
|
||||
|
||||
这也是它和一次性离线脚本的根本区别。
|
||||
222
docs/sample-pdf-question-bank-workflow.md
Normal file
222
docs/sample-pdf-question-bank-workflow.md
Normal file
@@ -0,0 +1,222 @@
|
||||
# `sample-pdf-question-bank` 端到端使用说明
|
||||
|
||||
这篇文档对应仓库里的真实案例:先把 PDF 解析成 question bank,再用 online evaluator 基于证据 chunk 生成答案并打分。
|
||||
|
||||
完整链路是:
|
||||
|
||||
```text
|
||||
PDFs
|
||||
-> dataset_build
|
||||
-> sample-pdf-question-bank.csv + latest/source_chunks.jsonl
|
||||
-> online adapter
|
||||
-> answer + contexts
|
||||
-> ragas metrics
|
||||
```
|
||||
|
||||
## 1. 先准备环境
|
||||
|
||||
先复制环境变量模板:
|
||||
|
||||
```powershell
|
||||
Copy-Item .env.example .env
|
||||
```
|
||||
|
||||
这条案例链路依赖两类能力:
|
||||
|
||||
- OpenAI 兼容模型
|
||||
- `OPENAI_API_KEY`
|
||||
- `OPENAI_BASE_URL`
|
||||
- 阿里云文档解析
|
||||
- `ALIBABA_ACCESS_KEY_ID`
|
||||
- `ALIBABA_ACCESS_KEY_SECRET`
|
||||
- `ALIBABA_ENDPOINT`
|
||||
|
||||
默认还会用到这些模型配置:
|
||||
|
||||
- `DATASET_GENERATOR_MODEL=qwen3.6-plus`
|
||||
- `RAGAS_JUDGE_MODEL=deepseek-v4-flash`
|
||||
- `RAGAS_EMBEDDING_MODEL=text-embedding-v3`
|
||||
|
||||
如果少了 `OPENAI_API_KEY`,dataset build 里的题库生成和 online eval 都无法运行。
|
||||
如果少了阿里云凭据,PDF 解析阶段无法运行。
|
||||
|
||||
## 2. 跑 dataset build
|
||||
|
||||
使用 sample 配置:
|
||||
|
||||
- config: [scenarios/dataset_build/sample-pdf-build.yaml](/C:/Users/A200477427/Learnings/ragas-template/scenarios/dataset_build/sample-pdf-build.yaml)
|
||||
|
||||
执行命令:
|
||||
|
||||
```powershell
|
||||
uv run main.py --dataset-build-config scenarios/dataset_build/sample-pdf-build.yaml
|
||||
or
|
||||
.\.venv\Scripts\python.exe main.py --dataset-build-config scenarios/dataset_build/sample-pdf-build.yaml
|
||||
```
|
||||
|
||||
这一步会做四件事:
|
||||
|
||||
1. 扫描 `datasets/raw/pdfs` 下的 PDF
|
||||
2. 调用阿里云解析生成结构化 `source chunks`
|
||||
3. 调用 LLM 生成 question bank 草稿
|
||||
4. 写出稳定 dataset 和详细 run 资产
|
||||
|
||||
## 3. 看 build 产物
|
||||
|
||||
跑完以后,你会看到两类输出。
|
||||
|
||||
第一类是稳定入口,给后续 online eval 和教程使用:
|
||||
|
||||
- question bank CSV:
|
||||
[datasets/raw/generated/sample-pdf-question-bank.csv](/C:/Users/A200477427/Learnings/ragas-template/datasets/raw/generated/sample-pdf-question-bank.csv)
|
||||
- latest source chunks:
|
||||
[source_chunks.jsonl](/C:/Users/A200477427/Learnings/ragas-template/outputs/dataset-builds/sample-pdf-question-bank/latest/source_chunks.jsonl)
|
||||
- latest dataset draft:
|
||||
[dataset_draft.csv](/C:/Users/A200477427/Learnings/ragas-template/outputs/dataset-builds/sample-pdf-question-bank/latest/dataset_draft.csv)
|
||||
- latest metadata:
|
||||
[metadata.json](/C:/Users/A200477427/Learnings/ragas-template/outputs/dataset-builds/sample-pdf-question-bank/latest/metadata.json)
|
||||
|
||||
第二类是带时间戳的 run 级资产,用来审计和排查:
|
||||
|
||||
- 目录模式:
|
||||
`outputs/dataset-builds/sample-pdf-question-bank/<run_id>/`
|
||||
|
||||
其中常见文件有:
|
||||
|
||||
- `documents.jsonl`
|
||||
- `semantic_blocks.jsonl`
|
||||
- `source_chunks.jsonl`
|
||||
- `dataset_draft.csv`
|
||||
- `parse_failures.csv`
|
||||
- `metadata.json`
|
||||
|
||||
理解这个区别很重要:
|
||||
|
||||
- 稳定入口用于“继续往下跑”
|
||||
- 时间戳目录用于“回看某次具体构建”
|
||||
|
||||
## 4. question bank CSV 里是什么
|
||||
|
||||
sample build 输出的是 online-ready question bank,不是离线评测格式。
|
||||
|
||||
核心字段包括:
|
||||
|
||||
- `question`
|
||||
- `ground_truth`
|
||||
- `source_chunk_ids`
|
||||
- `doc_id`
|
||||
- `doc_name`
|
||||
|
||||
它故意不预写 `answer`。
|
||||
原因是这条链路的目标是在线评测:由 adapter 在评测时,根据 `source_chunk_ids` 去 `source_chunks.jsonl` 里取证据,再调用模型生成 `answer`。
|
||||
|
||||
## 5. 跑 online eval
|
||||
|
||||
使用 sample online scenario:
|
||||
|
||||
- scenario: [scenarios/online/sample-pdf-question-bank-online.yaml](/C:/Users/A200477427/Learnings/ragas-template/scenarios/online/sample-pdf-question-bank-online.yaml)
|
||||
- adapter: [apps/pdf_question_bank/adapter.py](/C:/Users/A200477427/Learnings/ragas-template/apps/pdf_question_bank/adapter.py)
|
||||
|
||||
执行命令:
|
||||
|
||||
```powershell
|
||||
uv run main.py --scenario scenarios/online/sample-pdf-question-bank-online.yaml
|
||||
or
|
||||
.\.venv\Scripts\python.exe main.py --scenario scenarios/online/sample-pdf-question-bank-online.yaml
|
||||
```
|
||||
|
||||
这个 scenario 的关键点有两个:
|
||||
|
||||
1. dataset 指向稳定 question bank CSV
|
||||
2. `app_adapter.static_kwargs.source_chunks_path` 指向稳定的 `latest/source_chunks.jsonl`
|
||||
|
||||
因此,只要你重新跑过 sample dataset build,online scenario 就不需要再手改时间戳路径。
|
||||
|
||||
## 6. online adapter 在做什么
|
||||
|
||||
`apps/pdf_question_bank/adapter.py` 的处理方式是固定的:
|
||||
|
||||
1. 从题库行里读取 `source_chunk_ids`
|
||||
2. 打开 `source_chunks.jsonl`
|
||||
3. 只解析被引用的 chunk
|
||||
4. 把这些 chunk 文本原样作为 `contexts`
|
||||
5. 用这些证据 prompt 模型生成 `answer`
|
||||
6. 把 `resolved_chunk_ids` 和模型响应写进 `raw_response`
|
||||
|
||||
所以评测关系是:
|
||||
|
||||
- `ground_truth` 是参考答案
|
||||
- `answer` 是运行时生成答案
|
||||
- `contexts` 是题目显式引用的证据块
|
||||
|
||||
这条链路没有单独做 retrieval。
|
||||
它评测的是“给定明确证据后,应用/模型能否稳定生成正确答案”。
|
||||
|
||||
## 7. 结果在哪里看
|
||||
|
||||
online eval 完成后,结果会写到:
|
||||
|
||||
- `outputs/online/sample-pdf-question-bank/<run_id>/`
|
||||
|
||||
常见文件包括:
|
||||
|
||||
- `scores.csv`
|
||||
- `invalid.csv`
|
||||
- `summary.md`
|
||||
- `metadata.json`
|
||||
|
||||
优先看这几个点:
|
||||
|
||||
- `scores.csv`:逐题指标分数
|
||||
- `invalid.csv`:哪些样本因为 adapter 失败或空结果被剔除了
|
||||
- `summary.md`:汇总视图
|
||||
|
||||
## 8. 常见问题
|
||||
|
||||
### `source_chunk_ids` 找不到
|
||||
|
||||
这通常表示 question bank CSV 和 `source_chunks.jsonl` 不是同一次 build 的产物。
|
||||
|
||||
正确做法:
|
||||
|
||||
1. 重新跑一次 `sample-pdf-build.yaml`
|
||||
2. 确认 `outputs/dataset-builds/sample-pdf-question-bank/latest/source_chunks.jsonl` 已更新
|
||||
3. 再运行 online scenario
|
||||
|
||||
### dataset build 成功,但 online eval 结果是 invalid
|
||||
|
||||
先看 `invalid.csv`。
|
||||
当前实现里,以下情况会进入 invalid:
|
||||
|
||||
- adapter 生成 `answer` 为空
|
||||
- adapter 返回 `contexts` 为空
|
||||
- adapter 在解析 chunk 或调模型时抛异常
|
||||
|
||||
### 只想快速看离线 smoke,不想重建题库
|
||||
|
||||
直接运行:
|
||||
|
||||
- [scenarios/offline/sample-pdf-offline-smoke.yaml](/C:/Users/A200477427/Learnings/ragas-template/scenarios/offline/sample-pdf-offline-smoke.yaml)
|
||||
|
||||
这个案例是固化好的离线 smoke dataset,不依赖 online adapter。
|
||||
|
||||
## 9. 换成你自己的 PDF 时改哪里
|
||||
|
||||
如果你要复用这条模式处理自己的 PDF,最少只改这几个点:
|
||||
|
||||
1. 复制一份 dataset build YAML
|
||||
2. 修改 `input.path` 指向你的 PDF 或 PDF 目录
|
||||
3. 修改 `output.dataset_path` 为你的 question bank CSV
|
||||
4. 修改 `output.artifact_dir` 为你的 build 资产根目录
|
||||
5. 复制一份 online scenario
|
||||
6. 修改 `dataset` 指向你的 question bank CSV
|
||||
7. 修改 `app_adapter.static_kwargs.source_chunks_path` 指向你的 `artifact_dir/latest/source_chunks.jsonl`
|
||||
|
||||
不需要改 question bank CSV 的字段结构。
|
||||
也不需要把 `answer` 预先写进 CSV。
|
||||
|
||||
如果你的 online answer 逻辑仍然是“根据显式证据块生成答案”,就可以继续复用:
|
||||
|
||||
- [apps/pdf_question_bank/adapter.py](/C:/Users/A200477427/Learnings/ragas-template/apps/pdf_question_bank/adapter.py)
|
||||
|
||||
如果你的应用是 HTTP 服务或有自己独立的 RAG 流程,再换成对应 adapter 即可。
|
||||
Reference in New Issue
Block a user