Кастомизация Amazon Nova: функции вознаграждения на AWS Lambda 2026 | AiManual
AiManual Logo Ai / Manual.
13 Апр 2026 Гайд

Как создавать функции вознаграждения для кастомизации Amazon Nova: руководство с AWS Lambda и кодом

Пошаговое руководство по созданию serverless функций вознаграждения для RL-финальной настройки Amazon Nova в AWS Bedrock. Готовый код Python для AWS Lambda.

Почему функции вознаграждения — это самое слабое звено в кастомизации AI

Вы настраиваете Amazon Nova под свою задачу. SFT (Supervised Fine-Tuning) прошло успешно — модель освоила ваш стиль ответов. Пришло время Reinforcement Fine-Tuning (RFT) — той самой финальной настройки, где Nova учится не просто повторять, а выбирать лучшие ответы. И здесь все упёрлось в одну деталь: как вы определяете, что такое «лучший»?

Функция вознаграждения (reward function) — это математическая совесть вашей модели. Она шепчет Nova: «Этот ответ хорош, получи +1 балл. А этот — полная чушь, -3». Классический RLHF (Reinforcement Learning from Human Feedback) предполагает, что эту функцию в голове держит человек-аннотатор. Но в 2026 году это непозволительная роскошь для большинства проектов. Медленно, дорого, субъективно.

Главная ловушка: если ваша функция вознаграждения плохо определена, модель оптимизируется не под задачу, а под формулу. Вы получите идеально оптимизированную чушь. Например, если награждать только за длину ответа, Nova научится генерировать бесконечные бессмысленные тексты.

Serverless как выход: зачем переносить логику вознаграждения в AWS Lambda

RLAIF (Reinforcement Learning from AI Feedback) и RLVR (Reinforcement Learning with Video/Verbal Rewards) стали стандартом де-факто для кастомизации больших моделей в 2025-2026 годах. Идея проста: вместо человека «судит» другая AI-система. А её логику нужно где-то запускать.

Три причины выбрать Lambda:

  • Масштабирование до нуля. Когда процесс RFT в SageMaker или Bedrock не активен, вы не платите за оценку ответов.
  • Сложная логика без сложной инфраструктуры. Ваша функция может вызывать другие модели (например, детерминированную модель для проверки фактов), работать с базами данных, анализировать тон текста — всё в одном месте.
  • Скорость разработки. От идеи до работающей функции — 15 минут. Это меняет итерационный цикл настройки модели.
💡
AWS Bedrock теперь напрямую поддерживает вызов Lambda-функций как части конвейера RFT (фича появилась в конце 2025 года). Это официальный путь, а не костыль. Вам больше не нужно строить промежуточные API Gateway или очереди.

1 Создаём скелет Lambda-функции на Python

Забудьте про сложные Docker-образы для начала. Bedrock ожидает от функции определённый формат ввода и вывода. Вот минимальный шаблон, который работает с Amazon Nova 2 (последняя стабильная версия на апрель 2026).

import json
import boto3
from typing import Dict, Any, List

# Инициализируем клиента для Bedrock, если нужно вызывать другие модели внутри функции
bedrock_runtime = boto3.client('bedrock-runtime', region_name='us-east-1')

def lambda_handler(event: Dict[str, Any], context) -> Dict[str, Any]:
    """
    Основной обработчик Lambda.
    Ожидает event в формате от Amazon Bedrock RFT job.
    
    Структура event (упрощённо):
    {
        "model_input": "Вопрос пользователя",
        "candidate_responses": ["Ответ модели 1", "Ответ модели 2", ...],
        "custom_parameters": {"domain": "medical", "strict_facts": True}
    }
    """
    try:
        # 1. Извлекаем данные из события
        prompt = event.get('model_input', '')
        candidates = event.get('candidate_responses', [])
        params = event.get('custom_parameters', {})
        
        # 2. Проверяем входные данные
        if not prompt or not candidates:
            return {
                'statusCode': 400,
                'rewards': [],
                'error': 'Invalid input: missing prompt or candidates'
            }
        
        # 3. Вычисляем вознаграждение для каждого кандидата
        # Это ядро вашей логики!
        rewards = []
        for candidate in candidates:
            score = compute_reward(prompt, candidate, params)
            rewards.append(float(score))  # Bedrock ждёт список float
        
        # 4. Возвращаем результат в формате, ожидаемом Bedrock
        return {
            'statusCode': 200,
            'rewards': rewards,
            'evaluation_metadata': {
                'scoring_model': 'custom_lambda_v1',
                'params_used': params
            }
        }
        
    except Exception as e:
        # В случае ошибки возвращаем нулевые вознаграждения, чтобы не сломать job
        print(f"Critical error in reward function: {e}")
        return {
            'statusCode': 500,
            'rewards': [0.0] * len(candidates),
            'error': str(e)
        }

