MeMemo: ブラウザで動作するベクトル検索ライブラリの紹介

MeMemoは、ブラウザ上でベクトル検索とRetrieval Augmented Generation (RAG)を実現するJavaScriptライブラリです。ブラウザ環境でHNSW（Hierarchical Navigable Small World）アルゴリズムを実装し、クライアントサイドで効率的なベクトル検索を可能にします。

## 特徴

1. **ブラウザネイティブ**
   - IndexedDBを使用したデータの永続化
   - Web Workersによる並列処理
   - クライアントサイドで完結

2. **高速な検索**
   - HNSWアルゴリズムによる近似最近傍探索
   - インデックスによる効率的な検索
   - ブラウザの性能を最大限活用

3. **プライバシー重視**
   - データはすべてクライアントサイドで処理
   - サーバーへの送信不要
   - オフライン動作可能

## 基本的な使い方

### 1. インストール

npmを使用してMeMemoをインストールします：

```bash
npm install mememo
```

### 2. インデックスの作成

HNSWインデックスを初期化します：

```typescript
import { HNSW } from 'mememo';

const index = new HNSW({ 
  // コサイン類似度を使用
  distanceFunction: 'cosine-normalized',
  
  // 各ノードの最大接続数（5-48の範囲を推奨）
  m: 16,
  
  // ブラウザのIndexedDBを使用してデータを永続化
  useIndexedDB: true
});
```

### 3. ドキュメントの登録

テキストをチャンクに分割し、ベクトル化してインデックスに登録します：

```typescript
// テキストをチャンクに分割
function splitIntoChunks(text: string, maxChunkSize = 512) {
  const sentences = text.match(/[^.!?]+[.!?]+/g) || [];
  let chunks = [];
  let currentChunk = '';

  for (const sentence of sentences) {
    if (currentChunk.length + sentence.length > maxChunkSize) {
      chunks.push(currentChunk.trim());
      currentChunk = sentence;
    } else {
      currentChunk += ' ' + sentence;
    }
  }
  if (currentChunk) chunks.push(currentChunk.trim());
  return chunks;
}

// ドキュメントを登録
async function indexDocument(text: string) {
  // テキストをチャンクに分割
  const chunks = splitIntoChunks(text);
  
  // チャンクをベクトル化（例：GTE-Smallモデルを使用）
  const vectors = await model.embed(chunks);
  
  // キーを生成
  const keys = chunks.map((_, i) => `chunk_${i}`);
  
  // インデックスに登録
  await index.bulkInsert(keys, vectors);
  
  return keys;
}
```

### 4. 検索の実行

登録したドキュメントから類似コンテンツを検索します：

```typescript
// 基本的な検索
async function searchSimilar(query: string, k: number = 5) {
  // クエリをベクトル化
  const queryVector = await model.embed(query);
  
  // k個の類似ドキュメントを検索
  const { keys, distances } = await index.query(queryVector, k);
  
  // 結果を類似度スコアと共に返す
  return keys.map((key, i) => ({
    key,
    chunk: chunks[key],  // オリジナルのテキストチャンク
    similarity: 1 - distances[i]  // 距離を類似度スコアに変換
  }));
}

// メタデータを含む検索の実装例
interface DocumentChunk {
  id: string;
  text: string;
  metadata: {
    title: string;
    source: string;
    timestamp: string;
  };
}

const documentStore = new Map<string, DocumentChunk>();

async function indexDocumentWithMetadata(
  text: string,
  metadata: { title: string; source: string }
) {
  const chunks = splitIntoChunks(text);
  const vectors = await model.embed(chunks);
  
  // チャンク情報をストアに保存
  const chunkDocuments = chunks.map((chunk, i) => ({
    id: `doc_${metadata.title}_${i}`,
    text: chunk,
    metadata: {
      ...metadata,
      timestamp: new Date().toISOString()
    }
  }));
  
  // メタデータを含むチャンク情報を保存
  for (const doc of chunkDocuments) {
    documentStore.set(doc.id, doc);
  }
  
  // ベクトルインデックスに登録
  await index.bulkInsert(
    chunkDocuments.map(d => d.id),
    vectors
  );
  
  return chunkDocuments.map(d => d.id);
}
```

### 5. 使用例

```typescript
// ドキュメントの登録と検索
async function example() {
  // ドキュメントを登録
  await indexDocumentWithMetadata(
    "長いテキストドキュメント...",
    {
      title: "サンプル文書",
      source: "ウェブサイト"
    }
  );
  
  // 検索を実行
  const results = await searchWithMetadata("検索クエリ");
  
  // 結果を表示
  for (const result of results) {
    console.log(`スコア: ${result.similarity}`);
    console.log(`タイトル: ${result.metadata.title}`);
    console.log(`ソース: ${result.metadata.source}`);
    console.log(`テキスト: ${result.text}`);
    console.log('---');
  }
}
```

### 実装の注意点

1. **チャンクサイズ**
   - 適切なサイズ（300-1000トークン程度）に分割
   - 文脈を保持できる大きさを維持

2. **ベクトル化**
   - 一貫した埋め込みモデルを使用
   - 正規化されたベクトルを使用（cosine-normalizedの場合）

3. **検索結果の処理**
   - 類似度スコアに基づくフィルタリング
   - メタデータを活用した結果のグループ化
   - 適切なスコアのしきい値設定

## 参考URL

- [MeMemo 公式GitHub](https://github.com/poloclub/mememo)
- [デモサイト](https://poloclub.github.io/mememo/)
- [研究論文（SIGIR'24）](https://arxiv.org/abs/2407.01972)
- [npmパッケージ](https://www.npmjs.com/package/mememo)

デモサイトでは、以下の3つのユースケースを試すことができます：
- ML Paper Reviewer: 機械学習論文のレビュー支援
- Prompt Enhancer: 生成AIのプロンプト最適化
- Responsible AI Assistant: AIの倫理と安全性に関する知識提供

## まとめ

MeMemoを使用することで、ブラウザ上で効率的なベクトル検索を実現できます。すべての処理がクライアントサイドで完結するため、プライバシーを保ちながらRAGシステムを構築できます。

基本的な実装に加えて、メタデータの活用や検索結果の後処理など、実用的な機能を追加することで、より高度な検索システムを構築することが可能です。