

# Implementação de funções de recompensa
<a name="nova-implementing-reward-functions"></a>

## Visão geral
<a name="nova-reward-overview"></a>

A função de recompensa (também chamada de pontuador ou avaliador) é o componente principal que avalia as respostas do modelo e fornece sinais de feedback para o treinamento. Ela deve ser implementada como uma função do Lambda que aceita respostas do modelo e retorna pontuações de recompensa.

## Formato da interface
<a name="nova-reward-interface"></a>

A sua função de recompensa deve aceitar e retornar dados no seguinte formato:

**Exemplo de entrada para treinamento**

```
{  
    "messages": [  
        {  
            "role": "user",  
            "content": "Do you have a dedicated security team?"  
        }  
    ],              
   "reference_answer": {  
       "compliant": "No",  
       "explanation": "As an AI developed by Company, I do not have a traditional security team..."  
    }  
}
```

**Exemplo de carga útil para a recompensa do Lambda**

O contêiner transforma automaticamente seus dados antes de enviá-los para sua função do Lambda ao:

1. Gerar uma resposta de modelo para cada prompt

1. Anexar o turno do assistente (resposta gerada) à matriz de mensagens

1. Adicionar um campo de `id` único de rastreamento

Sua função do Lambda receberá dados neste formato transformado:

```
{    
   "id": "123",  
    "messages": [  
        {  
            "role": "user",  
            "content": "Do you have a dedicated security team?"  
        },  
        {  
            "role": "assistant",  
            "content": "As an AI developed by Amazon, I don not have a dedicated security team..."  
        }  
    ],              
    # Following section will be same as your training dataset sample  
    "reference_answer": {  
        "compliant": "No",  
        "explanation": "As an AI developed by Company, I do not have a traditional security team..."  
    }  
}
```

**Contrato de recompensa do Lambda**

```
def lambda_handler(event, context):  
   return lambda_grader(event)  
  
def lambda_grader(samples: list[dict]) -> list[dict]:  
    """  
    Args:  
        samples: List of dictionaries in OpenAI format  
          
        Example input:  
        {     
            "id": "123",  
            "messages": [  
                {  
                    "role": "user",  
                    "content": "Do you have a dedicated security team?"  
                },  
                {  
                    "role": "assistant",  
                    "content": "As an AI developed by Company, I don nott have a dedicated security team..."  
                }  
            ],              
            # This section will be same as your training dataset  
            "reference_answer": {  
                "compliant": "No",  
                "explanation": "As an AI developed by Company, I do not have a traditional security team..."  
            }  
        }  
      
    Returns:  
        List of dictionaries with reward scores:  
        {  
            "id": str,                              # Same id as input sample  
            "aggregate_reward_score": float,        # Overall score for the sample  
            "metrics_list": [                       # OPTIONAL: Component scores  
                {  
                    "name": str,                    # Name of the component score  
                    "value": float,                 # Value of the component score  
                    "type": str                     # "Reward" or "Metric"  
                }  
            ]  
        }  
    """
```

## Campos de entrada e saída
<a name="nova-reward-fields"></a>

### Campos de entrada
<a name="nova-reward-input-fields"></a>


| Campo | Descrição | Notas adicionais | 
| --- | --- | --- | 
| id | Identificador único do exemplo | Ecoou de volta na saída. Formato de string | 
| mensagens | Histórico sequencial do chat no formato OpenAI | Matriz de objetos de mensagem | 
| messages[].role | Quem fala a mensagem | Valores comuns: “user”, “assistant”, “system” | 
| messages[].content | O conteúdo do texto da mensagem | String simples | 
| \*\*metadados | Informações de formato livre para auxiliar na classificação | Objeto; campos opcionais dos dados de treinamento | 

### Campos de saída
<a name="nova-reward-output-fields"></a>