def compute_reward(prompt: str, response: str, params: Dict) -> float:
    """
    Здесь живёт ваша бизнес-логика.
    Возвращает числовой скор от -10.0 до +10.0.
    """
    base_score = 0.0
    
    # Пример 1: Награда за отсутствие отказов
    if is_refusal(response):
        base_score -= 5.0
    
    # Пример 2: Награда за структурированность (наличие маркеров списка)
    if contains_structured_elements(response):
        base_score += 2.0
        
    # Пример 3: Штраф за излишнюю многословность
    word_count = len(response.split())
    if word_count > 300:
        base_score -= (word_count - 300) * 0.01
    
    return max(-10.0, min(10.0, base_score))  # Ограничиваем диапазон

# --- Вспомогательные функции (заглушки) ---
def is_refusal(text: str) -> bool:
    """Определяет, является ли ответ отказом."""
    refusal_phrases = ["не могу", "я не", "извините", "как ai", "этического" ]
    lower_text = text.lower()
    return any(phrase in lower_text for phrase in refusal_phrases)

def contains_structured_elements(text: str) -> bool:
    """Проверяет наличие маркеров списка или структуры."""
    markers = ["1.", "2.", "•", "-", "*", "во-первых", "во-вторых"]
    return any(marker in text for marker in markers)

Этот код — отправная точка. Он обрабатывает несколько candidate_responses (так работает Nova 2 в режиме RFT — генерирует несколько вариантов для одного промпта), применяет простые эвристики и возвращает scores. Самое главное — обработка ошибок. Если ваша функция упадёт, RFT job прервётся. Поэтому мы ловим все исключения и возвращаем нулевые оценки в случае беды.

2 Подключаем сложную логику: вызов других моделей внутри Lambda

Сила подхода в том, что compute_reward может быть сколь угодно сложной. Например, вы хотите проверять фактические утверждения ответа Nova против вашей базы знаний. Или оценивать тон — должен ли он быть формальным для финансовых отчётов.

def compute_reward_advanced(prompt: str, response: str, params: Dict) -> float:
    """Продвинутая функция с вызовом других моделей Bedrock."""
    scores = []
    
    # 1. Проверка на токсичность с помощью Nova-Lite (меньшая модель)
    toxicity_score = check_toxicity(response)
    scores.append(toxicity_score * 3.0)  # Большой вес безопасности
    
    # 2. Проверка фактов через RAG-систему (если в params есть контекст)
    if params.get('fact_checking'):
        fact_score = fact_check_via_rag(prompt, response, params['fact_context'])
        scores.append(fact_score * 4.0)  # Самый высокий вес для factual accuracy
    
    # 3. Оценка соответствия инструкции (Instruction Following)
    instruction_score = evaluate_instruction_following(prompt, response)
    scores.append(instruction_score * 2.0)
    
    # 4. Длина ответа (предпочтение золотой середине)
    length_penalty = evaluate_length(response, target_length=150)
    scores.append(length_penalty)
    
    # Итоговый скор — взвешенная сумма
    final_score = sum(scores) / len(scores) if scores else 0.0
    return final_score

def check_toxicity(text: str) -> float:
    """Используем компактную модель для классификации токсичности."""
    # В реальности здесь будет invoke_model к Nova-Lite или специальной модели модерации
    # Это пример запроса к API Bedrock 2026 года
    try:
        response = bedrock_runtime.invoke_model(
            modelId='amazon.nova-lite-text-v2:0',
            contentType='application/json',
            accept='application/json',
            body=json.dumps({
                'prompt': f"Classify toxicity (0-10) of text: {text}",
                'max_tokens': 10,
                'temperature': 0.1
            })
        )
        result = json.loads(response['body'].read())
        # Предполагаем, что модель возвращает число от 0 до 10
        toxicity_value = float(result.get('completion', '5'))
        # Преобразуем в награду: 0 токсичности = +5, 10 токсичности = -5
        return 5.0 - toxicity_value  
    except Exception as e:
        print(f"Toxicity check failed: {e}")
        return 0.0  # Нейтральный скор при ошибке

Важный нюанс 2026 года: вызовы invoke_model внутри Lambda теперь не требуют увеличения timeout до 15 минут, если вы используете Nova-Lite или аналогичные быстрые модели. Для Nova 2 используйте асинхронный вызов, чтобы не упираться в лимит выполнения Lambda в 15 минут.

