mixvideo-v2/docs/outfit-matching-search-syst...

356 lines
10 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

# 服装搭配智能搜索系统设计文档
## 概述
基于promptx/outfit-match的Python实现设计一个集成到Tauri桌面应用的服装搭配智能搜索系统。该系统的核心功能是用户上传服装图片 → AI分析提取特征 → 在数据库中搜索相似商品 → 提供LLM驱动的情景搭配建议。
## 核心功能流程
### 1. 图像分析流程
```
用户上传图片 → Gemini API分析 → 提取颜色/风格/类别 → 生成搜索条件 → 显示解析结果
```
### 2. 智能搜索流程
```
解析结果 → 构建搜索过滤器 → Vertex AI Search → 返回相似商品 → 分页展示结果
```
### 3. LLM问答流程
```
用户输入情景 → RAG检索相关数据 → LLM生成搭配建议 → 展示推荐结果
```
## 系统架构设计
### 前端架构 (React + TypeScript)
```
src/pages/OutfitSearch.tsx # 主搜索页面
src/components/outfit/
├── ImageUploader.tsx # 图片上传组件
├── AnalysisResult.tsx # AI分析结果展示
├── SearchPanel.tsx # 搜索配置面板
├── SearchResults.tsx # 搜索结果展示
├── ColorPicker.tsx # 颜色选择器
├── FilterPanel.tsx # 筛选条件面板
└── LLMChat.tsx # LLM问答组件
```
### 后端架构 (Rust)
```
src/data/models/
├── outfit_search.rs # 搜索相关数据模型
└── gemini_analysis.rs # Gemini分析结果模型
src/business/services/
├── gemini_service.rs # Gemini API集成服务
├── outfit_search_service.rs # 搜索引擎服务
└── vertex_ai_service.rs # Vertex AI Search集成
src/presentation/commands/
└── outfit_search_commands.rs # Tauri命令接口
```
## 核心数据模型
### Gemini分析结果模型
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct OutfitAnalysisResult {
pub environment_tags: Vec<String>,
pub environment_color_pattern: ColorHSV,
pub dress_color_pattern: ColorHSV,
pub style_description: String,
pub products: Vec<ProductAnalysis>,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ProductAnalysis {
pub category: String,
pub description: String,
pub color_pattern: ColorHSV,
pub design_styles: Vec<String>,
pub color_pattern_match_dress: f64,
pub color_pattern_match_environment: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColorHSV {
pub hue: f64, // 色相 (0-1)
pub saturation: f64, // 饱和度 (0-1)
pub value: f64, // 明度 (0-1)
}
```
### 搜索配置模型
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchConfig {
pub relevance_threshold: RelevanceThreshold,
pub environments: Vec<String>,
pub categories: Vec<String>,
pub color_filters: HashMap<String, ColorFilter>,
pub design_styles: HashMap<String, Vec<String>>,
pub max_keywords: usize,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct ColorFilter {
pub enabled: bool,
pub color: ColorHSV,
pub hue_threshold: f64,
pub saturation_threshold: f64,
pub value_threshold: f64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub enum RelevanceThreshold {
LOWEST,
LOW,
MEDIUM,
HIGH,
}
```
### 搜索结果模型
```rust
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResponse {
pub results: Vec<SearchResult>,
pub total_size: usize,
pub next_page_token: Option<String>,
pub search_time_ms: u64,
}
#[derive(Debug, Clone, Serialize, Deserialize)]
pub struct SearchResult {
pub id: String,
pub image_url: String,
pub style_description: String,
pub environment_tags: Vec<String>,
pub products: Vec<ProductInfo>,
pub relevance_score: f64,
}
```
## API接口设计
### Tauri命令接口
```rust
// 图像分析
#[tauri::command]
pub async fn analyze_outfit_image(
state: State<'_, AppState>,
image_path: String,
) -> Result<OutfitAnalysisResult, String>;
// 智能搜索
#[tauri::command]
pub async fn search_similar_outfits(
state: State<'_, AppState>,
config: SearchConfig,
) -> Result<SearchResponse, String>;
// LLM问答
#[tauri::command]
pub async fn ask_llm_outfit_advice(
state: State<'_, AppState>,
user_input: String,
) -> Result<String, String>;
// 获取搜索建议
#[tauri::command]
pub async fn get_search_suggestions(
state: State<'_, AppState>,
query: String,
) -> Result<Vec<String>, String>;
```
## 核心算法实现
### HSV颜色匹配算法
```rust
impl ColorHSV {
pub fn from_rgb_hex(hex: &str) -> Result<Self> {
// RGB转HSV实现
}
pub fn to_rgb_hex(&self) -> String {
// HSV转RGB实现
}
pub fn is_in_range(&self, other: &ColorHSV, thresholds: &ColorThresholds) -> bool {
let hue_diff = (self.hue - other.hue).abs();
let sat_diff = (self.saturation - other.saturation).abs();
let val_diff = (self.value - other.value).abs();
hue_diff <= thresholds.hue &&
sat_diff <= thresholds.saturation &&
val_diff <= thresholds.value
}
}
```
### 搜索过滤器构建
```rust
impl SearchFilterBuilder {
pub fn build_filters(config: &SearchConfig) -> String {
let mut filters = Vec::new();
// 类别过滤
if !config.categories.is_empty() {
for category in &config.categories {
let mut inner_filters = vec![
format!("products.category: ANY(\"{}\")", category)
];
// 颜色过滤
if let Some(color_filter) = config.color_filters.get(category) {
if color_filter.enabled {
inner_filters.extend(Self::build_color_filters(color_filter));
}
}
// 设计风格过滤
if let Some(styles) = config.design_styles.get(category) {
if !styles.is_empty() {
let styles_str = styles.iter()
.map(|s| format!("\"{}\"", s))
.collect::<Vec<_>>()
.join(",");
inner_filters.push(format!("products.design_styles: ANY({})", styles_str));
}
}
filters.push(format!("({})", inner_filters.join(" AND ")));
}
}
// 环境标签过滤
if !config.environments.is_empty() {
let env_str = config.environments.iter()
.map(|e| format!("\"{}\"", e))
.collect::<Vec<_>>()
.join(",");
filters.push(format!("environment_tags: ANY({})", env_str));
}
filters.join(" AND ")
}
}
```
## 用户界面设计
### 主搜索页面布局
```typescript
export const OutfitSearch: React.FC = () => {
return (
<div className="outfit-search-container">
<div className="search-panel">
<ImageUploader onAnalysisComplete={handleAnalysis} />
<SearchPanel config={searchConfig} onConfigChange={updateConfig} />
<LLMChat onAskLLM={handleLLMQuery} />
</div>
<div className="results-panel">
<SearchResults
results={searchResults}
onPageChange={handlePageChange}
/>
</div>
</div>
);
};
```
### 搜索配置面板
```typescript
export const SearchPanel: React.FC<SearchPanelProps> = ({ config, onConfigChange }) => {
return (
<div className="search-config">
<div className="relevance-threshold">
<label>匹配严格程度</label>
<select value={config.relevance_threshold} onChange={handleThresholdChange}>
<option value="LOWEST">最低</option>
<option value="LOW"></option>
<option value="MEDIUM">中等</option>
<option value="HIGH"></option>
</select>
</div>
<div className="category-filters">
{config.categories.map(category => (
<CategoryFilter
key={category}
category={category}
colorFilter={config.color_filters[category]}
designStyles={config.design_styles[category]}
onFilterChange={handleFilterChange}
/>
))}
</div>
<button onClick={handleSearch}>搜索</button>
</div>
);
};
```
## 集成方案
### 与现有系统集成
1. **导航系统集成**: 搜索功能作为顶部导航栏的独立页面,类似现有的"模特管理"、"AI分类设置"等
2. **AI分类系统复用**: 复用现有的Gemini API集成和提示词生成逻辑
3. **配置管理**: project_id等配置作为全局环境变量或应用配置不依赖具体项目
4. **状态管理**: 使用现有的Zustand状态管理模式
### 配置管理
```rust
// 全局配置结构
#[derive(Debug, Clone)]
pub struct OutfitSearchConfig {
pub google_project_id: String, // "gen-lang-client-0413414134"
pub vertex_ai_app_id: String, // "jeans-search_1751353769585"
pub storage_bucket_name: String, // "fashion_image_block"
pub data_store_id: String, // "jeans_pattern_data_store"
pub cloudflare_project_id: String, // "67720b647ff2b55cf37ba3ef9e677083"
pub cloudflare_gateway_id: String, // "bowong-dev"
}
impl Default for OutfitSearchConfig {
fn default() -> Self {
Self {
google_project_id: "gen-lang-client-0413414134".to_string(),
vertex_ai_app_id: "jeans-search_1751353769585".to_string(),
storage_bucket_name: "fashion_image_block".to_string(),
data_store_id: "jeans_pattern_data_store".to_string(),
cloudflare_project_id: "67720b647ff2b55cf37ba3ef9e677083".to_string(),
cloudflare_gateway_id: "bowong-dev".to_string(),
}
}
}
```
### 部署配置
1. **环境变量**: 配置Google Cloud访问令牌、Cloudflare网关等
2. **依赖管理**: 添加必要的Rust crates和npm包
3. **构建配置**: 更新Tauri配置以支持网络访问权限
## 开发优先级
### P0 (核心功能)
- [ ] Gemini API集成和图像分析
- [ ] 基础搜索界面和结果展示
- [ ] HSV颜色匹配算法
### P1 (完整功能)
- [ ] Vertex AI Search集成
- [ ] 高级搜索过滤器
- [ ] LLM问答功能
### P2 (优化功能)
- [ ] 搜索历史和偏好
- [ ] 性能优化和缓存
- [ ] 用户体验改进