Funciones de recompensa personalizadas para el aprendizaje reforzado en varios turnos con Amazon Nova Forge

En el aprendizaje por refuerzo (RL) de múltiples turnos, su función de recompensa personalizada decide qué aprende realmente el modelo. Una recompensa sutilmente incorrecta puede enseñar silenciosamente algo incorrecto mientras cada curva de entrenamiento parece saludable. Diseñar una recompensa que se mantenga en tareas de agente de varios turnos es una de las partes más difíciles de personalizar los modelos de Amazon Nova. Para el entrenamiento de varios turnos, Amazon Nova Forge ejecuta su lógica de recompensa en su propio entorno a través de su capacidad Bring Your Own Orchestration (BYOO). Puede concentrarse en definir cómo se ve un buen resultado mientras Nova Forge coordina las implementaciones, la transmisión de mensajes y el estado de la conversación en los turnos. Nova Forge también ofrece una opción de RL de múltiples turnos sin servidor, ahora disponible de forma generalizada, para equipos que prefieren no administrar ese entorno. Esta publicación utiliza la ruta BYOO.

Amazon Nova ofrece múltiples enfoques de personalización, destacando el ajuste fino de refuerzo (RFT) porque puede enseñar a los modelos los comportamientos que desea a través de comentarios iterativos. RFT adopta un enfoque diferente al de ajuste fino supervisado (SFT). En lugar de requerir ejemplos seleccionados con rutas de razonamiento anotadas, aprende de las señales de evaluación de los propios resultados del modelo. RFT de múltiples turnos extiende esto a agentes que actúan en una secuencia de pasos, como llamar a herramientas, ejecutar código o recuperarse de un error. Optimiza la recompensa acumulativa a lo largo de toda la trayectoria en lugar de calificar una única respuesta. En el corazón de RFT se encuentra la función de recompensa: el mecanismo de puntuación que guía el modelo y la pieza que diseña.

Figura 1: Rendimiento fuera de distribución (OOD) después del entrenamiento posterior de cálculo igual desde un punto de control compartido. RL mejora la generalización de OOD en todas las variantes de tareas, mientras que SFT se degrada. Adaptado de Chu et al., 2025

Esta publicación se centra en la función de recompensa en sí: cómo diseñar una recompensa compuesta de múltiples turnos de la que la Optimización de políticas relativas al grupo (GRPO) pueda aprender. Esta publicación también muestra cómo ejecutar código generado por el modelo de forma segura dentro de la recompensa y por qué instrumentar cada componente para que pueda confiar en lo que se aprende en la capacitación. La parte 1 de esta serie cubre la infraestructura de Amazon SageMaker HyperPod y Nova Forge. También cubre la configuración de entrenamiento que ejecuta estas recompensas. Cerramos con los obstáculos que pueden colapsar silenciosamente una recompensa, extraídas de una ejecución real en la que el componente con mayor ponderación silenciosamente no aportó ninguna señal de aprendizaje. Te mostramos cómo atraparlos. El código completo es ilustrativo. Úselo como punto de partida para su propia implementación de recompensas.

Requisitos previos

Para seguirlo, necesita lo siguiente:

Una suscripción a Amazon Nova Forge, que proporciona el SDK de personalización de Nova y las API RFT de múltiples turnos. La infraestructura RFT de múltiples turnos de la Parte 1 de esta serie: un clúster HyperPod de Amazon SageMaker, un entorno administrado por el cliente en Amazon Elastic Container Service (Amazon ECS). Un depósito de Amazon Simple Storage Service (Amazon S3) para datos de implementación y puntos de control. El código de ejemplo para esta publicación, incluido el entorno de recompensas y un tutorial, del repositorio aws-samples/sample-nova-multi-turn-rl-infra. El entorno de recompensa personalizado es opcional: en cdk.json, establezca use_custom_env en "true" y custom_env_id en el ID de su entorno (por ejemplo, "my-custom-env") antes de implementar. De forma predeterminada, la pila utiliza el entorno Wordle integrado. Familiaridad con el ajuste de refuerzo y GRPO.

