PlantAI-rag-new 首次提交:文献知识工作台

- BFF 薄适配层(FastAPI,8090):QA 代理/SSE 中继/Neo4j 直连/抽取代理/会话持久化
- Vue3 前端:五相位检索流水线、文献来源与证据链、知识图谱、提取任务四 tab
- 四档响应式(桌面/笔记本/平板/手机)、深浅主题
- 81 个 FR 全部实现,测试追溯矩阵 81/81,后端 121 测/前端 156 测
- 真实凭据已脱敏,运行时配置在 backend/.env(不入 git)

Co-Authored-By: Claude Fable 5 <noreply@anthropic.com>
This commit is contained in:
jerry
2026-08-25 20:47:52 +08:00
co-authored by Claude Fable 5
commit 1e94de7464
171 changed files with 26291 additions and 0 deletions
+46
View File
@@ -0,0 +1,46 @@
{
"job_id": "2943168e9c48",
"title": "wang2019.pdf",
"status": "completed",
"stage": "completed",
"progress_pct": 100,
"message": "MinerU+LLM 方法学提取完成",
"paper_id": "demo_paper",
"family": "",
"stats": {
"stage_count": 6,
"species_count": 1,
"figure_table_caption_count": 9,
"figure_table_stage_count": 12
},
"detail": {
"updated_at": "2026-08-07T09:42:17",
"job_id": "2943168e9c48",
"status": "completed",
"stage": "completed",
"substage": "done",
"message": "MinerU+LLM 方法学提取完成",
"source_path": "/home/xzq/5090/PlantAI-rag/data/evaluation/methodology_upload_jobs/2943168e9c48/upload/wang2019.pdf",
"content_list": "/home/xzq/5090/PlantAI-rag/data/evaluation/methodology_upload_jobs/2943168e9c48/mineru/wang2019/txt/wang2019_content_list.json",
"output_dir": "/home/xzq/5090/PlantAI-rag/methodology_batch_demo_paper_2943168e9c48",
"figure_table_warning": ""
},
"plan": {
"plan_id": "plan_2943168e9c48",
"paper_id": "demo_paper",
"gene_family": "",
"source_batch_dir": "/home/xzq/5090/PlantAI-rag/methodology_batch_demo_paper_2943168e9c48",
"stages": [
{
"observation_id": "obs_1",
"literature_stage": "gene_family_identification",
"literature_tools": [
{ "tool_name": "hmmsearch", "version": "3.3.2", "role": "domain search", "url": "", "parameters": "E-value 1e-5" }
],
"method_components": [],
"literature_parameters": [],
"inputs": []
}
]
}
}
@@ -0,0 +1,81 @@
{
"jobs": [
{
"job_id": "2943168e9c48",
"title": "wang2019.pdf",
"status": "completed",
"stage": "completed",
"substage": "done",
"progress_pct": 100,
"message": "MinerU+LLM 方法学提取完成",
"paper_id": "demo_paper",
"family": "",
"source_filename": "wang2019.pdf",
"stage_count": 6,
"species_count": 1,
"figure_table_caption_count": 9,
"figure_table_stage_count": 12,
"updated_at": "2026-08-07T09:42:17",
"config_url": "/api/v1/methodology/extract-jobs/2943168e9c48/bbav5-config",
"methodology_plan_url": "/api/v1/methodology/extract-jobs/2943168e9c48/plan"
},
{
"job_id": "a1b2c3d4e5f6",
"title": "Pecan_SPL_2021.pdf",
"status": "running",
"stage": "llm",
"substage": null,
"progress_pct": 75,
"message": "LLM 关系抽取中…",
"paper_id": "pecan_spl",
"family": "SPL",
"source_filename": "Pecan_SPL_2021.pdf",
"stage_count": 4,
"species_count": 1,
"figure_table_caption_count": 3,
"figure_table_stage_count": 4,
"updated_at": "2026-08-25T09:52:00",
"config_url": null,
"methodology_plan_url": null
},
{
"job_id": "b2c3d4e5f6a7",
"title": "Rice_NAC_2020.pdf",
"status": "failed",
"stage": "failed",
"substage": null,
"progress_pct": null,
"message": "MinerU 解析失败:/home/xia-lab/miniconda3/envs/minerupy311/bin/mineru 不存在",
"paper_id": "rice_nac",
"family": "NAC",
"source_filename": "Rice_NAC_2020.pdf",
"stage_count": 0,
"species_count": 0,
"figure_table_caption_count": 0,
"figure_table_stage_count": 0,
"updated_at": "2026-08-24T14:38:00",
"config_url": null,
"methodology_plan_url": null
},
{
"job_id": "c3d4e5f6a7b8",
"title": "At_HDZIP_2018.pdf",
"status": "queued",
"stage": "queued",
"substage": null,
"progress_pct": 5,
"message": "排队等待处理",
"paper_id": "at_hdzip",
"family": "HD-ZIP",
"source_filename": "At_HDZIP_2018.pdf",
"stage_count": 0,
"species_count": 0,
"figure_table_caption_count": 0,
"figure_table_stage_count": 0,
"updated_at": "2026-08-24T16:00:00",
"config_url": null,
"methodology_plan_url": null
}
],
"count": 4
}
@@ -0,0 +1,3 @@
{
"rows": []
}
@@ -0,0 +1,3 @@
{
"rows": []
}
@@ -0,0 +1,6 @@
{
"success": true,
"job_id": "d4e5f6a7b8c9",
"status_url": "/api/v1/methodology/extract-jobs/d4e5f6a7b8c9",
"message": "已提交抽取任务"
}
+11
View File
@@ -0,0 +1,11 @@
{
"status": "ok",
"version": "0.1.0",
"upstreams": {
"qa": { "ok": true, "version": "1.1.73-assertion-anchored" },
"rag": { "ok": true },
"ragAlt": { "ok": true },
"neo4j": { "ok": true, "active": "neo4j-plant-srt-pc2025-v3-20260817-normalized" }
},
"db": { "ok": true, "sessions": 12 }
}
+10
View File
@@ -0,0 +1,10 @@
{
"query": "OsSPL",
"results": [
{ "id": "cent_ospl14", "name": "OsSPL14", "type": "gene", "support_count": 18 },
{ "id": "cent_ospl7", "name": "OsSPL7", "type": "gene", "support_count": 9 },
{ "id": "cent_ospl16", "name": "OsSPL16", "type": "gene", "support_count": 7 },
{ "id": "cent_ospl2", "name": "OsSPL2", "type": "gene", "support_count": 5 },
{ "id": "cent_spl_family", "name": "SPL gene family", "type": "gene_family", "support_count": 120 }
]
}
+13
View File
@@ -0,0 +1,13 @@
{
"id": "cent_ospl14",
"name": "OsSPL14",
"type": "gene",
"properties": {
"canonical_id": "cent_ospl14",
"name": "OsSPL14",
"name_norm": "osspl14",
"entity_type": "gene",
"rule": "exact_name_norm_same_type",
"support_count": 18
}
}
@@ -0,0 +1,20 @@
{
"center": "cent_ospl14",
"center_name": "OsSPL14",
"depth": 1,
"nodes": [
{ "id": "cent_ospl14", "label": "OsSPL14", "type": "gene" },
{ "id": "cent_panicle", "label": "panicle", "type": "experimental_material" },
{ "id": "cent_branching", "label": "panicle branching", "type": "process" },
{ "id": "cent_mir156", "label": "miR156", "type": "sequence_feature" },
{ "id": "cent_ospl7", "label": "OsSPL7", "type": "gene" },
{ "id": "cent_ospl16", "label": "OsSPL16", "type": "gene" }
],
"edges": [
{ "source": "cent_ospl14", "target": "cent_panicle", "relation": "expressed_in", "label": "expressed_in", "assertion_id": "cass_aaaa01", "paper_count": 5, "support_count": 9, "title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice", "doi": "10.1038/ng.591", "pmid": "20393464" },
{ "source": "cent_ospl14", "target": "cent_branching", "relation": "involved_in", "label": "involved_in", "assertion_id": "cass_aaaa02", "paper_count": 3, "support_count": 4, "title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice", "doi": "10.1038/ng.591", "pmid": "20393464" },
{ "source": "cent_ospl14", "target": "cent_mir156", "relation": "regulated_by", "label": "regulated_by", "assertion_id": "cass_aaaa03", "paper_count": 2, "support_count": 2, "title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice", "doi": "10.1038/ng.591", "pmid": "20393464" },
{ "source": "cent_ospl7", "target": "cent_branching", "relation": "involved_in", "label": "involved_in", "assertion_id": "cass_bbbb01", "paper_count": 1, "support_count": 1, "title": "OsSPL7 regulates panicle development in rice", "doi": null, "pmid": "22429691" },
{ "source": "cent_ospl16", "target": "cent_panicle", "relation": "expressed_in", "label": "expressed_in", "assertion_id": "cass_cccc01", "paper_count": 1, "support_count": 1, "title": "OsSPL16 controls grain size and quality via the GW8 pathway", "doi": "10.1038/ng.2327", "pmid": "22635119" }
]
}
+18
View File
@@ -0,0 +1,18 @@
{
"entities_by_type": [
{ "type": "gene", "count": 43210 },
{ "type": "gene_family", "count": 1288 },
{ "type": "phenotype", "count": 1560 },
{ "type": "process", "count": 8912 },
{ "type": "trait", "count": 2341 },
{ "type": "experimental_material", "count": 340 },
{ "type": "method", "count": 1204 }
],
"relations_by_category": [
{ "type": "characterized_in", "count": 12033 },
{ "type": "involved_in", "count": 9812 },
{ "type": "regulated_by", "count": 7780 },
{ "type": "identified_in", "count": 5620 },
{ "type": "collinear_with", "count": 9350 }
]
}
+11
View File
@@ -0,0 +1,11 @@
{
"ok": true,
"active": "neo4j-plant-srt-direct-v1-full",
"schema": "canonical",
"labels": ["SRT2CanonicalEntity", "SRT2CanonicalAssertion", "SRT2Paper"],
"relationship_types": ["SRT2_CANONICAL_ASSERTS", "SRT2_SUPPORTED_BY"],
"entity_count": 3820996,
"assertion_count": 8246875,
"name_index": "srt2_canonical_entity_name_norm",
"fulltext_index": null
}
+145
View File
@@ -0,0 +1,145 @@
{
"query_id": "q-mock-001",
"version": "1.1.73-assertion-anchored",
"mode": "chat",
"answer": "水稻 **SPL**(SQUAMOSA PROMOTER BINDING PROTEIN-LIKE)基因家族共有 **19 个成员**,主要功能包括:\n\n1. **穗部形态建成**:*OsSPL14*(IPA1/WFP)通过调控分蘖数与穗分枝,是理想株型的关键因子\n2. **生育期调控**:部分成员参与抽穗期的光周期响应\n3. **miR156 调控网络**:多数成员受 miR156 转录后调控,随植株发育表达量上升\n\n| 成员 | 关键功能 | 证据来源 |\n|------|---------|----------|\n| OsSPL14 | 穗分枝、株型建成 | PMID 20393464 |\n| OsSPL7 | 穗发育负调控 | PMID 22429691 |\n| OsSPL16 | 籽粒大小与品质 | PMID 22635119 |\n",
"source_count": 4,
"sources": [
{
"id": "src-1",
"title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice",
"paper_id": "PMID20393464",
"chunk_id": "chunk_20393464_12",
"pmid": "20393464",
"pmcid": null,
"doi": "10.1038/ng.591",
"url": "https://doi.org/10.1038/ng.591",
"snippet": "OsSPL14 positively regulates panicle branching and grain yield in rice, acting as a key factor of ideal plant architecture.",
"score": 0.92,
"locator": "Abstract",
"section": "Results",
"support_count": 3,
"type": "assertion",
"metadata": { "evidence_confidence": "high" }
},
{
"id": "src-2",
"title": "Genome-wide identification of the SPL gene family in Tartary buckwheat",
"paper_id": "PMID31286919",
"chunk_id": "chunk_31286919_07",
"pmid": "31286919",
"pmcid": "PMC6612345",
"doi": "10.1186/s12870-019-1903-y",
"url": "https://doi.org/10.1186/s12870-019-1903-y",
"snippet": "A total of 28 SPL genes were identified in Fagopyrum tataricum genome and their expression profiles were analyzed across tissues.",
"score": 0.85,
"locator": "Results",
"section": "Results",
"support_count": 2,
"type": "assertion",
"metadata": {}
},
{
"id": "src-3",
"title": "OsSPL16 controls grain size and quality via the GW8 pathway",
"paper_id": "PMID22635119",
"chunk_id": "chunk_22635119_03",
"pmid": "22635119",
"pmcid": null,
"doi": "10.1038/ng.2327",
"url": "https://doi.org/10.1038/ng.2327",
"snippet": "OsSPL16 positively regulates cell proliferation and promotes grain width, improving yield and quality.",
"score": 0.81,
"locator": "Abstract",
"section": "Results",
"support_count": 1,
"type": "assertion",
"metadata": {}
},
{
"id": "src-4",
"title": "OsSPL7 regulates panicle development in rice",
"paper_id": "PMID22429691",
"chunk_id": "chunk_22429691_09",
"pmid": "22429691",
"pmcid": "PMC3301234",
"doi": null,
"url": "https://pubmed.ncbi.nlm.nih.gov/22429691/",
"snippet": "OsSPL7 acts as a negative regulator of panicle branching in rice.",
"score": 0.72,
"locator": "Results",
"section": "Results",
"support_count": 1,
"type": "assertion",
"metadata": {}
}
],
"used_sources": [
{
"id": "src-1",
"title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice",
"paper_id": "PMID20393464",
"chunk_id": "chunk_20393464_12",
"pmid": "20393464",
"pmcid": null,
"doi": "10.1038/ng.591",
"url": "https://doi.org/10.1038/ng.591",
"snippet": "OsSPL14 positively regulates panicle branching and grain yield in rice.",
"score": 0.92,
"locator": "Abstract",
"section": "Results",
"support_count": 3,
"type": "assertion",
"metadata": {}
}
],
"chains": [
{
"chain_id": "c1",
"hops": [
{ "source": "OsSPL14", "target": "panicle", "relation": "expressed_in", "source_type": "Gene", "target_type": "Tissue", "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL14", "target": "panicle branching", "relation": "involved_in", "source_type": "Gene", "target_type": "Process", "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" }
]
},
{
"chain_id": "c2",
"hops": [
{ "source": "OsSPL14", "target": "miR156", "relation": "regulated_by", "source_type": "Gene", "target_type": "Molecule", "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" }
]
}
],
"graph": {
"nodes": [
{ "id": "OsSPL14", "label": "OsSPL14", "type": "Gene" },
{ "id": "panicle", "label": "panicle", "type": "Tissue" },
{ "id": "panicle branching", "label": "panicle branching", "type": "Process" },
{ "id": "miR156", "label": "miR156", "type": "Molecule" },
{ "id": "OsSPL16", "label": "OsSPL16", "type": "Gene" },
{ "id": "grain width", "label": "grain width", "type": "Trait" }
],
"edges": [
{ "source": "OsSPL14", "target": "panicle", "relation": "expressed_in", "label": "expressed_in", "chain_id": "c1", "hop": 1, "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL14", "target": "panicle branching", "relation": "involved_in", "label": "involved_in", "chain_id": "c1", "hop": 2, "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL14", "target": "miR156", "relation": "regulated_by", "label": "regulated_by", "chain_id": "c2", "hop": 1, "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL16", "target": "grain width", "relation": "involved_in", "label": "involved_in", "chain_id": "c3", "hop": 1, "paper_id": "PMID22635119", "pmid": "22635119", "pmcid": null, "doi": "10.1038/ng.2327" }
],
"chains": [
{
"chain_id": "c1",
"hops": [
{ "source": "OsSPL14", "target": "panicle", "relation": "expressed_in", "source_type": "Gene", "target_type": "Tissue", "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL14", "target": "panicle branching", "relation": "involved_in", "source_type": "Gene", "target_type": "Process", "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" }
]
}
]
},
"retrieval_plan": {
"anchor_entity": "SPL gene family",
"relation_family": "function",
"preferred_papers": ["PMID20393464", "PMID31286919"]
},
"retrieval_notes": "graph-balanced top30 evidence chain, with 15 graph-derived items",
"timing": { "total_s": 41.2, "retrieval_s": 22.1, "generation_s": 19.1 },
"citation_map": { "1": "PMID20393464", "2": "PMID31286919", "3": "PMID22635119" },
"raw": {}
}
+16
View File
@@ -0,0 +1,16 @@
{
"query_id": "q-mock-empty-001",
"version": "1.1.73-assertion-anchored",
"mode": "chat",
"answer": "未找到与该问题直接相关的文献证据。建议调整关键词或换一个更具体的问法。",
"source_count": 0,
"sources": [],
"used_sources": [],
"chains": [],
"graph": { "nodes": [], "edges": [], "chains": [] },
"retrieval_plan": null,
"retrieval_notes": "zero evidence retained",
"timing": { "total_s": 8.1 },
"citation_map": {},
"raw": {}
}
+35
View File
@@ -0,0 +1,35 @@
{
"name": "QA V1.1.73 Assertion-Anchored Full Graph",
"version": "1.1.73-assertion-anchored",
"pipeline": [
"question",
"provisional answer",
"structured planner",
"direct Qdrant recall over V15/V16 canonical assertions",
"canonical seed extraction",
"Neo4j exact seed retrieval",
"Neo4j bounded graph expansion",
"BGE rerank",
"graph-balanced top30 evidence chain",
"BGE top10 Qdrant source seed selection",
"native Neo4j two-hop chain retrieval",
"final LLM answer composition"
],
"config": {
"max_rounds": 1,
"max_evidence": 40,
"provisional_draft": false,
"second_round": true,
"bge_rerank": true,
"graph_schema": "canonical",
"knowledge_chain_defaults": {
"chain_top_k": 3,
"chain_hops": 2,
"chain_max_edges": 12,
"chain_max_nodes": 10,
"chain_mode": "display",
"native_chain_max_hops": 3,
"native_chain_top_k": 5
}
}
}
+45
View File
@@ -0,0 +1,45 @@
{
"query_id": "q-mock-search-001",
"version": "1.1.73-assertion-anchored",
"mode": "search",
"answer": "",
"source_count": 4,
"sources": [
{
"id": "src-1", "title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice",
"paper_id": "PMID20393464", "chunk_id": "chunk_20393464_12", "pmid": "20393464", "pmcid": null,
"doi": "10.1038/ng.591", "url": "https://doi.org/10.1038/ng.591",
"snippet": "OsSPL14 positively regulates panicle branching and grain yield in rice.", "score": 0.92,
"locator": "Abstract", "section": "Results", "support_count": 3, "type": "assertion", "metadata": {}
},
{
"id": "src-2", "title": "Genome-wide identification of the SPL gene family in Tartary buckwheat",
"paper_id": "PMID31286919", "chunk_id": "chunk_31286919_07", "pmid": "31286919", "pmcid": "PMC6612345",
"doi": "10.1186/s12870-019-1903-y", "url": "https://doi.org/10.1186/s12870-019-1903-y",
"snippet": "28 SPL genes identified in Fagopyrum tataricum.", "score": 0.85,
"locator": "Results", "section": "Results", "support_count": 2, "type": "assertion", "metadata": {}
},
{
"id": "src-3", "title": "OsSPL16 controls grain size and quality via the GW8 pathway",
"paper_id": "PMID22635119", "chunk_id": "chunk_22635119_03", "pmid": "22635119", "pmcid": null,
"doi": "10.1038/ng.2327", "url": "https://doi.org/10.1038/ng.2327",
"snippet": "OsSPL16 positively regulates cell proliferation.", "score": 0.81,
"locator": "Abstract", "section": "Results", "support_count": 1, "type": "assertion", "metadata": {}
},
{
"id": "src-4", "title": "OsSPL7 regulates panicle development in rice",
"paper_id": "PMID22429691", "chunk_id": "chunk_22429691_09", "pmid": "22429691", "pmcid": "PMC3301234",
"doi": null, "url": "https://pubmed.ncbi.nlm.nih.gov/22429691/",
"snippet": "OsSPL7 acts as a negative regulator of panicle branching.", "score": 0.72,
"locator": "Results", "section": "Results", "support_count": 1, "type": "assertion", "metadata": {}
}
],
"used_sources": [],
"chains": [],
"graph": { "nodes": [], "edges": [], "chains": [] },
"retrieval_plan": null,
"retrieval_notes": "retrieval only, no LLM answer",
"timing": { "total_s": 4.7 },
"citation_map": {},
"raw": {}
}
@@ -0,0 +1,9 @@
{
"id": "sess-new-mock",
"title": "新会话",
"status": "idle",
"created_at": "2026-08-25T12:00:00Z",
"updated_at": "2026-08-25T12:00:00Z",
"message_count": 0,
"meta": { "title_manual": false }
}
+62
View File
@@ -0,0 +1,62 @@
{
"id": "sess-003",
"title": "水稻SPL基因家族的功能研究有哪些关键进展?",
"status": "done",
"created_at": "2026-08-25T09:41:00Z",
"updated_at": "2026-08-25T10:02:00Z",
"meta": { "title_manual": false },
"messages": [
{
"id": "msg-001",
"role": "user",
"kind": "qa",
"status": "done",
"blocks": [],
"raw": null,
"text": "水稻SPL基因家族的功能研究有哪些关键进展?",
"created_at": "2026-08-25T09:41:00Z",
"updated_at": "2026-08-25T09:41:00Z"
},
{
"id": "msg-002",
"role": "assistant",
"kind": "qa",
"status": "done",
"blocks": [
{ "type": "text", "html": "<p>水稻 <strong>SPL</strong>(SQUAMOSA PROMOTER BINDING PROTEIN-LIKE)基因家族共有 <strong>19 个成员</strong>,主要功能包括:</p><ol><li><strong>穗部形态建成</strong>:<em>OsSPL14</em>(IPA1/WFP)通过调控分蘖数与穗分枝,是理想株型的关键因子</li><li><strong>生育期调控</strong>:部分成员参与抽穗期的光周期响应</li><li><strong>miR156 调控网络</strong>:多数成员受 miR156 转录后调控</li></ol>" },
{ "type": "table", "headers": ["成员", "关键功能", "证据来源"], "rows": [
["OsSPL14", "穗分枝、株型建成", "PMID 20393464"],
["OsSPL7", "穗发育负调控", "PMID 22429691"],
["OsSPL16", "籽粒大小与品质", "PMID 22635119"]
] },
{ "type": "pipeline", "status": "done", "steps": [
{ "name": "规划", "state": "done", "desc": "意图理解与实体识别", "phase": "planning" },
{ "name": "检索", "state": "done", "desc": "多路检索与子问题召回", "phase": "retrieval" },
{ "name": "证据", "state": "done", "desc": "证据筛选与交叉验证", "phase": "evidence" },
{ "name": "生成", "state": "done", "desc": "答案生成与引用锚定", "phase": "generation" }
] },
{ "type": "tool", "icon": "🧬", "name": "已识别实体", "status": "done", "dur": "0.8s", "open": false, "phase": "planning",
"params": "SPL\nOsSPL14\nrice\npanicle", "result": "4 个实体", "logs": [
{ "text": "> 实体识别完成: 4 entities", "cls": "log-ok" }
] },
{ "type": "tool", "icon": "🔬", "name": "证据筛选", "status": "done", "dur": "3.2s", "open": false, "phase": "evidence",
"params": "accepted_count: 4\npublication_count: 4", "result": "4 条证据通过审核", "logs": [
{ "text": "> 候选证据 18 条", "cls": "log-run" },
{ "text": "> 采纳 4 条(high confidence)", "cls": "log-ok" }
] },
{ "type": "sources", "items": [] },
{ "type": "graph", "nodes": [
{ "id": "OsSPL14", "label": "OsSPL14", "type": "Gene" },
{ "id": "panicle", "label": "panicle", "type": "Tissue" },
{ "id": "miR156", "label": "miR156", "type": "Molecule" }
], "edges": [
{ "source": "OsSPL14", "target": "panicle", "relation": "expressed_in", "label": "expressed_in", "chain_id": "c1", "hop": 1, "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" },
{ "source": "OsSPL14", "target": "miR156", "relation": "regulated_by", "label": "regulated_by", "chain_id": "c2", "hop": 1, "paper_id": "PMID20393464", "pmid": "20393464", "pmcid": null, "doi": "10.1038/ng.591" }
], "chains": [] }
],
"raw": {},
"created_at": "2026-08-25T09:41:00Z",
"updated_at": "2026-08-25T10:02:00Z"
}
]
}
+7
View File
@@ -0,0 +1,7 @@
[
{ "id": "sess-003", "title": "水稻SPL基因家族的功能研究有哪些关键进展?", "status": "done", "created_at": "2026-08-25T09:41:00Z", "updated_at": "2026-08-25T10:02:00Z", "message_count": 2, "meta": { "title_manual": false } },
{ "id": "sess-002", "title": "拟南芥HD-ZIP家族的生物学功能", "status": "done", "created_at": "2026-08-25T08:10:00Z", "updated_at": "2026-08-25T08:15:00Z", "message_count": 2, "meta": { "title_manual": false } },
{ "id": "sess-001", "title": "文献检索记录", "status": "error", "created_at": "2026-08-24T15:20:00Z", "updated_at": "2026-08-24T15:23:00Z", "message_count": 2, "meta": { "title_manual": true } },
{ "id": "sess-000", "title": "NAC 转录因子研究综述", "status": "done", "created_at": "2026-08-23T11:05:00Z", "updated_at": "2026-08-23T11:30:00Z", "message_count": 4, "meta": { "title_manual": false } },
{ "id": "sess-099", "title": "番茄TCP家族选择压力", "status": "idle", "created_at": "2026-08-20T09:00:00Z", "updated_at": "2026-08-20T09:00:00Z", "message_count": 0, "meta": { "title_manual": false } }
]
@@ -0,0 +1,38 @@
{
"session_id": "sess-003",
"total": 4,
"sources": [
{
"id": "src-1", "title": "Regulation of OsSPL14 by OsmiR156 defines ideal plant architecture in rice",
"paper_id": "PMID20393464", "chunk_id": "chunk_20393464_12", "pmid": "20393464", "pmcid": null,
"doi": "10.1038/ng.591", "url": "https://doi.org/10.1038/ng.591",
"snippet": "OsSPL14 positively regulates panicle branching and grain yield in rice.",
"score": 0.92, "locator": "Abstract", "section": "Results", "support_count": 3,
"type": "assertion", "metadata": {}, "count": 2
},
{
"id": "src-2", "title": "Genome-wide identification of the SPL gene family in Tartary buckwheat",
"paper_id": "PMID31286919", "chunk_id": "chunk_31286919_07", "pmid": "31286919", "pmcid": "PMC6612345",
"doi": "10.1186/s12870-019-1903-y", "url": "https://doi.org/10.1186/s12870-019-1903-y",
"snippet": "28 SPL genes identified in Fagopyrum tataricum.",
"score": 0.85, "locator": "Results", "section": "Results", "support_count": 2,
"type": "assertion", "metadata": {}, "count": 1
},
{
"id": "src-3", "title": "OsSPL16 controls grain size and quality via the GW8 pathway",
"paper_id": "PMID22635119", "chunk_id": "chunk_22635119_03", "pmid": "22635119", "pmcid": null,
"doi": "10.1038/ng.2327", "url": "https://doi.org/10.1038/ng.2327",
"snippet": "OsSPL16 positively regulates cell proliferation.",
"score": 0.81, "locator": "Abstract", "section": "Results", "support_count": 1,
"type": "assertion", "metadata": {}, "count": 1
},
{
"id": "src-4", "title": "OsSPL7 regulates panicle development in rice",
"paper_id": "PMID22429691", "chunk_id": "chunk_22429691_09", "pmid": "22429691", "pmcid": "PMC3301234",
"doi": null, "url": "https://pubmed.ncbi.nlm.nih.gov/22429691/",
"snippet": "OsSPL7 acts as a negative regulator of panicle branching.",
"score": 0.72, "locator": "Results", "section": "Results", "support_count": 1,
"type": "assertion", "metadata": {}, "count": 1
}
]
}
+14
View File
@@ -0,0 +1,14 @@
[
{ "stage": "understanding_started", "phase": "planning", "label": "开始理解问题", "details": {}, "delay_ms": 400 },
{ "stage": "entities_identified", "phase": "planning", "label": "已识别实体", "details": { "entities": ["SPL", "OsSPL14", "rice", "panicle"] }, "delay_ms": 800 },
{ "stage": "related_search_started", "phase": "planning", "label": "开始关联检索", "details": {}, "delay_ms": 600 },
{ "stage": "concepts_found", "phase": "retrieval", "label": "发现相关概念", "details": { "concepts": ["panicle branching", "grain yield", "miR156"], "concept_count": 3, "connection_count": 12 }, "delay_ms": 900 },
{ "stage": "subquestion_retrieved", "phase": "retrieval", "label": "子问题召回完成", "details": { "candidate_count": 18 }, "delay_ms": 900 },
{ "stage": "agent_review_started", "phase": "retrieval", "label": "开始智能体审核", "details": {}, "delay_ms": 600 },
{ "stage": "subquestion_retry_started", "phase": "retrieval", "label": "子问题重试中", "details": {}, "delay_ms": 600 },
{ "stage": "evidence_accepted", "phase": "evidence", "label": "证据已采纳", "details": { "accepted_count": 6 }, "delay_ms": 800 },
{ "stage": "evidence_review_completed", "phase": "evidence", "label": "证据审核完成", "details": { "accepted_count": 4, "publication_count": 4 }, "delay_ms": 700 },
{ "stage": "answer_generation_started", "phase": "generation", "label": "开始生成答案", "details": {}, "delay_ms": 600 },
{ "stage": "completed", "phase": "done", "label": "检索完成", "details": {}, "delay_ms": 400 },
"__complete__"
]
@@ -0,0 +1,145 @@
{
"_comment": "真实上游 7920 /api/v1/qa-v1-1-73/search 响应形态样本(DISCOVERY.md §4.1 结构,已截断)——供 normalize 单元测试,不直接作为 API 响应。",
"success": true,
"version": "1.1.73",
"query_id": "20260825_000715_49f801a9",
"question": "What is the SPL gene family?",
"answer": "",
"citation_validation": { "valid": true, "cited_pmids": ["28655295"], "unknown_pmids": [] },
"sources": [
{
"evidence_id": "S1",
"kb": "V16/V15",
"source": "SPL gene family",
"relation": "characterized_in",
"target": "Oryza sativa",
"evidence": "## Phylogenetic analysis of the PeSPL genes ...",
"paper_id": "PMC5488377_12864_2017_Article_3882",
"title": "Genome-wide analysis of the SPL gene family in pecan",
"pmid": "28655295",
"pmcid": "PMC5488377",
"doi": "10.1186/s12864-017-3882-4",
"species": "",
"section": "",
"output_layer": "main_kg",
"claim_scope": "",
"relation_id": "cass_d12f62f9abffbd903b97",
"retrieval_methods": ["qdrant_vector"],
"retrieval_queries": ["SPL gene family", "SBP box domain"],
"vector_score": 0.714,
"rrf_score": 1.114,
"rerank_score": 0.989,
"final_score": 0.982,
"cluster_id": "clu_0f8da21642adc4f0",
"cluster_member_count": 3,
"cluster_member_assertion_ids": ["cass_a", "cass_b", "cass_c"],
"cluster_member_paper_ids": ["PMC5488377_12864_2017_Article_3882"],
"cluster_member_pmids": ["28655295"],
"raw": {
"kb_version": "V16",
"source_system": "srt",
"assertion_id": "cass_d12f62f9abffbd903b97",
"source": "SPL gene family",
"relation": "characterized_in",
"target": "Oryza sativa",
"evidence": "## Phylogenetic analysis ...",
"best_evidence": "The SPL gene family is characterized in rice.",
"paper_id": "PMC5488377_12864_2017_Article_3882",
"support_count": 12,
"paper_count": 5,
"quality_score": 0.91,
"best_score": 0.95
}
}
],
"source_count": 1,
"evidence_chain_top30": [],
"provisional_answer": "",
"retrieval_hypothesis": { "question_type": "raw_question", "direct_question_entities": [], "hypothesized_entities": [], "relation_intents": [], "concept_queries": [], "answer_slots": [], "atomic_claims": [], "negative_constraints": [], "parse_status": "ok" },
"query_plan": {
"queries": {},
"alias_audit": {},
"agentic_plan": { "anchor_entity": "SPL gene family", "relation_family": "characterized_in" }
},
"retrieval_audit": { "qdrant": [], "seed_extraction": {}, "neo4j": [], "dedup": {} },
"graph_knowledge_chains": {
"mode": "neo4j_native_path",
"chain_policy": "display",
"chains": [
{
"chain_id": "G1",
"chain_source": "neo4j_native_path",
"hop_count": 2,
"path_score": 0.861,
"start": "SPL gene",
"end": "18 SPL genes in rice",
"bridge_entities": [],
"hops": [
{
"hop_index": 0,
"from_id": "cent_spl", "from_name": "SPL gene",
"to_id": "cent_oryza", "to_name": "Oryza sativa",
"source": "SPL gene", "relation": "characterized_in", "target": "Oryza sativa",
"source_type": "gene_family", "target_type": "species",
"relation_category": "characterized_in",
"assertion_id": "cass_d12f62f9abffbd903b97",
"paper_id": "PMC5488377_12864_2017_Article_3882",
"title": "Genome-wide analysis of the SPL gene family in pecan",
"pmid": "28655295", "pmcid": "PMC5488377", "doi": "10.1186/s12864-017-3882-4",
"evidence": "…", "support_count": 12, "paper_count": 5, "best_score": 0.95,
"edge_score": 0.861
},
{
"hop_index": 1,
"from_id": "cent_oryza", "from_name": "Oryza sativa",
"to_id": "cent_18spl", "to_name": "18 SPL genes in rice",
"source": "Oryza sativa", "relation": "has_gene_family_member", "target": "18 SPL genes in rice",
"source_type": "species", "target_type": "gene",
"relation_category": "has_member",
"assertion_id": "cass_e23a",
"paper_id": "PMC5488377_12864_2017_Article_3882",
"title": "Genome-wide analysis of the SPL gene family in pecan",
"pmid": "28655295", "pmcid": "PMC5488377", "doi": "10.1186/s12864-017-3882-4",
"support_count": 8, "paper_count": 3, "best_score": 0.88,
"edge_score": 0.79
}
],
"score_breakdown": { "path_score": 0.861 }
}
],
"chain_count": 1,
"audit": {},
"summary": { "note": "used_for_answer_generation", "used_for_answer_generation": true }
},
"native_graph_chains": [
{
"chain_id": "G1",
"chain_source": "neo4j_native_path",
"hop_count": 2,
"path_score": 0.861,
"start": "SPL gene",
"end": "18 SPL genes in rice",
"bridge_entities": [],
"hops": [
{
"hop_index": 0,
"from_id": "cent_spl", "from_name": "SPL gene",
"to_id": "cent_oryza", "to_name": "Oryza sativa",
"source": "SPL gene", "relation": "characterized_in", "target": "Oryza sativa",
"source_type": "gene_family", "target_type": "species",
"relation_category": "characterized_in",
"assertion_id": "cass_d12f62f9abffbd903b97",
"paper_id": "PMC5488377_12864_2017_Article_3882",
"pmid": "28655295", "pmcid": "PMC5488377", "doi": "10.1186/s12864-017-3882-4",
"support_count": 12, "paper_count": 5, "best_score": 0.95
}
]
}
],
"assertion_anchored_graph_chains": [],
"degraded_execution": false,
"subquestion_evidence_groups": [],
"rerank_audit": [],
"timings": { "provisional_answer": 0.5, "retrieval": 12.3, "native_graph_chain_retrieval": 70.1, "total": 91.9 },
"flow": ["question", "provisional answer", "structured planner", "retrieval", "native_graph_chain_retrieval"]
}
+224
View File
@@ -0,0 +1,224 @@
"""PlantAI-rag-new mock server —— 前端开发与 E2E 场景的固定数据源。
监听 127.0.0.1:8099。实现 API.md 的全部接口,返回 fixtures/ 中的固定数据。
场景切换:查询参数 ?scenario= 或请求头 X-Mock-Scenario:
normal(默认)| empty(空结果)| slow(慢响应 5s)| error(上游故障)| degraded(QA 不可用)
SSE 流按真实时序回放,让流式 UI 在没有上游时也能开发。
"""
from __future__ import annotations
import asyncio
import json
import time
from pathlib import Path
from fastapi import FastAPI, Header, Query, Request
from fastapi.responses import JSONResponse, StreamingResponse
FIXTURES = Path(__file__).parent / "fixtures"
app = FastAPI(title="PlantAI-rag-new mock")
def load(name: str):
return json.loads((FIXTURES / name).read_text(encoding="utf-8"))
def scenario_of(request: Request, q: str | None = None) -> str:
s = q or request.headers.get("X-Mock-Scenario") or "normal"
return s if s in {"normal", "empty", "slow", "error", "degraded"} else "normal"
def envelope(code: str, message: str, degraded: bool, status: int):
return JSONResponse(
status_code=status,
content={"ok": False, "error": {"code": code, "message": message, "degraded": degraded}},
)
# ---------- 健康与元信息 ----------
@app.get("/api/health")
async def health():
return load("health.json")
@app.get("/api/qa/info")
async def qa_info():
return load("qa_info.json")
# ---------- QA ----------
@app.post("/api/qa/chat")
async def qa_chat(request: Request):
sc = scenario_of(request)
if sc == "degraded":
return envelope("UPSTREAM_DOWN", "知识问答服务当前不可用,请稍后重试", True, 503)
if sc == "error":
return envelope("UPSTREAM_ERROR", "上游问答服务返回错误", True, 502)
if sc == "slow":
await asyncio.sleep(5)
if sc == "empty":
return load("qa_chat_empty.json")
return load("qa_chat.json")
@app.post("/api/qa/search")
async def qa_search(request: Request):
sc = scenario_of(request)
if sc in ("degraded",):
return envelope("UPSTREAM_DOWN", "知识问答服务当前不可用,请稍后重试", True, 503)
return load("qa_search.json")
@app.post("/api/qa/chat/stream")
async def qa_chat_stream(request: Request):
sc = scenario_of(request)
if sc == "degraded":
return envelope("UPSTREAM_DOWN", "知识问答服务当前不可用,请稍后重试", True, 503)
frames = load("sse_frames.json") # [{stage, phase, label, details, delay_ms}, ..., "__complete__"]
async def gen():
for f in frames:
if f == "__complete__":
yield "event: complete\n"
yield f"data: {json.dumps(load('qa_chat.json'), ensure_ascii=False)}\n\n"
break
await asyncio.sleep(f.get("delay_ms", 600) / 1000)
payload = {
"stage": f["stage"], "phase": f["phase"], "label": f["label"],
"details": f.get("details", {}), "at": time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime()),
}
yield f"event: progress\n"
yield f"data: {json.dumps(payload, ensure_ascii=False)}\n\n"
if int(f.get("delay_ms", 600)) > 3000: # 慢帧之间补 keepalive
yield ": ping\n\n"
return StreamingResponse(gen(), media_type="text/event-stream",
headers={"Cache-Control": "no-cache", "X-Accel-Buffering": "no"})
# ---------- 会话与消息 ----------
_sessions = []
_messages = {}
_seq = 0
def _now():
return time.strftime("%Y-%m-%dT%H:%M:%SZ", time.gmtime())
@app.get("/api/sessions")
async def sessions():
return load("sessions.json")
@app.post("/api/sessions")
async def create_session(request: Request):
body = await request.json()
return {**load("session_created.json"), "title": body.get("title") or "新会话"}
@app.get("/api/sessions/{sid}")
async def session_detail(sid: str):
return load("session_detail.json")
@app.patch("/api/sessions/{sid}")
async def session_patch(sid: str, request: Request):
body = await request.json()
return {**load("session_created.json"), **body}
@app.delete("/api/sessions/{sid}")
async def session_delete(sid: str):
return JSONResponse(status_code=204, content=None)
@app.post("/api/sessions/{sid}/messages")
async def create_message(sid: str, request: Request):
body = await request.json()
return {"id": "msg-mock-001", "role": body.get("role", "user"),
"kind": body.get("kind", "qa"), "status": body.get("status", "done"),
"blocks": body.get("blocks", []), "raw": None,
"created_at": _now(), "updated_at": _now()}
@app.patch("/api/sessions/{sid}/messages/{mid}")
async def patch_message(sid: str, mid: str, request: Request):
body = await request.json()
return {"id": mid, "role": "assistant", "kind": "qa",
"status": body.get("status", "done"), "blocks": body.get("blocks", []),
"raw": body.get("raw"), "created_at": _now(), "updated_at": _now()}
@app.get("/api/sessions/{sid}/sources")
async def session_sources(sid: str):
return load("sources_aggregate.json")
# ---------- 知识图谱 ----------
@app.get("/api/kg/status")
async def kg_status():
return load("kg_status.json")
@app.get("/api/kg/overview")
async def kg_overview():
return load("kg_overview.json")
@app.get("/api/kg/entities")
async def kg_entities(q: str = "", limit: int = 20):
return load("kg_entities.json")
@app.get("/api/kg/entities/{name}")
async def kg_entity(name: str):
return load("kg_entity.json")
@app.get("/api/kg/entities/{name}/neighborhood")
async def kg_neighborhood(name: str, depth: int = 2, limit: int = 50):
return load("kg_neighborhood.json")
# ---------- 抽取任务 ----------
@app.get("/api/extraction/jobs")
async def extraction_jobs():
return load("extraction_jobs.json")
@app.get("/api/extraction/jobs/{job_id}")
async def extraction_job(job_id: str):
return load("extraction_job.json")
@app.get("/api/extraction/processing-status")
async def extraction_processing_status():
return load("extraction_processing_status.json")
@app.get("/api/extraction/run-log")
async def extraction_run_log():
return {"text": "2026-08-25 10:00:01 INFO MinerU 解析完成,11 sections\n2026-08-25 10:03:44 INFO 实体识别 712 entities\n2026-08-25 10:12:09 INFO 关系抽取 751 relations\n"}
@app.get("/api/extraction/summary")
async def extraction_summary():
return load("extraction_summary.json")
@app.post("/api/extraction/upload")
async def extraction_upload(request: Request):
return load("extraction_upload.json")
if __name__ == "__main__":
import uvicorn
uvicorn.run(app, host="127.0.0.1", port=8099, log_level="info")