RAGAS_EVALUATION_IMPLEMENTATION_CHECKLIST.md 27 KB

Ragas 独立评估项目实施清单

1. 目标

基于 WeKnora 的公开 API 构建一个独立评估项目,不依赖 WeKnora 内置的 /evaluation 接口。

这个项目用于评估:

  • 检索质量:WeKnora 是否召回了正确的 chunks。
  • 生成质量:WeKnora 是否基于检索上下文正确、忠实地回答问题。
  • 端到端 RAG 效果:问题 -> 检索 -> 回答 -> Ragas 指标。

最终输入 Ragas 的单条记录格式:

{
  "user_input": "问题",
  "response": "WeKnora 生成的答案",
  "retrieved_contexts": ["检索到的 chunk 文本 1", "检索到的 chunk 文本 2"],
  "reference": "标准答案",
  "reference_contexts": ["标准答案依据的原文片段"]
}

2. 推荐独立项目结构

按照以下结构创建独立的项目:

  README.md
  pyproject.toml
  .env.example
  configs/
    eval.yaml
  data/
    raw_docs/
      pdf/
      xlsx/
    parsed_docs/
      documents.jsonl
      parse_summary.json
      mineru_raw/
    exported/
      knowledge.jsonl
      chunks.jsonl
    testsets/
      testset.raw.jsonl
      testset.reviewed.jsonl
    runs/
      weknora_answers.jsonl
      ragas_input.jsonl
    reports/
      ragas_scores.csv
      summary.md
  scripts/
    01_upload_docs.py
    02_wait_ingestion.py
    03_export_chunks.py
    04_parse_docs.py
    05_generate_testset.py
    06_review_testset.py
    07_run_weknora_qa.py
    08_build_ragas_input.py
    09_run_ragas_eval.py
    10_report.py
  src/
    weknora_eval/
      api.py
      schemas.py
      loaders.py
      parsers/
        local.py
        mineru.py
      testset.py
      sse.py
      ragas_runner.py
      report.py

pyproject.toml 示例:

[project]
name = "weknora-ragas-eval"
version = "0.1.0"
requires-python = ">=3.10"
dependencies = [
  "ragas>=0.3,<0.5",
  "datasets>=2.19.0",
  "pandas>=2.2.0",
  "openpyxl>=3.1.0",
  "requests>=2.32.0",
  "sseclient-py>=1.8.0",
  "python-dotenv>=1.0.0",
  "pyyaml>=6.0.0",
  "langchain>=0.2.0",
  "langchain-community>=0.2.0",
  "langchain-openai>=0.1.0",
  "pypdf>=4.2.0"
]

[project.optional-dependencies]
pdf = [
  "pymupdf>=1.24.0",
  "pdfplumber>=0.11.0"
]
dev = [
  "ruff>=0.6.0",
  "pytest>=8.0.0"
]

安装命令:

python -m venv .venv
source .venv/bin/activate
pip install -e .

如果 PDF 解析效果不好,安装 PDF 增强依赖:

pip install -e ".[pdf]"

如果需要开发和测试工具:

pip install -e ".[dev,pdf]"

3. 环境配置

.env.example

WEKNORA_BASE_URL=http://localhost:8080/api/v1
WEKNORA_API_KEY=replace-me
WEKNORA_KB_ID=replace-me

# Ragas 生成测试集和评测打分使用的模型服务。
# 这里不是 WeKnora 后端的模型配置,而是评估项目自己调用的 LLM/Embedding。
# 如果使用 OpenAI 官方接口:
OPENAI_API_KEY=replace-me
OPENAI_BASE_URL=https://api.openai.com/v1

# 如果使用 OpenAI 兼容服务,例如自建网关、OneAPI、LiteLLM、硅基流动、OpenRouter 等:
# OPENAI_API_KEY=replace-me
# OPENAI_BASE_URL=https://your-openai-compatible-endpoint/v1

# 用于 Ragas 自动生成 QA 测试集的 LLM。
RAGAS_GENERATOR_MODEL=gpt-4o-mini

# 用于 Ragas 评测打分的 LLM,也就是 judge/evaluator。
RAGAS_JUDGE_MODEL=gpt-4o-mini

# 用于 Ragas 中部分语义相似度或问题生成流程的 embedding 模型。
RAGAS_EMBEDDING_MODEL=text-embedding-3-small