| Campo | Descrição | Notas adicionais | 
| --- | --- | --- | 
| id | Mesmo identificador do exemplo de entrada | Deve corresponder à entrada | 
| aggregate\_reward\_score | Pontuação geral do exemplo | Flutuante (por exemplo, 0,0 a 1,0, ou intervalo definido pela tarefa) | 
| metrics\_list | Pontuações de componentes que compõem o agregado | Matriz de objetos de métricas | 

## Restrições técnicas
<a name="nova-reward-constraints"></a>
+ **Limite de tempo**: 15 minutos de tempo máximo de execução por invocação do Lambda
+ **Simultaneidade**: deve lidar com solicitações `rollout_worker_replicas * 64` simultâneas
+ **Confiabilidade**: deve implementar o tratamento adequado de erros e retornar pontuações válidas de forma consistente
+ **Desempenho**: otimiza para uma execução rápida (segundos, não minutos) para permitir um treinamento eficiente

**Práticas recomendadas**
+ Minimize as chamadas de API externas
+ Use algoritmos e estruturas de dados eficientes
+ Implementar a lógica de novas tentativas para falhas transitórias
+ Armazenar em cache cálculos reutilizáveis
+ Testar minuciosamente antes do treinamento para garantir uma execução sem erros

## Uso de funções de recompensa personalizadas
<a name="nova-reward-using-custom"></a>

Implemente funções de recompensa personalizadas quando você tiver critérios de avaliação específicos da tarefa:
+ **Definir critérios de avaliação**: determine o que constitui uma boa resposta para sua tarefa
+ **Implementar a função do Lambda**: crie uma função do Lambda seguindo o formato da interface
+ **Testar localmente**: valide se sua função retorna as pontuações corretas para as entradas do exemplo
+ **Implantar na AWS**: implante seu Lambda e anote o ARN
+ **Configurar fórmula**: adicione o ARN do Lambda ao campo `reward_lambda_arn` da sua fórmula
+ **Testar com um pequeno conjunto de dados**: execute o RFT com o mínimo de dados para verificar a integração

## permissões do IAM
<a name="nova-reward-iam"></a>

### Permissões obrigatórias
<a name="nova-reward-required-permissions"></a>

O seu perfil de execução do SageMaker precisa ter as permissões para invocar sua função do Lambda. Adicione esta política ao seu perfil de execução do SageMaker:

```
{  
  "Version": "2012-10-17",		 	 	   
  "Statement": [  
    {  
      "Effect": "Allow",  
      "Action": [  
        "lambda:InvokeFunction"  
      ],  
      "Resource": "arn:aws:lambda:region:account-id:function:function-name"  
    }  
  ]  
}
```

### Função de execução do Lambda
<a name="nova-reward-lambda-role"></a>

O perfil de execução de uma função do Lambda precisa de permissões básicas de execução do Lambda:

```
{  
  "Version": "2012-10-17",		 	 	   
  "Statement": [  
    {  
      "Effect": "Allow",  
      "Action": [  
        "logs:CreateLogGroup",  
        "logs:CreateLogStream",  
        "logs:PutLogEvents"  
      ],  
      "Resource": "arn:aws:logs:*:*:*"  
    }  
  ]  
}
```

Permissões adicionais: se a função do Lambda acessar outros serviços da AWS (por exemplo, S3 para dados de referência, DynamoDB para registro em log), adicione essas permissões ao perfil de execução do Lambda.

## Exemplo: função de recompensa para LLM como avaliador
<a name="nova-reward-llm-judge-example"></a>

Este exemplo demonstra o uso de modelos do Amazon Bedrock como avaliadores para analisar as respostas do modelo comparando-as com as respostas de referência. Esse modelo do Lambda fornece um framework para os clientes implementarem chamadas no Amazon Bedrock de solicitações de inferência para processar as análises dos avaliadores. A função do Lambda mantém o mesmo contrato de entrada/saída que outras funções de recompensa.

### Implementação
<a name="nova-reward-llm-judge-implementation"></a>