Creación de recompensas personalizadas con Amazon Nova Forge

RFT funciona muestreando las terminaciones del modelo actual y calificándolas con una función de recompensa. En Nova Forge, la función de recompensa es un calificador que usted escribe en código y no un modelo de recompensa entrenado por separado. Puede ser una verificación basada en reglas que verifica el resultado (aprendizaje por refuerzo con recompensas verificables), o puede llamar a otro modelo de lenguaje grande (LLM) para juzgar la respuesta, un enfoque conocido como LLM-as-Judge.

Luego, RFT ajusta las ponderaciones del modelo para hacer que sea más probable que se completen con recompensas más altas. Nova Forge utiliza GRPO. Para cada conversación, GRPO utiliza la función de recompensa para clasificar las implementaciones del modelo K. GRPO utiliza las finalizaciones de modelos mejor clasificadas para actualizar el modelo de acuerdo con la recompensa normalizada (la ventaja) del lote. RFT con GRPO es una técnica fundamental que logra mejoras notables en el rendimiento con respecto a la SFT inicial.

Una señal de recompensa influye en el aprendizaje sólo a través de la variación que crea dentro de un grupo. Si un término toma el mismo valor para cada finalización de un grupo, no contribuye en nada a la ventaja. Por tanto, no contribuye en nada al gradiente.

La forma en que se ejecuta su función de recompensa con Nova Forge depende de la tarea. Con RFT de un solo giro, usted registra la recompensa como una función AWS Lambda y le apunta su receta a través dereward_lambda_arn. Las tareas de varios turnos como la de esta publicación superan lo que admite una única invocación Lambda. Las conversaciones de varios turnos y las puntuaciones de larga duración superan el límite de invocación de Lambda de 15 minutos. Para estos, Nova Forge utiliza BYOO. Usted configura rollout.delegate: true y ejecuta su entorno y la lógica de recompensa en un contenedor de entorno, por ejemplo en Amazon ECS. Nova Forge delega cada implementación a su entorno. Luego recopila los episodios completos para su entrenamiento. Su contenedor gestiona el estado de conversación e interacción de múltiples turnos: ejecuta el simulador de usuario, ejecuta código y llama a un verificador. Luego devuelve una recompensa agregada por muestra (aggregate_reward_score), más una lista opcional de puntuaciones por componente (metrics_list). La parte 1 de esta serie cubre esta infraestructura y su implementación del kit de desarrollo de nube de AWS (AWS CDK). Esta publicación se centra en la recompensa.

Cómo funciona la evaluación de recompensas

El trabajo de capacitación genera implementaciones de candidatos a partir del modelo Nova para cada mensaje. En una tarea de varios turnos, un lanzamiento es un episodio completo con una secuencia de turnos (una trayectoria), no una única respuesta. Su función de recompensa recibe cada implementación y realiza tres pasos:

Ejecuta la lógica de la tarea. Para una tarea conversacional, esto puede incluir un simulador de usuario que responda al modelo paso a paso. Califica la trayectoria completa en uno o más componentes de recompensa (por ejemplo, corrección de la tarea, una señal de comportamiento intermedio y penalizaciones), informando cada uno a través de metrics_list. Devuelve una recompensa agregada por implementación (aggregate_reward_score), que el entrenamiento convierte en ventajas dentro del grupo.

Diagrama de una implementación de varios turnos: Nova Forge delega en el contenedor del entorno, que devuelve una puntuación de recompensa para GRPO

Figura 2: Una implementación única de varios turnos: Nova Forge delega en el contenedor de su entorno, que solicita al simulador o ejecuta el código comprometido y luego devuelve una puntuación de recompensa para GRPO.

Este ciclo se repite a lo largo de muchos pasos de entrenamiento, dando forma progresivamente al modelo para maximizar la recompensa acumulativa a lo largo de toda la secuencia. El modelo se optimiza hacia lo que realmente recompensa su recompensa, que, como mostramos, no siempre es lo que cree que escribió.

Elegir la estructura de una recompensa de varios turnos

