Проблема, которая бесит: как найти старую фотографию без тегов?
У вас на S3 лежит терабайт семейных фотографий, архив клиентских проектов или просто куча мемов. Вы точно помните: там была фотография с красной машиной на фоне гор, снятая зимним утром. Но как ее найти? Перебирать тысячи файлов? Придумывать сложные названия папок? Я ненавижу этот процесс.
Обычный поиск по имени файла или EXIF-данным работает только если вы дисциплинированный архивариус (я - нет). Нужно что-то умнее. Система, которая понимает содержание фотографий и связи между ними. Которая найдет "то самое фото с собакой у озера" по описанию, даже если файл называется IMG_5487.jpg.
Забудьте про ручную разметку. В 2026 году нейросети делают это за вас, причем с пониманием контекста, который вы бы сами не заметили.
Архитектура: три кита, которые держат ваш фотоархив
Мы строим систему из трех сервисов AWS. Каждый решает свою задачу, вместе они создают полноценный интеллектуальный поиск.
| Сервис | Зачем нужен | Что делает в нашей системе |
|---|---|---|
| Amazon Rekognition | Компьютерное зрение | Распознает объекты, лица, текст, сцены на фотографиях. Извлекает структурированные метаданные. |
| Amazon Bedrock | Мультимодальные эмбеддинги | Превращает изображения и текстовые описания в семантические векторы (эмбеддинги) для поиска по смыслу. |
| Amazon Neptune | Графовая база данных | Хранит связи между фотографиями, объектами, людьми, местами. Позволяет искать по сложным запросам типа "покажи все фото, где я и Маша были на природе". |
Плюс AWS CDK для развертывания инфраструктуры как кода. Потому что настраивать это через консоль - самоубийство.
Подготовка: что нужно перед стартом
Убедитесь, что у вас есть AWS аккаунт с правами на создание ресурсов. Установите AWS CLI и CDK. Все примеры кода будут на Python - он сейчас де-факто стандарт для таких задач.
npm install -g aws-cdk
pip install aws-cdk.aws-rekognition aws-cdk.aws-neptune aws-cdk.aws-bedrock
Создайте новый CDK проект. Я не буду разжевывать базовые вещи - если вы не знаете, как инициализировать CDK стэк, сначала разберитесь с основами.
1Загрузка и анализ: заставляем Rekognition смотреть на ваши фото
Первый шаг - анализ фотографий. Закидываем их в S3, запускаем Rekognition, получаем JSON с разметкой. Кажется просто? Почти.
Вот как НЕ надо делать:
# Плохой код: анализируем каждую фотографию отдельным вызовом
for photo in photos:
response = rekognition.detect_labels(Image={'S3Object': {'Bucket': bucket, 'Name': photo}})
# Сохраняем куда-то
# Это дорого и медленно
Вместо этого используем асинхронный анализ для пачек фотографий и сохраняем результаты в DynamoDB. Стоимость снижается в разы.
# Хороший подход: запускаем задание для всей папки
response = rekognition.start_label_detection(
Video={'S3Object': {'Bucket': bucket, 'Name': 'photos/'}},
NotificationChannel={'SNSTopicArn': topic_arn, 'RoleArn': role_arn}
)
job_id = response['JobId']
# Когда задание завершится (через SNS), получаем результаты
results = rekognition.get_label_detection(JobId=job_id, MaxResults=1000)
Rekognition в 2026 году умеет определять не только объекты, но и сцены, эмоции, текст (с помощью Textract), даже небезопасный контент. Для семейного архива последнее может быть неожиданным сюрпризом.
Важно: настройте политику хранения результатов. DynamoDB с TTL (Time To Live) в 90 дней плюс экспорт в S3 для долгосрочного хранения - разумный компромисс между стоимостью и доступностью.
2Семантические эмбеддинги: Bedrock превращает картинки в числа
Метаданные от Rekognition - это хорошо, но недостаточно. Пользователь ищет "уютный вечер с друзьями", а не "диван, пицца, 3 человека". Нужен семантический поиск.
Здесь в игру вступает Amazon Bedrock, точнее, его мультимодальные модели для создания эмбеддингов. На момент марта 2026 года актуальны модели Titan Multimodal Embeddings G2 - они работают и с текстом, и с изображениями, приводя их к одному векторному пространству.
import boto3
from botocore.exceptions import ClientError
bedrock = boto3.client('bedrock-runtime', region_name='us-east-1')
# Получаем эмбеддинг для изображения из S3
response = bedrock.invoke_model(
modelId='amazon.titan-embed-image-v2:0', # Актуальная модель на 2026 год
body=json.dumps({
"inputImage": {
"imageSource": {
"s3Location": {
"uri": f"s3://{bucket}/{image_key}"
}
}
}
})
)
embedding = json.loads(response['body'].read())['embedding']
# То же самое для текстового запроса
text_response = bedrock.invoke_model(
modelId='amazon.titan-embed-text-v2:0',
body=json.dumps({"inputText": "уютный вечер с друзьями"})
)
text_embedding = json.loads(text_response['body'].read())['embedding']
Теперь можно искать фотографии, сравнивая косинусное сходство векторов. Фото "уютного вечера" будет близко к текстовому запросу, даже если на нем нет явных тегов "диван" или "пицца".
Если хотите глубже понять, как работают мультимодальные эмбеддинги, посмотрите мой разбор про кроссмодальный поиск на практике.
3Граф знаний: Neptune запоминает, кто, что и с кем
Самый интересный этап. Мы берем разрозненные данные (метки от Rekognition, эмбеддинги от Bedrock, EXIF-данные) и превращаем их в граф. В графе появляются сущности: Фотография, Человек, Объект, Место, Событие. И связи между ними.
Почему граф, а не реляционная база? Попробуйте в SQL сделать запрос "найди все фотографии, где я и мой брат были вместе в горах, но без наших родителей". В графе это естественно и быстро.
# Запрос на Gremlin для Neptune
# Находим все фотографии, где я (id: person123) и мой брат (id: person456)
# были вместе в местах с тегом 'горы', но без родителей (id: person789, person999)
g.V().hasLabel('Person').has('id', 'person123').as('me')
.out('APPEARED_IN').as('photo')
.in('APPEARED_IN').hasLabel('Person').has('id', 'person456').as('brother')
.select('photo')
.out('TAGGED_WITH').has('name', 'горы').as('location')
.select('photo')
.not(__.in('APPEARED_IN').hasLabel('Person').has('id', within('person789', 'person999')))
.valueMap('id', 's3_url', 'date_taken')
.toList()
Настройка Neptune - отдельная история. Не экономьте на инстансе. T3.medium для продакшна - путь к боли. Берите хотя бы r6g.large с памятью оптимизированной. Графовые запросы жрут RAM.
Ошибка новичков: пытаться хранить сами эмбеддинги в Neptune. Не делайте так. Храните их в S3 или векторной базе типа OpenSearch, а в Neptune держите только ссылки и метаданные.
4API поиска: соединяем все вместе
Финальный шаг - создать API, который принимает текстовый запрос или фотографию и возвращает релевантные результаты. Логика такая:
- Пользователь отправляет запрос "фото с моря прошлым летом"
- Bedrock создает эмбеддинг запроса
- Ищем похожие эмбеддинги в векторной базе (я использую OpenSearch с k-NN плагином)
- Получаем кандидатов (скажем, 50 фотографий)
- Фильтруем через Neptune: оставляем только те, которые были сделаны летом и имеют тег "море" или "пляж"
- Ранжируем по релевантности и дате
- Возвращаем топ-10 результатов
API лучше делать на Lambda с API Gateway. Почему? Потому что поиск - не постоянная нагрузка. Lambda запускается по запросу, вы платите только за время выполнения. Для CDK это выглядит так:
from aws_cdk import (
aws_lambda as _lambda,
aws_apigateway as apigateway,
)
search_lambda = _lambda.Function(self, "SearchFunction",
runtime=_lambda.Runtime.PYTHON_3_12,
handler="search.handler",
code=_lambda.Code.from_asset("lambda"),
environment={
"NEPTUNE_ENDPOINT": neptune.cluster_endpoint.hostname,
"OPENSEARCH_ENDPOINT": opensearch.domain_endpoint,
"BEDROCK_MODEL_ID": "amazon.titan-embed-text-v2:0"
}
)
api = apigateway.RestApi(self, "PhotoSearchApi",
rest_api_name="Photo Search Service",
description="Search photos by semantic similarity"
)
search_resource = api.root.add_resource("search")
search_resource.add_method("POST",
apigateway.LambdaIntegration(search_lambda,
proxy=True,
integration_responses=[{
'statusCode': '200',
'responseParameters': {
'method.response.header.Access-Control-Allow-Origin': "'*'"
}
}]
),
method_responses=[{
'statusCode': '200',
'responseParameters': {
'method.response.header.Access-Control-Allow-Origin': True
}
}]
)
Нюансы, которые заставят вас вырвать волосы (но не надо)
Первый нюанс - стоимость. Rekognition стоит $1 за 1000 изображений (при пакетном анализе). Bedrock - $0.0001 за 1000 токенов. Neptune - от $0.5 в час за инстанс. Для архива в 100к фото система обойдется в ~$200 в месяц. Это много или мало? Зависит от того, сколько времени ваши сотрудники тратят на поиск фото вручную.
Второй нюанс - задержки. Bedrock + Neptune + OpenSearch дают ответ за 200-500 мс. Для веб-приложения нормально. Но если вам нужна молниеносная скорость, кэшируйте популярные запросы в ElastiCache (Redis).
Третий нюанс - точность. Система ищет "счастливые моменты" и находит фото с улыбающимися людьми. Но также может найти фото грустного человека на фоне надписи "счастье". Это проблема всех семантических моделей. Решение - комбинировать подходы: семантический поиск + фильтрация по точным тегам.
Четвертый нюанс - приватность. Если у вас фотографии людей, нужны согласия на обработку персональных данных. Rekognition определяет лица, эмоции, возраст. В некоторых странах это регулируется строго. Для корпоративных архивов смотрите в сторону автоматической чистки персональных данных.
Что дальше? Куда развивать систему
Базовая система работает. Но есть куда расти:
- Добавьте поиск по видео. Тот же принцип: вырезаем кадры, анализируем, индексируем. Только дороже в 10 раз.
- Подключите генерацию описаний. Bedrock может не только искать, но и описывать фото: "На фотографии пожилая пара держится за руки на закате". Полезно для альтернативных текстов (alt text) и архивирования.
- Сделайте рекомендации: "Вам может понравиться вот эта фотография из того же места".
- Добавьте временные линии и автоматические альбомы: "Июнь 2025: поездка на Байкал".
Главное - не увлекайтесь. Каждая фича увеличивает сложность и стоимость. Начните с минимально рабочего продукта: поиск по текстовым запросам. Когда пользователи привыкнут, добавляйте фильтры по дате, людям, местам.
И последний совет: если вы делаете систему для e-commerce, посмотрите, как Amazon использует AI для каталогов. Там свои специфические требования к точности и скорости.
А если проект кажется слишком сложным - начните с локального прототипа. Инструменты вроде AI File Sorter показывают, что можно сделать многое даже без облачной инфраструктуры.
Только не храните терабайты фото на своем ноутбуке. Это я проверил на собственном опыте.