Реализация AI-ассистента для подбора одежды (стилист) в мобильном приложении
AI-стилист в мобильном приложении работает с двумя задачами: анализ собственного гардероба (что у меня есть, что с чем сочетается) и подбор новых вещей по предпочтениям. Обе требуют работы с изображениями — это отличает стилиста от большинства других AI-ассистентов, где достаточно текста.
Инвентаризация гардероба: Vision API для одежды
Пользователь фотографирует одежду, ассистент должен распознать:
- Тип вещи (рубашка, джинсы, платье, пальто...)
- Цвет и паттерн
- Стиль (casual, formal, sport, vintage...)
- Сезонность
GPT-4o Vision справляется с этим из коробки — не нужна отдельная модель:
// iOS: анализ изображения одежды через GPT-4o Vision
func analyzeClothingItem(_ image: UIImage) async throws -> ClothingItem {
guard let imageData = image.jpegData(compressionQuality: 0.7) else {
throw AnalysisError.invalidImage
}
let base64Image = imageData.base64EncodedString()
let messages: [[String: Any]] = [{
"role": "user",
"content": [
["type": "image_url", "image_url": ["url": "data:image/jpeg;base64,\(base64Image)"]],
["type": "text", "text": """
Analyze this clothing item. Return JSON:
{
"type": "shirt|pants|dress|jacket|shoes|...",
"colors": ["primary color", "secondary color if exists"],
"pattern": "solid|striped|checkered|floral|...",
"style": ["casual", "formal", "sport", ...],
"season": ["spring", "summer", "autumn", "winter"],
"material_guess": "cotton|denim|leather|..."
}
"""]
]
}]
let response = try await openAIClient.chat(messages: messages, responseFormat: .jsonObject)
return try JSONDecoder().decode(ClothingItem.self, from: response.data(using: .utf8)!)
}
Для хранения гардероба — Core Data / Room с thumbnail изображения и JSON-атрибутами. Полный размер фото в локальном файловом хранилище, ссылка в БД.
Подбор аутфитов из существующего гардероба
Когда гардероб наполнен, главная функция — «что надеть сегодня». Запрос включает погоду, повод, предпочтения.
func suggestOutfit(
wardrobe: [ClothingItem],
occasion: String, // "work", "casual friday", "date", "sport"
weather: WeatherContext,
avoidItems: [String] // уже надетые сегодня/вчера
) async throws -> OutfitSuggestion {
let wardrobeDescription = wardrobe.map {
"ID:\($0.id) - \($0.type), colors: \($0.colors.joined(separator: "/")), style: \($0.style.joined(separator: ","))"
}.joined(separator: "\n")
let prompt = """
Select a complete outfit from the wardrobe below.
Occasion: \(occasion)
Weather: \(weather.temperature)°C, \(weather.condition)
Avoid (recently worn): \(avoidItems.joined(separator: ", "))
Wardrobe:
\(wardrobeDescription)
Return JSON: {items: [id], reasoning: "brief style logic", alternatives: [[id]]}
"""
// ...
}
reasoning — текстовое объяснение выбора. Пользователи ценят понимание, почему эта комбинация сочетается: «синяя рубашка + светлые чинос создаёт контраст тёмное/светлое, подходящий для casual office».
Цветовая совместимость
LLM понимает правила сочетания цветов, но иногда галлюцинирует. Добавляем детерминированный фильтр на основе цветовой теории: комплементарные, аналогичные, триадные цвета через HSL-пространство.
// Android - базовая цветовая совместимость
fun areColorsCompatible(color1: HslColor, color2: HslColor): Boolean {
val hueDiff = abs(color1.hue - color2.hue)
val normalizedDiff = minOf(hueDiff, 360 - hueDiff)
return when {
normalizedDiff < 30 -> true // аналогичные цвета
normalizedDiff in 150f..210f -> true // комплементарные
normalizedDiff in 110f..130f -> true // триадные
color1.saturation < 0.15f -> true // нейтральный с любым
color2.saturation < 0.15f -> true // нейтральный с любым
else -> false
}
}
Этот фильтр применяется до запроса к LLM: если модель предложила несочетаемую пару, возвращаем альтернативу.
AR-примерка
Try-on функция — высокий приоритет для fashion-приложений. Варианты реализации:
Overlay на фото: пользователь загружает фото, сверху накладывается предмет одежды. На iOS — CoreML с сегментацией тела (DeepLab v3+), затем CoreImage композитинг. Качество среднее.
Сервис виртуальной примерки: Kiri Engine, Fashn AI, Replicate's IDM-VTON — cloud API. Качество значительно лучше, но 5–15 секунд на генерацию. Для мобильного приложения — асинхронный запрос с push-уведомлением о готовности.
func startTryOn(userPhoto: UIImage, clothingImage: UIImage) async throws -> String {
// Возвращает jobId, результат приходит через webhook/polling
let jobId = try await tryOnAPI.submitJob(person: userPhoto, garment: clothingImage)
return jobId
}
func pollTryOnResult(jobId: String) async throws -> UIImage {
for _ in 0..<30 { // максимум 30 попыток x 2 сек = 60 сек
try await Task.sleep(nanoseconds: 2_000_000_000)
let status = try await tryOnAPI.checkStatus(jobId: jobId)
if status.isReady, let url = status.resultUrl {
return try await loadImage(from: url)
}
}
throw TryOnError.timeout
}
Ориентиры по срокам
Базовый ассистент с текстовыми рекомендациями — 3–5 дней. Полный гардероб с Vision-анализом фото + подбор аутфитов + цветовая совместимость — 3–4 недели. AR-примерка через cloud API — плюс 1–2 недели.







