ai_bot / precompute_embeddings.py
gloomy_pooplar
Improve search: pymorphy3 stemming, top_k=4, RRF 0.3/0.7, restructured KB
5de4047
Raw
History Blame Contribute Delete
2.89 kB
"""
Pre-compute KB section embeddings + build FAISS index.
Run during Docker build (and locally). ONNX export optional (--export-onnx).
"""
import os, sys, json, time, argparse
sys.stdout.reconfigure(encoding="utf-8")
parser = argparse.ArgumentParser()
parser.add_argument("--export-onnx", action="store_true")
args = parser.parse_args()
import numpy as np
import faiss
import torch
from sentence_transformers import SentenceTransformer
os.makedirs("embeddings", exist_ok=True)
# 1. Load model
t0 = time.time()
model = SentenceTransformer("BAAI/bge-m3", trust_remote_code=True)
print(f"Model loaded in {time.time()-t0:.1f}s")
# 2. Load KB sections
paths = ["knowledge_base.json", "knowledge_base_insoles.json"]
sections = []
for path in paths:
with open(path, encoding="utf-8") as f:
data = json.load(f)
for name, sec in data["sections"].items():
sections.append({
"id": f"{path}:{name}",
"title": sec["title"],
"content": sec["content"],
})
print(f"Loaded {len(sections)} sections")
# 3. Embed all sections
texts = [f"{s['title']}\n\n{s['content']}" for s in sections]
t1 = time.time()
embeddings = model.encode(texts, normalize_embeddings=True, show_progress_bar=True)
print(f"Embedded {len(embeddings)} sections in {time.time()-t1:.1f}s")
# 4. Build and save FAISS index
index = faiss.IndexFlatIP(embeddings.shape[1])
index.add(embeddings.astype(np.float32))
faiss.write_index(index, "embeddings/index.faiss")
with open("embeddings/sections.json", "w", encoding="utf-8") as f:
json.dump(sections, f, ensure_ascii=False, indent=2)
np.save("embeddings/embeddings.npy", embeddings)
print(f"FAISS: {index.ntotal} vectors x {embeddings.shape[1]} dim")
print(f" index.faiss: {os.path.getsize('embeddings/index.faiss')/1024:.1f} KB")
print(f" sections.json: {os.path.getsize('embeddings/sections.json')/1024:.1f} KB")
if not args.export_onnx:
print("Skipping ONNX export (not needed at runtime)")
print(f"Done in {time.time()-t0:.1f}s")
sys.exit(0)
# 5. Export to ONNX via torch
t2 = time.time()
print("Exporting transformer to ONNX...")
raw_model = model[0].auto_model.eval()
tok = model.tokenizer
dummy = tok("test query", return_tensors="pt", padding=True, truncation=True, max_length=256)
with torch.no_grad():
torch.onnx.export(
raw_model,
(dummy["input_ids"], dummy["attention_mask"]),
"embeddings/bge-m3.onnx",
input_names=["input_ids", "attention_mask"],
output_names=["last_hidden_state"],
dynamic_axes={
"input_ids": {0: "batch", 1: "seq"},
"attention_mask": {0: "batch", 1: "seq"},
},
opset_version=14,
)
onnx_size_mb = os.path.getsize("embeddings/bge-m3.onnx") / 1024 / 1024
print(f"ONNX exported in {time.time()-t2:.1f}s, {onnx_size_mb:.1f} MB")
print(f"Done in {time.time()-t0:.1f}s")