TESTSET_SIZE=50
REQUEST_INTERVAL_SECONDS=0.2

这几个模型变量的来源:

变量 用途 从哪里来
RAGAS_GENERATOR_MODEL 生成 QA 测试集 你选择的评估侧 LLM 服务中的模型名称
RAGAS_JUDGE_MODEL Ragas 指标打分,例如 faithfulness、context recall 你选择的评估侧 LLM 服务中的模型名称
RAGAS_EMBEDDING_MODEL Ragas 生成/评估中需要 embedding 的步骤 你选择的评估侧 embedding 服务中的模型名称
OPENAI_API_KEY 调用评估侧模型服务的 API Key OpenAI 或 OpenAI 兼容服务提供
OPENAI_BASE_URL 调用评估侧模型服务的 Base URL OpenAI 官方或兼容服务地址

注意:

  • WeKnora 自己回答问题时使用的是 WeKnora 后端已经配置好的模型。
  • Ragas 评估项目调用的 RAGAS_GENERATOR_MODELRAGAS_JUDGE_MODELRAGAS_EMBEDDING_MODEL 是独立的“评估侧模型”。
  • 两边可以使用同一个模型服务,也可以分开。为了避免被测系统和评测裁判互相影响,建议评测侧 judge 模型能力不低于 WeKnora 回答模型。
  • 如果你不用 OpenAI 官方服务,只要目标服务兼容 OpenAI Chat Completions 和 Embeddings API,一般可以通过 OPENAI_BASE_URL 接入。

configs/eval.yaml

weknora:
  base_url: "${WEKNORA_BASE_URL}"
  api_key: "${WEKNORA_API_KEY}"
  knowledge_base_id: "${WEKNORA_KB_ID}"
  timeout_seconds: 300
  request_interval_seconds: 0.2

testset:
  size: 50
  include_pdf: true
  include_xlsx: true
  min_context_chars: 80
  require_manual_review: true

parsing:
  # 可选:local 或 mineru
  provider: "local"
  output_path: "data/parsed_docs/documents.jsonl"
  local:
    pdf_backend: "pymupdf"   # 可选:pypdf, pymupdf, pdfplumber
    xlsx_mode: "row_text"    # 可选:row_text, markdown_table
    min_chars: 80
  mineru:
    mode: "cli"              # 可选:cli, http
    cli_bin: "mineru"
    output_dir: "data/parsed_docs/mineru_raw"
    http_base_url: ""
    api_key: ""
    timeout_seconds: 600
    fallback_to_local: true

qa:
  one_session_per_question: true
  disable_title: true
  enable_memory: false
  channel: "api"

ragas:
  provider: "openai-compatible"
  api_key: "${OPENAI_API_KEY}"
  base_url: "${OPENAI_BASE_URL}"
  generator_model: "${RAGAS_GENERATOR_MODEL}"
  judge_model: "${RAGAS_JUDGE_MODEL}"
  embedding_model: "${RAGAS_EMBEDDING_MODEL}"
  metrics:
    - faithfulness
    - response_relevancy
    - context_precision
    - context_recall
    - factual_correctness

4. Ragas 侧文档解析方案

Ragas 生成 QA 测试集前,需要先把原始 PDF/XLSX 转成统一的文本 Document。这里不要直接把文件路径交给 Ragas,而是先执行独立解析步骤,产出标准化的 documents.jsonl

支持两种解析方式:

  • 本地解析:适合快速验证、纯文本 PDF、结构简单的 XLSX。
  • MinerU 解析:适合复杂 PDF、扫描件、表格/公式/多栏排版较多的文档。

4.1 统一解析产物

无论使用本地解析还是 MinerU,最终都要产出 data/parsed_docs/documents.jsonl,一行一个 Document:

{
  "doc_id": "contract.pdf::page-1",
  "source_file": "contract.pdf",
  "file_type": "pdf",
  "page": 1,
  "sheet": null,
  "row_index": null,
  "content": "第1页解析后的正文文本...",
  "metadata": {
    "parser": "local:pymupdf"
  }
}

XLSX 行级记录示例:

{
  "doc_id": "sales.xlsx::Sheet1::row-12",
  "source_file": "sales.xlsx",
  "file_type": "xlsx",
  "page": null,
  "sheet": "Sheet1",
  "row_index": 12,
  "content": "产品: A产品\n年份: 2024\n销售额: 120万元",
  "metadata": {
    "parser": "local:openpyxl",
    "columns": ["产品", "年份", "销售额"]
  }
}