Las recompensas escalares únicas son fáciles de jugar y una recompensa terminal única suele ser demasiado escasa para aprender de ella en tareas de varios turnos. Por lo tanto, la mayoría de las recompensas de producción de múltiples turnos combinan tres tipos de señales: recompensas de resultados, recompensas de comportamiento y penalizaciones.

Las recompensas (resultados) a nivel de episodio capturan si el artefacto final cumplió con el objetivo. Por ejemplo, ¿pasaron las pruebas unitarias o se completó el flujo de trabajo? Se dirigen a lo que en última instancia te importa, pero tienden a ser escasos y casi nulos al principio del entrenamiento.

Las recompensas de nivel de turno (de comportamiento) capturan si el modelo exhibió el comportamiento intermedio que usted desea, como preguntar antes de actuar, llamar a la herramienta correcta o evitar bucles. Son mejores para moldear el comportamiento; la recompensa del resultado es demasiado escasa para enseñar, aunque pueden obtenerse sin un progreso real si no se diseñan cuidadosamente. Las sanciones desalientan explícitamente un modo de falla como adivinar, repetir o detenerse. Separan las estrategias buenas y malas para que el optimizador vea un gradiente.

Combínelos para que el modelo aprenda tanto el comportamiento como el resultado, sin que un componente enmascare o mate al otro. El resto de esta publicación lo concreta. Diseñamos una recompensa de cuatro componentes para una tarea real y ejecutamos código generado por el modelo de forma segura dentro de ella. Luego analizamos los obstáculos que pueden hacer fracasar dicha recompensa y cómo solucionarlos.

Ejemplo resuelto: enseñar a Amazon Nova Lite 2.0 a preguntar antes de codificar

Creamos una tarea de codificación colaborativa de múltiples turnos con más de 500 tareas de programación únicas. Entrenamos a Amazon Nova Lite 2.0 con RFT de múltiples turnos, usando GRPO con Adaptación de rango bajo (LoRA), en Amazon SageMaker HyperPod, implementando la recompensa dentro de un contenedor de entorno administrado por el cliente (la ruta BYOO de Nova Forge).

La mecánica es la siguiente:

El modelo ve una solicitud de codificación breve y poco especificada. Un simulador de usuario mantiene la especificación completa de forma privada y revela un detalle sólo cuando el modelo lo solicita. En cada turno, el modelo hace una pregunta aclaratoria o confirma el código. Si pregunta, el simulador responde y la conversación continúa. Si confirma el código, la implementación finaliza y su administrador de recompensas ejecuta ese código contra pruebas unitarias ocultas para calificar la corrección. (La ejecución segura del código generado por el modelo es una preocupación a la que volveremos más adelante).

La intención del diseño es que adivinar produzca un código incorrecto, mientras que preguntar muestre los detalles ocultos y conduzca al código correcto. La tarea debe obligar a “preguntar primero”.

Diseñando la recompensa

Hacer que la conducta objetivo sea directa e independientemente recompensable y penalizar explícitamente el modo de falla. Para esta tarea, la recompensa es una suma ponderada de cuatro componentes:

Componente Peso Definición corrección 1,0 fracción de pruebas unitarias ocultas que pasan el código final questioned_before_coding 0,6 1,0 si se solicita en el turno 1 y luego se confirma; 0,6 si se solicita más tarde y luego se comete; else 0 (sin puerta) adivinado_inmediatamente Penalización de 0,4: -1,0 si el primer turno es código sin preguntas loop_penalty 0,2 -0,5 si los dos últimos turnos son más del 80% similares

Dos principios impulsan el diseño. Primero, desactive el comportamiento que desea: Asked_before_coding se acredita por sí solo, no está condicionado a la corrección, pero requiere que el modelo finalmente confirme el código, lo que cierra la laguna jurídica de "preguntar siempre, nunca responder". En segundo lugar, penalice el modo de error: adivinado_immediatamente hace que adivinar sea estrictamente peor que preguntar, lo que restaura la variación entre estrategias dentro de un grupo GRPO, la variación que el algoritmo necesita para producir un gradiente.

