Скриншот с паспортом в корпоративном чате — это 20 млн евро штрафа
Представьте: сотрудник загружает скриншот с паспортными данными в трекер задач. Через минуту изображение улетает в логи, потом в аналитику. GDPR — до 4% от глобального оборота. PCI DSS — до 100 тыс. долларов за инцидент. И это не гипотетика: в 2025 году Meta оштрафовали на €1,2 млрд за DPA violations.
Проблема классическая: PII бывает не только в тексте, но и в картинках. Лица на фото, номера кредиток на чеках, паспорта в сканах. Старые методы (OCR + regex) работают только с текстом, а детекция лиц — отдельная боль. Традиционные пайплайны дробятся: одна модель для OCR, вторая для лиц, третья для номеров. Дорого, сложно, негибко.
Но с середины 2025 года Amazon выкатила Nova Vision — мультимодальную модель, способную одновременно распознавать текст, объекты и лица. И задавать ей можно практически любые правила. Давайте соберём production-ready пайплайн для автоматической PII-маскировки в изображениях. Без костылей, на чистом AWS.
💡 Кстати: если вы хотите разобраться, как Amazon Nova справляется с галлюцинациями в критических сценариях, почитайте статью про детерминированные LLM от Artificial Genius. Там показано, как использовать Nova для финансов и медицины без риска.
Почему Amazon Nova, а не куча отдельных моделей?
Звучит логично: взять Rekognition для лиц, Textract для документов, а потом всё сшить. Но у этого подхода три проблемы:
- Стоимость. Каждый сервис — отдельный биллинг. Для одного изображения вы платите три раза.
- Задержка. Последовательные вызовы — время, которое в real-time недопустимо.
- Контекст. Модели не видят полной картины. Номер карты, напечатанный на футболке, может быть не распознан как PII.
Amazon Nova Vision (на июль 2026 — актуальны версии Nova Lite 2.0 и Nova Pro 2.0) решает всё одной моделью. Вы просто даёте промпт: “Найди все персональные данные: лица, номера паспортов, кредитных карт, телефоны, email-адреса. Для каждого найденного объекта верни bounding box в формате [x1,y1,x2,y2].” И модель возвращает структурированный JSON с координатами.
Кстати, о мультимодальности. В статье Кроссмодальный поиск на практике я показывал, как Nova Multimodal Embeddings позволяют искать по контенту без текстовых меток. Тот же принцип используется и здесь — модель видит изображение целиком.
Архитектура: три сервиса, один пайплайн
Нам понадобится минимум AWS-компонентов:
- S3 buckets: один для исходных изображений (input), второй для очищенных (output).
- Lambda function на Python, которая срабатывает по S3-триггеру.
- Amazon Bedrock с включённой моделью Amazon Nova Pro (или Lite — зависит от требований по точности).
Схема простая: загрузка файла в input-bucket → триггер запускает Lambda → функция читает изображение, отправляет его в Bedrock, получает bounding boxes → размывает/закрашивает области → сохраняет результат в output-bucket.
⚠️ Подводный камень: размер изображения влияет на стоимость и latency. Статья Snapcompact подсказывает, как можно сжимать изображения, сохраняя ключевые детали — это прямо применимо к нашему пайплайну. Рекомендую уменьшать изображения до 1024x1024 перед отправкой, если позволяет качество.
Шаг 1. Готовим инфраструктуру
Создаём два S3-бакета (имена уникальные, можно с префиксом pii-input-* и pii-output-*). Включаем версионирование на всякий случай.
Далее — IAM-роль для Lambda. Политики:
s3:GetObjectна input-bucket.s3:PutObjectна output-bucket.bedrock:InvokeModel(можно ограничить конкретной моделью).
{
"Version": "2012-10-17",
"Statement": [
{
"Effect": "Allow",
"Action": ["s3:GetObject"],
"Resource": "arn:aws:s3:::pii-input-2026/*"
},
{
"Effect": "Allow",
"Action": ["s3:PutObject"],
"Resource": "arn:aws:s3:::pii-output-2026/*"
},
{
"Effect": "Allow",
"Action": "bedrock:InvokeModel",
"Resource": "arn:aws:bedrock:us-east-1::foundation-model/amazon.nova-pro-v1:0"
}
]
}На момент 06.07.2026 модель Nova Pro — самая мощная. В регионах us-east-1 и eu-west-1 она доступна по умолчанию.
Шаг 2. Пишем Lambda-функцию
Код на Python 3.12. Используем библиотеки boto3, Pillow, json.
import boto3
import json
from PIL import Image, ImageFilter
import io
import os
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
s3 = boto3.client("s3")
def lambda_handler(event, context):
# Получаем информацию о загруженном файле
bucket = event["Records"][0]["s3"]["bucket"]["name"]
key = event["Records"][0]["s3"]["object"]["key"]
# Читаем изображение из S3
response = s3.get_object(Bucket=bucket, Key=key)
image_bytes = response["Body"].read()
# Вызываем Bedrock с изображением
pii_regions = detect_pii(image_bytes)
if not pii_regions:
print("PII не найдено, просто копируем")
s3.put_object(Bucket=os.environ["OUTPUT_BUCKET"], Key=key, Body=image_bytes)
return {"statusCode": 200, "body": "No PII"}
# Размываем PII-области
redacted_image = blur_regions(image_bytes, pii_regions)
# Сохраняем результат
s3.put_object(
Bucket=os.environ["OUTPUT_BUCKET"],
Key=key,
Body=redacted_image,
ContentType=response["ContentType"]
)
return {"statusCode": 200, "body": f"Redacted {len(pii_regions)} regions"}Функция detect_pii — ключевая. Отправляем изображение в Nova с промптом, который требует вернуть bounding boxes.
def detect_pii(image_bytes: bytes):
# Конвертируем bytes в base64
import base64
image_b64 = base64.b64encode(image_bytes).decode("utf-8")
# Определяем mime-type (можно вытащить из заголовка S3, упрощаем)
mime = "image/jpeg"
prompt = """
Analyze the image carefully. Find all personally identifiable information (PII):
- Faces (any human face)
- Passport numbers, ID numbers
- Credit card numbers (16 digits)
- Phone numbers
- Email addresses
- Full names (if clearly visible)
For each PII element, return a JSON object with:
- "type": one of "face", "passport_number", "credit_card", "phone", "email", "name"
- "bbox": [x1, y1, x2, y2] where coordinates are in pixels relative to image width/height (0-1)
- "confidence": 0.0 to 1.0
Return ONLY a JSON array, no other text. Example:
[
{"type": "face", "bbox": [0.1, 0.2, 0.3, 0.4], "confidence": 0.95},
{"type": "credit_card", "bbox": [0.4, 0.5, 0.8, 0.6], "confidence": 0.99}
]
If no PII, return empty array []
"""
body = {
"anthropic_version": "bedrock-2023-05-31",
"messages": [
{
"role": "user",
"content": [
{"type": "image", "source": {"type": "base64", "media_type": mime, "data": image_b64}},
{"type": "text", "text": prompt}
]
}
],
"max_tokens": 2000,
"temperature": 0 # Детерминированность важна для PII
}
try:
response = bedrock.invoke_model(
modelId="amazon.nova-pro-v1:0",
contentType="application/json",
accept="application/json",
body=json.dumps(body)
)
result = json.loads(response["body"].read())
# Извлекаем ответ ассистента
content = result["content"][0]["text"]
# Парсим JSON
pii_list = json.loads(content)
return pii_list
except Exception as e:
print(f"Error invoking Nova: {e}")
return []💡 Почему temperature=0? Если вы читали статью Детерминированные LLM от Artificial Genius, то знаете: для чувствительных данных галлюцинации недопустимы. Temperature=0 даёт воспроизводимый результат.
Теперь функция размытия:
def blur_regions(image_bytes: bytes, regions: list) -> bytes:
img = Image.open(io.BytesIO(image_bytes))
width, height = img.size
for region in regions:
bbox = region["bbox"]
# Конвертируем относительные координаты в абсолютные
x1 = int(bbox[0] * width)
y1 = int(bbox[1] * height)
x2 = int(bbox[2] * width)
y2 = int(bbox[3] * height)
# Вырезаем область
crop = img.crop((x1, y1, x2, y2))
# Размываем
blurred = crop.filter(ImageFilter.GaussianBlur(radius=30))
# Вставляем обратно
img.paste(blurred, (x1, y1))
buf = io.BytesIO()
img.save(buf, format="JPEG", quality=85)
return buf.getvalue()Готово. Загружаете тестовую картинку — и через секунду получаете чистый вариант в output-бакете.
Шаг 3. Тестирование и подводные камни
Первым делом проверьте, что модель действительно возвращает корректные bounding boxes. Частая ошибка — модель начинает описывать PII словами, а не координатами. Я советую сначала запустить промпт вручную через Bedrock Playground, убедиться, что ответ парсится как JSON.
Вторая проблема — точность. Nova не идеальна. Она может пропустить мелкий текст или лицо в тени. В таких случаях комбинируйте с другими инструментами. Например, статья Локальный OCR с PII-маскировкой показывает, как Qwen 3 VL не справляется один — и как собрать гибридный пайплайн. Здесь та же логика: Nova — голова, но для страховки можно добавить детекцию лиц от Rekognition или Tesseract.
⚠️ Грабли с координатами: Nova может вернуть bbox в формате [left, top, right, bottom] в пикселях, а не в относительных величинах. В моём примере я использую относительные, потому что так проще для разных разрешений. Если модель возвращает пиксели — нормируйте их на размер изображения.
Третье — лимиты Lambda. При обработке больших изображений ( > 10 MB) может упасть timeout (максимум 15 минут, но лучше держать в пределах 30 секунд). Решение: либо уменьшать изображение перед отправкой в Bedrock, либо использовать AWS Step Functions для асинхронного пайплайна.
Четвёртое — стоимость. Один вызов Nova Pro стоит ~$0.003 за изображение (цены на июль 2026). Плюс Lambda и S3. Для тысяч изображений в день — копейки, но для миллионов может выйти ощутимо. Оптимизируйте промпт, используйте Nova Lite (дешевле, но точнее) или функцию max_tokens по минимуму.
Когда это не работает (и что делать)
Nova Vision, как и любая LLM, иногда галлюцинирует — видит кредитку на картинке с котиком. Поэтому:
- Добавьте в промпт фразу
"Only return PII if you are at least 80% confident". - Используйте confidence threshold при размытии: если confidence < 0.7 — пропускаем.
- Для надежности можно сделать двухэтапную проверку: сначала Nova, потом дополнительный сервис (например, Amazon Rekognition для лиц).
Кстати, интересный кейс — когда PII находится не в тексте, а в метаданных изображения (EXIF). Тут Vision не поможет. Нужно чистить EXIF отдельно. Я упоминал об этом в статье Автоматическая чистка персональных данных — советую почитать для комплексного подхода.
Ещё одна ловушка — изображения с низким разрешением или поворотом. Nova может не распознать текст. В таком случае предварительно делайте препроцессинг: поворот, увеличение резкости. OpenCV в помощь.
Масштабирование: от фоточек до миллионов документов
Lambda + S3 — отличное решение для небольшого потока. Но если вам нужно обрабатывать 10 000 изображений в час, Lambda не потянет (concurrency limits). Выход — использовать SQS + Lambda с партионной обработкой, или AWS Batch для пакетной загрузки.
Пример из жизни: Huntington Bank обработал 400 миллионов документов на AWS за пару месяцев. Они использовали комбинацию Textract и Lambda с очередями. Наш пайплайн с Nova можно встроить в ту же архитектуру.
Краткий чек-лист перед продакшеном
- Тесты на разных типах PII: лица, паспорта, кредитки, email в разных шрифтах.
- A/B тестирование: сравнивайте результат Nova с ручной разметкой.
- Мониторинг: CloudWatch метрики на количество обработанных изображений, ошибки, среднюю задержку.
- Fallback: если Nova не уверена — отправляйте изображение на ручную проверку.
И да, не забудьте включить логирование запросов к Bedrock. Без логов сложно расследовать инциденты.
Пара слов о сравнении с альтернативами
Мы привыкли, что PII-фильтрация — это удел NLP моделей. Но с Vision LLM открывается новый уровень. Если вы хотите увидеть больше примеров, как PII чистят в тексте — статья Как собрать PII-фильтр на коленке покажет простой способ с OpenAI. У нас подход схож, но на стороне AWS.
И напоследок: не доверяйте модели на 100%. Всегда настраивайте fallback — отправляйте подозрительные изображения на ручную проверку. Автоматика — это щит, но не меч. Через год подобные пайплайны станут стандартом де-факто для compliance. Amazon Nova — лишь первая ласточка, но уже сейчас можно строить production-решения, которые спасут от миллионных штрафов.