后续 Ragas 测试集生成只读取 documents.jsonl,不直接读取原始 PDF/XLSX。

4.2 本地解析

本地解析用于最低依赖、最快跑通。

PDF 可选 backend:

  • pypdf:依赖轻,适合文本型 PDF。
  • pymupdf:解析速度快,通常比 pypdf 稳。
  • pdfplumber:适合需要保留部分表格/版面信息的 PDF。

XLSX 解析模式:

  • row_text:每行转成 列名: 值 的文本,适合问答和检索。
  • markdown_table:每个 sheet 转成 Markdown 表格,适合保留表格整体结构,但长表容易过长。

本地解析配置:

parsing:
  provider: "local"
  output_path: "data/parsed_docs/documents.jsonl"
  local:
    pdf_backend: "pymupdf"
    xlsx_mode: "row_text"
    min_chars: 80

scripts/04_parse_docs.py 在本地解析模式下的职责:

  • 遍历 data/raw_docs/pdfdata/raw_docs/xlsx
  • PDF 按页或按段落输出 Document。
  • XLSX 按行或按 sheet 输出 Document。
  • 过滤过短文本。
  • 写入 data/parsed_docs/documents.jsonl
  • 保留 source_filepagesheetrow_index 等元数据。

4.3 MinerU 解析

MinerU 作为可选增强解析能力。适用于:

  • PDF 版面复杂。
  • PDF 中有表格、公式、多栏排版。
  • 扫描件或图片型 PDF。
  • 需要 Markdown 格式作为 QA 生成上下文。

MinerU 支持两种接入模式。

4.3.1 MinerU CLI 模式

配置:

parsing:
  provider: "mineru"
  mineru:
    mode: "cli"
    cli_bin: "mineru"
    output_dir: "data/parsed_docs/mineru_raw"
    timeout_seconds: 600
    fallback_to_local: true

预期行为:

  • scripts/04_parse_docs.py 调用 MinerU CLI。
  • 每个 PDF 解析到 data/parsed_docs/mineru_raw/{file_stem}/
  • 从 MinerU 输出中读取 Markdown 或 JSON。
  • 转换成统一 documents.jsonl

CLI 命令需要按实际安装的 MinerU 版本适配。独立项目中应把 MinerU CLI 调用封装在 src/weknora_eval/parsers/mineru.py,不要把具体命令散落在业务脚本里。

4.3.2 MinerU HTTP 服务模式

如果已有 MinerU 服务,可以通过 HTTP 调用。

配置:

parsing:
  provider: "mineru"
  mineru:
    mode: "http"
    http_base_url: "http://mineru.example.com"
    api_key: "replace-me"
    output_dir: "data/parsed_docs/mineru_raw"
    timeout_seconds: 600
    fallback_to_local: true

预期行为:

  • 上传 PDF 到 MinerU HTTP 服务。
  • 等待解析任务完成。
  • 下载 Markdown/JSON 结果。
  • 转换成统一 documents.jsonl

HTTP 接口路径需要按实际 MinerU 服务部署约定实现,因此 MinerU HTTP Client 必须做成可替换模块。

4.4 解析回退策略

建议实现以下策略:

  1. 默认使用配置指定的 provider。
  2. 如果 provider=mineru 且某个文件解析失败:
    • 记录到 data/parsed_docs/failed_parse.jsonl
    • 如果 fallback_to_local=true,回退到本地解析。
  3. 如果本地解析结果为空或过短:
    • 标记该文件为低质量解析。
    • 不进入自动 QA 生成,等待人工处理。

失败记录格式:

{
  "source_file": "contract.pdf",
  "parser": "mineru:cli",
  "status": "failed",
  "error": "timeout",
  "fallback_used": "local:pymupdf"
}

4.5 解析质量检查

解析完成后生成 data/parsed_docs/parse_summary.json

{
  "total_files": 3,
  "parsed_files": 3,
  "failed_files": 0,
  "total_documents": 128,
  "empty_documents": 0,
  "avg_chars": 512.4,
  "parser": "local:pymupdf"
}

最低质量要求:

  • 每个文件至少产生 1 条 Document。
  • content 非空。
  • 大部分 Document 长度不低于 min_chars
  • metadata 中必须保留 source_file

5. WeKnora API 调用契约

5.1 上传文档