Llame a estos puntuadores de componentes dentro del controlador de recompensas en el contenedor del entorno e informe cada valor a través de metrics_list:

def questioned_before_coding(completado, respuesta, **kw) -> float: msgs = _messages(completion, kw) first_q = _first_question_turn(msgs, parser) final = _final_code(completion, parser) comprometido = bool(final) y no _is_question(final) si first_q == 1 y comprometido: devuelve 1.0 # preguntado primero, luego comprometido (ideal) si first_q no es Ninguno y está comprometido: devuelve 0.6 # se pregunta más tarde, luego se confirma devuelve 0.0 # nunca se pregunta, o se pregunta pero nunca se confirma def adivinado_immediatamente(completado, respuesta, **kw) -> float: for m in _assistant_turns(completado, kw): código = _code_of(parser.parse(m["content"])) devuelve -1.0 if (código y no _is_question(código)) más 0.0 devuelve 0.0

Ejecutar código generado por modelo de forma segura

El componente de corrección ejecuta código generado por el modelo contra pruebas unitarias. La salida del modelo bajo RL se optimiza mediante la exploración, así que trátela como no validada. El contenedor se ejecuta en su propio entorno de ejecución aislado, pero aun así debes tomar precauciones. No exponga las credenciales o la red al código generado. Aplique límites de recursos y ejecútelo en un directorio temporal. Utilice un centinela aleatorio por ejecución para que el modelo no pueda falsificar el resultado escribiendo el marcador esperado en stderr. Para ejecuciones que requieren aislamiento adicional, llame a un entorno limitado de pruebas dedicado. Este arnés muestra el patrón:

importar recursos, secretos, subprocesos, sys, archivo temporal desde pathlib importar ruta def run_tests(código: str, prueba: str, timeout_s: int = 30) -> float: nonce = secrets.token_hex(8) # arnés de marcador por ejecución inolvidable = ( "import sys, unittest, jsonn" f"{code}nn{test}nn" 'if __name__ == "__main__":n' " r = unittest.TextTestRunner(stream=sys.stderr, verbosity=0).run(n" " unittest.TestLoader().loadTestsFromModule(sys.modules[__name__]))n" f" sys.stderr.write('__{nonce}__' + json.dumps(" "{'total': r.testsRun, 'aprobado': r.testsRun – len(r.failures) – len(r.errors)}) + '__" f"{nonce}__')n" ) def _limit(): recurso.setrlimit(recurso.RLIMIT_CPU, (timeout_s, timeout_s)) recurso.setrlimit(resource.RLIMIT_AS, (2 * 1024**3, 2 * 1024**3)) # 2 GB recurso.setrlimit(resource.RLIMIT_NPROC, (64, 64)) con tempfile.TemporaryDirectory() como cwd: ruta = Ruta(cwd) / "h.py" ruta.write_text(harness) intente: proc = subprocess.run( [sys.executable, str(ruta)], capture_output=True, text=True, timeout=timeout_s, cwd=cwd, env={"PATH": "/usr/bin"}, # sin creds, sin entorno de red preexec_fn=_limit, ) excepto Excepción: devolver 0.0 # analiza el resumen no delimitado y valida el recuento de pruebas antes de puntuar…

También valide la cantidad de pruebas realmente ejecutadas con respecto a la cantidad esperada, de modo que el modelo no pueda diluir la puntuación con sus propias pruebas que pasan trivialmente. Para funciones de recompensa implementadas en entornos reales, implemente estas medidas de seguridad en lugar de tratarlas como opcionales.

Errores: qué hace que una recompensa colapse y cómo solucionarlo

El diseño de recompensa de múltiples turnos tiene un conjunto bien conocido de modos de falla. El hacking de recompensas es donde el modelo juega con un proxy en lugar de lograr el objetivo. La inestabilidad del entrenamiento es donde las actualizaciones divergen y la entropía colapsa o el término Kullback-Leibler (KL) explota. El colapso de la recompensa es donde la señal degenera hasta que la variación dentro del grupo desaparece y el aprendizaje se detiene silenciosamente. Los dos primeros suelen anunciarse en transcripciones o en curvas de pérdida y KL. El colapso es el peligroso: las curvas de recompensa agregada, pérdida y longitud de finalización pueden parecer saludables, mientras que un componente con el que se cuenta no aporta nada. Esta sección cubre las dos fallas de colapso que nos costaron más tiempo en esta tarea y cómo detectarlas.

