Add committed zh judge-prompt cache + enable in Siemens scenarios + docs

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
This commit is contained in:
wangwei
2026-07-01 18:15:53 +08:00
co-authored by Copilot
parent 065b0e0f1a
commit 24a8688a34
14 changed files with 1670 additions and 3 deletions
+44 -1
View File
@@ -148,7 +148,50 @@ def run(question: str, **kwargs) -> dict:
这意味着后续你比较不同模型、不同 prompt、不同检索策略时,不需要再靠手工记参数。 这意味着后续你比较不同模型、不同 prompt、不同检索策略时,不需要再靠手工记参数。
## 7. PDF 题库构建 ## 7. 中文评判 Prompt 适配
RAGAS 评判 prompt 默认为英文,对中文语料启用中文适配可提升判定一致性(架构设计 §10.5)。
### 配置方式
**场景 YAML**(离线/在线评估场景):
```yaml
judge_language: zh # 启用中文评判(默认 en
```
**Score API**`/api/score``/api/score/async``/api/score/session_async`):
```json
{ "judge_language": "zh", ... }
```
**全局默认**`.env`):
```env
RAGAS_JUDGE_LANGUAGE=zh
```
优先级:请求/场景显式值 > `RAGAS_JUDGE_LANGUAGE`(默认 `en`)。
### 支持的指标
| 指标 | 中文适配 |
|---|---|
| `faithfulness` | ✅ |
| `answer_relevancy` | ✅ |
| `context_recall` | ✅ |
| `context_precision` | ✅ |
| `factual_correctness` | ✅ |
| `noise_sensitivity` | ⚠️ 使用内置函数式 prompt,不支持 adapt,保持英文 |
| `semantic_similarity` | — 无 LLM 调用,不涉及 |
### 缓存机制
中文 prompt 以 JSON 缓存提交在 `configs/judge_prompts/zh/`,运行时零延迟加载。RAGAS 升级后若检测到 prompt 变化(`stale cache` 日志告警),请重新运行:
```bash
python -m scripts.build_judge_prompt_cache --language zh
```
仓库现在额外支持把 PDF 文档解析成可人工复核的在线题库草稿。最推荐的阅读顺序是: 仓库现在额外支持把 PDF 文档解析成可人工复核的在线题库草稿。最推荐的阅读顺序是:
@@ -0,0 +1,37 @@
{
"metric": "answer_relevancy",
"prompt_attr": "prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "f6f3a804bf3542363e3ce438ca62e1545de2736058e77900820bacc86be05ac3",
"instruction": "根据给定的回答生成对应的问题,并判断该回答是否为回避性回答(noncommittal)。\n如果回答是回避性的(含糊、模糊或有歧义),则 noncommittal 为 1;如果回答具有实质内容,则为 0。\n回避性回答示例:「我不知道」「我不确定」「这取决于具体情况」。",
"examples": [
{
"input": {
"response": "阿尔伯特·爱因斯坦出生于德国。"
},
"output": {
"question": "阿尔伯特·爱因斯坦出生在哪里?",
"noncommittal": 0
}
},
{
"input": {
"response": "法国的首都是巴黎,这座城市以其建筑和文化而闻名。"
},
"output": {
"question": "法国的首都是哪里?",
"noncommittal": 0
}
},
{
"input": {
"response": "关于2023年发明的智能手机的突破性功能,我不了解,因为我没有2022年以后的信息。"
},
"output": {
"question": "2023年发明的智能手机有哪些突破性功能?",
"noncommittal": 1
}
}
]
}
@@ -0,0 +1,43 @@
{
"metric": "context_precision",
"prompt_attr": "prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "948aa1ff0d12e1903926ddadbdf4420554b0c0bded6d01bf4eabd46b69697511",
"instruction": "给定问题、回答和上下文,验证该上下文在得出给定回答时是否有用。若有用则输出 verdict 为 1,若无用则输出 0,并以 JSON 格式返回结果。",
"examples": [
{
"input": {
"question": "请介绍一下阿尔伯特·爱因斯坦。",
"context": "阿尔伯特·爱因斯坦(1879年3月14日—1955年4月18日)是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最具影响力的科学家之一。以创立相对论著称,同时对量子力学做出了重要贡献。他的质能等价公式 E = mc² 被称为「世界上最著名的方程」。他因「对理论物理学的贡献,特别是发现光电效应定律」而荣获1921年诺贝尔物理学奖。",
"answer": "阿尔伯特·爱因斯坦生于1879年3月14日,是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最具影响力的科学家之一。他因对理论物理学的贡献荣获1921年诺贝尔物理学奖。"
},
"output": {
"reason": "提供的上下文对得出给定回答确实有帮助。上下文包含了关于爱因斯坦生平和贡献的关键信息,这些内容都体现在回答中。",
"verdict": 1
}
},
{
"input": {
"question": "2020年ICC世界杯谁获胜?",
"context": "2022年ICC男子T20世界杯于2022年10月16日至11月13日在澳大利亚举行,是该赛事第八届。原定于2020年举办,因新冠疫情推迟。英格兰队在决赛中以五个击球门差击败巴基斯坦队,赢得了第二个ICC男子T20世界杯冠军。",
"answer": "英格兰。"
},
"output": {
"reason": "该上下文有助于澄清2020年ICC世界杯的情况,并表明英格兰是原定于2020年、实际于2022年举办的锦标赛的获胜者。",
"verdict": 1
}
},
{
"input": {
"question": "世界上最高的山是什么?",
"context": "安第斯山脉是世界上最长的大陆山脉,位于南美洲,横跨七个国家,拥有西半球许多最高峰。该山脉以其多样的生态系统而闻名,包括高海拔的安第斯高原和亚马逊雨林。",
"answer": "珠穆朗玛峰。"
},
"output": {
"reason": "提供的上下文讨论安第斯山脉,虽然令人印象深刻,但不包含珠穆朗玛峰,也与世界最高山峰的问题无直接关联。",
"verdict": 0
}
}
]
}
@@ -0,0 +1,73 @@
{
"metric": "context_recall",
"prompt_attr": "prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "51995670010acd56920fa3cd9959b4cdb4a82a692e5cd2e9cfd545b3dfc68a22",
"instruction": "给定一段上下文和一个回答,分析回答中每一条陈述,判断该陈述是否可以归因于给定的上下文。\n仅使用二分类:如果陈述可以从上下文中归因,则为 1;否则为 0。\n请为每条分类提供详细的推理说明。",
"examples": [
{
"input": {
"question": "请介绍一下阿尔伯特·爱因斯坦。",
"context": "阿尔伯特·爱因斯坦(1879年3月14日—1955年4月18日)是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最具影响力的科学家之一。他以创立相对论而闻名,同时对量子力学也做出了重要贡献,是20世纪初物理学革命的核心人物。他的质能等价公式 E = mc²被称为「世界上最著名的方程」。他因「对理论物理学的贡献,特别是发现光电效应定律」而荣获1921年诺贝尔物理学奖。",
"answer": "阿尔伯特·爱因斯坦生于1879年3月14日,是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最具影响力的科学家之一。他因对理论物理学的贡献荣获1921年诺贝尔物理学奖。他于1905年发表了4篇论文。爱因斯坦于1895年移居瑞士。"
},
"output": {
"classifications": [
{
"statement": "阿尔伯特·爱因斯坦生于1879年3月14日,是一位出生于德国的理论物理学家,被广泛认为是有史以来最伟大、最具影响力的科学家之一。",
"reason": "上下文明确提到了爱因斯坦的出生日期及其理论物理学家身份。",
"attributed": 1
},
{
"statement": "他因对理论物理学的贡献荣获1921年诺贝尔物理学奖。",
"reason": "上下文中有完全一致的表述。",
"attributed": 1
},
{
"statement": "他于1905年发表了4篇论文。",
"reason": "上下文中没有提到他发表论文的信息。",
"attributed": 0
},
{
"statement": "爱因斯坦于1895年移居瑞士。",
"reason": "上下文中没有支持该陈述的依据。",
"attributed": 0
}
]
}
},
{
"input": {
"question": "2020年ICC世界杯谁获胜?",
"context": "2022年ICC男子T20世界杯于2022年10月16日至11月13日在澳大利亚举行,这是该赛事的第八届。原定于2020年举办,因新冠疫情推迟。英格兰队在决赛中以五个击球门差击败巴基斯坦队,赢得了第二个ICC男子T20世界杯冠军。",
"answer": "英格兰。"
},
"output": {
"classifications": [
{
"statement": "英格兰。",
"reason": "上下文说明英格兰赢得了2022年届(原定2020年)ICC世界杯冠军。",
"attributed": 1
}
]
}
},
{
"input": {
"question": "世界上最高的山是什么?",
"context": "安第斯山脉是世界上最长的大陆山脉,位于南美洲,横跨七个国家,拥有西半球许多最高峰。该山脉以其多样的生态系统而闻名,包括高海拔的安第斯高原和亚马逊雨林。",
"answer": "珠穆朗玛峰。"
},
"output": {
"classifications": [
{
"statement": "珠穆朗玛峰。",
"reason": "上下文讨论的是安第斯山脉,与世界最高峰珠穆朗玛峰无关。",
"attributed": 0
}
]
}
}
]
}
@@ -0,0 +1,45 @@
{
"metric": "factual_correctness",
"prompt_attr": "nli_prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "28e28e3c3cd02af21729a850633791390f2173deeaddfb006bba11e8569e7af5",
"instruction": "你的任务是根据给定的上下文,判断一系列陈述的真实性(忠实度)。对于每条陈述,如果该陈述可以直接从上下文中推断出来,则返回 verdict 为 1;如果不能从上下文中直接推断,则返回 verdict 为 0。",
"examples": [
{
"input": {
"context": "张明是XYZ大学的一名学生,主修计算机科学。本学期他选修了数据结构、算法和数据库管理等几门课程。张明是一名勤奋的学生,花大量时间学习和完成作业。他经常在图书馆里工作到很晚,完成他的项目。",
"statements": [
"张明主修生物学。",
"张明正在修读人工智能课程。",
"张明是一名努力认真的学生。",
"张明有一份兼职工作。"
]
},
"output": {
"statements": [
{
"statement": "张明主修生物学。",
"reason": "上下文明确指出张明的专业是计算机科学,而非生物学。",
"verdict": 0
},
{
"statement": "张明正在修读人工智能课程。",
"reason": "上下文提到的课程包括数据结构、算法和数据库管理,未提及人工智能课程。",
"verdict": 0
},
{
"statement": "张明是一名努力认真的学生。",
"reason": "上下文描述张明是一名勤奋的学生,花大量时间学习和完成作业,与该陈述相符。",
"verdict": 1
},
{
"statement": "张明有一份兼职工作。",
"reason": "上下文中没有关于张明兼职工作的任何信息。",
"verdict": 0
}
]
}
}
]
}
@@ -0,0 +1,35 @@
{
"metric": "factual_correctness",
"prompt_attr": "prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "bacda198cc6f32b2f450e3aa63a492d461fc63a60222e91a3ac9d22013ab6113",
"instruction": "将每个输入句子分解为一条或多条独立陈述。每条陈述应是一个可独立验证的独立论断。\n请按照示例中展示的原子性(atomicity)和覆盖度(coverage)水平进行分解。",
"examples": [
{
"input": {
"response": "查尔斯·巴贝奇是一位法国数学家、哲学家和美食评论家。",
"atomicity": "low",
"coverage": "low"
},
"output": {
"claims": [
"查尔斯·巴贝奇是一位数学家和哲学家。"
]
}
},
{
"input": {
"response": "阿尔伯特·爱因斯坦是一位德国理论物理学家。他创立了相对论,并对量子力学的发展做出了贡献。",
"atomicity": "low",
"coverage": "low"
},
"output": {
"claims": [
"阿尔伯特·爱因斯坦是一位德国物理学家。",
"阿尔伯特·爱因斯坦创立了相对论并对量子力学做出了贡献。"
]
}
}
]
}
@@ -0,0 +1,45 @@
{
"metric": "faithfulness",
"prompt_attr": "nli_statement_prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "28e28e3c3cd02af21729a850633791390f2173deeaddfb006bba11e8569e7af5",
"instruction": "你的任务是根据给定的上下文,判断一系列陈述的真实性(忠实度)。对于每条陈述,如果该陈述可以直接从上下文中推断出来,则返回 verdict 为 1;如果不能从上下文中直接推断,则返回 verdict 为 0。",
"examples": [
{
"input": {
"context": "张明是XYZ大学的一名学生,主修计算机科学。本学期他选修了数据结构、算法和数据库管理等几门课程。张明是一名勤奋的学生,花大量时间学习和完成作业。他经常在图书馆里工作到很晚,完成他的项目。",
"statements": [
"张明主修生物学。",
"张明正在修读人工智能课程。",
"张明是一名努力认真的学生。",
"张明有一份兼职工作。"
]
},
"output": {
"statements": [
{
"statement": "张明主修生物学。",
"reason": "上下文明确指出张明的专业是计算机科学,而非生物学。",
"verdict": 0
},
{
"statement": "张明正在修读人工智能课程。",
"reason": "上下文提到的课程包括数据结构、算法和数据库管理,未提及人工智能课程。",
"verdict": 0
},
{
"statement": "张明是一名努力认真的学生。",
"reason": "上下文描述张明是一名勤奋的学生,花大量时间学习和完成作业,与该陈述相符。",
"verdict": 1
},
{
"statement": "张明有一份兼职工作。",
"reason": "上下文中没有关于张明兼职工作的任何信息。",
"verdict": 0
}
]
}
}
]
}
@@ -0,0 +1,24 @@
{
"metric": "faithfulness",
"prompt_attr": "statement_generator_prompt",
"language": "chinese",
"ragas_version": "0.4.3",
"source_hash": "7950551a460f88534523f1f8b7d5819395edb6eb90bb6d246b71a51dab85dfaf",
"instruction": "给定一个问题和对应的回答,分析回答中每个句子的复杂性,将每个句子拆解为一条或多条完整、可独立理解的陈述。每条陈述中不得使用代词,必须明确指代对象。",
"examples": [
{
"input": {
"question": "阿尔伯特·爱因斯坦是谁?他最著名的成就是什么?",
"answer": "他是一位出生于德国的理论物理学家,被公认为有史以来最伟大、最具影响力的物理学家之一。他最著名的成就是创立相对论,同时也对量子力学的发展做出了重要贡献。"
},
"output": {
"statements": [
"阿尔伯特·爱因斯坦是一位出生于德国的理论物理学家。",
"阿尔伯特·爱因斯坦被公认为有史以来最伟大、最具影响力的物理学家之一。",
"阿尔伯特·爱因斯坦最著名的成就是创立相对论。",
"阿尔伯特·爱因斯坦对量子力学的发展做出了重要贡献。"
]
}
}
]
}
File diff suppressed because it is too large Load Diff
@@ -35,7 +35,7 @@
| `answer_relevancy` | `prompt` | | `answer_relevancy` | `prompt` |
| `context_recall` | `prompt` | | `context_recall` | `prompt` |
| `context_precision` | `prompt` | | `context_precision` | `prompt` |
| `noise_sensitivity` | `statement_prompt`, `faithfulness_prompt` | | `noise_sensitivity` | 使用函数式 `to_string()`(无 `instruction`/`examples`),无法通过 `adapt()` 本地化,**跳过** |
| `factual_correctness` | `prompt`, `nli_prompt` | | `factual_correctness` | `prompt`, `nli_prompt` |
| `semantic_similarity` | 无(纯 embedding,跳过) | | `semantic_similarity` | 无(纯 embedding,跳过) |
+3 -1
View File
@@ -23,12 +23,14 @@ CACHE_ROOT = _REPO_ROOT / "configs" / "judge_prompts"
# Metric name -> prompt instance attribute names holding a BasePrompt. # Metric name -> prompt instance attribute names holding a BasePrompt.
# Verified against RAGAS 0.4.3 collections source; semantic_similarity has none. # Verified against RAGAS 0.4.3 collections source; semantic_similarity has none.
# NOTE: noise_sensitivity's statement_prompt and faithfulness_prompt use function-based
# to_string() (ragas.prompt.metrics.common) without instruction/examples attributes,
# so they cannot be adapted with adapt() and are intentionally excluded here.
METRIC_PROMPT_ATTRS: dict[str, tuple[str, ...]] = { METRIC_PROMPT_ATTRS: dict[str, tuple[str, ...]] = {
"faithfulness": ("statement_generator_prompt", "nli_statement_prompt"), "faithfulness": ("statement_generator_prompt", "nli_statement_prompt"),
"answer_relevancy": ("prompt",), "answer_relevancy": ("prompt",),
"context_recall": ("prompt",), "context_recall": ("prompt",),
"context_precision": ("prompt",), "context_precision": ("prompt",),
"noise_sensitivity": ("statement_prompt", "faithfulness_prompt"),
"factual_correctness": ("prompt", "nli_prompt"), "factual_correctness": ("prompt", "nli_prompt"),
} }
@@ -4,6 +4,8 @@ app_adapter: null
dataset: ../../datasets/normalized/siemens_pdf_offline_smoke.csv dataset: ../../datasets/normalized/siemens_pdf_offline_smoke.csv
judge_model: deepseek-v4-flash judge_model: deepseek-v4-flash
embedding_model: text-embedding-v3 embedding_model: text-embedding-v3
# 使用中文评判 prompt 以提升对中文语料的判定一致性(架构设计 §10.5)
judge_language: zh
metrics: metrics:
- faithfulness - faithfulness
- answer_relevancy - answer_relevancy
@@ -3,6 +3,8 @@ mode: online
dataset: ../../datasets/raw/generated/siemens-pdf-question-bank.csv dataset: ../../datasets/raw/generated/siemens-pdf-question-bank.csv
judge_model: deepseek-v4-flash judge_model: deepseek-v4-flash
embedding_model: text-embedding-v3 embedding_model: text-embedding-v3
# 使用中文评判 prompt 以提升对中文语料的判定一致性(架构设计 §10.5)
judge_language: zh
optimization_advisor: true optimization_advisor: true
metrics: metrics:
- faithfulness - faithfulness
+56
View File
@@ -0,0 +1,56 @@
"""Integration test: committed zh cache files load cleanly onto real RAGAS metric instances."""
from pathlib import Path
from rag_eval.metrics.judge_prompts import CACHE_ROOT, METRIC_PROMPT_ATTRS
def test_zh_cache_files_all_present():
"""All expected zh cache files are committed and present on disk."""
zh_dir = CACHE_ROOT / "zh"
for metric, attrs in METRIC_PROMPT_ATTRS.items():
for attr in attrs:
path = zh_dir / f"{metric}__{attr}.json"
assert path.exists(), f"Missing committed cache: {path}"
def test_zh_cache_loads_onto_real_metrics():
"""Localize all registry metrics to zh — zero skips, zero errors."""
from unittest.mock import MagicMock
from ragas.llms.base import InstructorBaseRagasLLM
from ragas.embeddings.base import BaseRagasEmbedding
from rag_eval.metrics.factory import build_metric_registry
from rag_eval.metrics.judge_prompts import localize_pipeline_prompts, reset_cache
reset_cache()
llm = MagicMock(spec=InstructorBaseRagasLLM)
emb = MagicMock(spec=BaseRagasEmbedding)
registry = build_metric_registry(llm, emb)
report = localize_pipeline_prompts(registry, "zh")
expected_count = sum(len(attrs) for attrs in METRIC_PROMPT_ATTRS.values())
assert len(report.applied) == expected_count, (
f"Expected {expected_count} applied, got {len(report.applied)}; "
f"skipped={report.skipped}, warnings={report.warnings}"
)
assert not report.skipped, f"Unexpected skips: {report.skipped}"
def test_zh_cache_instructions_are_chinese():
"""After localization, faithfulness instruction starts with Chinese characters."""
from unittest.mock import MagicMock
from ragas.llms.base import InstructorBaseRagasLLM
from ragas.embeddings.base import BaseRagasEmbedding
from rag_eval.metrics.factory import build_metric_registry
from rag_eval.metrics.judge_prompts import localize_pipeline_prompts, reset_cache
reset_cache()
llm = MagicMock(spec=InstructorBaseRagasLLM)
emb = MagicMock(spec=BaseRagasEmbedding)
registry = build_metric_registry(llm, emb)
localize_pipeline_prompts(registry, "zh")
instr = registry["faithfulness"].statement_generator_prompt.instruction
# Chinese instruction should contain at least one CJK character.
has_chinese = any("\u4e00" <= ch <= "\u9fff" for ch in instr)
assert has_chinese, f"Instruction does not contain Chinese: {instr[:60]}"