diff --git a/knowledge/manifest.yaml b/knowledge/manifest.yaml index 407dcc7..550b974 100644 --- a/knowledge/manifest.yaml +++ b/knowledge/manifest.yaml @@ -9,18 +9,17 @@ sources: size_gb: 70 export: snapshot API + docker cp neo4j: + # KB 交付 = normalized 库(断言检索主库,QA 唯一依赖);canonical 18788 属 SDK KG 浏览,不打包 normalized_container: neo4j-plant-srt-pc2025-v3-20260817-normalized normalized_bolt: bolt://127.0.0.1:19788 normalized_size_gb: 8.2 - canonical_container: neo4j-plant-srt-direct-v1-full - canonical_bolt: bolt://127.0.0.1:18788 - canonical_size_gb: 14 - export: neo4j-admin database dump(在线一致) + note: 5.15 community 不支持在线 dump;frozen 数据目录(08-24 起无写入)可冷拷 assertions: raw_export: staging/frozen_inputs/.../baseline_assertions/raw_export.jsonl(9.5G) parquet: staging/frozen_inputs/.../baseline_assertions/assertions.parquet(2.7G) registry: 主项目 data/knowledge_base/paper_metadata_registry_v1_20260717(17M,仅论文元数据,无断言文本) encryption: + schedule: 数据库定版后执行(定版前数据持续增长,不做真实加密;工具已验证,见 tooling/KB_ENCRYPT_GUIDE.md) vectors: plaintext # 检索数学需要 assertion_export_jsonl: AES-256-GCM # raw_export.jsonl properties_json 字段 assertion_export_parquet: AES-256-GCM # 整文件加密 diff --git a/tooling/KB_ENCRYPT_GUIDE.md b/tooling/KB_ENCRYPT_GUIDE.md new file mode 100644 index 0000000..bafff17 --- /dev/null +++ b/tooling/KB_ENCRYPT_GUIDE.md @@ -0,0 +1,88 @@ +# KB 加密与打包操作指南(agent 专用) + +> 适用时机:**数据库定版后**执行。定版前数据库持续增长,不做真实加密。 +> 本指南对应方案 §五(Knowledge 设计);工具均已通过加密往返验证(2026-08-31)。 + +## 一、工具清单(tooling/) + +| 工具 | 作用 | +|---|---| +| `kb/encrypt_kb.sh` | 一键:快照/冷拷 → 断言加密 → 单一 tar → sha256 → 计数清单 | +| `kb/kb_encrypt_backfill.py` | AES-256-GCM 加密器,两种模式(见下) | +| `crypto/init_keys.sh` | 生成 KB 主密钥(已生成:build/2.0.0/kb.key,勿重复生成) | + +## 二、加密语义(已确认) + +| 数据 | 处理 | 原因 | +|---|---|---| +| Qdrant 向量(70G) | **明文**,snapshot API 导出 | 检索数学需要 | +| raw_export.jsonl(9.5G) | `properties_json` 字段 AES-256-GCM | 断言全文 | +| assertions.parquet(2.7G) | 整文件 AES-256-GCM(`--file-encrypt`) | 断言导出 | +| Neo4j normalized data(8.2G) | **明文冷拷**(frozen 目录) | 存储引擎限制;08-24 起无写入 | +| registry JSONL(17M) | 明文(仅论文元数据,无断言文本) | 无敏感字段 | + +**加密格式**:`enc:v1::`(字段级=字符串,文件级=二进制)。解密见文末。 + +## 三、执行步骤(数据库定版后) + +```bash +cd /home/xzq/5090/teamWorkspace/zh/PlantAI-rag-new + +# 0. 预检:磁盘余量 ≥200G;密钥存在 +df -h /home | tail -1 +ls -la tooling/build/2.0.0/kb.key # 若无 → VERSION=2.0.0 bash tooling/crypto/init_keys.sh + +# 1. 一键执行(全部步骤,耗时约 1–2 小时,建议 setsid 后台) +bash tooling/kb/encrypt_kb.sh 2.0.0 +``` + +### 分步执行(排查问题时) + +```bash +# ① Qdrant 快照(70G,约 20–60 分钟;POST 后轮询容器目录) +curl -X POST http://127.0.0.1:27333/collections/plant_srt_pc2025_v3_20260817_normalized/snapshots +# 完成后从容器拷贝(文件出现在 /qdrant/snapshots//) +docker cp :/qdrant/snapshots/plant_srt_pc2025_v3_20260817_normalized/.snapshot tooling/build/2.0.0/knowledge-stage/ + +# ② Neo4j 冷拷(frozen 目录,rsync 8.2G) +# ③ 断言导出加密(9.5G jsonl 字段级 + 2.7G parquet 整文件) +python3 tooling/kb/kb_encrypt_backfill.py \ + --input \ + --output tooling/build/2.0.0/knowledge-stage/assertions/raw_export.enc.jsonl \ + --key-file tooling/build/2.0.0/kb.key --fields properties_json +python3 tooling/kb/kb_encrypt_backfill.py \ + --input \ + --output tooling/build/2.0.0/knowledge-stage/assertions/assertions.parquet.enc \ + --key-file tooling/build/2.0.0/kb.key --file-encrypt + +# ④ 单一 tar(不压缩)+ sha256 +tar -C tooling/build/2.0.0/knowledge-stage -cf tooling/build/2.0.0/knowledge-2.0.0.tar . +sha256sum tooling/build/2.0.0/knowledge-2.0.0.tar > tooling/build/2.0.0/knowledge-2.0.0.tar.sha256 +``` + +## 四、关键约束(踩坑记录) + +1. **绝不 rsync 活动挂载卷**——Qdrant 70G 存储是 27333 容器在线库,直拷会撕裂数据。必须走 snapshot API。Neo4j 5.15 community **不支持在线 dump**(`neo4j-admin database dump` 拒绝运行库),只能用 frozen 目录冷拷(08-24 后无写入已核实)。 +2. **canonical 库(18788, 14G)不在 KB 交付内**——它是 SDK KG 浏览依赖,不是 QA 检索链(NEO4J_URI=19788)。 +3. **kb.key 权限 600,离线备份**——丢失无法解密已交付包。密钥在 `tooling/build/2.0.0/kb.key`(gitignored)。 +4. **计数校验**:解密安装后由 QA backend_guard 验证,期望值见 `tooling/build/2.0.0/EXPECTED_COUNTS.json`(papers=31207 / canon_entities=3820996 / asserts=8246875 / norm_entities=6316654 / points=12963472)。 +5. **registry JSONL 顶层无断言文本字段**(实测:paper_id/title/doi/pmid/pmcid…),断言文本在 Qdrant payload(evidence/best_evidence)与 staging 的 raw_export.jsonl——加密目标已按实测调整。 + +## 五、解密验证(交付后 QA) + +```python +import base64, json +from cryptography.hazmat.primitives.ciphers.aead import AESGCM +key = base64.b64decode(open("tooling/build/2.0.0/kb.key").read().strip()) +aesgcm = AESGCM(key) +# 字段级 +enc = rec["properties_json"]; _, n, ct = enc.split(":") +pt = aesgcm.decrypt(base64.b64decode(n), base64.b64decode(ct), None).decode() +# 文件级 +blob = open("assertions.parquet.enc","rb").read(); _, n, ct = blob.split(b":") +pt = aesgcm.decrypt(base64.b64decode(n), base64.b64decode(ct), None) +``` + +## 六、定版后接入打包流水线 + +`encrypt_kb.sh` 已预留为 `tooling/build_all.sh` 的子步骤(方案 §六):编译 SDK+3 agent → 审计 → 冒烟 → **encrypt_kb.sh** → assemble → SHA256SUMS → 泄露检查。定版后只需在 build_all.sh 中启用该步骤(当前标注 SKIP_KB=1 占位)。 diff --git a/tooling/kb/encrypt_kb.sh b/tooling/kb/encrypt_kb.sh index 8c902ce..e2adea6 100755 --- a/tooling/kb/encrypt_kb.sh +++ b/tooling/kb/encrypt_kb.sh @@ -17,8 +17,9 @@ OUT_DIR="$TOOLING_ROOT/build/$VERSION" QDRANT_URL="${QDRANT_URL:-http://127.0.0.1:27333}" QDRANT_COLLECTION="${V15_QDRANT_COLLECTION:-plant_srt_pc2025_v3_20260817_normalized}" NEO4J_NORM_NAME="neo4j-plant-srt-pc2025-v3-20260817-normalized" -NEO4J_CANON_NAME="neo4j-plant-srt-direct-v1-full" NEO4J_DB="${NEO4J_DB:-neo4j}" +# 2026-08-31 修正:KB 交付不含 canonical(18788 属 SDK KG 浏览依赖); +# normalized 数据目录 08-24 起无写入(frozen),在线 dump 不支持(5.15 community),改冷拷 STAGING="/home/xzq/phase2b-5090-staging-20260823T191150Z/plant_srt_pc2025_merge_20260817_v3_normalized" ASSERT_DIR="$STAGING/frozen_inputs/ef0d58871e05afbb957d652a5fe932eda24b68b3655bbbc5feb40d5ae9c47dc8/baseline_assertions" REGISTRY_SRC="/home/xzq/5090/teamWorkspace/zh/PlantAI-rag/data/knowledge_base/paper_metadata_registry_v1_20260717/paper_metadata_registry_full.jsonl" @@ -44,17 +45,10 @@ docker cp "$(docker ps -q -f name=qdrant-plant-srt-pc2025-v3-20260817-normalized || { echo "snapshot 拷贝失败" >&2; exit 1; } echo " Qdrant snapshot 拷贝完成: $(du -sh "$STAGE_ROOT/$SNAP_NAME" | cut -f1)" -echo "[1/5] Neo4j normalized online dump(8.2G)..." -docker exec "$NEO4J_NORM_NAME" neo4j-admin database dump "$NEO4J_DB" --to-path /tmp --overwrite-destination 2>/dev/null -docker cp "$NEO4J_NORM_NAME:/tmp/$NEO4J_DB.dump" "$STAGE_ROOT/neo4j-normalized.dump" -docker exec "$NEO4J_NORM_NAME" rm -f "/tmp/$NEO4J_DB.dump" -echo " normalized dump: $(du -sh "$STAGE_ROOT/neo4j-normalized.dump" | cut -f1)" - -echo "[1/5] Neo4j canonical online dump(14G)..." -docker exec "$NEO4J_CANON_NAME" neo4j-admin database dump "$NEO4J_DB" --to-path /tmp --overwrite-destination 2>/dev/null -docker cp "$NEO4J_CANON_NAME:/tmp/$NEO4J_DB.dump" "$STAGE_ROOT/neo4j-canonical.dump" -docker exec "$NEO4J_CANON_NAME" rm -f "/tmp/$NEO4J_DB.dump" -echo " canonical dump: $(du -sh "$STAGE_ROOT/neo4j-canonical.dump" | cut -f1)" +echo "[1/5] Neo4j normalized 冷拷(8.2G,数据目录 08-24 起无写入=frozen)..." +NEO4J_NORM_SRC="/home/xzq/phase2b-5090-staging-20260823T191150Z/plant_srt_pc2025_merge_20260817_v3_normalized/databases/neo4j/data" +rsync -a --info=progress2 "$NEO4J_NORM_SRC/" "$STAGE_ROOT/neo4j-normalized/data/" 2>&1 | tail -1 +echo " normalized 冷拷: $(du -sh "$STAGE_ROOT/neo4j-normalized/data" | cut -f1)" # ---- ② 断言导出文件加密 ---- mkdir -p "$STAGE_ROOT/assertions"