如果独立评估项目负责把原始 PDF/XLSX 上传到 WeKnora,使用这个接口。

请求:

POST /api/v1/knowledge-bases/{knowledge_base_id}/knowledge/file
X-API-Key: <api-key>
Content-Type: multipart/form-data

Multipart 字段:

file=@/path/to/file.pdf
enable_multimodel=false

响应示例:

{
  "success": true,
  "data": {
    "id": "knowledge-0001",
    "knowledge_base_id": "kb-0001",
    "type": "file",
    "title": "contract.pdf",
    "parse_status": "processing",
    "enable_status": "disabled",
    "file_name": "contract.pdf",
    "file_type": "pdf",
    "error_message": ""
  }
}

需要持久化:

{
  "knowledge_id": "knowledge-0001",
  "file_name": "contract.pdf",
  "file_type": "pdf",
  "parse_status": "processing"
}

5.2 轮询文档入库状态

请求:

GET /api/v1/knowledge-bases/{knowledge_base_id}/knowledge?page=1&page_size=100
X-API-Key: <api-key>

响应示例:

{
  "success": true,
  "data": [
    {
      "id": "knowledge-0001",
      "title": "contract.pdf",
      "parse_status": "completed",
      "enable_status": "enabled",
      "file_name": "contract.pdf",
      "file_type": "pdf",
      "processed_at": "2026-04-20T10:03:00+08:00",
      "error_message": ""
    }
  ],
  "page": 1,
  "page_size": 100,
  "total": 1
}

完成条件:

parse_status == "completed"
enable_status == "enabled"

失败条件:

parse_status == "failed"

5.3 导出 chunks

请求:

GET /api/v1/chunks/{knowledge_id}?page=1&page_size=100
X-API-Key: <api-key>

响应示例:

{
  "success": true,
  "data": [
    {
      "id": "chunk-0001",
      "knowledge_id": "knowledge-0001",
      "knowledge_base_id": "kb-0001",
      "content": "分块文本...",
      "chunk_index": 0,
      "is_enabled": true,
      "status": 2,
      "start_at": 0,
      "end_at": 500,
      "chunk_type": "text",
      "parent_chunk_id": "",
      "metadata": null,
      "image_info": ""
    }
  ],
  "page": 1,
  "page_size": 100,
  "total": 35
}

保存为 data/exported/chunks.jsonl

{
  "chunk_id": "chunk-0001",
  "knowledge_id": "knowledge-0001",
  "knowledge_base_id": "kb-0001",
  "chunk_index": 0,
  "content": "分块文本...",
  "source_file": "contract.pdf",
  "chunk_type": "text"
}

5.4 创建会话

建议每个评测问题创建一个独立 session,避免历史上下文影响答案。

请求:

POST /api/v1/sessions
X-API-Key: <api-key>
Content-Type: application/json

请求体:

{
  "title": "ragas-eval-qa-0001",
  "description": "Ragas evaluation session"
}

响应示例:

{
  "success": true,
  "data": {
    "id": "session-0001",
    "title": "ragas-eval-qa-0001",
    "description": "Ragas evaluation session"
  }
}

5.5 执行知识库问答

请求:

POST /api/v1/agent-chat/{session_id}
X-API-Key: <api-key>
Content-Type: application/json

请求体:

{
  "query": "合同中的付款期限是什么?",
  "agent_id": "builtin-quick-answer",
  "agent_enabled": false,
  "knowledge_base_ids": ["kb-0001"],
  "disable_title": true,
  "enable_memory": false,
  "channel": "api"
}

如果要限制在指定文件内检索,可以传:

{
  "query": "合同中的付款期限是什么?",
  "agent_id": "builtin-quick-answer",
  "agent_enabled": false,
  "knowledge_ids": ["knowledge-0001"],
  "disable_title": true,
  "enable_memory": false,
  "channel": "api"
}

响应类型:Server-Sent Events。

引用事件:

event: message
data: {
  "id": "request-0001",
  "response_type": "references",
  "content": "",
  "done": false,
  "knowledge_references": [
    {
      "id": "chunk-0012",
      "content": "买方应在收到合法有效发票后30日内完成付款。",
      "knowledge_id": "knowledge-0001",
      "chunk_index": 12,
      "knowledge_title": "contract.pdf",
      "start_at": 1200,
      "end_at": 1480,
      "seq": 12,
      "score": 0.92,
      "match_type": 3,
      "metadata": {},
      "chunk_type": "text",
      "parent_chunk_id": "",
      "image_info": "",
      "knowledge_filename": "contract.pdf",
      "knowledge_source": "file"
    }
  ]
}

