Files
PlantAI-rag-new/docs/DISCOVERY.md
T
jerryandClaude Fable 5 1e94de7464 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>
2026-08-25 20:47:52 +08:00

346 lines
31 KiB
Markdown
Raw 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.
# 发现报告 — PlantAI RAG BFF 上游探测
> 探测日期:2026-08-24 至 2026-08-25(远程机 `100.109.52.26`,SSH 用户 xzq)
> 本文件为团队事实基线。所有结构均来自真实探测响应或源码/OpenAPI,不是猜测。
> 未能确认的事项集中在第 6 节,明确标注,勿当作事实使用。
---
## 1. 摘要与关键结论
1. **Neo4j 选型:`neo4j-plant-srt-direct-v1-full`**(bolt `bolt://127.0.0.1:18788`,HTTP `http://127.0.0.1:18575`,db `neo4j`,用户 `neo4j`,密码 `<neo4j-password-in-.env>`)。
- 这是 QA 服务 7920 实际使用的实例(进程 `/proc/2586988/environ` 中 `NEO4J_URI=bolt://127.0.0.1:18788`),是权威答案。
- 该实例 schema 为 **canonical**(`QA_V1_GRAPH_SCHEMA=canonical`):实体标签 `SRT2CanonicalEntity`(3,820,996 节点),断言标签 `SRT2CanonicalAssertion`(8,246,875),关系 `SRT2_CANONICAL_ASSERTS` / `SRT2_SUPPORTED_BY`(共 16,449,159 条边)。
- **任务书假设的 `:Entity` / `[:RELATION]` / `entity_name_ftx` 全文索引在本实例不存在**。名字检索走 `name_norm` RANGE 索引(`srt2_canonical_entity_name_norm`),实测 0.09–0.10s。全文检索需求用 `STARTS WITH`/`CONTAINS` + name_norm 索引即可。
2. **抽取接口 8000 可用**:`/health` 返回 `{"status":"healthy"}`。`extract-jobs` 有 49 个历史任务(22 completed / 27 failed)。状态枚举为 `queued → running → completed/failed`(stage 相应为 `queued → mineru → llm → completed/failed`)。**注意:`processing-status`、`progress`、`summary` 均返回 `{"rows":[]}`**(因为当前 `METHODOLOGY_BATCH_DIR` 默认指向 `methodology_batch_llm` 目录且该目录不存在,见第 6 节)。`run-log` 返回 JSON `{"text":""}`(不是纯文本)。
3. **QA 7920 检索接口正常**:search 返回 31 个顶层键(见第 4 节),一次检索耗时约 91.9s(其中 88.8s 是 native_graph_chain_retrieval)。**当前 LLM 余额不足(DeepSeek HTTP 402 Insufficient Balance)**,`degraded_execution=true`,所有 LLM 阶段走 deterministic recovery,`answer` 为空或为 fallback 文案。这影响 chat/stream 的最终答案质量,但不影响 retrieval-only search 的证据输出。
4. **SSE 阶段序列(有序,五阶段 UI 映射基准)**:
`understanding_started → entities_identified → related_search_started → concepts_found → subquestion_retrieved → agent_review_started → evidence_accepted → answer_generation_started → completed`,随后 `event: complete` + 完整 JSON(与 search 同构)。有 `: keepalive` 注释帧(实测 2 次)。
5. **端口 8090、5180 空闲**。node v18.19.1 / npm 9.2.0,`npm ping` 通(573ms),Playwright chromium 下载可行(约 150MB,未实际下载)。
6. **7900 是加密打包容器**(`plantai-protected-rag-1`,镜像 `plantai-rag-protected:1.0.0`),其 `/api/v1/kg/*` 路由指向空的 `plantai-protected-neo4j-1`(0 节点),kg/stats 全零、kg/query 空结果——**7900 的 kg 路由目前实质不可用**。其 methodology 路由可用但数据为空。
---
## 2. Neo4j 实例探测
### 2.1 对比表
| 属性 | `neo4j-plant-srt-pc2025-v3-20260817-normalized` | `neo4j-plant-srt-direct-v1-full` | `plantai-protected-neo4j-1` |
|---|---|---|---|
| bolt | 127.0.0.1:19788 | 127.0.0.1:18788 | 127.0.0.1:17687 |
| http | 127.0.0.1:19575 | 127.0.0.1:18575 | 127.0.0.1:17474 |
| 镜像 | neo4j:5.15.0-community | neo4j:5.15.0-community | neo4j:5.15.0-community |
| 用户名/密码 | neo4j / `<normalized-neo4j-password-in-.env>` | neo4j / `<neo4j-password-in-.env>`(容器 NEO4J_AUTH 原始值为 `password`,运行中已被改) | neo4j / `test1234` |
| 数据卷 | `/home/xzq/phase2b-5090-staging-20260823T191150Z/plant_srt_pc2025_merge_20260817_v3_normalized/databases/neo4j/data` | `/home/xzq/home/wutx/gene-family-mas-v2/data/neo4j_plant_srt_direct_v1_docker/data` | docker 匿名卷 `/var/lib/docker/volumes/68af.../` + `/home/xzq/5090/PlanAI-cooperate/hsj-workspace/encryption-test/.../service_data/neo4j` |
| heap 设置 | initial 16G / max 32G / pagecache 64G | initial 4G / max 12G / pagecache 24G | 无自定义(默认) |
| 标签 | `Entity`(6,316,654), `Paper`(96,648) | `SRT2CanonicalEntity`(3,820,996), `SRT2CanonicalAssertion`(8,246,875), `SRT2Paper`(31,207) | **空库(0 节点)** |
| 关系类型 | `ASSERTS`(12,963,472), `SUPPORTED_BY`(13,012,368) | `SRT2_CANONICAL_ASSERTS`, `SRT2_SUPPORTED_BY`(合计 16,449,159) | 无 |
| 名字索引 | 无 name_norm 索引(CONTAINS 全扫 0.19s;name_norm 等值 6.81s——**很慢**) | `srt2_canonical_entity_name_norm` RANGE 索引(等值 0.10s / 前缀 0.09s) | 无 |
| 全文索引 `entity_name_ftx` | **不存在** | **不存在** | 不存在 |
| QA 7920 是否使用 | 否 | **是(bolt://127.0.0.1:18788)** | 否 |
| 上游所属 | normalized 版(phase2b staging) | gene-family-mas-v2 直接版(QA 生产) | 加密保护产品线(空) |
### 2.2 节点与关系真实属性
**direct-v1-full(当选实例)**
- `SRT2CanonicalEntity` 节点属性:`entity_type`(gene/gene_family/phenotype/process/protein/experimental_material/method/compound/sequence_feature/genotype/trait/… 共 24+ 类)、`canonical_id`(`cent_*`)、`name`、`name_norm`(小写规范化)、`rule`(如 `exact_name_norm_same_type`、`alias_config_exact`)、`support_count`。
- `SRT2CanonicalAssertion` 节点属性(BFF 证据核心,样例见下):
`assertion_id`(`cass_*`)、`source`、`source_type`、`source_canonical_id`、`target`、`target_type`、`target_canonical_id`、`relation_norm`、`relation_category`、`best_evidence`、`best_paper_id`、`best_score`、`support_count`、`paper_count`、`title`、`doi`、`pmid`、`publication_year`、`journal`。
- `SRT2_CANONICAL_ASSERTS` 关系属性:`assertion_id`、`best_score`、`relation_category`、`relation_norm`、`support_count`、`paper_count`。
- `SRT2_SUPPORTED_BY` 关系:**无属性**(`{}`)。
- `SRT2Paper` 节点属性:`paper_id`、`title`、`doi`、`pmid`、`journal`、`publication_year`、`metadata_lookup_status`、`metadata_sources`、`metadata_checked_at`。
- 数据库列表:仅 `neo4j`(默认库)+ `system`。
**normalized(备选实例)**
- `Entity`:`entity_type`、`name`、`name_norm`、`canonical_id`、`layer`。
- `ASSERTS` 关系属性:仅 `source_system`、`assertion_id`、`relation_category`、`relation_norm`、`layer`(**没有 evidence/pubmed/paper_id/confidence 等属性**,证据信息挂在 `SRT2CanonicalAssertion` 同构的节点/或需 join Paper 节点,未深挖)。
- `SUPPORTED_BY` 关系:`support_id`、`assertion_id`、`layer`。
> 任务书预期的 `evidence/section/pubmed_id/paper_id/confidence/claim_scope/output_layer` 属性在**两个实例的关系上都未出现**;这些字段实际出现在 QA 服务返回的 `sources[].raw` 里(QA 服务在应用层拼装),图库关系本身不携带。
### 2.3 选型理由(当选:`neo4j-plant-srt-direct-v1-full`)
1. **权威性**:QA 7920 服务(产生可用的图谱证据)的进程环境明确指向 `bolt://127.0.0.1:18788`(= direct-v1-full)。BFF 直接复用该实例能保证与 QA 检索结果一致。
2. **schema 匹配**:QA 服务的 Cypher(`direct_flow_retriever.py`、`graph_path_chain.py` 等)全部使用 `SRT2Canonical*` 标签,只存在于 direct 实例。
3. **检索性能**:名字检索走 `name_norm` RANGE 索引,等值 0.10s / 前缀 0.09s;canonical_id 一跳扩展 0.16s。normalized 实例无 name_norm 索引(等值 6.81s),不适合交互式 BFF。
4. **数据规模**:实体 382 万 + 断言 824 万 + 边 1644 万,远大于 protected(空)。
5. **normalized 不满足** `entity_name_ftx` 要求(两库都没有该索引);`Entity` 标签虽存在且更大(631 万),但 QA 不用它,且其关系无证据属性。
**回退顺序**:① direct-v1-full(首选)→ ② normalized(`neo4j/<normalized-neo4j-password-in-.env>`,bolt 19788,schema 不同,需单独的 Cypher 适配层)→ ③ protected(空库,仅在需要读保护线数据时用,密码 `test1234`,bolt 17687)。
### 2.4 三条可用的 BFF Cypher(已对当选实例实测)
**(a) 实体名检索(精确 + 前缀,走 name_norm 索引)**
```cypher
MATCH (e:SRT2CanonicalEntity)
WHERE e.name_norm = $q_norm OR e.name_norm STARTS WITH $q_norm
RETURN e.canonical_id, e.name, e.entity_type, e.support_count
LIMIT 20
```
参数:`q_norm = toLower(用户输入)`。实测 0.09s。(模糊检索可加 `CONTAINS`,但注意 CONTAINS 不走索引;替代方案是先 `STARTS WITH` 取候选再应用层过滤。)
**(b) 获取单个实体属性**
```cypher
MATCH (e:SRT2CanonicalEntity {canonical_id: $canonical_id})
RETURN e {.*}
```
实测 0.10s(canonical_id 有唯一约束索引 `srt2_canonical_entity_id`)。名字定位:`MATCH (e:SRT2CanonicalEntity {name_norm: $name_norm}) RETURN e LIMIT 5`。
**(c) 有界邻域扩展(深度 N、节点/边上限,一跳实测 0.16s)**
```cypher
MATCH (s:SRT2CanonicalEntity {canonical_id: $seed_id})
OPTIONAL MATCH (s)-[r1:SRT2_CANONICAL_ASSERTS]-(n1:SRT2CanonicalEntity)
WITH s, r1, n1
ORDER BY coalesce(r1.paper_count,0) DESC, coalesce(r1.support_count,0) DESC
WITH s, collect(DISTINCT n1)[..$max_nodes] AS level1, collect(DISTINCT r1)[..$max_edges] AS edges1
OPTIONAL MATCH (n1)-[r2:SRT2_CANONICAL_ASSERTS]-(n2:SRT2CanonicalEntity)
WHERE n1 IN level1
WITH s, level1, edges1,
collect(DISTINCT n2)[..$max_nodes] AS level2,
collect(DISTINCT r2)[..$max_edges] AS edges2
RETURN s {.*} AS start,
[n IN (level1 + level2) | n {.*}] AS nodes,
[r IN (edges1 + edges2) | r {.*}] AS edges
```
要点:每跳分别 cap 并 `ORDER BY paper_count DESC` 优先取高质量边;`OPTIONAL MATCH` 保证孤立节点不丢;深度 >2 时按同模式追加。注意 `[...]` 截取语法在 cap 前的排序至关重要,否则超度节点(hub)会吃满预算。一跳带全属性实测 0.16s,两跳约 13s(可接受,但建议 BFF 先只做一跳 + 按需第二跳)。
---
## 3. 抽取接口真实结构(`http://127.0.0.1:8000`,fallback `7900`)
服务由 `/home/xzq/5090/PlantAI-rag/scripts/start_methodology_api_8000.sh` 启动:conda `gfpy312`,工作目录 `/home/xzq/5090/PlantAI-rag`,`uvicorn api.main:app --host 0.0.0.0 --port 8000`。启动脚本自带 health 检查和 PID 文件(`runtime/methodology_api/methodology_api_8000.pid`)。当前存活,**不要重启**。
### 3.1 `GET /api/v1/methodology/extract-jobs`(及 `?limit=N`)
响应形状:`{"jobs": [...], "count": N}`(`limit` 默认 50;`?limit=5` 会截断 jobs 数组,count 仍是截断后的条数)。item 字段(全部实测出现):
| 字段 | 类型 | 说明/示例 |
|---|---|---|
| `job_id` | str | 12 位 hex,如 `2943168e9c48` |
| `status` | str | 枚举:`queued` / `running` / `completed` / `failed`(磁盘上 49 个任务只有 completed 22 + failed 27) |
| `stage` | str | 与 status 同步:`queued` / `mineru` / `llm` / `completed` / `failed` |
| `substage` | str | completed 时为 `done`,failed 时为 null |
| `message` | str | 中文进度描述 |
| `updated_at` | str | `2026-08-07T09:42:17`(无时区) |
| `paper_id` / `family` | str | 如 `demo_paper` / `""` |
| `source_filename` / `source_path` | str | 上传文件名与绝对路径 |
| `output_dir` / `batch_dir` | str | 结果目录 |
| `stage_count` / `species_count` / `figure_table_caption_count` / `figure_table_stage_count` | int | 提取统计 |
| `config_url` | str | `/api/v1/methodology/extract-jobs/{id}/bbav5-config` |
| `methodology_plan_url` | str | `/api/v1/methodology/extract-jobs/{id}/plan` |
| `methodology_sandbox_requested` | bool | |
| `methodology_sandbox_status` / `_dir` / `_registry` / `_collection` | str | 沙箱相关 |
### 3.2 `GET /api/v1/methodology/extract-jobs/{job_id}`
单个 job 的扁平 JSON(无包裹):与列表 item 同字段 + 额外 `content_list`(mineru 结果路径)、`figure_table_files`(object,含 `figure_table_inventory`/`figure_table_inventory_csv`/`figure_table_audit`/`reproduction_plan_preview` 四个路径)、`figure_table_warning`、`figure_table_inventory_url`、`reproduction_plan_preview_url`、`log_path`(failed 时)。样例(`2943168e9c48`,已截断):
```json
{"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":".../mineru/wang2019/txt/wang2019_content_list.json","output_dir":".../methodology_batch_demo_paper_2943168e9c48","paper_id":"demo_paper","family":"","stage_count":6,"species_count":1,"figure_table_caption_count":9,"figure_table_stage_count":12,"figure_table_warning":"","figure_table_files":{...},"config_url":"...","methodology_plan_url":"...","figure_table_inventory_url":"...","reproduction_plan_preview_url":"..."}
```
未完成时(非 completed)访问 `bbav5-config`/`plan` 会返回 **409** + `{"detail":"Extraction job is not completed: ..."}`;job 不存在返回 404。
### 3.3 `GET /api/v1/methodology/extract-jobs/{job_id}/plan`
顶层:`plan_id`、`paper_id`、`gene_family`、`source_batch_dir`、`stages[]`。每个 stage 含:
`observation_id`、`literature_stage`(如 `gene_family_identification`)、`literature_tools[]`(`tool_name/version/role/url/parameters`)、`method_components[]`(`component_type/tool/tools/parameters/evidence_sentence`)、`literature_parameters[]`(`component_type/component_tool/parameter_name/parameter_value/parameter_type/evidence_sentence`)、`inputs[]`、更多字段被截断。这是执行计划的完整结构,BFF 的 plan 视图可直接透传。
### 3.4 `processing-status` / `progress` / `summary` / `run-log`
| 接口 | 实测响应 | Content-Type |
|---|---|---|
| `GET .../processing-status` | `{"rows":[]}` | JSON |
| `GET .../progress` | `{"rows":[]}` | JSON(正常时 rows 为 progress.jsonl 的逐行 JSON 对象) |
| `GET .../summary` | `{"rows":[]}` | JSON(rows 为 summary.csv 的 dict 行) |
| `GET .../run-log` | `{"text":""}` | JSON(**不是文本流**;text 内是日志文件原文) |
**空的原因是** `BATCH_DIR_NAME` 默认 `methodology_batch_llm`(目录不存在)。8000 进程环境里没有 `METHODOLOGY_BATCH_DIR` 覆盖。若 BFF 需要这些接口有数据,须用 `METHODOLOGY_BATCH_DIR=methodology_batch_demo_paper_*` 之类实际存在的目录重启服务——**此动作需团队决策,本探测未执行**。
### 3.5 `GET /api/v1/methodology/sandbox/example-batches`
```json
{"success":true,"sandbox_root":"/home/xzq/5090/PlantAI-rag/data/evaluation/methodology_qa_sandbox","batches":[{"name":"methodology_batch_Ft_spl","path":"/home/xzq/5090/PlantAI-rag/methodology_batch_Ft_spl","has_plan":false,"has_preview":false},{"name":"methodology_batch_pecan_spl",...},{"name":"methodology_batch_sunflowers_spl",...}]}
```
### 3.6 `POST /api/v1/methodology/extract-upload`(未实际上传,来源:OpenAPI + 源码)
multipart/form-data,**必填字段仅 `file`**:
| 字段 | 类型 | 默认 | 说明 |
|---|---|---|---|
| `file` | UploadFile | (必填) | PDF 或 `*_content_list.json` |
| `paper_id` | str(form) | `uploaded_paper` | 只保留 `[0-9A-Za-z._-]`,否则替换为 `_` |
| `family` | str(form) | `""` | |
| `output_name` | str(form) | `""` | 输出目录名基名,最终为 `{output_name}_{job_id}` |
| `llm_model` | str(form) | `deepseek-v4-flash` | |
| `llm_request_timeout` | int(form) | `75` | 秒 |
| `sandbox` | bool(form) | `false` | |
| `sandbox_run_id` | str(form) | `""` | |
成功响应:`{"success": true, "job_id": "<12hex>", "status_url": "/api/v1/methodology/extract-jobs/<job_id>"}`。任务在 FastAPI BackgroundTasks 中异步执行;状态流转:`queued` →(MinerU)`running/stage=mineru` →(LLM 提取)`running/stage=llm` → `completed`(含 `config_url`/`methodology_plan_url`/统计字段/沙箱字段)或 `failed`(`message` 为异常文本,含 `log_path`)。失败样例原因:MinerU 路径不存在(`/home/xia-lab/miniconda3/envs/minerupy311/bin/mineru`)——历史 27 个 failed 任务多为这种环境问题。
### 3.7 7900 上的同构接口
7900(容器 `plantai-protected-rag-1`)暴露同样的 `/api/v1/methodology/*` 路由(51 条路径,`extract-upload`/`extract-jobs` 均存在),但 `extract-jobs` 返回 `{"jobs":[],"count":0}`(容器内数据目录独立)。另有 `/api/v1/kg/*`(stats/families/species/query/search)指向空 Neo4j,**全部为空结果**,BFF 不应依赖 7900 的 kg 路由。源码在容器内是编译过的 `.so`(`api/kg_routes.cpython-311-x86_64-linux-gnu.so`),无法读源码,以 OpenAPI + 实测为准。
---
## 4. QA 服务响应结构(`http://127.0.0.1:7920`)
服务:`uvicorn scripts.qa_v1_1_73_server:app`,app-dir `/home/xzq/5090/PlanAI-cooperate/RAG/rag-qa-v1_1_73`,conda `gfpy312`,版本 `1.1.73-assertion-anchored`。启动脚本:`docs/legacy_startup_scripts/start_qa_v1_1_73_*.sh`(注意 legacy 脚本里写的 NEO4J_URI 是 17687,**与运行中进程的 18788 不一致**,以进程环境为准)。LLM:`https://api.deepseek.com` + `deepseek-v4-flash`,**当前 402 Insufficient Balance**。
### 4.1 `POST /api/v1/qa-v1-1-73/search`(实测 2026-08-25,1.78MB 响应,329 sources)
**顶层键完整清单(31 个)**:
| 键 | 类型 | 说明 |
|---|---|---|
| `success` | bool | true |
| `version` | str | `"1.1.73"` |
| `query_id` | str | `"20260825_000715_49f801a9"` |
| `question` | str | 原问题 |
| `answer` | str | retrieval-only 时为空串 |
| `citation_validation` | dict | `{valid, cited_pmids[], unknown_pmids[]}` |
| `sources` | list[329] | 证据条目(见下) |
| `source_count` | int | 329 |
| `evidence_chain_top30` | list[30] | 与 sources 同构(top30 精选) |
| `provisional_answer` | str | 空 |
| `retrieval_hypothesis` | dict | `{question_type, direct_question_entities[], hypothesized_entities[], relation_intents[], concept_queries[], answer_slots[], atomic_claims[], negative_constraints[], parse_status}` |
| `query_plan` | dict | `{queries{}, alias_audit{}, agentic_plan{14}, retrieval_options{29}}` |
| `retrieval_audit` | dict | `{qdrant[], seed_extraction{}, neo4j[], dedup{}, options{}, neo4j_status:"enabled", bounded_graph_backend{12}, public_citation_filter{}}` |
| `graph_evidence_quota_audit` | dict | `{policy, top_k_for_llm, graph_min_requested, graph_candidates_available, non_graph_candidates_available, graph_selected, non_graph_selected, graph_quota_satisfied, note}` |
| `graph_knowledge_chains` | dict | `{mode:"neo4j_native_path", chain_policy, chains[305], chain_count, audit{35}, summary{note, used_for_answer_generation}}` |
| `native_graph_chains` | list[305] | 图链(见下) |
| `native_graph_chain_audit` | dict | 35 个审计键(enabled/chain_source/max_hops/beam_width/top_k_chains/seed_ids/…) |
| `assertion_anchored_graph_chains` | list | 本次为 `[]` |
| `assertion_anchored_graph_audit` | dict | `{enabled, status, error, fallback_required, hop_policy}` |
| `endpoint_path_audit` | dict | `{enabled:false, status:"not_attempted", fallback_to_native_seed_expansion:false}` |
| `native_seed_policy_audit` | dict | 20 键(policy/seed_top_k_after_bge/canonical_ids/names/…) |
| `agentic_controller_audit` | dict | `{enabled, status, strict_review_accepted, fallback_reason, attempt_count, replan_count, attempts[], final_plan{}}` |
| `agentic_review` | dict | `{decision, accepted_evidence_ids[], accepted_chain_ids[], problems[], final_handoff, strict_review_decision, review_fallback_used, fallback_reason}` |
| `question_decomposition` | dict | search 时为 `{}` |
| `runtime_recovery_audit` | list | 每次 LLM 失败一条:`{stage, mode, degraded:true, error}` |
| `degraded_execution` | bool | **true**(LLM 402) |
| `subquestion_evidence_groups` | list | `[]`(search 时) |
| `subquestion_retrieval_audit` | list | `[]` |
| `cluster_audit` | list[106] | `{cluster_id, cluster_key[], representative_assertion_id, representative_srt{source,relation,target}, member_count, member_assertion_ids[], member_paper_ids[], ...}` |
| `rerank_audit` | list[80] | `{rank, evidence_id, source, relation, target, rrf_score, bge_score, final_score}` |
| `timings` | dict | 秒,float:`{provisional_answer, structured_planner, question_decomposition, agentic_retrieval_plan, retrieval, cluster_bge_rerank_and_select, native_graph_chain_retrieval, final_answer, total}`(本次 total 91.9s) |
| `flow` | list[17] | 管道阶段名有序列表 |
**一条 `sources[]` 的全字段(26 个键,S1 实例)**:
| 键 | 类型 | 示例值 |
|---|---|---|
| `evidence_id` | str | `"S1"` |
| `kb` | str | `"V16/V15"` |
| `source` | str | `"SPL gene family"` |
| `relation` | str | `"characterized_in"` |
| `target` | str | `"Oryza sativa"` |
| `evidence` | str | markdown 段落(`## Phylogenetic analysis of the PeSPL genes ...`,可长) |
| `paper_id` | str | `"PMC5488377_12864_2017_Article_3882"` |
| `title` | str | 论文标题 |
| `pmid` | str | `"28655295"` |
| `pmcid` | str | `"PMC5488377"` |
| `doi` | str | `"10.1186/s12864-017-3882-4"` |
| `species` | str | 常为空串 |
| `section` | str | 常为空串 |
| `output_layer` | str | `"main_kg"` |
| `claim_scope` | str | 常为空串 |
| `relation_id` | str | `"cass_d12f62f9abffbd903b97"`(= assertion_id) |
| `retrieval_methods` | list[str] | `["qdrant_vector"]` |
| `retrieval_queries` | list[str] | 3 条查询串 |
| `vector_score` | float | 0.714 |
| `rrf_score` | float | 1.114 |
| `rerank_score` | float | 0.989 |
| `final_score` | float | 0.982 |
| `cluster_id` | str | `"clu_0f8da21642adc4f0"` |
| `cluster_member_count` | int | 3 |
| `cluster_member_assertion_ids` | list[str] | 3 个 cass_* |
| `cluster_member_paper_ids` | list[str] | 2 个 |
| `cluster_member_pmids` | list[str] | 1 个 |
| `raw` | dict | 28 键,完整断言原样:`kb_version, source_system, assertion_id, relation_id, source, source_type, source_canonical_id, relation, relation_norm, relation_category, target, target_type, target_canonical_id, evidence, best_evidence, paper_id, best_paper_id, support_count, paper_count, quality_score, best_score, embedding_text, cluster_id, cluster_key[], cluster_member_assertion_ids[], cluster_member_count, cluster_member_paper_ids[], cluster_member_pmids[]` |
**`native_graph_chains[]` item**:`{chain_id:"G1", chain_source:"neo4j_native_path", hop_count:3, path_score:0.861, start:"SPL gene", end:"18 SPL genes in rice", bridge_entities[], hops[], score_breakdown{22}}`。
**hop**:`{hop_index, from_id, from_name, to_id, to_name, source, relation, target, source_canonical_id, target_canonical_id, source_type, target_type, relation_category, assertion_id, paper_id, title, pmid, pmcid, doi, species, evidence, support_count, paper_count, best_score, edge_score, score_breakdown, passive_relation, structural_direction, semantic_direction}`。
### 4.2 `POST /api/v1/qa-v1-1-73/chat/stream` SSE 实测
帧格式:`event: <name>` + `data: <JSON>` 两行一组,空行分隔;期间有 `: keepalive` 注释帧(实测 2 次)。
**有序 stage 序列(data.stage,五阶段 UI 映射基准)**:
1. `understanding_started` — details `{}`
2. `entities_identified` — details `{entities:["SPL gene family"], question_type:"raw_question"}`
3. `related_search_started` — details `{}`
4. `concepts_found` — details `{concepts[], concept_count:250, connection_count:335, publication_count:109, candidate_count:328}`
5. `subquestion_retrieved` — details `{subquestion_id:"Q0", attempt:1, candidate_count:30, connection_count:305}`
6. `agent_review_started` — details `{attempt:1}`
7. `evidence_accepted` — details `{concepts[], concept_count, connection_count, publication_count, accepted_count:30}`
8. `answer_generation_started` — details `{}`
9. `completed` — details `{elapsed_seconds:38.357}`
**终端帧**:`event: complete` + `data: <完整 JSON>`(与 search 响应同构,约 2.37MB;顶层键同 search 但 `citation_validation` 12 键、`sources` 仅 4 条、`timings` 10 键、`question_decomposition` 13 键、`subquestion_evidence_groups` 1 项、`agentic_controller_audit` 12 键、`agentic_review` 7 键)。**LLM 402 影响**:`answer` 为 fallback 文案 `"The retrieval pipeline completed. The answer model did not return usable text; the most relevant retrieved evidence facts are: - SPL gene family has domain SBP box [PMID: 33234605] ..."`(事实列表而非自然语言答案),`degraded_execution=true`,`runtime_recovery_audit` 4 条 LLM 失败记录。全流程耗时约 37–38s(不带 keepalive 间隙)。
**health**:`/api/v1/qa-v1-1-73/health` → `{"status":"ok","service":"qa_v1_1_73","version":"1.1.73-assertion-anchored"}`。
**info**:返回服务名、管道描述、config(alias_path/taxonomy_path/reranker 模型路径/knowledge_chain_defaults 等)。
其他路由:`/chat`(非流式)、`/search`。
---
## 5. 环境与启动脚本
### 5.1 `/home/xzq/5090/PlantAI-rag/scripts/` 清单(15 个)
| 脚本 | 用途(据头部注释/内容) |
|---|---|
| `download_marker_models.sh` | 手动下载 marker-pdf 模型(解决自动下载中断) |
| `organize_project.sh` | gene-family-mas-v2 项目文件整理(建目录结构) |
| `process_all_pdfs.sh` | 批量处理 `gfpaper` 目录 PDF |
| `process_by_inode_shell.sh` | 用 inode 处理乱码文件名 PDF → rag_index_v3 |
| `process_failed_by_inode.sh` | 用 inode 重处理 failed_to_process 目录 → rag_index_v4 |
| `run_v15_3_4_q1_5papers_smoke_20260604.sh` | V15.3.4 Q1 5 篇论文冒烟测试(gfpy312) |
| `run_v15_3_4_q1_remaining_500_20260604.sh` | V15.3.4 Q1 剩余 500 篇批量(加载 .env.v15_deepseek) |
| `run_v16_rerun6_20260625.sh` | V16 batch10 重跑 6(/data/gene-family-mas-v2) |
| `start_dual_kb_qa_api_7902.sh` | 双 KB QA API 7902(/data/gene-family-mas-v2) |
| `start_dual_kb_qa_api_8000.sh` | **历史名**;实际启动 `combined_qa_lightrag_keyword_server` 于 **7905**(V15+PC Qdrant 检索) |
| `start_methodology_api_8000.sh` | **当前 8000 的启动脚本**(见 5.2) |
| `start_mineru_api_50215_20260605.sh` | MinerU API 50215 |
| `start_qa_v1_0_0_7908.sh` | QA v1.0.0 7908 |
| `start_taxonomy_bge_unified_7907.sh` | taxonomy+BGE 统一服务 7907 |
| `start-webui.sh` | WebUI(端口 8787,state 在 ~/.hermes/webui-mas-v2) |
### 5.2 三个上游服务
| 服务 | 端口 | 启动方式 | conda / 工作目录 | 重启安全性 |
|---|---|---|---|---|
| QA v1.1.73(graph QA) | 7920 | `python -m uvicorn scripts.qa_v1_1_73_server:app --app-dir /home/xzq/5090/PlanAI-cooperate/RAG/rag-qa-v1_1_73 --host 127.0.0.1 --port 7920`(PID 2586988,父进程 1 = 守护化;legacy 脚本在 `rag-qa-v1_1_73/docs/legacy_startup_scripts/`,其硬编码 URI 17687 已过时,**不能照抄**) | `gfpy312`;环境来自 `RAG/runtime/qa_v1_1_73.env` + 进程内联 env(`NEO4J_URI=bolt://127.0.0.1:18788`、`NEO4J_PASSWORD=<neo4j-password-in-.env>`、`V15_QDRANT_URL=http://127.0.0.1:26333`、collection `plant_srt_direct_v1_bge_m3_20260702`) | 正在服务流量,**未验证重启安全性**;重启前需确认无长任务依赖(LLM 目前 402 已降级,重启不影响数据) |
| Methodology API | 8000 | `/home/xzq/5090/PlantAI-rag/scripts/start_methodology_api_8000.sh`(脚本自带 health 检查 + 幂等启动,health 通过则直接退出) | `gfpy312`;工作目录 `/home/xzq/5090/PlantAI-rag`;日志 `runtime/methodology_api/methodology_api_8000.log` | 脚本幂等、支持 `METHODOLOGY_API_PORT`/`METHODOLOGY_BATCH_DIR` 覆盖;停止后重跑脚本即可。**当前别重启**(本探测依赖其持续在线) |
| Protected RAG | 7900 | Docker 容器 `plantai-protected-rag-1`(镜像 `plantai-rag-protected:1.0.0`,compose:`hsj-workspace/encryption-test/server/protected_docker_packager/docker-compose.yml`),同一 compose 栈含 web/workers/redis/qdrant/neo4j 等 8+ 容器 | 容器内 `python -m uvicorn api.app:app`(2 workers),`NEO4J_URI=bolt://neo4j:7687`(compose 内部) | 容器健康检查通过;其 kg 路由数据为空(protected Neo4j 空库),**BFF 不应依赖** |
### 5.3 其他环境事实
- 端口 8090、5180:`ss -tln` 无监听 → **空闲可用**。
- `node -v` = v18.19.1,`npm -v` = 9.2.0;`npm ping` PONG 573ms → Playwright chromium 下载可行(约 150MB,未实际执行)。
- Qdrant 端口映射:QA 用 `qdrant-qa-v1-1-73-full` 26333;normalized 用 `qdrant-plant-srt-pc2025-v3-20260817-normalized` 27333;protected 用 `plantai-protected-qdrant-1` 16333。
- 全部 docker 容器(13 个)状态 Up,无异常退出。
---
## 6. 遗留问题与风险
1. **QA LLM 余额不足(最重要风险)**:DeepSeek API 402 Insufficient Balance。所有 LLM 阶段(planner/agentic/review/answer)降级为 deterministic recovery。**chat/stream 目前无法产出自然语言答案**(只有证据列表 fallback),BFF 五阶段 UI 中"答案生成"阶段会看到降级行为。充值时注意:`QA_V1_LLM_BASE_URL=https://api.deepseek.com`,`QA_V1_LLM_MODEL=deepseek-v4-flash`、`QA_V1_FINAL_LLM_MODEL=deepseek-v4-flash`、`QA_V1_LLM_MODEL`(实际 env) = `deepseek-chat` 并存(进程 env 与 runtime env 文件略有差异,未确认哪行最终生效)。
2. **`entity_name_ftx` 全文索引不存在于任何实例**。若 BFF 必须全文检索,需要团队决策是否创建(会修改共享实例的 schema——本探测未创建任何东西)。
3. **normalized 实例名字检索慢**(name_norm 无索引,等值 6.81s):若团队将来切到 normalized(`Entity` 标签 631 万),需要先建索引。
4. **methodology `processing-status`/`progress`/`summary`/`run-log` 当前空数据**:原因是 `METHODOLOGY_BATCH_DIR` 指向不存在的 `methodology_batch_llm` 目录(默认值)。未确认哪个目录名才是"有数据"的正确批次目录(历史任务输出在 `data/evaluation/0505/methodology_batch_demo_paper_*`)。需要读有数据的目录必须重启 8000 并设 env——未执行。
5. **QA 7920 的正式启动脚本未找到**:只有 legacy 脚本且其中 NEO4J_URI(17687)与运行中进程(18788)矛盾。进程由谁、以何完整 env 启动,未确认。以 `/proc/2586988/environ` 实测为准。
6. **failed 任务(27 个)失败原因**:主要是 MinerU 二进制路径不存在(`/home/xia-lab/miniconda3/envs/minerupy311/bin/mineru`)。上传新 PDF 是否会再次失败取决于 MinerU 环境是否修复——**未测试实际上传**(按任务要求未执行)。
7. **7900 的 kg 路由源码不可读**(编译 .so),其 schema 以 OpenAPI + 实测为准;kg 数据为空的原因是 protected Neo4j 空库,后续若保护线灌库,kg 路由可能恢复。
8. **一次 search 约 92s**(88.8s 在 native_graph_chain_retrieval),stream 约 37s。BFF 需要长超时(>120s)和进度展示;未做并发压测。
9. **`SSH_CONNECTION` 显示会话来自 192.168.0.142 → 192.168.0.175**:远程机在 192.168.0.175(通过 Tailscale IP 100.109.52.26 可达)。BFF 若部署在远程机上,绑定 127.0.0.1 的端口只能本机访问——BFF 与本机服务同机部署即可;跨机需要另开隧道,未做。