openAIApiKey: config.apiKey || 'ollama', // ローカルモデルの場合は key が不要な場合がある modelName: config.modelId, // modelId に修正 ); // modelId に修正 selectedLLMId?: string; // 新增:选中的 LLM 模型 ID selectedGroups?: string[]; // 新增 selectedFiles?: string[]; // 新增:选中的文件 historyId?: string; // 新增 enableRerank?: boolean; // 新增 selectedRerankId?: string; // 新增 temperature?: number; // 新增:temperature 参数 maxTokens?: number; // 新增:maxTokens 参数 topK?: number; // 新增:topK 参数 similarityThreshold?: number; // 新増:similarityThreshold 参数 rerankSimilarityThreshold?: number; // 新増:rerankSimilarityThreshold 参数 enableQueryExpansion?: boolean; // 新增 enableHyDE?: boolean; // 新增 console.log('Final LLM model used (default):', llmModel ? llmModel.name : '无'); `data: ${JSON.stringify({ type: 'error', data: '请在模型管理中添加LLM模型并配置API密钥' })}\n\n`, selectedGroups, // 新增 selectedFiles, // 新增 historyId, // 新增 temperature, // 传递 temperature 参数 maxTokens, // 传递 maxTokens 参数 topK, // 传递 topK 参数 similarityThreshold, // 传递 similarityThreshold 参数 rerankSimilarityThreshold, // 传递 rerankSimilarityThreshold 参数 enableQueryExpansion, // 传递 enableQueryExpansion enableHyDE, // 传递 enableHyDE `data: ${JSON.stringify({ type: 'error', data: error.message || '服务器错误' })}\n\n`, `data: ${JSON.stringify({ type: 'error', data: '未找到LLM模型配置' })}\n\n`, selectedGroups?: string[], // 新規:選択されたグループ selectedFiles?: string[], // 新規:選択されたファイル historyId?: string, // 新規:対話履歴ID temperature?: number, // 新規: temperature パラメータ maxTokens?: number, // 新規: maxTokens パラメータ topK?: number, // 新規: topK パラメータ similarityThreshold?: number, // 新規: similarityThreshold パラメータ rerankSimilarityThreshold?: number, // 新規: rerankSimilarityThreshold パラメータ enableQueryExpansion?: boolean, // 新規 enableHyDE?: boolean, // 新規 tenantId?: string // 新規: tenant isolation console.log('ユーザーID:', userId); console.log('API Key プレフィックス:', modelConfig.apiKey?.substring(0, 10) + '...'); tenantId || 'default', // 新規 let effectiveFileIds = selectedFiles; // 明示的に指定されたファイルを優先 提供されたテキスト内容を、ユーザーの指示に基づいて修正または改善してください。 挨拶や結びの言葉(「わかりました、こちらが...」など)は含めず、修正後の内容のみを直接出力してください。 コンテキスト(現在の内容): ユーザーの指示: selectedGroups?: string[], // 新規パラメータ explicitFileIds?: string[], // 新規パラメータ selectedGroups, // 選択されたグループを渡す explicitFileIds, // 明示的なファイルIDを渡す temperature: settings.temperature ?? 0.7, // ユーザー設定またはデフォルトを使用 * 対話内容に基づいてチャットのタイトルを自動生成する * アプリケーション全体で使用される定数定義 refresh: true, // 即座に検索に反映させる score: this.normalizeScore(hit._score), // スコアの正規化 selectedGroups?: string[], // 後方互換性のために残す(未使用) explicitFileIds?: string[], // 明示的に指定されたファイルIDリスト const maxScore = Math.max(...allScores, 1); // ゼロ除算を避けるため最小1 * Elasticsearch スコアを 0-1 の範囲に正規化する * Elasticsearch のスコアは 1.0 を超える可能性があるため、正規化が必要 * ただし、kNN検索の類似度スコアは既に0-1の範囲にある(cosine similarity)ので、 * 特別な正規化は不要。必要に応じて最小値保護のみ行う。 if (!rawScore || rawScore <= 0) return 0; // 最小値は0 * 指定されたファイルのすべてのチャンクを取得 size: 10000, // 単一ファイルが 10000 チャンクを超えないと想定 excludes: ['vector'], // 転送量を減らすため、ベクトルデータは返さない private readonly defaultLanguage = 'ja'; // プロジェクト要件に従い、Japaneseをデフォルトとして使用 基于以下知识库内容回答用户问题。 **重要提示**: 用户已选择特定知识组,请严格基于以下知识库内容回答。如果知识库中没有相关信息,请明确告知用户:"${noMatchMsg}",然后再提供答案。 知识库内容: 历史对话: 用户问题:{question} 请用Chinese回答,并严格遵循以下 Markdown 格式要求: 1. **段落与结构**: - 使用清晰的段落分隔,每个要点之间空一行 - 使用标题(## 或 ###)组织长回答 2. **文本格式**: - 使用 **粗体** 强调重要概念和关键词 - 使用列表(- 或 1.)组织多个要点 - 使用 \`代码\` 标记技术术语、命令、文件名 3. **代码展示**: - 使用代码块展示代码,并指定语言: return "示例" - 支持语言:python, javascript, typescript, java, bash, sql 等 4. **图表与可视化**: - 使用 Mermaid 语法绘制流程图、序列图等: A[开始] --> B[处理] B --> C[结束] - 适用场景:流程、架构、状态机、时序图 5. **其他要求**: - 回答精炼准确 - 多步骤操作使用有序列表 - 对比类信息建议用表格展示(如果适用) 作为智能助手,请回答用户的问题。 请用Chinese回答。 } else { // 默认为日语,符合项目要求 以下のナレッジベースの内容に基づいてユーザーの質問に答えてください。 **重要**: ユーザーが特定の知識グループを選択しました。以下のナレッジベースの内容に厳密に基づいて回答してください。ナレッジベースに関連情報がない場合は、「${noMatchMsg}」とユーザーに明示的に伝えてから、回答を提供してください。 ナレッジベースの内容: 会話履歴: ユーザーの質問:{question} Japaneseで回答してください。以下の Markdown 書式要件に厳密に従ってください: 1. **段落と構造**: - 明確な段落分けを使用し、要点間に空行を入れる - 長い回答には見出し(## または ###)を使用 2. **テキスト書式**: - 重要な概念やキーワードを強調するために **太字** を使用 - 複数のポイントを整理するためにリスト(- または 1.)を使用 - 技術用語、コマンド、ファイル名をマークするために \`コード\` を使用 3. **コード表示**: - 言語を指定してコードブロックを使用: return "例" - 対応言語:python, javascript, typescript, java, bash, sql など 4. **図表とチャート**: - フローチャート、シーケンス図などに Mermaid 構文を使用: A[開始] --> B[処理] B --> C[終了] - 使用例:プロセスフロー、アーキテクチャ図、状態図、シーケンス図 5. **その他の要件**: - 簡潔で明確な回答を心がける - 複数のステップがある場合は番号付きリストを使用 - 比較情報には表を使用(該当する場合) インテリジェントアシスタントとして、ユーザーの質問に答えてください。 Japaneseで回答してください。 return `你是一个文档分析师。请阅读以下文本(文档开Header分),并生成一个简炼、专业的标题(不超过50个字符)。 只返回标题文本。不要包含任何解释性文字或前导词(如“标题是:”)。 语言:Chinese 文本内容: return `あなたはドキュメントアナライザーです。以下のテキスト(ドキュメントの冒頭部分)を読み、簡潔でプロフェッショナルなタイトル(最大50文字)を生成してください。 タイトルテキストのみを返してください。説明文や前置き(例:「タイトルは:」)は含めないでください。 言語:Japanese テキスト: return `根据以下对话片段,生成一个简短、描述性的标题(不超过50个字符),总结讨论的主题。 只返回标题文本。不要包含任何前导词。 片段: 用户: ${userMessage} 助手: ${aiResponse}`; return `以下の会話スニペットに基づいて、トピックを要約する短く説明的なタイトル(最大50文字)を生成してください。 タイトルのみを返してください。前置きは不要です。 スニペット: ユーザー: ${userMessage} アシスタント: ${aiResponse}`; * Chunk configurationサービス * チャンクパラメータの検証と管理を担当し、モデルの制限や環境変数の設定に適合していることを確認します * 制限の優先順位: * 1. 環境変数 (MAX_CHUNK_SIZE, MAX_OVERLAP_SIZE) * 2. データベース内のモデル設定 (maxInputTokens, maxBatchSize) * 3. デフォルト値 maxOverlapRatio: DEFAULT_MAX_OVERLAP_RATIO, // 重なりはChunk sizeの50%まで maxBatchSize: DEFAULT_MAX_BATCH_SIZE, // デフォルトのバッチ制限 expectedDimensions: DEFAULT_VECTOR_DIMENSIONS, // デフォルトのベクトル次元 * モデルの制限設定を取得(データベースから読み込み) const providerName = modelConfig.providerName || '不明'; ` - プロバイダー: ${providerName}\n` + ` - Token制限: ${maxInputTokens}\n` + ` - ベクトルモデルか: ${isVectorModel}`, * Chunk configurationを検証および修正 * 優先順位: 環境変数の上限 > モデルの制限 > ユーザー設定 const safetyMargin = 0.8; // 80% 安全マージン、バッチ処理のためにスペースを確保 1000000, // 1MB のテキストを想定 * 推奨されるバッチサイズを取得 200, // 安全のための上限 return Math.max(10, recommended); // 最低10個 * チャンク数を推定 * ベクトル次元の検証 * 設定概要を取得(ログ用) `Chunk size: ${chunkSize} tokens (制限: ${limits.maxInputTokens})`, `重なりサイズ: ${chunkOverlap} tokens`, `バッチサイズ: ${limits.maxBatchSize}`, * フロントエンド用のConfig limitsを取得 * フロントエンドのスライダーの上限設定に使用 throw new Error(`埋め込みモデル設定 ${embeddingModelConfigId} が見つかりません`); throw new Error(`モデル ${modelConfig.name} は無効化されているため、埋め込みベクトルを生成できません`); throw new Error(`モデル ${modelConfig.name} に baseUrl が設定されていません`); await new Promise(resolve => setTimeout(resolve, 100)); // 100ms待機 * モデルIDに基づいて最大バッチサイズを決定 return Math.min(10, configuredMaxBatchSize || 100); // Googleの場合は10を上限 return Math.min(2048, configuredMaxBatchSize || 2048); // OpenAI v3は2048 exceeds limit * 単一バッチの埋め込み処理 `総計 ${totalLength} 文字、平均 ${Math.round(avgLength)} 文字、` + `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens` `テキスト長がモデルの制限。` + `現在: ${texts.length} 個のテキストで計 ${totalLength} 文字、` + `モデル制限: ${modelConfig.maxInputTokens || 8192} tokens。` + `アドバイス: Chunk sizeまたはバッチサイズを小さくしてください` this.logger.error(`リクエストパラメータ: model=${modelConfig.modelId}, inputLength=${texts[0]?.length}`); throw new Error(`埋め込み API の呼び出しに失敗しました: ${response.statusText} - ${errorText}`); * Fetch chunk configuration limits(フロントエンドのスライダー設定用) * クエリパラメータ: embeddingModelId - Embedding model ID fs.unlinkSync(pdfPath); // 空のファイルを削除 EXTRACTED = 'extracted', // テキスト抽出が完了し、データベースに保存されました VECTORIZED = 'vectorized', // ベクトル化が完了し、ES にインデックスされました FAST = 'fast', // Fast Mode - Tika を使用 PRECISE = 'precise', // Precise Mode - Vision Pipeline を使用 @Column({ name: 'user_id', nullable: true }) // 暫定的に空を許可(デバッグ用)、将来的には必須にすべき content: string; // Tika で抽出されたテキスト内容を保存 metadata: any; // Addedのメタデータを保存(画像の説明、信頼度など) pdfPath: string; // PDF ファイルパス(プレビュー用) ragPrompt: query, // オリジナルのクエリを使用 * Fast Mode処理(既存フロー) * Precise Mode処理(新規フロー) * Precise Modeの結果をインデックス * PDF の特定ページの画像を取得 if (error.message && (error.message.includes('context length') || error.message.includes('コンテキスト長 exceeds limit ') || error.message.includes('コンテキスト長 exceeds limit '))) { [chunk.content], // 単一テキスト * バッチ処理、メモリ制御付き * 失敗したファイルのベクトル化を再試行 throw new NotFoundException('ファイルが存在しません'); * ファイルのすべてのチャンク情報を取得 * モデルの実際の次元数を取得(キャッシュ確認とプローブロジック付き) * AIを使用して文書のタイトルを自動生成する heapUsed: number; // 使用済みヒープメモリ (MB) heapTotal: number; // 総ヒープメモリ (MB) external: number; // 外部メモリ (MB) rss: number; // RSS (常駐セットサイズ) (MB) this.MAX_MEMORY_MB = parseInt(process.env.MAX_MEMORY_USAGE_MB || '1024'); // 1GB上限 this.BATCH_SIZE = parseInt(process.env.CHUNK_BATCH_SIZE || '100'); // 1バッチあたり100チャンク this.GC_THRESHOLD_MB = parseInt(process.env.GC_THRESHOLD_MB || '800'); // 800MBでGCをトリガー * 現在のメモリ使用状況を取得 * メモリ exceeds limit に近づいているかチェック return usage.heapUsed > this.MAX_MEMORY_MB * 0.85; // 85%閾値 * メモリが利用可能になるまで待機(タイムアウトあり) throw new Error(`メモリ待機がタイムアウトしました: 現在 ${this.getMemoryUsage().heapUsed}MB > ${this.MAX_MEMORY_MB * 0.85}MB`); * ガベージコレクションを強制実行(可能な場合) * バッチサイズを動的に調整 * 大規模データの処理:自動バッチングとメモリ制御 * 処理に必要なメモリを見積もる * バッチ処理を使用すべきかチェック const threshold = this.MAX_MEMORY_MB * 0.7; // 70%閾値 * LibreOffice サービスインターフェース定義 pdf_data?: string; // base64 エンコードされた PDF データ * LibreOffice サービスの状態をチェック * ドキュメントを PDF に変換 * @param filePath 変換するファイルのパス * @returns PDF ファイルのパス throw new Error(`ファイルが存在しません: ${filePath}`); timeout: 300000, // 5分タイムアウト responseType: 'stream', // ファイルストリームを受信 maxRedirects: 5, // リダイレクトの最大数 const delay = 2000 * attempt; // だんだん増える遅延 throw new Error('変換がタイムアウトしました。ファイルが大きすぎる可能性があります'); throw new Error(`変換に失敗しました: ${detail}`); throw new Error(`変換に失敗しました: ${lastError.message}`); throw new Error('LibreOffice サービスが実行されていません。サービスの状態を確認してください'); throw new Error('LibreOffice サービスとの接続が切断されました。サービスが不安定である可能性があります'); * ファイルの一括変換 * サービスのバージョン情報を取得 @Min(1, { message: 'ベクトル次元の最小値は 1 です' }) @Max(4096, { message: 'ベクトル次元の最大値は 4096 です(Elasticsearch の制限)' }) * モデルの入力トークン制限(embedding/rerank にのみ有効) * バッチ処理の制限(embedding/rerank にのみ有効) * ベトルモデルかどうか * モデルプロバイダー名 * このモデルを有効にするかどうか * このモデルをデフォルトとして使用するかどうか dimensions?: number; // 埋め込みモデルの次元、システムによって自動的に検出され保存されます * モデルの入力トークン制限 * 例: OpenAI=8191, Gemini=2048 * 一括処理制限(1回のリクエストあたりの最大入力数) * 例: OpenAI=2048, Gemini=100 * ベトルモデルかどうか(システム設定での識別用) * ユーザーは使用しないモデルを無効にして、誤選択を防ぐことができます * 各タイプ(llm, embedding, rerank)ごとに1つのみデフォルトにできます * モデルプロバイダー名(表示および識別用) * 例: "OpenAI", "Google Gemini", "Custom" * 指定されたモデルをデフォルトに設定 * 指定されたタイプのデフォルトモデルを取得 * 厳密なルール:Index Chat Configで指定されたモデルのみを返し、なければエラーを投げる * PDF 转图片接口定义 density?: number; // DPI 分辨率,默认 300 quality?: number; // JPEG 质量 (1-100),默认 85 format?: 'jpeg' | 'png'; // 输出格式,默认 jpeg outDir?: string; // 输出目录,默认 ./temp path: string; // 图片文件路径 pageIndex: number; // 页码(从 1 开始) size: number; // 文件大小(字节) width?: number; // 图片宽度 height?: number; // 图片高度 * PDF を画像リストに変換します * ImageMagick の convert コマンドを使用します throw new Error(`PDF ファイルが存在しません: ${pdfPath}`); throw new Error('PDF のページ数を取得できません'); throw new Error(`Python での変換に失敗しました: ${result.error}`); throw new Error(`PDF から画像への変換に失敗しました: ${error.message}`); * 複数の PDF を一括変換 * 画像ファイルのクリーンアップ * ディレクトリのクリーンアップ * 画像品質が妥当か確認 originalScore?: number; // Rerank前のスコア(デバッグ用) vectorSimilarityThreshold: number = 0.3, // ベクトル検索のしきい値 rerankSimilarityThreshold: number = 0.5, // Rerankのしきい値(デフォルト0.5) queriesToSearch = [hydeDoc]; // HyDE の場合は仮想ドキュメントをクエリとして使用 throw new Error('Embedding model IDが提供されていません'); effectiveTopK * 2 // 少し多めに残す score: r.score, // Rerank スコア originalScore: originalItem.score // 元のスコア * Search resultsの重複排除 * クエリを拡張してバリエーションを生成 .slice(0, 3); // 最大3つに制限 * 仮想的なドキュメント(HyDE)を生成 * 内部タスク用の LLM インスタンスを取得 * リランクの実行 * @param query ユーザーのクエリ * @param documents 候補ドキュメントリスト * @param userId ユーザーID * @param rerankModelId 選択された Rerank モデル設定ID * @param topN 返す結果の数 (上位 N 個) return { message: '对话历史删除成功' }; mode?: 'fast' | 'precise'; // 処理モード `ユーザー ${req.user.id} がファイルをアップロードしました: ${file.originalname} (${this.formatBytes(file.size)})`, estimatedChunks: Math.ceil(file.size / (indexingConfig.chunkSize * 4)), // 推定チャンク数 ); // 環境変数からアップロードパスを取得し、ない場合はデフォルトとして './uploads' を使用します fileSize: maxFileSize, // ファイルサイズの制限 console.log('パスワード:', randomPassword); import { User } from '../user/user.entity'; // Userエンティティのパス console.log('=== updateLanguage デバッグ ==='); console.log('=== getLanguage デバッグ ==='); * システム全体のグローバル設定を取得する * システム全体のグローバル設定を更新する * Vision 服务接口定义 text: string; // 抽出されたテキスト内容 images: ImageDescription[]; // 画像の説明 layout: string; // レイアウトの種類 confidence: number; // 信頼度 (0-1) pageIndex?: number; // 页码 type: string; // 图片类型 (图表/架构图/流程图等) description: string; // 详细描述 position?: number; // ページ内での位置 estimatedCost: number; // 预估成本(美元) * 単一画像の分析(ドキュメントページ) const baseDelay = 3000; // 3秒の基礎遅延 const delay = baseDelay + Math.random() * 2000; // 3-5秒のランダムな遅延 * 実際の画像分析を実行 temperature: 0.1, // ランダム性を抑え、一貫性を高める page: pageIndex ? ` (第 ${pageIndex} ページ)` : '', throw error; // 重新抛出错误供重试机制处理 * 再試行可能なエラーかどうかを判断 if (errorCode === 429 || errorMessage.includes('rate limit') || errorMessage.includes('リクエストが多すぎます')) { * 遅延関数 * 複数画像の一括分析 * 画像品質のチェック return { isGood: false, reason: `ファイルが小さすぎます (${sizeKB.toFixed(2)}KB)`, score: 0 }; return { isGood: false, reason: `ファイルが大きすぎます (${sizeKB.toFixed(2)}KB)`, score: 0 }; * サポートされている画像ファイルかどうかを確認 * MIME タイプを取得 * 旧インターフェース互換:単一画像の内容を抽出 * コスト制御およびクォータ管理サービス * Vision Pipeline の API 呼び出しコストを管理するために使用されます monthlyCost: number; // 今月の使用済みコスト maxCost: number; // 月間最大コスト remaining: number; // 残りコスト lastReset: Date; // 最終リセット時間 estimatedCost: number; // 推定コスト estimatedTime: number; // 推定時間(秒) pageBreakdown: { // ページごとの明細 private readonly COST_PER_PAGE = 0.01; // 1ページあたりのコスト(USD) private readonly DEFAULT_MONTHLY_LIMIT = 100; // デフォルトの月間制限(USD) * 処理コストの推定 const estimatedTime = pageCount * 3; // 1ページあたり約 3 秒 * ユーザーのクォータをチェック reason: `クォータ不足: 残り $${quota.remaining.toFixed(2)}, 必要 $${estimatedCost.toFixed(2)}`, * クォータの差し引き * ユーザーのクォータを取得 throw new Error(`ユーザー ${userId} は存在しません`); * 月間クォータのチェックとリセット * ユーザーのクォータ制限を設定 * コストレポートの取得 quotaUsage: number; // パーセンテージ * コスト警告閾値のチェック message: `⚠️ クォータ使用率が ${usagePercent.toFixed(1)}% に達しました。残り $${quota.remaining.toFixed(2)}`, message: `💡 クォータ使用率 ${usagePercent.toFixed(1)}%。コストの管理に注意してください`, * コスト表示のフォーマット * 時間表示のフォーマット return `${seconds.toFixed(0)}秒`; return `${minutes}分${remainingSeconds.toFixed(0)}秒`; * Vision Pipeline サービス(コスト制御付き) * これは vision-pipeline.service.ts の拡張版であり、コスト制御が統合されています private costControl: CostControlService, // 新增成本控制服务 * メイン処理フロー:Precise Mode(コスト制御付き) this.updateStatus('converting', 10, 'ドキュメント形式を変換中...'); this.updateStatus('splitting', 30, 'PDF を画像に変換中...'); throw new Error('PDF から画像への変換に失敗しました。画像が生成されませんでした'); this.updateStatus('checking', 40, 'クォータを確認し、コストを見積もり中...'); this.updateStatus('analyzing', 50, 'ビジョンモデルを使用してページをAnalyzing...'); this.updateStatus('completed', 100, '処理が完了しました。一時ファイルをクリーンアップ中...'); * Vision モデル設定の取得 throw new Error(`モデル設定が見つかりません: ${modelId}`); * PDF への変換 * 形式検出とモードの推奨(コスト見積もり付き) reason: `サポートされていないファイル形式です: ${ext}`, warnings: ['Fast Mode(テキスト抽出のみ)を使用します'], reason: `形式 ${ext} はPrecise Modeをサポートしていません`, reason: 'ファイルが大きいため、完全な情報を保持するためにPrecise Modeを推奨します', warnings: ['処理時間が長くなる可能性があります', 'API 費用が発生します'], reason: 'Precise Modeが利用可能です。テキストと画像の混合コンテンツを保持できます', warnings: ['API 費用が発生します'], * ユーザーのクォータ情報を取得 * 処理状態の更新(リアルタイムフィードバック用) * Vision Pipeline 接口定义 duration: number; // 秒 estimatedTime?: number; // 秒