# Embedding モデル ID 連携の修正 ## 🐛 問題の記述 ``` 混合検索失敗: NotFoundException: ModelConfig with ID "embedding-3" not found or not owned by user. ``` ## 🔍 問題の分析 ### 混同されやすい概念 システム内には2種類の異なる「ID」が存在します: 1. **モデル設定テーブルの ID** (`ModelConfig.id`) - データベースの主キー - 例:`"embedding-3"`, `"default-embedding"` - 用途:`ModelConfigService.findOne(id, userId)` 2. **モデル識別子** (`ModelConfig.modelId`) - AI ベンダー側でのモデル名 - 例:`"text-embedding-3-large"`, `"text-embedding-ada-002"` - 用途:AI API 呼び出し時のパラメータ ### データフロー ``` ユーザー設定: user_setting.selectedEmbeddingId = "embedding-3" ✅ テーブルID フロントエンド: settings.selectedEmbeddingId ↓ 転送 バックエンド Controller: selectedEmbeddingId = "embedding-3" ↓ 転送 ChatService: embeddingModel.id = "embedding-3" ✅ 正常 ↓ 転送 hybridSearch: embeddingModelId = "embedding-3" ↓ 転送 EmbeddingService.getEmbeddings(embeddingModelId) ↓ 呼び出し ModelConfigService.findOne("embedding-3", userId) ✅ 正常 ``` ### 以前の誤り **ChatService.ts (誤り):** ```typescript // 182行目付近 searchResults = await this.hybridSearch( [message], userId, embeddingModel.modelId, // ❌ 誤り! "text-embedding-3-large" を渡してしまっていた ); ``` **hybridSearch (受信側):** ```typescript private async hybridSearch( keywords: string[], userId: string, embeddingModelId?: string, // "text-embedding-3-large" を受け取ってしまう ) ``` **EmbeddingService (期待値):** ```typescript async getEmbeddings( texts: string[], userId: string, embeddingModelConfigId: string, // 本来は "embedding-3" を期待 ) { const modelConfig = await this.modelConfigService.findOne( embeddingModelConfigId, // ❌ "text-embedding-3-large" で検索しても見つからない! userId, ); } ``` ## ✅ 修正内容 ### 修正箇所 **server/src/chat/chat.service.ts:** ```typescript // 182行目付近 searchResults = await this.hybridSearch( [message], userId, embeddingModel.id, // ✅ テーブルID "embedding-3" を使用するように変更 ); ``` ### 修正後のフロー ``` 1. ユーザーが埋め込みモデルを選択: text-embedding-3-large ↓ 2. システムが user_setting テーブルに保存: selectedEmbeddingId = "embedding-3" (ModelConfig テーブルの主キー) ↓ 3. フロントエンドがチャットリクエストを送信: { selectedEmbeddingId: "embedding-3" } ↓ 4. バックエンド Controller が受信: selectedEmbeddingId = "embedding-3" ↓ 5. ChatService がモデルを検索: embeddingModel = models.find(m => m.id === "embedding-3") // 結果: { id: "embedding-3", modelId: "text-embedding-3-large", ... } ↓ 6. ChatService が hybridSearch を呼び出し: hybridSearch(..., embeddingModel.id) // "embedding-3" を渡す ↓ 7. hybridSearch が EmbeddingService を呼び出し: getEmbeddings(..., "embedding-3") ↓ 8. EmbeddingService が設定を検索: findOne("embedding-3", userId) // ✅ 設定が見つかる ↓ 9. AI API を呼び出し: model: "text-embedding-3-large" // modelId を用いて API を実行 ``` ## 📊 ID の対応関係 | シーン | 使用するフィールド | 例 | 用途 | |------|-----------|--------|------| | ユーザー設定 | `user_setting.selectedEmbeddingId` | `"embedding-3"` | ユーザーの選択を保存 | | 設定の検索 | `ModelConfig.id` | `"embedding-3"` | データベースクエリ | | API 呼び出し | `ModelConfig.modelId` | `"text-embedding-3-large"` | AI ベンダーのインターフェース | ## 🔑 重要な原則 ### 1. データベース操作にはテーブル ID を使用する ```typescript // ✅ 正解 const model = await modelConfigService.findOne(modelId, userId); // modelId = "embedding-3" // ❌ 誤り const model = await modelConfigService.findOne(modelId, userId); // modelId = "text-embedding-3-large" ``` ### 2. API 呼び出しにはモデル識別子を使用する ```typescript // ✅ 正解 fetch(apiUrl, { body: JSON.stringify({ model: modelConfig.modelId, // "text-embedding-3-large" }), }); ``` ### 3. 内部的な受け渡しにはテーブル ID を使用する ```typescript // ✅ 正解 embeddingService.getEmbeddings(texts, userId, modelConfig.id); // "embedding-3" // ❌ 誤り embeddingService.getEmbeddings(texts, userId, modelConfig.modelId); // "text-embedding-3-large" ``` ## 🧪 検証 ### テスト手順 1. **ユーザー設定の確認** ```sql SELECT selectedEmbeddingId FROM user_setting WHERE userId = 'xxx'; -- 期待値: "embedding-3" (テーブルID) ``` 2. **モデル設定の確認** ```sql SELECT id, modelId, name FROM model_config WHERE userId = 'xxx'; -- 期待値: embedding-3 | text-embedding-3-large | Text Embedding 3 Large ``` 3. **チャットメッセージの送信** - バックエンドログを確認 - 期待される出力: "使用嵌入模型: Text Embedding 3 Large text-embedding-3-large ID: embedding-3" 4. **埋め込みベクトルの生成確認** - ログに "从 Text Embedding 3 Large 获取到 X 个嵌入向量" と表示されること ### 期待されるログ出力 ``` === ChatService.streamChat === User ID: user-123 Selected Embedding ID: embedding-3 ID に基づいてモデルを検索: embedding-3 使用するモデル: Text Embedding 3 Large text-embedding-3-large ID: embedding-3 埋め込みベクトルを生成中... Text Embedding 3 Large から 1 個の埋め込みベクトルを取得しました。次元数: 2560 ``` ## 📁 修正されたファイル - `server/src/chat/chat.service.ts` - 182行目。 `embeddingModel.modelId` ではなく `embeddingModel.id` を渡すように変更。 ## 💡 学んだ教訓 1. **2種類の ID を区別すること**:テーブル主キー vs モデル識別子 2. **パラメータ名を明確にすること**:`embeddingModelConfigId` vs `embeddingModelId` 3. **呼び出し先の期待値を確認すること**:`EmbeddingService` がどのタイプの ID を求めているか 4. **ログ出力の工夫**:デバッグを容易にするため、両方の ID を出力する ```typescript console.log('使用するモデル:', embeddingModel.name, embeddingModel.modelId, 'ID:', embeddingModel.id); // 出力: 使用するモデル: Text Embedding 3 Large text-embedding-3-large ID: embedding-3 ```