答案事件:

event: message
data: {
  "id": "request-0001",
  "response_type": "final_answer",
  "content": "合同约定,付款期限为收到合法有效发票后30日内。",
  "done": false,
  "knowledge_references": null
}

结束事件:

event: message
data: {
  "id": "request-0001",
  "response_type": "final_answer",
  "content": "",
  "done": true,
  "knowledge_references": null
}

需要提取:

{
  "request_id": "request-0001",
  "response": "合同约定,付款期限为收到合法有效发票后30日内。",
  "retrieved_contexts": [
    "买方应在收到合法有效发票后30日内完成付款。"
  ],
  "weknora_references": [
    {
      "id": "chunk-0012",
      "content": "买方应在收到合法有效发票后30日内完成付款。",
      "knowledge_id": "knowledge-0001",
      "chunk_index": 12,
      "score": 0.92,
      "knowledge_filename": "contract.pdf"
    }
  ]
}

5.6 可选:读取落库后的消息

用于在 SSE 完成后校验或补取最终 assistant 答案。

请求:

GET /api/v1/messages/{session_id}/load?limit=10
X-API-Key: <api-key>

响应示例:

{
  "success": true,
  "data": [
    {
      "id": "assistant-message-0001",
      "session_id": "session-0001",
      "request_id": "request-0001",
      "content": "合同约定,付款期限为收到合法有效发票后30日内。",
      "role": "assistant",
      "knowledge_references": [
        {
          "id": "chunk-0012",
          "content": "买方应在收到合法有效发票后30日内完成付款。",
          "knowledge_id": "knowledge-0001",
          "chunk_index": 12,
          "knowledge_title": "contract.pdf",
          "score": 0.92,
          "match_type": 3,
          "chunk_type": "text",
          "knowledge_filename": "contract.pdf"
        }
      ],
      "is_completed": true,
      "is_fallback": false
    }
  ]
}

5.7 可选:纯检索接口

用于只评估检索,不评估生成。

请求:

POST /api/v1/knowledge-search
X-API-Key: <api-key>
Content-Type: application/json

请求体:

{
  "query": "合同中的付款期限是什么?",
  "knowledge_base_ids": ["kb-0001"]
}

响应示例:

{
  "success": true,
  "data": [
    {
      "id": "chunk-0012",
      "content": "买方应在收到合法有效发票后30日内完成付款。",
      "knowledge_id": "knowledge-0001",
      "chunk_index": 12,
      "knowledge_title": "contract.pdf",
      "start_at": 1200,
      "end_at": 1480,
      "seq": 12,
      "score": 0.92,
      "match_type": 3,
      "chunk_type": "text",
      "metadata": {},
      "knowledge_filename": "contract.pdf",
      "knowledge_source": "file"
    }
  ]
}

6. QA 测试集生成方案

6.1 输入数据

建议同时保留两类输入:

  • data/parsed_docs/documents.jsonl 中的标准化解析结果。
  • data/exported/chunks.jsonl 中从 WeKnora 导出的 chunks。

推荐顺序:

  1. 优先基于 documents.jsonl 生成候选 QA。
  2. 保存 QA 对应的来源文件和依据片段。
  3. 可选:把 reference_contexts 匹配回 WeKnora 的 chunk ID,用于计算 hit@k、recall@k、mrr 等非 LLM 检索指标。

6.2 测试集记录格式

data/testsets/testset.raw.jsonl

{
  "sample_id": "qa-0001",
  "user_input": "合同中的付款期限是什么?",
  "reference": "付款期限为收到合法有效发票后30日内。",
  "reference_contexts": [
    "买方应在收到合法有效发票后30日内完成付款。"
  ],
  "source_file": "contract.pdf",
  "gold_chunk_ids": ["chunk-0012"],
  "question_type": "single_hop",
  "review_status": "pending"
}

人工审核后的 data/testsets/testset.reviewed.jsonl

{
  "sample_id": "qa-0001",
  "user_input": "合同中的付款期限是什么?",
  "reference": "付款期限为收到合法有效发票后30日内。",
  "reference_contexts": [
    "买方应在收到合法有效发票后30日内完成付款。"
  ],
  "source_file": "contract.pdf",
  "gold_chunk_ids": ["chunk-0012"],
  "question_type": "single_hop",
  "review_status": "approved"
}

