#!/usr/bin/env python3 """ 交互式Gold Standard标注工具 通过问答方式完成标注,无需写代码 """ import sys sys.path.insert(0, '/home/xzq/gene-family-mas-v2') from evaluation.gold_standard_annotator import GoldStandardAnnotator import json def print_header(text): print("\n" + "="*60) print(text) print("="*60) def get_input(prompt, default=None): """获取用户输入""" if default: full_prompt = f"{prompt} [{default}]: " else: full_prompt = f"{prompt}: " value = input(full_prompt).strip() return value if value else default def select_from_list(prompt, options): """从列表中选择""" print(f"\n{prompt}") for i, opt in enumerate(options, 1): print(f" {i}. {opt}") while True: choice = input(f"选择 (1-{len(options)}): ").strip() try: idx = int(choice) - 1 if 0 <= idx < len(options): return options[idx] except: pass print("无效选择,请重试") def yes_no(prompt, default=True): """是/否问题""" default_str = "Y/n" if default else "y/N" while True: answer = input(f"{prompt} [{default_str}]: ").strip().lower() if not answer: return default if answer in ['y', 'yes']: return True if answer in ['n', 'no']: return False print("请输入 y 或 n") def interactive_annotate(): """交互式标注主流程""" print_header("GeneFamily-RAG Gold Standard 交互式标注") print("\n这个工具会引导你完成论文的人工标注") print("标注结果将用于评估自动提取的准确率\n") # ========== 步骤1:论文信息 ========== print_header("步骤1:论文基本信息") doc_id = get_input("论文ID(如pmid31286919)", "pmid31286919") pmid = get_input("PMID", "31286919") doi = get_input("DOI", "10.1186/s12870-019-1916-6") species = get_input("物种拉丁名", "Fagopyrum tataricum") species_cn = get_input("物种中文名", "苦荞") family = get_input("基因家族", "SPL") year = get_input("发表年份", "2019") # ========== 步骤2:读取论文文本 ========== print_header("步骤2:论文文本") text_source = select_from_list( "选择文本来源", ["从PDF自动提取", "手动粘贴文本", "从文件读取"] ) text = "" if text_source == "从PDF自动提取": pdf_path = get_input("PDF文件路径", "/home/xzq/gene-family-mas-v2/paper/test_v2_reproduce/input/苦荞SPL2019BMCplantbios12870-019-1916-6.pdf") try: import fitz doc = fitz.open(pdf_path) # 只提取前3页(Abstract + Results开头) for page in doc[:3]: text += page.get_text() doc.close() print(f"✓ 已提取 {len(text)} 字符") except Exception as e: print(f"✗ 提取失败: {e}") text = get_input("请手动粘贴论文文本(Abstract + Results开头)") elif text_source == "手动粘贴文本": print("请粘贴论文文本(Abstract + Results开头,按Ctrl+D结束):") lines = [] try: while True: line = input() lines.append(line) except EOFError: pass text = "\n".join(lines) else: # 从文件读取 file_path = get_input("文本文件路径") with open(file_path, 'r', encoding='utf-8') as f: text = f.read() if len(text) < 100: print("✗ 文本太短,请重新运行") return print(f"✓ 文本长度: {len(text)} 字符") print(f"预览: {text[:200]}...") # ========== 步骤3:创建标注器 ========== annotator = GoldStandardAnnotator( doc_id=doc_id, text=text, title=get_input("论文标题", "Genome-wide identification of SPL gene family") ) annotator.set_metadata( pmid=pmid, doi=doi, species=species, family=family ) # ========== 步骤4:标注实体 ========== print_header("步骤3:标注实体") print("请从论文中识别关键实体(基因、家族、结构域、组织等)\n") entity_types = [ "gene", "protein", "gene_family", "protein_domain", "motif", "organism", "tissue", "developmental_stage", "treatment", "phenotype", "assay", "method" ] entity_count = 0 while yes_no("是否添加新实体?", True): entity_count += 1 print(f"\n--- 实体 #{entity_count} ---") entity_id = get_input("实体ID(如FtSPL1、SPL、PF03110)") if not entity_id: break entity_name = get_input("实体名称", entity_id) entity_type = select_from_list("实体类型", entity_types) entity_species = None if entity_type in ['gene', 'protein']: entity_species = get_input("所属物种", species) primary_id = get_input("稳定外部ID(如UniProt、PFAM编号,可选)", "") description = get_input("描述(可选)", "") # 添加到标注器 annotator.add_entity( id=entity_id, name=entity_name, type=entity_type, species=entity_species, primary_id=primary_id if primary_id else None, description=description if description else None ) print(f"✓ 已添加: {entity_id} ({entity_type})") print(f"\n共标注 {len(annotator.entities)} 个实体") # ========== 步骤5:标注关系 ========== print_header("步骤4:标注关系") print("请标注实体之间的生物学关系\n") relation_types = [ "member_of", "contains", "expressed_in", "involved_in", "activates", "inhibits", "regulates", "orthologous_to", "paralogous_to", "classified_into", "belongs_to" ] # 显示已标注的实体供选择 print("已标注的实体:") for e in annotator.entities: print(f" - {e.id} ({e.type})") relation_count = 0 while yes_no("是否添加新关系?", relation_count < 3): relation_count += 1 print(f"\n--- 关系 #{relation_count} ---") source = get_input("源实体ID") target = get_input("目标实体ID") rel_type = select_from_list("关系类型", relation_types) confidence = select_from_list("置信度", ["high", "medium", "low"]) # 证据信息 print("\n证据信息(原文片段):") snippet = get_input("原文关键句子(50字以内)") section = select_from_list("所在章节", ["abstract", "introduction", "results", "discussion", "figure_legend"]) evidence_level = "experiment" if confidence in ["high", "medium"]: evidence_level = select_from_list("证据等级", ["experiment", "omics", "prediction", "curation"]) method = get_input("实验/分析方法(如qRT-PCR、RNA-seq)", "") # 添加到标注器 annotator.add_relation( source=source, target=target, type=rel_type, confidence=confidence, text_snippet=snippet, section=section, evidence_level=evidence_level, method=method if method else None ) print(f"✓ 已添加: {source} --[{rel_type}]--> {target}") print(f"\n共标注 {len(annotator.relations)} 个关系") # ========== 步骤6:关键发现 ========== print_header("步骤5:关键发现") print("请总结论文的主要发现(一句话一条)\n") finding_count = 0 while yes_no("是否添加关键发现?", finding_count < 3): finding_count += 1 finding = get_input(f"关键发现 #{finding_count}") if finding: annotator.add_key_finding(finding) print(f"✓ 已添加") # ========== 步骤7:验证和保存 ========== print_header("步骤6:验证和保存") # 显示摘要 annotator.print_summary() # 验证 issues = annotator.validate() if issues: print("\n⚠️ 发现问题:") for issue in issues: print(f" - {issue}") if not yes_no("是否仍要保存?"): print("已取消保存") return # 保存 default_output = f"/home/xzq/gene-family-mas-v2/evaluation/gold_standard/{doc_id}_manual.json" output_path = get_input("保存路径", default_output) annotator.save(output_path) print_header("标注完成!") print(f"✓ Gold Standard已保存: {output_path}") print(f"\n下一步:") print(f" 1. 运行自动提取: python extract_with_llm.py ...") print(f" 2. 对比评估: python evaluation/evaluate.py --gold {output_path} --pred ") if __name__ == "__main__": try: interactive_annotate() except KeyboardInterrupt: print("\n\n已中断") except Exception as e: print(f"\n✗ 错误: {e}") import traceback traceback.print_exc()