Files
PlantAI-rag/prompts/prompt_v2_revised.py
2026-07-10 17:59:59 +08:00

419 lines
13 KiB
Python
Executable File
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
#!/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)