3 Интеграция с Bedrock RFT: настраиваем конвейер обучения

Код функции готов. Теперь нужно подключить её к реальному процессу обучения в Bedrock. С ноября 2025 года это делается через консоль или CDK/CloudFormation.

Шаги интеграции:

  1. Создайте Lambda-функцию с кодом выше. Обязательно назначьте роль с правами на вызов bedrock-runtime (если используете внутренние вызовы моделей).
  2. В консоли Bedrock перейдите в "Custom Models" → "Create Training Job". Выберите тип "Reinforcement Fine-Tuning (RFT)".
  3. На шаге "Reward configuration" выберите "AWS Lambda function" и укажите ARN вашей функции.
  4. Настройте гиперпараметры обучения. Критически важный параметр — reward_scale. Начните с 0.1, чтобы не сломать политику модели резкими скачками градиента.
# Пример запуска через AWS CLI (актуально для версии CLI 2026)
aws bedrock create-model-customization-job \
  --job-name nova2-custom-reward-01 \
  --custom-model-name my-financial-nova \
  --role-arn arn:aws:iam::123456789012:role/BedrockCustomizationRole \
  --base-model-id amazon.nova2-v1:0 \
  --customization-type RFT \
  --training-data-config '{"s3Uri": "s3://my-bucket/rft-data.jsonl"}' \
  --reward-config '{"lambdaFunctionArn": "arn:aws:lambda:us-east-1:123456789012:function:nova-reward-v1"}' \
  --hyperparameters '{"rewardScale": 0.1, "klCoef": 0.05, "entropyCoef": 0.001}'

Процесс обучения займет от нескольких часов до суток в зависимости от объема данных. Стоимость — около $15-20 за час использования инстансов P4de в 2026 году. Полный гайд по кастомизации Nova описывает, как комбинировать SFT и RFT для устойчивых результатов.

Ошибки, которые сломают вашу модель: перекос вознаграждения и как его избежать

Я видел десятки сломанных кастомизаций из-за плохих reward functions. Вот типичные сценарии:

Ошибка Симптом Решение
Положительная обратная связь Модель генерирует одно слово или символ бесконечно, скор растёт Добавить penalty за повторение n-грамм. Ввести decay награды за длину.
Несбалансированные веса Модель идеально следует инструкциям, но начинает врать Использовать методику New Relic: факт-чекинг с весом в 3 раза выше, чем за стиль.
Локальный максимум Качество перестаёт расти после 1000 шагов Ввести стохастичность в функцию (небольшой случайный шум ±0.1). Или использовать curriculum learning — менять веса критериев в процессе обучения.

Правило отладки: всегда запускайте RFT сначала на маленьком датасете (100 примеров) с увеличенной частотой валидации. Смотрите не только на средний reward, но и на распределение. Если 95% ответов получают скор 9.8-10.0 — ваша функция слишком щедрая. Идеальное распределение — нормальное с центром около 5.0.

Самый опасный антипаттерн — создание функции, которую модель может «обмануть» без реального улучшения. Пример: награждать за наличие ключевых фраз. Nova быстро научится вставлять «Исходя из вышеизложенного, можно заключить, что...» в каждый ответ, не неся смысловой нагрузки.

Дальнейшие шаги: от простых эвристик к сложным AI-судиям

Ваша первая Lambda-функция с набором if-else работает. Но настоящая магия начинается, когда вы превращаете её в многослойную систему оценки.

  • Мультимодальные вознаграждения. Nova Act и другие агенты работают с изображениями. Ваша функция может анализировать скриншоты интерфейса, которые генерирует модель, используя Nova Multimodal Embeddings для оценки соответствия.
  • Динамическая загрузка правил. Храните конфигурацию функции в S3 или DynamoDB. Меняйте веса критериев без деплоя нового кода.
  • А/B тестирование reward functions. Запустите две разные Lambda-функции на параллельных RFT jobs. Сравните, какая даст лучшую модель по человеческой оценке.

В 2026 году тренд — автоматическая оптимизация самих функций вознаграждения с помощью мета-обучения. Но это уже тема для отдельной статьи.

Последний совет: никогда не делайте функцию, которая возвращает только 0 или 1 (бинарная классификация «хорошо/плохо»). Nova нужен градиент, плавное изменение. Ваши оценки должны быть непрерывными. Иначе обучение превратится в лотерею.

Теперь у вас есть рабочий инструмент. Осталось самое сложное — решить, что для вашей задачи действительно значит «хороший ответ». Машина ждёт ваших указаний.

Подписаться на канал