6.3 问题类型建议

建议包含:

  • PDF 单跳事实问答。
  • PDF 多跳问答,例如跨相邻章节综合。
  • PDF 定义、条件、例外条款类问题。
  • XLSX 单行查询问题。
  • XLSX 条件筛选问题。

第一阶段暂时避免:

  • 复杂表格聚合问题,除非期望 WeKnora 本身支持表格计算。
  • 依赖图片内容才能回答的问题。
  • 需要外部知识的问题。
  • 存在多个合理答案的模糊问题。

7. Ragas 输入构造方案

对每条审核通过的 QA:

  1. 创建一个干净 session。
  2. 调用 POST /agent-chat/{session_id},默认使用 agent_id=builtin-quick-answer
  3. 解析 SSE 中的 references 事件。
  4. 解析 SSE 中的 final_answer 事件。
  5. 构造一条 Ragas 输入记录。

data/runs/ragas_input.jsonl

{
  "sample_id": "qa-0001",
  "user_input": "合同中的付款期限是什么?",
  "response": "合同约定,付款期限为收到合法有效发票后30日内。",
  "retrieved_contexts": [
    "买方应在收到合法有效发票后30日内完成付款。"
  ],
  "reference": "付款期限为收到合法有效发票后30日内。",
  "reference_contexts": [
    "买方应在收到合法有效发票后30日内完成付款。"
  ],
  "session_id": "session-0001",
  "request_id": "request-0001",
  "weknora_references": [
    {
      "id": "chunk-0012",
      "knowledge_id": "knowledge-0001",
      "chunk_index": 12,
      "score": 0.92,
      "knowledge_filename": "contract.pdf"
    }
  ]
}

8. Ragas 指标方案

第一阶段建议使用:

指标 必要字段 作用
faithfulness response, retrieved_contexts 检查答案是否被检索内容支撑。
response_relevancy user_input, response 检查答案是否切题。
context_precision user_input, retrieved_contexts, reference 检查靠前的检索上下文是否相关。
context_recall retrieved_contexts, reference 检查检索上下文是否包含足够证据。
factual_correctness response, reference 检查答案与标准答案事实是否一致。

如果测试集里有 gold_chunk_ids,建议额外计算非 LLM 检索指标:

  • hit@1
  • hit@3
  • hit@5
  • recall@k
  • mrr
  • ndcg@k

9. 报告方案

生成 data/reports/summary.md

# Ragas 评估报告

## 运行信息
- WeKnora Base URL:
- 知识库 ID:
- 测试集规模:
- 审核通过样本数:
- 失败样本数:
- Judge 模型:

## 聚合指标
| 指标 | 平均值 | P50 | 失败阈值 |
| --- | --- | --- | --- |

## 检索失败样本
| sample_id | 问题 | 预期文件 | 实际召回文件 | context_recall | 备注 |

## 生成失败样本
| sample_id | 问题 | 模型答案 | 标准答案 | faithfulness | factual_correctness |

## 改进建议
- ...

同时保存:

  • ragas_scores.csv:每条样本的指标。
  • weknora_answers.jsonl:WeKnora 原始输出。
  • ragas_input.jsonl:实际输入 Ragas 的数据。
  • failed_requests.jsonl:API 失败或 SSE 解析失败记录。

10. 实施清单

阶段 1:项目脚手架

  • 创建独立仓库或目录 weknora-ragas-eval
  • 添加 Python 项目元数据和依赖锁定。
  • 添加 .env.example
  • 添加 configs/eval.yaml
  • 创建 data/ 下的各级目录。
  • 添加结构化日志。
  • 添加重试和超时策略。

阶段 2:WeKnora API Client

  • 实现 create_session
  • 实现 upload_file
  • 实现 list_knowledge
  • 实现 wait_ingestion_completed
  • 实现 list_chunks
  • 实现 knowledge_chat_sse
  • 实现 load_messages
  • 实现 knowledge_search
  • API 错误时保存响应体。
  • 实现分页工具函数。

阶段 3:文档与 chunk 导出

  • 上传 PDF 文件。
  • 上传 XLSX 文件。
  • 轮询直到所有文档 completed 或 failed。
  • 导出全部 knowledge 元数据。
  • 导出全部 chunks。
  • 过滤 disabled chunks。
  • 过滤空 chunks。
  • 保留来源文件元数据。

