EmpathemeBot_dev / src /vector /embedding_storage.py
tktm8's picture
Upload 59 files
fb05e78 verified
"""
埋め込み(embedding)の保存・読み込みユーティリティ
"""
import json
import logging
import os
from datetime import datetime
from pathlib import Path
from typing import List, Dict, Tuple
import numpy as np
from langchain_core.documents import Document
logger = logging.getLogger(__name__)
def save_embeddings(
documents: List[Document],
vectors: List[List[float]],
model_name: str,
embeddings_dir: str = 'data/embeddings'
) -> str:
"""
埋め込みとメタデータをJSONファイルに保存する
Args:
documents: Documentオブジェクトのリスト
vectors: 埋め込みベクトルのリスト
model_name: 使用した埋め込みモデル名
embeddings_dir: 埋め込みを保存するディレクトリ
Returns:
保存した埋め込みファイルのパス
"""
# ディレクトリがなければ作成
Path(embeddings_dir).mkdir(parents=True, exist_ok=True)
# タイムスタンプ生成(ファイル名用)
timestamp = datetime.now().strftime("%Y%m%d_%H%M%S")
# 埋め込みデータの準備
embeddings_data = {
"model": model_name,
"timestamp": datetime.now().isoformat(),
"total_documents": len(documents),
"embeddings": []
}
# Documentとベクトルを結合
for i, (doc, vector) in enumerate(zip(documents, vectors)):
chunk_id = f"doc_{i}_chunk_{doc.metadata.get('chunk_index', 0)}"
embeddings_data["embeddings"].append({
"chunk_id": chunk_id,
"metadata": doc.metadata,
"content": doc.page_content,
"vector": vector # リストとして保存(JSONシリアライズ可能)
})
# JSONファイルに保存
output_file = os.path.join(embeddings_dir, f"embeddings_{timestamp}.json")
with open(output_file, 'w', encoding='utf-8') as f:
json.dump(embeddings_data, f, ensure_ascii=False, indent=2)
# 最新の埋め込みへのシンボリックリンクを作成または更新
latest_link = os.path.join(embeddings_dir, "latest.json")
if os.path.exists(latest_link):
os.remove(latest_link)
try:
os.symlink(os.path.basename(output_file), latest_link)
except OSError:
# シンボリックリンク作成に失敗した場合(例: Windows)、パスをテキストファイルに保存
with open(os.path.join(embeddings_dir, "latest.txt"), 'w') as f:
f.write(output_file)
logger.info(f"Embeddings saved to: {output_file}")
logger.info(f"Total embeddings saved: {len(vectors)}")
return output_file
def load_embeddings(
embeddings_file: str = None,
embeddings_dir: str = 'data/embeddings'
) -> Tuple[List[Document], np.ndarray, str]:
"""
保存済みのJSONファイルから埋め込みを読み込む
Args:
embeddings_file: 読み込む埋め込みファイルのパス。Noneの場合は最新を読み込む
embeddings_dir: 埋め込みが保存されているディレクトリ
Returns:
(documents, ベクトル(numpy配列), モデル名) のタプル
"""
# ファイル指定がなければ最新を読み込む
if embeddings_file is None:
latest_link = os.path.join(embeddings_dir, "latest.json")
if os.path.exists(latest_link):
embeddings_file = latest_link
else:
# latest.txtからパスを取得
latest_txt = os.path.join(embeddings_dir, "latest.txt")
if os.path.exists(latest_txt):
with open(latest_txt, 'r') as f:
embeddings_file = f.read().strip()
else:
raise FileNotFoundError(f"{embeddings_dir} に埋め込みファイルが見つかりません")
# JSONから埋め込みを読み込む
with open(embeddings_file, 'r', encoding='utf-8') as f:
embeddings_data = json.load(f)
# Documentとベクトルを復元
documents = []
vectors = []
for item in embeddings_data["embeddings"]:
# Documentオブジェクトを作成
doc = Document(
page_content=item["content"],
metadata=item["metadata"]
)
documents.append(doc)
vectors.append(item["vector"])
# ベクトルをnumpy配列に変換
vectors_np = np.array(vectors)
logger.info(f"Loaded {len(documents)} embeddings from: {embeddings_file}")
logger.info(f"Model used: {embeddings_data['model']}")
logger.info(f"Created at: {embeddings_data['timestamp']}")
return documents, vectors_np, embeddings_data["model"]