Essa função do Lambda implementa um processo de avaliação em duas etapas: o `lambda_handler` extrai as respostas do modelo e as respostas de referência das amostras recebidas e, em seguida, a função `lambda_graded` chama o Amazon Bedrock para pontuar a semelhança semântica entre elas. A implementação inclui tratamento robusto de erros com novas tentativas automáticas para falhas transitórias e é compatível com formatos flexíveis de resposta de referência (formatos de string e dicionário estruturado).

**Detalhes da implantação:**
+ **Repetir a lógica**: implementa o recuo exponencial (1s, 2s, 4s) para controlar a utilização de exceções para lidar com os limites de taxa da API do Bedrock
+ **Tratamento de erros**: retorna a pontuação de 0,0 para avaliações com falha, em vez de gerar exceções
+ **Pontuação determinística**: usa temperature = 0,0 para garantir pontuações consistentes em todas as avaliações
+ **Formato de referência flexível**: processa automaticamente as respostas de referência de texto e dicionário
+ **Limitadores de pontuação**: garante que todas as pontuações estejam no intervalo válido [0,0, 1,0]
+ **Modelo agnóstico**: altera JUDGE\_MODEL\_ID para usar qualquer modelo do Amazon Bedrock (Nova, Llama, Mistral etc.)

```
"""  
LLM Judge Lambda POC - Working implementation using Amazon Bedrock  
"""  
  
import json  
import time  
import boto3  
  
bedrock_runtime = boto3.client('bedrock-runtime', region_name='us-east-1')  
JUDGE_MODEL_ID = "anthropic.claude-3-5-sonnet-20240620-v1:0"  
SYSTEM_PROMPT = "You must output ONLY a number between 0.0 and 1.0. No explanations, no text, just the number."  
  
JUDGE_PROMPT_TEMPLATE = """Compare the following two responses and rate how similar they are on a scale of 0.0 to 1.0, where:  
- 1.0 means the responses are semantically equivalent (same meaning, even if worded differently)  
- 0.5 means the responses are partially similar  
- 0.0 means the responses are completely different or contradictory  
  
Response A: {response_a}  
  
Response B: {response_b}  
  
Output ONLY a number between 0.0 and 1.0. No explanations."""  
  
  
def lambda_graded(response_a: str, response_b: str, max_retries: int = 3) -> float:  
    """Call Bedrock to compare responses and return similarity score."""  
    prompt = JUDGE_PROMPT_TEMPLATE.format(response_a=response_a, response_b=response_b)  
      
    for attempt in range(max_retries):  
        try:  
            response = bedrock_runtime.converse(  
                modelId=JUDGE_MODEL_ID,  
                messages=[{"role": "user", "content": [{"text": prompt}]}],  
                system=[{"text": SYSTEM_PROMPT}],  
                inferenceConfig={"temperature": 0.0, "maxTokens": 10}  
            )  
            print(f"Bedrock call successful: {response}")  
            output = response['output']['message']['content'][0]['text'].strip()  
            score = float(output)  
            print(f"Score parsed: {score}")  
            return max(0.0, min(1.0, score))  
                  
        except Exception as e:  
            if "ThrottlingException" in str(e) and attempt < max_retries - 1:  
                time.sleep(2 ** attempt)  
            else:  
                print(f"Bedrock call failed: {e}")  
                return None  
    return None  
  
  
def lambda_handler(event, context):  
    """AWS Lambda handler - processes samples from RFTEvalInvoker."""  
    try:  
        samples = event if isinstance(event, list) else [event]  
        results = []  
          
        for sample in samples:  
            sample_id = sample.get("id", "unknown")  
            messages = sample.get("messages", [])  
              
            # Extract assistant response (response A)  
            response_a = ""  
            for msg in messages:  
                if msg.get("role") in ["assistant", "nova_assistant"]:  
                    response_a = msg.get("content", "")  
                    break  
              
            # Extract reference answer from root level (no longer in metadata)  
            reference_answer = sample.get("reference_answer", "")  
              
            # Handle both string and dict reference_answer formats  
            if isinstance(reference_answer, dict):  
                # If reference_answer is a dict, extract the explanation or compliant field  
                response_b = reference_answer.get("explanation", reference_answer.get("compliant", ""))  
            else:  
                response_b = reference_answer  
              
            if not response_a or not response_b:  
                results.append({  
                    "id": sample_id,  
                    "aggregate_reward_score": 0.0,  
                    "metrics_list": [{"name": "similarity_score", "value": 0.0, "type": "Metric"}]  
                })  
                continue  
              
            # Get similarity score  
            score = lambda_graded(response_a, response_b)  
              
            results.append({  
                "id": sample_id,  
                "aggregate_reward_score": score,  
                "metrics_list": [  
                    {  
                        "name": "similarity_score",  
                        "value": score,  
                        "type": "Metric"  
                    }  
                ]  
            })  
          
        return {"statusCode": 200, "body": json.dumps(results)}  
          
    except Exception as e:  
        print(f"Error: {e}")  
        return {"statusCode": 500, "body": json.dumps({"error": str(e)})}
```

