Files

64 lines
2.5 KiB
Python
Raw Permalink Normal View History

#!/usr/bin/env python
# -*- coding: utf-8 -*-
"""知识库入库脚本:分块 → LLM 生成问答 → 双路向量索引 → 落盘 kb_index.json / kb_qa.json。
知识源 = backend/knowledge/ 下全部 *.mdpark.md / opc.md / park_data.md …)。
- 幂等:缓存有效时直接加载(不重新构建);--force 强制删除缓存重建。
- 首次运行会调用 DashScopetext-embedding-v4 向量化 + qwen-plus 生成问答),
耗时约数分钟,仅构建一次,之后命中缓存秒开。
用法(在 backend/ 目录下执行):
python scripts/ingest_kb.py # 有缓存则加载,无缓存则构建
python scripts/ingest_kb.py --force # 强制重建(任一知识文件变更后)
"""
import argparse
import sys
from pathlib import Path
BACKEND_DIR = Path(__file__).resolve().parent.parent
if str(BACKEND_DIR) not in sys.path:
sys.path.insert(0, str(BACKEND_DIR))
def main() -> None:
parser = argparse.ArgumentParser(description="知识库入库(分块 + 问答 + 双路向量索引)")
parser.add_argument("--force", action="store_true", help="删除缓存并强制重建")
parser.add_argument("--dry", action="store_true", help="只做分块预览,不调 API、不落盘")
args = parser.parse_args()
from app import rag
if args.dry:
chunks = rag._chunk_md(rag._kb_text())
print(f"[dry] 知识源 {len(list(rag.KB_DIR.glob(rag.KB_GLOB)))} 个文件,全库 "
f"{len(rag._kb_text())} 字 → {len(chunks)} 块(最长 {max(map(len, chunks))} 字)")
return
if args.force:
for p in (rag.META_FILE, rag.VECTOR_FILE, rag.QA_FILE, rag.LEGACY_INDEX_FILE):
if p.exists():
p.unlink()
print(f"[ingest] 已删除缓存 {p.name}")
print("[ingest] 知识源:")
for f in sorted(rag.KB_DIR.glob(rag.KB_GLOB)):
print(f" - {f.name} ({f.stat().st_size} B)")
idx = rag._load_index()
n_chunk = len(idx["chunks"])
n_vec = len(idx["embed_texts"])
dim = idx["embeddings"].shape[1]
n_q = sum(1 for qs in idx["questions"] for _ in qs)
print("[ingest] 入库完成:")
print(f" - 分块数: {n_chunk}")
print(f" - 生成问句数: {n_q}")
print(f" - 向量数: {n_vec}dim={dim},含原句+问句双路)")
print(f" - 向量矩阵: {rag.VECTOR_FILE}")
print(f" - 元数据: {rag.META_FILE}")
print(f" - 问答缓存: {rag.QA_FILE}")
print("[ingest] 成功。")
if __name__ == "__main__":
main()