419 lines
13 KiB
Python
Executable File
419 lines
13 KiB
Python
Executable File
#!/usr/bin/env python3
|
||
# -*- coding: utf-8 -*-
|
||
"""
|
||
修订版Prompt v2.0 - 基于证据约束的RAG系统
|
||
目标:降低幻觉率,强制证据标注
|
||
"""
|
||
|
||
# ==================== SYSTEM PROMPTS ====================
|
||
|
||
MULTI_HOP_SYSTEM_PROMPT = """你是基因家族分析领域的专家系统。
|
||
|
||
## 核心原则(必须遵守)
|
||
1. **证据优先**:所有回答必须基于提供的检索上下文,禁止添加外部知识
|
||
2. **诚实边界**:如果检索上下文不足以回答问题,明确说"信息不足",不要猜测
|
||
3. **来源标注**:每个关键事实后必须标注证据来源(格式:[module:rule])
|
||
|
||
## 检索上下文格式说明
|
||
你会收到结构化的检索片段,每个片段包含:
|
||
- module: 信息类型(family_scope, phylogeny, expression等)
|
||
- rule: 提取规则
|
||
- snippet: 原文片段
|
||
- section: 所在章节
|
||
- score: 相关度分数(0-1)
|
||
|
||
## 证据可信度分级
|
||
- **高可信**:来自Table S1/S2的精确匹配数据
|
||
- **中可信**:正文明确陈述的事实
|
||
- **低可信**:模式匹配结果,需进一步验证
|
||
|
||
## 禁止行为(违反会导致幻觉)
|
||
- ❌ 不要编造基因ID、表达数值、染色体位置等具体数据
|
||
- ❌ 不要推断提取器未明确提取的关系
|
||
- ❌ 不要将"可能"表述为"确定"
|
||
- ❌ 不要回答证据不足的问题
|
||
|
||
## 输出格式要求
|
||
1. 直接答案(基于证据)
|
||
2. 证据来源列表(必须)
|
||
3. 置信度评估(高/中/低/信息不足)
|
||
4. 缺失信息说明(如有)
|
||
"""
|
||
|
||
REASONING_SYSTEM_PROMPT = """你是基因家族分析专家,擅长分析信息缺口并规划检索策略。
|
||
|
||
## 任务
|
||
分析当前检索到的信息,判断是否能回答原始问题,并规划下一步检索。
|
||
|
||
## 分析维度
|
||
1. **信息覆盖度**:当前信息是否覆盖了问题的所有方面?
|
||
2. **证据质量**:信息的可信度如何?是否有矛盾?
|
||
3. **缺失识别**:还缺少哪些关键信息?
|
||
4. **后续查询**:如果需要补充,应该查询什么?
|
||
|
||
## 输出要求(JSON格式)
|
||
{
|
||
"analysis": "对当前信息的详细分析",
|
||
"missing_info": ["缺失信息1", "缺失信息2"],
|
||
"follow_up_queries": ["后续查询1", "后续查询2"],
|
||
"is_sufficient": true/false,
|
||
"confidence": "high/medium/low"
|
||
}
|
||
|
||
## 停止条件
|
||
当is_sufficient为true时,系统将停止迭代并生成最终答案。
|
||
"""
|
||
|
||
# ==================== USER PROMPTS ====================
|
||
|
||
FINAL_ANSWER_USER_PROMPT_TEMPLATE = """基于以下多跳检索获取的信息,请回答用户问题。
|
||
|
||
## 用户问题
|
||
{question}
|
||
|
||
## 检索到的信息(按模块组织)
|
||
{all_chunks_text}
|
||
|
||
## 回答要求
|
||
|
||
### 第一步:评估证据充分性(必须)
|
||
检查检索到的信息是否足以回答问题:
|
||
- [ ] 是否包含问题涉及的所有实体?
|
||
- [ ] 是否包含所需的数值/关系数据?
|
||
- [ ] 证据是否来自高可信度来源?
|
||
|
||
### 第二步:生成答案
|
||
**如果证据充分:**
|
||
- 给出直接答案
|
||
- 每个事实后标注来源(格式:[module:rule])
|
||
- 说明置信度(高/中/低)
|
||
|
||
**如果证据不足:**
|
||
- 明确说"根据现有信息无法回答"
|
||
- 列出缺失的关键信息
|
||
- 建议用户查询的具体数据类型
|
||
|
||
### 第三步:格式输出(必须JSON)
|
||
```json
|
||
{{
|
||
"can_answer": true/false,
|
||
"answer": "答案内容(如can_answer为false则为空)",
|
||
"evidence_refs": ["module:rule", ...],
|
||
"confidence": "high/medium/low/insufficient",
|
||
"missing_info": ["缺失信息1", ...],
|
||
"reasoning": "简要说明判断依据"
|
||
}}
|
||
```
|
||
|
||
注意:只输出JSON,不要有其他内容。
|
||
"""
|
||
|
||
REASONING_USER_PROMPT_TEMPLATE = """## 原始问题
|
||
{original_query}
|
||
|
||
## 先前查询
|
||
{previous_queries_text}
|
||
|
||
## 当前检索到的信息(第 {hop_number} 跳)
|
||
{chunks_text}
|
||
|
||
## 你的任务
|
||
1. 分析已检索到的信息与原始问题的关系
|
||
2. 确定能够更完整回答问题的特定缺失信息
|
||
3. 提出1-3个针对性的后续查询,以检索缺失信息
|
||
4. 判断当前信息是否足够回答原始问题
|
||
|
||
请以JSON格式回答:
|
||
{{
|
||
"analysis": "对当前信息的详细分析",
|
||
"missing_info": ["缺失信息1", "缺失信息2"],
|
||
"follow_up_queries": ["后续查询1", "后续查询2"],
|
||
"is_sufficient": true/false,
|
||
"confidence": "high/medium/low"
|
||
}}
|
||
"""
|
||
|
||
# ==================== EVALUATION PROMPT ====================
|
||
|
||
LLM_JUDGE_PROMPT_TEMPLATE = """你是基因家族分析领域的RAG系统评估专家。
|
||
|
||
## 评估任务
|
||
根据查询、生成的答案和检索到的上下文,评估答案质量。
|
||
|
||
## 评估维度
|
||
|
||
1. **忠实度 (Faithfulness)** - 权重30%
|
||
- 答案中的信息是否全部来自检索上下文?
|
||
- 有无编造数据(基因ID、数值、位置等)?
|
||
- 是否添加了外部知识?
|
||
|
||
2. **相关性 (Relevance)** - 权重25%
|
||
- 答案是否直接回答了用户的问题?
|
||
- 是否包含无关信息?
|
||
|
||
3. **完整性 (Completeness)** - 权重25%
|
||
- 答案是否完整?
|
||
- 是否遗漏了检索上下文中的重要信息?
|
||
|
||
4. **证据标注 (Citation)** - 权重20%
|
||
- 是否标注了证据来源?
|
||
- 标注是否准确?
|
||
|
||
## 幻觉检测重点
|
||
- 基因ID是否在检索上下文中存在?
|
||
- 数值(表达量、基因数等)是否与上下文一致?
|
||
- 染色体位置是否有明确来源?
|
||
- 分组归属是否有证据支持?
|
||
|
||
## 输出格式(JSON)
|
||
```json
|
||
{{
|
||
"faithfulness": 0.0-1.0,
|
||
"relevance": 0.0-1.0,
|
||
"completeness": 0.0-1.0,
|
||
"citation_quality": 0.0-1.0,
|
||
"overall_score": 0.0-1.0,
|
||
"hallucination_detected": true/false,
|
||
"hallucination_details": ["幻觉描述1", ...],
|
||
"reasoning": "评估理由",
|
||
"feedback": "改进建议"
|
||
}}
|
||
```
|
||
|
||
## 输入数据
|
||
|
||
### 查询
|
||
{query}
|
||
|
||
### 检索上下文
|
||
{contexts}
|
||
|
||
### 生成的答案
|
||
{answer}
|
||
|
||
请严格按JSON格式输出评估结果。
|
||
"""
|
||
|
||
# ==================== TEST QUESTIONS ====================
|
||
|
||
TEST_QUESTIONS = [
|
||
# 基础信息类 (5题)
|
||
{
|
||
"id": "Q1",
|
||
"category": "基础信息",
|
||
"question": "SPL基因家族在苦荞中有多少个成员?",
|
||
"expected_evidence": ["family_scope:total_genes_pattern"],
|
||
"difficulty": "简单"
|
||
},
|
||
{
|
||
"id": "Q2",
|
||
"category": "基础信息",
|
||
"question": "SPL基因家族可以分为几个组?",
|
||
"expected_evidence": ["clade_subfamily_assignment:total_groups_pattern"],
|
||
"difficulty": "简单"
|
||
},
|
||
{
|
||
"id": "Q3",
|
||
"category": "基础信息",
|
||
"question": "SPL基因家族的全称是什么?",
|
||
"expected_evidence": ["family_scope:family_match"],
|
||
"difficulty": "简单"
|
||
},
|
||
{
|
||
"id": "Q4",
|
||
"category": "基础信息",
|
||
"question": "SPL基因家族包含哪些保守结构域?",
|
||
"expected_evidence": ["motif_domain_architecture"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q5",
|
||
"category": "基础信息",
|
||
"question": "这篇论文研究的物种是什么?",
|
||
"expected_evidence": ["family_scope:species_match"],
|
||
"difficulty": "简单"
|
||
},
|
||
|
||
# 染色体位置类 (5题)
|
||
{
|
||
"id": "Q6",
|
||
"category": "染色体位置",
|
||
"question": "FtSPL1基因位于哪个染色体?",
|
||
"expected_evidence": ["chromosome_distribution"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q7",
|
||
"category": "染色体位置",
|
||
"question": "SPL基因家族在染色体上的分布特征是什么?",
|
||
"expected_evidence": ["chromosome_distribution"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q8",
|
||
"category": "染色体位置",
|
||
"question": "FtSPL2的起始和终止位置是多少?",
|
||
"expected_evidence": ["chromosome_distribution"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q9",
|
||
"category": "染色体位置",
|
||
"question": "哪些SPL基因位于1号染色体?",
|
||
"expected_evidence": ["chromosome_distribution"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q10",
|
||
"category": "染色体位置",
|
||
"question": "SPL基因在染色体上是随机分布还是成簇分布?",
|
||
"expected_evidence": ["chromosome_distribution", "duplication_type"],
|
||
"difficulty": "困难"
|
||
},
|
||
|
||
# 表达模式类 (5题)
|
||
{
|
||
"id": "Q11",
|
||
"category": "表达模式",
|
||
"question": "FtSPL2在根中的表达量是多少?",
|
||
"expected_evidence": ["expression_rnaseq_differential"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q12",
|
||
"category": "表达模式",
|
||
"question": "哪些SPL基因在干旱胁迫下表达上调?",
|
||
"expected_evidence": ["expression_rnaseq_differential"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q13",
|
||
"category": "表达模式",
|
||
"question": "SPL基因家族在哪些组织中表达?",
|
||
"expected_evidence": ["expression_rnaseq_differential"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q14",
|
||
"category": "表达模式",
|
||
"question": "FtSPL3的表达模式是什么?",
|
||
"expected_evidence": ["expression_rnaseq_differential"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q15",
|
||
"category": "表达模式",
|
||
"question": "SPL基因在种子发育过程中的表达变化趋势如何?",
|
||
"expected_evidence": ["expression_rnaseq_differential"],
|
||
"difficulty": "困难"
|
||
},
|
||
|
||
# 综合分析类 (5题)
|
||
{
|
||
"id": "Q16",
|
||
"category": "综合分析",
|
||
"question": "Group I包含哪些基因,它们在根中的表达如何?",
|
||
"expected_evidence": ["clade_subfamily_assignment:group_label_detect", "expression_rnaseq_differential"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q17",
|
||
"category": "综合分析",
|
||
"question": "SPL基因家族的进化特征是什么?",
|
||
"expected_evidence": ["phylogeny_tree_method", "duplication_type", "selection_pressure_ka_ks"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q18",
|
||
"category": "综合分析",
|
||
"question": "位于同一染色体的SPL基因是否具有相似的表达模式?",
|
||
"expected_evidence": ["chromosome_distribution", "expression_rnaseq_differential"],
|
||
"difficulty": "困难"
|
||
},
|
||
{
|
||
"id": "Q19",
|
||
"category": "综合分析",
|
||
"question": "SPL基因家族与拟南芥的同源基因有哪些?",
|
||
"expected_evidence": ["phylogeny_tree_method"],
|
||
"difficulty": "中等"
|
||
},
|
||
{
|
||
"id": "Q20",
|
||
"category": "综合分析",
|
||
"question": "SPL基因家族的基因结构特征(外显子/内含子)是什么?",
|
||
"expected_evidence": ["gene_structure"],
|
||
"difficulty": "中等"
|
||
}
|
||
]
|
||
|
||
|
||
# ==================== HELPER FUNCTIONS ====================
|
||
|
||
def format_final_answer_prompt(question: str, all_chunks_text: str) -> str:
|
||
"""格式化最终答案生成的User Prompt"""
|
||
return FINAL_ANSWER_USER_PROMPT_TEMPLATE.format(
|
||
question=question,
|
||
all_chunks_text=all_chunks_text
|
||
)
|
||
|
||
|
||
def format_reasoning_prompt(original_query: str, previous_queries_text: str,
|
||
hop_number: int, chunks_text: str) -> str:
|
||
"""格式化推理分析的User Prompt"""
|
||
return REASONING_USER_PROMPT_TEMPLATE.format(
|
||
original_query=original_query,
|
||
previous_queries_text=previous_queries_text,
|
||
hop_number=hop_number,
|
||
chunks_text=chunks_text
|
||
)
|
||
|
||
|
||
def format_evaluation_prompt(query: str, answer: str, contexts: list) -> str:
|
||
"""格式化评估的User Prompt"""
|
||
contexts_text = "\n\n".join([f"[Context {i+1}]\n{c}" for i, c in enumerate(contexts)])
|
||
return LLM_JUDGE_PROMPT_TEMPLATE.format(
|
||
query=query,
|
||
answer=answer,
|
||
contexts=contexts_text
|
||
)
|
||
|
||
|
||
def get_test_questions():
|
||
"""获取测试问题列表"""
|
||
return TEST_QUESTIONS
|
||
|
||
|
||
def get_questions_by_category(category: str):
|
||
"""按类别获取测试问题"""
|
||
return [q for q in TEST_QUESTIONS if q["category"] == category]
|
||
|
||
|
||
def get_questions_by_difficulty(difficulty: str):
|
||
"""按难度获取测试问题"""
|
||
return [q for q in TEST_QUESTIONS if q["difficulty"] == difficulty]
|
||
|
||
|
||
if __name__ == "__main__":
|
||
print("=" * 60)
|
||
print("修订版Prompt v2.0 - 基于证据约束的RAG系统")
|
||
print("=" * 60)
|
||
print(f"\n测试问题总数: {len(TEST_QUESTIONS)}")
|
||
|
||
# 统计
|
||
categories = {}
|
||
difficulties = {}
|
||
for q in TEST_QUESTIONS:
|
||
categories[q["category"]] = categories.get(q["category"], 0) + 1
|
||
difficulties[q["difficulty"]] = difficulties.get(q["difficulty"], 0) + 1
|
||
|
||
print("\n按类别分布:")
|
||
for cat, count in categories.items():
|
||
print(f" {cat}: {count}题")
|
||
|
||
print("\n按难度分布:")
|
||
for diff, count in difficulties.items():
|
||
print(f" {diff}: {count}题")
|
||
|
||
print("\n" + "=" * 60)
|
||
print("✅ Prompt配置加载完成")
|
||
print("=" * 60) |