Cuando una recompensa se reduce a una sola estrategia

Una versión anterior de esta recompensa otorgaba el bono de solicitud detrás de la corrección. Obtuviste la recompensa solicitada solo si el código final también fue aprobado. También agregó un término de eficiencia que recompensaba las conversaciones más breves. El entrenamiento colapsó. El modelo convergió para adivinar en el turno uno. La recompensa media se congeló y la ventaja del GRPO se redujo a cero.

Dos errores de diseño lo provocaron. Primero, la puerta se encontraba detrás de una condición inalcanzable. La corrección era casi nula en estas tareas difíciles, por lo que el bono de solicitud casi nunca se activaba. El comportamiento que queríamos recompensar era invisible para el optimizador. En segundo lugar, el término de eficiencia tenía un óptimo degenerado. Menos turnos lo maximizaron, por lo que la política colapsó en un solo giro sin compromiso. Cada finalización parecía igual, la variación dentro del grupo desapareció y el aprendizaje se detuvo.

La solución es el diseño de la sección anterior: desactivar el comportamiento que desea y penalizar el modo de falla explícitamente. Con ambos implementados, las distintas estrategias siguen produciendo distintas recompensas dentro de un grupo, lo que preserva la variación que GRPO necesita aprender.

Componente silenciosamente muerto

Cuando un componente de recompensa devuelve el mismo valor por cada finalización en un grupo GRPO, su variación dentro del grupo es cero. Por lo tanto, no contribuye en nada a la ventaja ni a la pendiente, ni siquiera con el peso más alto. Los componentes que aún varían hacen que la recompensa agregada, la pérdida de la póliza, la ventaja y la duración de la finalización parezcan saludables, por lo que las curvas nunca lo revelan. Una causa común en las recompensas de código es un marcador de corrección que devuelve 0 en cada implementación porque el arnés nunca ejecuta la salida del modelo. Esto puede ocurrir debido a nombres de puntos de entrada que no coinciden, importaciones fallidas o un error de configuración que hace que todas las pruebas fallen antes de que se ejecuten sus aserciones. En nuestro análisis, esto es exactamente lo que sucedió: la tasa de preguntas aclaratorias del modelo aumentó de aproximadamente 34 a 96 por ciento. La corrección del código apenas se movió, porque el evaluador de corrección devolvía el mismo valor en cada implementación.

Para detectar un componente muerto, realice un seguimiento de la desviación estándar dentro del grupo de cada componente, no de la curva de recompensa agregada. Las curvas agregadas ocultan un canal muerto detrás de las curvas vivas. Si esa dispersión se sitúa en o cerca de cero, el componente no está entrenando, sea cual sea su peso. La causa principal habitual en las recompensas de código es un marcador de corrección atascado en 0 porque el arnés nunca se vincula ni ejecuta la salida del modelo. Solucione eso y confirme que el diferencial sea distinto de cero.

Instrumento para que los detectes temprano

Varios hábitos detectan estos fallos, y habrían detectado los nuestros desde el primer día:

