0a9588abb7
- Add pagination support to findAll (page, limit query params) - Add findByTemplateId method to service - Add GET /by-template/:templateId endpoint to controller - Service already includes CRUD for QuestionBank and QuestionBankItem
6.6 KiB
6.6 KiB
Embedding モデル ID 連携の修正
🐛 問題の記述
混合検索失敗: NotFoundException: ModelConfig with ID "embedding-3" not found or not owned by user.
🔍 問題の分析
混同されやすい概念
システム内には2種類の異なる「ID」が存在します:
-
モデル設定テーブルの ID (
ModelConfig.id)- データベースの主キー
- 例:
"embedding-3","default-embedding" - 用途:
ModelConfigService.findOne(id, userId)
-
モデル識別子 (
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 (誤り):
// 182行目付近
searchResults = await this.hybridSearch(
[message],
userId,
embeddingModel.modelId, // ❌ 誤り! "text-embedding-3-large" を渡してしまっていた
);
hybridSearch (受信側):
private async hybridSearch(
keywords: string[],
userId: string,
embeddingModelId?: string, // "text-embedding-3-large" を受け取ってしまう
)
EmbeddingService (期待値):
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:
// 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 を使用する
// ✅ 正解
const model = await modelConfigService.findOne(modelId, userId); // modelId = "embedding-3"
// ❌ 誤り
const model = await modelConfigService.findOne(modelId, userId); // modelId = "text-embedding-3-large"
2. API 呼び出しにはモデル識別子を使用する
// ✅ 正解
fetch(apiUrl, {
body: JSON.stringify({
model: modelConfig.modelId, // "text-embedding-3-large"
}),
});
3. 内部的な受け渡しにはテーブル ID を使用する
// ✅ 正解
embeddingService.getEmbeddings(texts, userId, modelConfig.id); // "embedding-3"
// ❌ 誤り
embeddingService.getEmbeddings(texts, userId, modelConfig.modelId); // "text-embedding-3-large"
🧪 検証
テスト手順
-
ユーザー設定の確認
SELECT selectedEmbeddingId FROM user_setting WHERE userId = 'xxx'; -- 期待値: "embedding-3" (テーブルID) -
モデル設定の確認
SELECT id, modelId, name FROM model_config WHERE userId = 'xxx'; -- 期待値: embedding-3 | text-embedding-3-large | Text Embedding 3 Large -
チャットメッセージの送信
- バックエンドログを確認
- 期待される出力: "使用嵌入模型: Text Embedding 3 Large text-embedding-3-large ID: embedding-3"
-
埋め込みベクトルの生成確認
- ログに "从 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を渡すように変更。
💡 学んだ教訓
- 2種類の ID を区別すること:テーブル主キー vs モデル識別子
- パラメータ名を明確にすること:
embeddingModelConfigIdvsembeddingModelId - 呼び出し先の期待値を確認すること:
EmbeddingServiceがどのタイプの ID を求めているか - ログ出力の工夫:デバッグを容易にするため、両方の ID を出力する
console.log('使用するモデル:', embeddingModel.name, embeddingModel.modelId, 'ID:', embeddingModel.id);
// 出力: 使用するモデル: Text Embedding 3 Large text-embedding-3-large ID: embedding-3