### Formato de entrada
<a name="nova-reward-llm-judge-input"></a>

O Lambda recebe o mesmo formato de entrada que outras funções de recompensa:

```
{  
    "id": "sample-001",  
    "messages": [  
        {  
            "role": "user",  
            "content": "Do you have a dedicated security team?"  
        },  
        {  
            "role": "assistant",  
            "content": "As an AI developed by Amazon, I don't have a dedicated security team..."  
        }  
    ],  
    "reference_answer": {  
        "compliant": "No",  
        "explanation": "As an AI developed by Company, I do not have a traditional security team..."  
    },  
    "my_custom_field": "custom_value"  
}
```

### Formato de saída
<a name="nova-reward-llm-judge-output"></a>

```
{  
    "id": "sample-001",  
    "aggregate_reward_score": 0.85,  
    "metrics_list": [  
        {  
            "name": "similarity_score",  
            "value": 0.85,  
            "type": "Metric"  
        }  
    ]  
}
```

### Considerações de implantação
<a name="nova-reward-llm-judge-deployment"></a>

Talvez você também precise ajustar o modelo do prompt e os parâmetros de inferência com base nos recursos do modelo escolhido e no formato da API.
+ **Permissões do IAM**: o perfil de execução do Lambda deve ter a permissão `bedrock:InvokeModel` para o modelo escolhido
+ **Tempo limite**: defina o tempo limite do Lambda para pelo menos 60 segundos para acomodar a latência e as novas tentativas da API do Bedrock
+ **Região**: implante em uma região onde o modelo escolhido do Bedrock esteja disponível
+ **Custo**: monitore o uso da API do Bedrock à medida que cada avaliação faz uma chamada de API por exemplo
+ **Throughput**: para avaliações em grande escala, solicite maiores cotas do Bedrock para evitar o controle de utilização

**Aumento do throughput do Bedrock**

Se você observar controle de utilização durante a avaliação, aumente suas cotas do modelo Bedrock:
+ Navegue até o console do AWS Service Quotas
+ Pesquise por “Bedrock” e selecione sua região
+ Encontre a cota para o modelo escolhido (por exemplo, “Invocações por minuto para o Claude 3.5 Sonnet”)
+ Clique em “Solicitar aumento de cota” e especifique o throughput desejado
+ Forneça uma justificativa para o aumento (por exemplo, “workload de avaliação de RFT”)

A lógica de repetição integrada do Lambda lida com o controle de utilização ocasional, mas as avaliações sustentadas de alto volume exigem aumentos de cota apropriados.

**Política do IAM necessária:**

```
{  
    "Version": "2012-10-17",		 	 	   
    "Statement": [  
        {  
            "Effect": "Allow",  
            "Action": [  
                "bedrock:InvokeModel"  
            ],  
            "Resource": "arn:aws:bedrock:*::foundation-model/*"  
        }  
    ]  
}
```