Instrumente la contribución por componente a la ventaja, no solo la recompensa por componente. Informe cada componente a través de metrics_list y realice un seguimiento de su media y su desviación estándar dentro del grupo. Cualquier componente con una variación dentro del grupo cercana a cero no contribuye en nada al aprendizaje, independientemente de su peso. Se podría descartar una media de recompensa fija de 0,000 porque “estas tareas son simplemente difíciles”, pero una variación plana dentro del grupo no es ambigua. Automatice esto como un panel de variación de ventajas por componente para que los canales inactivos se marquen automáticamente, sin inspección manual. Lea las transcripciones ordenadas por el componente que está probando, no por la recompensa total. La clasificación por recompensa total oculta un componente muerto detrás de los vivos. La clasificación por componente sospechoso revela el problema inmediatamente. Elimine o reviva cada componente que usted afirma que está funcionando. Si eliminar un componente no cambia nada, no estaba funcionando. Si al revivir un componente se recupera una métrica que supuso que ya estaba optimizada, no estaba en el objetivo. Diseño para variación dentro del grupo. GRPO aprende de las diferencias entre la finalización del mismo mensaje. Las puertas inalcanzables, los óptimos de configuración degenerados y los términos saturados colapsan esa variación y dejan de aprender incluso cuando la recompensa parece buena. Deshaga el comportamiento objetivo y penalice el modo de falla para que las estrategias se separen. Esté atento a una densa recompensa que mata de hambre a otra. Una vez que nuestra densa recompensa solicitada se saturó, la escasa recompensa por la corrección no pudo impulsar la política. Si domina un término que configura el comportamiento, es posible que el término de resultado que le interesa nunca obtenga un gradiente. Considere reducir la ponderación de un término de configuración una vez que se sature, o aumentar la ponderación del término de resultado. Trate la salida del modelo como no validada. Sandbox cualquier ejecución de código generado (sin credenciales, sin red, límites de recursos) y haga que los verificadores sean inolvidables (centinelas aleatorios, validación de recuento de pruebas).

Limpiar

La ejecución de capacitación y el entorno de esta publicación utilizan recursos de SageMaker HyperPod y Amazon ECS que generan costos mientras se ejecutan. Cuando termine de experimentar, siga los pasos de desmontaje de la Parte 1 de esta serie para eliminar el clúster SageMaker HyperPod y el entorno de Amazon ECS, lo que detiene las cargas más grandes. Elimine los datos de implementación y los puntos de control de su depósito de Amazon S3 si ya no los necesita.

Conclusión

La función de recompensa es la parte de RFT que usted diseña y es donde residen los fallos sutiles. En sus ejecuciones, el modelo puede aprender el comportamiento para el que entrena, mientras que un término que le interesa no aporta nada al aprendizaje, sin que ninguna métrica agregada lo revele. Una mejor instrumentación, no un mejor algoritmo, solucionó el problema. Mida la contribución de cada componente a la ventaja, lea las transcripciones a través de la lente del componente que está probando y elimine lo que afirma que está funcionando. Con una función de recompensa personalizada en Amazon Nova Forge, usted tiene control total sobre la recompensa, lo que significa que la responsabilidad de hacerlo bien es suya. Para conocer la infraestructura y la implementación de AWS CDK que hacen que estas ejecuciones sean reproducibles, consulte la Parte 1 de esta serie.

Expresiones de gratitud

Un agradecimiento especial a Mahima Chaudhary por su revisión y contribuciones a esta publicación.

Sobre los autores

María Masod

María Masod

María se especializa en IA agente, ajuste de refuerzos y capacitación de agentes de múltiples turnos. Tiene experiencia en aprendizaje automático, que abarca la personalización de grandes modelos de lenguaje, el modelado de recompensas y la creación de canales de capacitación de un extremo a otro para agentes de IA. María, una entusiasta de la sostenibilidad de corazón, disfruta de la jardinería y de preparar café con leche.

Nick Biso

Nick Biso

Nick es ingeniero de aprendizaje automático en AWS Professional Services. Resuelve complejos desafíos organizativos y técnicos utilizando la ciencia y la ingeniería de datos. Además, crea e implementa modelos de IA/ML en la nube de AWS. Su pasión se extiende a su propensión a viajar y diversas experiencias culturales.

Laurent Mombaerts

Laurent Mombaerts

Laurent es científico aplicado sénior en el equipo de ciencia y economía de Ventas y marketing mundiales de AWS. Su investigación abarca la poscapacitación de LLM, los sistemas agentes multimodales y la búsqueda y recuperación de información, con énfasis en traducir los avances en inteligencia artificial en soluciones prácticas para los clientes de AWS. Tiene un doctorado en ingeniería de la Universidad de Luxemburgo y una investigación doctoral realizada en colaboración con la Universidad de Cambridge.