import OutfitSearchService from './outfitSearchService'; import { SearchResponse, OutfitAnalysisResult, LLMQueryResponse, SearchConfig, DEFAULT_SEARCH_CONFIG, } from '../types/outfitSearch'; /** * 智能检索服务 * 整合图像分析、LLM问答和搜索功能,提供统一的智能检索体验 * 遵循 Tauri 开发规范的服务层设计原则 */ export class IntelligentSearchService { /** * 智能搜索模式枚举 */ static readonly SearchMode = { TEXT: 'text', // 文本搜索 IMAGE: 'image', // 图像分析搜索 LLM: 'llm', // LLM智能搜索 HYBRID: 'hybrid', // 混合搜索 } as const; /** * 执行智能搜索 * 根据输入类型自动选择最佳搜索策略 */ static async executeIntelligentSearch(params: { query?: string; imagePath?: string; llmQuery?: string; config?: SearchConfig; mode?: keyof typeof IntelligentSearchService.SearchMode; }): Promise<{ searchResponse: SearchResponse; analysisResult?: OutfitAnalysisResult; llmResponse?: LLMQueryResponse; searchMode: string; suggestions: string[]; }> { const { query, imagePath, llmQuery, config = DEFAULT_SEARCH_CONFIG, mode } = params; // 自动检测搜索模式 const detectedMode = mode || this.detectSearchMode({ query, imagePath, llmQuery }); let searchResponse: SearchResponse; let analysisResult: OutfitAnalysisResult | undefined; let llmResponse: LLMQueryResponse | undefined; let finalConfig = { ...config }; let suggestions: string[] = []; switch (detectedMode) { case this.SearchMode.IMAGE: if (!imagePath) throw new Error('Image path is required for image search'); // 1. 分析图像 const imageAnalysis = await OutfitSearchService.analyzeOutfitImage({ image_path: imagePath, image_name: imagePath.split('/').pop() || 'image', }); analysisResult = imageAnalysis.result as OutfitAnalysisResult; // 2. 基于分析结果生成搜索配置 finalConfig = await OutfitSearchService.generateSearchConfigFromAnalysis(analysisResult); // 3. 执行搜索 searchResponse = await OutfitSearchService.searchSimilarOutfits({ query: analysisResult.style_description || 'model', config: finalConfig, page_size: 9, page_offset: 0, }); // 4. 生成搜索建议 suggestions = this.generateImageSearchSuggestions(analysisResult); break; case this.SearchMode.LLM: if (!llmQuery) throw new Error('LLM query is required for LLM search'); // 1. 获取LLM回答 llmResponse = await OutfitSearchService.askLLMOutfitAdvice({ user_input: llmQuery, }); // 2. 从LLM回答中提取搜索关键词 const extractedKeywords = this.extractKeywordsFromLLMResponse(llmResponse.answer); // 3. 执行基于关键词的搜索 searchResponse = await OutfitSearchService.searchSimilarOutfits({ query: extractedKeywords.join(' ') || llmQuery, config: finalConfig, page_size: 9, page_offset: 0, }); // 4. 生成搜索建议 suggestions = extractedKeywords; break; case this.SearchMode.HYBRID: // 混合搜索:结合多种输入 const hybridResults = await this.executeHybridSearch({ query, imagePath, llmQuery, config: finalConfig, }); searchResponse = hybridResults.searchResponse; analysisResult = hybridResults.analysisResult; llmResponse = hybridResults.llmResponse; suggestions = hybridResults.suggestions; break; case this.SearchMode.TEXT: default: if (!query) throw new Error('Query is required for text search'); // 1. 生成搜索建议 suggestions = await OutfitSearchService.getSearchSuggestions(query); // 2. 执行文本搜索 searchResponse = await OutfitSearchService.searchSimilarOutfits({ query, config: finalConfig, page_size: 9, page_offset: 0, }); break; } return { searchResponse, analysisResult, llmResponse, searchMode: detectedMode, suggestions, }; } /** * 自动检测搜索模式 */ private static detectSearchMode(params: { query?: string; imagePath?: string; llmQuery?: string; }): string { const { query, imagePath, llmQuery } = params; // 如果有多个输入,使用混合模式 const inputCount = [query, imagePath, llmQuery].filter(Boolean).length; if (inputCount > 1) { return this.SearchMode.HYBRID; } // 单一输入模式检测 if (imagePath) return this.SearchMode.IMAGE; if (llmQuery) return this.SearchMode.LLM; if (query) return this.SearchMode.TEXT; // 默认文本模式 return this.SearchMode.TEXT; } /** * 执行混合搜索 */ private static async executeHybridSearch(params: { query?: string; imagePath?: string; llmQuery?: string; config: SearchConfig; }): Promise<{ searchResponse: SearchResponse; analysisResult?: OutfitAnalysisResult; llmResponse?: LLMQueryResponse; suggestions: string[]; }> { const { query, imagePath, llmQuery, config } = params; const results: any = {}; const allSuggestions: string[] = []; // 并行执行各种分析 const promises: Promise[] = []; if (imagePath) { promises.push( OutfitSearchService.analyzeOutfitImage({ image_path: imagePath, image_name: imagePath.split('/').pop() || 'image', }).then(result => { results.analysisResult = result.result as OutfitAnalysisResult; allSuggestions.push(...this.generateImageSearchSuggestions(results.analysisResult)); }) ); } if (llmQuery) { promises.push( OutfitSearchService.askLLMOutfitAdvice({ user_input: llmQuery, }).then(result => { results.llmResponse = result; const keywords = this.extractKeywordsFromLLMResponse(result.answer); allSuggestions.push(...keywords); }) ); } if (query) { promises.push( OutfitSearchService.getSearchSuggestions(query).then(suggestions => { allSuggestions.push(...suggestions); }) ); } // 等待所有分析完成 await Promise.all(promises); // 合并搜索配置 let finalConfig = { ...config }; if (results.analysisResult) { const imageConfig = await OutfitSearchService.generateSearchConfigFromAnalysis(results.analysisResult); finalConfig = this.mergeSearchConfigs(finalConfig, imageConfig); } // 构建综合搜索查询 const searchQueries = [ query, results.analysisResult?.style_description, ...allSuggestions.slice(0, 3), // 限制关键词数量 ].filter(Boolean); const finalQuery = searchQueries.join(' ').substring(0, 200); // 限制查询长度 // 执行最终搜索 const searchResponse = await OutfitSearchService.searchSimilarOutfits({ query: finalQuery || 'model', config: finalConfig, page_size: 9, page_offset: 0, }); return { searchResponse, analysisResult: results.analysisResult, llmResponse: results.llmResponse, suggestions: [...new Set(allSuggestions)].slice(0, 5), // 去重并限制数量 }; } /** * 从图像分析结果生成搜索建议 */ private static generateImageSearchSuggestions(analysisResult: OutfitAnalysisResult): string[] { const suggestions: string[] = []; // 添加环境标签 if (analysisResult.environment_tags) { suggestions.push(...analysisResult.environment_tags); } // 添加产品类别和设计风格 if (analysisResult.products) { analysisResult.products.forEach(product => { suggestions.push(product.category); if (product.design_styles) { suggestions.push(...product.design_styles); } }); } return [...new Set(suggestions)].slice(0, 5); } /** * 从LLM回答中提取关键词 */ private static extractKeywordsFromLLMResponse(response: string): string[] { const keywords: string[] = []; const text = response.toLowerCase(); // 服装相关关键词模式 const patterns = [ /(?:推荐|建议|选择).*?([\u4e00-\u9fa5]+(?:装|衣|裤|鞋|包|帽))/g, /(?:休闲|正式|运动|街头|优雅|可爱|性感|简约|复古|时尚)/g, /(?:春|夏|秋|冬)(?:季|天)/g, /(?:上班|约会|聚会|旅行|运动|居家)/g, ]; patterns.forEach(pattern => { let match; while ((match = pattern.exec(text)) !== null) { if (match[1]) { keywords.push(match[1]); } else { keywords.push(match[0]); } } }); return [...new Set(keywords)].slice(0, 3); } /** * 合并搜索配置 */ private static mergeSearchConfigs(config1: SearchConfig, config2: SearchConfig): SearchConfig { return { relevance_threshold: config1.relevance_threshold, environments: [...new Set([...config1.environments, ...config2.environments])], categories: [...new Set([...config1.categories, ...config2.categories])], color_filters: { ...config1.color_filters, ...config2.color_filters }, design_styles: { ...config1.design_styles, ...config2.design_styles, }, max_keywords: Math.max(config1.max_keywords, config2.max_keywords), debug_mode: config1.debug_mode || config2.debug_mode, custom_filters: [...new Set([...config1.custom_filters, ...config2.custom_filters])], query_enhancement_enabled: config1.query_enhancement_enabled && config2.query_enhancement_enabled, color_thresholds: config1.color_thresholds, }; } /** * 获取搜索历史和建议 */ static async getSearchRecommendations(): Promise<{ popularKeywords: string[]; recentSearches: string[]; trendingStyles: string[]; }> { // 这里可以从本地存储或服务器获取数据 return { popularKeywords: ['休闲搭配', '正式风格', '运动装', '街头风', '优雅风格'], recentSearches: [], // 从本地存储获取 trendingStyles: ['简约风', '复古风', '韩式风格', '日系风格', '欧美风'], }; } } export default IntelligentSearchService;