概要
ドキュメント・コードセクションのEmbedding(ベクトル表現)を生成する基盤を構築する。
背景・動機
Phase 5 (Semantic Extension) の基盤。意味的な類似度検索を実現するために、テキストをベクトルに変換する仕組みが必要。
提案する解決策
対応モデル
| モデル |
方式 |
次元数 |
備考 |
| nomic-embed-text(Ollama) |
ローカル |
768 |
デフォルト、オフライン対応 |
| OpenAI text-embedding-3-small |
API |
1536 |
オプション、高速 |
アーキテクチャ
// trait定義
pub trait EmbeddingProvider: Send + Sync {
fn embed(&self, texts: &[String]) -> Result<Vec<Vec<f32>>, EmbeddingError>;
fn dimension(&self) -> usize;
}
// エラー型
pub enum EmbeddingError {
NetworkError(String), // 接続失敗
ApiError(String), // API応答エラー
ModelNotFound(String), // モデル未検出
InvalidResponse(String), // 不正なレスポンス
RateLimited, // レート制限
Timeout, // タイムアウト
ConfigError(String), // 設定エラー
StorageError(String), // SQLite格納エラー
}
// Ollama実装(reqwest::blocking 使用)
pub struct OllamaProvider { model: String, endpoint: String }
// OpenAI実装(reqwest::blocking 使用)
pub struct OpenAiProvider { api_key: String, model: String }
モジュール構成
src/
├── embedding/
│ ├── mod.rs # EmbeddingProviderトレイト、EmbeddingError、config読み込み
│ ├── ollama.rs # OllamaProvider実装
│ ├── openai.rs # OpenAiProvider実装
│ └── store.rs # SQLite embeddings.db 操作
├── cli/
│ ├── embed.rs # embedサブコマンド(新規)
│ ├── index.rs # --with-embedding オプション追加(変更)
│ └── clean.rs # --keep-embeddings オプション追加(変更)
├── indexer/
│ └── mod.rs # embeddings_db_path() 追加(変更)
├── main.rs # Commands::Embed追加、Index/Update/Clean引数追加(変更)
└── lib.rs # pub mod embedding; 追加(変更)
Embedding格納
- SQLite(
.commandindex/embeddings.db)にベクトルを格納(symbols.dbとは独立)
- tantivy 0.25 はネイティブベクトル検索に非対応のため、SQLiteを採用
- テーブル定義:
embeddings(section_path TEXT, section_heading TEXT, embedding BLOB, dimension INTEGER, model TEXT, file_hash TEXT)
- 将来のPhase 5でANN検索ライブラリ(hnswlib等)との連携を想定
- 格納先を
.commandindex/embeddings.db として独立管理(cleanコマンドでの選択的削除に対応)
indexer/mod.rs に embeddings_db_path() を追加(symbol_db_path() と同様のパターン)
HTTPクライアント方針
- reqwest::blocking を使用(同期HTTP)
- 現在のコードベースが完全同期のため、tokio runtime導入は行わない
- 将来的なasync化は別Issueで対応
Embedding対象
- tantivy インデックス内の各セクション(見出し単位のテキスト)
- 1セクション = 1ベクトル
- コードファイルは現状1ファイル = 1セクション(heading_level = 0)としてembedding生成
- 将来的にシンボル単位(関数/クラス)のembeddingはPhase 5拡張として検討
バッチ処理
commandindex index / update 時にオプションで同時生成(--with-embedding)
- 別コマンド
commandindex embed で後から生成も可能
- 前提: インデックス構築済み(manifest.json + tantivyインデックスが存在)
- tantivyインデックスからセクションのbodyテキストを読み出してembedding生成
- インデックス未構築時は適切なエラーメッセージを表示
- バッチサイズ: 10テキスト/リクエスト(Ollama)、100テキスト/リクエスト(API)
設定
# .commandindex/config.toml
[embedding]
provider = "ollama" # "ollama" | "openai"
model = "nomic-embed-text" # モデル名
endpoint = "http://localhost:11434" # Ollamaのエンドポイント
# api_key = "sk-..." # OpenAI使用時
- config.toml基盤はこのIssue内で新規構築する
toml クレートを依存に追加
- デフォルト値生成、型定義、バリデーション、エラーハンドリングを含む
- config.toml不在時はembedding無効をデフォルト動作とする
エラーハンドリング
EmbeddingError を新設し、既存 IndexError に Embedding(EmbeddingError) バリアントを追加
ConfigError → EmbeddingError::ConfigError として統合
From<EmbeddingError> 実装で既存エラーパターンに統合
キャッシュ戦略
- ファイルハッシュベースでキャッシュ(既存の
manifest::compute_file_hash() を再利用)
- ファイル未変更ならembedding再生成をスキップ
- ※ファイルハッシュベースのため、1セクション変更でもファイル内全セクションのembeddingを再生成する(将来最適化可能)
clean でembeddingも削除(--keep-embeddings で保持可能)
- embeddingデータは
.commandindex/embeddings.db に独立格納されているため選択的削除が容易
--keep-embeddings 時は .commandindex/ 内のファイルを個別削除し、embeddings.dbを残す
依存関係の追加
# Cargo.toml
reqwest = { version = "0.12", features = ["blocking", "json"] }
toml = "0.8"
serde = { version = "1", features = ["derive"] } # config.tomlのデシリアライズ用(既存なら不要)
受け入れ基準
テスト戦略
- EmbeddingProviderトレイトのモック実装によるユニットテスト
- Ollama/OpenAI統合テストは
#[cfg(feature = "integration-test")] で分離
- CI環境ではモックテストのみ実行
- 新テストは
tests/e2e_embedding.rs として分離
--with-embedding のデフォルトは false で既存テストとの後方互換性を維持
影響を受けるファイル
直接変更
| ファイル |
変更内容 |
Cargo.toml |
reqwest, toml 依存追加 |
src/main.rs |
Commands::Embed追加、Index/Update/Clean引数追加 |
src/lib.rs |
pub mod embedding; 追加 |
src/cli/mod.rs |
pub mod embed; 追加 |
src/cli/index.rs |
--with-embedding オプション対応 |
src/cli/clean.rs |
--keep-embeddings 対応、選択的削除 |
src/indexer/mod.rs |
embeddings_db_path() 追加 |
新規作成
| ファイル |
内容 |
src/embedding/mod.rs |
トレイト、エラー型、config読み込み |
src/embedding/ollama.rs |
OllamaProvider |
src/embedding/openai.rs |
OpenAiProvider |
src/embedding/store.rs |
SQLite操作 |
src/cli/embed.rs |
embedサブコマンド |
依存 Issue
概要
ドキュメント・コードセクションのEmbedding(ベクトル表現)を生成する基盤を構築する。
背景・動機
Phase 5 (Semantic Extension) の基盤。意味的な類似度検索を実現するために、テキストをベクトルに変換する仕組みが必要。
提案する解決策
対応モデル
アーキテクチャ
モジュール構成
Embedding格納
.commandindex/embeddings.db)にベクトルを格納(symbols.dbとは独立)embeddings(section_path TEXT, section_heading TEXT, embedding BLOB, dimension INTEGER, model TEXT, file_hash TEXT).commandindex/embeddings.dbとして独立管理(cleanコマンドでの選択的削除に対応)indexer/mod.rsにembeddings_db_path()を追加(symbol_db_path()と同様のパターン)HTTPクライアント方針
Embedding対象
バッチ処理
commandindex index/update時にオプションで同時生成(--with-embedding)commandindex embedで後から生成も可能設定
tomlクレートを依存に追加エラーハンドリング
EmbeddingErrorを新設し、既存IndexErrorにEmbedding(EmbeddingError)バリアントを追加ConfigError→EmbeddingError::ConfigErrorとして統合From<EmbeddingError>実装で既存エラーパターンに統合キャッシュ戦略
manifest::compute_file_hash()を再利用)cleanでembeddingも削除(--keep-embeddingsで保持可能).commandindex/embeddings.dbに独立格納されているため選択的削除が容易--keep-embeddings時は.commandindex/内のファイルを個別削除し、embeddings.dbを残す依存関係の追加
受け入れ基準
--with-embeddingオプションでindex/update時に同時生成commandindex embedで後からembedding生成テスト戦略
#[cfg(feature = "integration-test")]で分離tests/e2e_embedding.rsとして分離--with-embeddingのデフォルトは false で既存テストとの後方互換性を維持影響を受けるファイル
直接変更
Cargo.tomlsrc/main.rssrc/lib.rspub mod embedding;追加src/cli/mod.rspub mod embed;追加src/cli/index.rs--with-embeddingオプション対応src/cli/clean.rs--keep-embeddings対応、選択的削除src/indexer/mod.rsembeddings_db_path()追加新規作成
src/embedding/mod.rssrc/embedding/ollama.rssrc/embedding/openai.rssrc/embedding/store.rssrc/cli/embed.rs依存 Issue