File size: 4,673 Bytes
fb05e78
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
"""
埋め込み(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"]