阶段 4:Ragas 侧文档解析

  • 实现本地 PDF 解析。
  • 实现本地 XLSX 解析。
  • 实现 MinerU CLI 解析适配。
  • 实现 MinerU HTTP 解析适配。
  • 将所有解析结果转换为 documents.jsonl
  • 记录解析失败文件。
  • 生成 parse_summary.json
  • 支持 MinerU 失败后回退本地解析。

阶段 5:测试集生成

  • 加载 data/parsed_docs/documents.jsonl
  • 使用 Ragas 生成候选 QA。
  • 保存原始候选测试集。
  • 增加人工审核字段。
  • 生成审核后的测试集。
  • 执行最低质量检查:
    • 问题可以从给定上下文回答。
    • 标准答案有依据。
    • reference_contexts 非空。
    • 记录来源文件。

阶段 6:运行 WeKnora QA

  • 每条 QA 创建一个干净 session。
  • 调用 agent-chat,默认使用 builtin-quick-answer
  • 解析 SSE references 事件。
  • 解析 SSE final_answer 事件。
  • 按 chunk ID 去重引用。
  • 保存原始答案和引用。
  • 记录空答案失败。
  • 记录空检索失败。
  • 可选:通过 message load API 校验最终答案。

阶段 7:构造 Ragas 输入

  • 合并审核后的 QA 与 WeKnora 输出。
  • 构造 user_input
  • 构造 response
  • 构造 retrieved_contexts
  • 构造 reference
  • 构造 reference_contexts
  • 保留 sample_idsession_idrequest_id 和 references,便于排查。
  • 校验必要字段不缺失。

阶段 8:运行 Ragas 评估

  • 配置 judge LLM。
  • 配置 embedding 模型。
  • 运行 faithfulness。
  • 运行 response relevancy。
  • 运行 context precision。
  • 运行 context recall。
  • 运行 factual correctness。
  • 保存逐样本分数。
  • 保存聚合分数。
  • 按样本捕获 Ragas 异常。

阶段 9:基于 chunk ID 的检索指标

  • 如果存在 gold_chunk_ids,计算 hit@k。
  • 计算 recall@k。
  • 计算 mrr。
  • 计算 ndcg@k。
  • 对比 chunk-ID 指标和 Ragas LLM-based context 指标。

阶段 10:生成报告

  • 生成 Markdown 报告。
  • 写入运行信息。
  • 写入聚合指标。
  • 写入最差检索样本。
  • 写入最差生成样本。
  • 写入空检索数量。
  • 写入 fallback 答案数量。
  • 写入来源文件分布。
  • 写入改进建议。

11. 验收标准

独立评估项目达到以下条件即认为可用:

  • 可以上传一小批 PDF/XLSX 到 WeKnora。
  • 可以检测文档入库完成。
  • 可以从 WeKnora 导出 chunks。
  • 可以通过本地解析或 MinerU 解析生成 documents.jsonl
  • 可以创建或导入至少 10 条审核通过的 QA。
  • 可以对每条 QA 调用 WeKnora。
  • 可以解析 responseretrieved_contexts
  • 可以构造合法的 Ragas 输入 JSONL。
  • 可以产出逐样本 Ragas 分数。
  • 可以产出可读的汇总报告。
  • 所有中间产物都已保存,便于复盘和排查。

12. 首轮 Pilot Run

先用很小的数据集跑通闭环:

  • 2 个 PDF 文件。
  • 1 个 XLSX 文件。
  • 10 条人工审核通过的 QA。
  • 每条样本一个独立 session。
  • 指标:
    • faithfulness
    • response_relevancy
    • context_precision
    • context_recall
    • factual_correctness

预期产物:

data/exported/knowledge.jsonl
data/exported/chunks.jsonl
data/parsed_docs/documents.jsonl
data/parsed_docs/parse_summary.json
data/testsets/testset.reviewed.jsonl
data/runs/weknora_answers.jsonl
data/runs/ragas_input.jsonl
data/reports/ragas_scores.csv
data/reports/summary.md

只有当首轮确认以下问题都正常后,再扩展到 50-300 条样本:

  • retrieved_contexts 没有系统性为空。
  • response 能正确捕获。
  • Ragas 输入字段合法。
  • 人工审核确认 QA 集有评测意义。