LLM + Salida Estructurada como Servicio de Traducción
Otros idiomas: English 日本語 한국어 Português 中文
En una publicación anterior dedicada a la localización de una aplicación con IA, discutimos la automatización de la traducción de cadenas de UI. Para esa tarea, elegí una herramienta de traducción en lugar de un LLM, basada en el siguiente razonamiento:
…Planeaba usar el GPT3.5 Turbo de OpenAI, pero dado que no es estrictamente un modelo de traducción, requiere esfuerzo extra para configurar el mensaje de entrada. Además, sus resultados tienden a ser menos estables, así que elegí un servicio de traducción dedicado que se me vino a la mente primero…
Más tarde ese año, varios proveedores de LLM introdujeron la función de salida estructurada, haciendo que los LLM sean significativamente más adecuados para el caso de uso discutido. Hoy, vamos a examinar más detalladamente el problema y cómo esta característica lo resuelve.

El problema
Los LLM son inherentemente no deterministas, lo que significa que podrían producir diferentes resultados para la misma entrada. Esta aleatoriedad hace que los LLM sean “creativos”, pero también reduce su fiabilidad cuando se requiere seguir un formato de salida específico.
Considera el caso de uso de la traducción como ejemplo. Una solución ingenua sería describir el formato requerido en el prompt:
> Translate 'cat' to Dutch. ONLY GIVE A TRANSLATION AND NOTHING ELSE.
Esto funciona, aunque de vez en cuando, un LLM interpretará mal las instrucciones:
> Certainly, allow me to provide the translation for you.
The word 'cat' is translated as 'kat' in Dutch. Sometimes, female cats may also be referred to as 'poes'.
Dado que la aplicación espera una sola palabra como respuesta, cualquier cosa que no sea una sola palabra podría llevar a errores o a datos corruptos. Incluso con comprobaciones adicionales y reintentos, tales inconsistencias comprometen la fiabilidad del flujo de trabajo.
¿Qué es la salida estructurada?
La salida estructurada aborda los problemas con el formato inconsistente permitiéndote definir un esquema para la respuesta. Por ejemplo, puedes solicitar una estructura como esta:
{
"word": "cat",
"target_locale": "nl",
"translation_1": "kat",
"translation_2": "poes"
}
Bajo el capó, la implementación de la salida estructurada puede variar entre modelos. Los enfoques comunes incluyen:
- usar una máquina de estado finito para rastrear las transiciones entre tokens y podar los caminos de generación que violen el esquema proporcionado
- modificación de las probabilidades de los tokens candidatos para disminuir la probabilidad de seleccionar opciones no conformes
- ajustar el modelo con un conjunto de datos centrado en el reconocimiento de esquemas JSON y otros formatos de datos estructurados
Estos mecanismos adicionales ayudan a lograr una mayor fiabilidad en comparación con simplemente añadir instrucciones de formato al mensaje de entrada.
Código
Actualicemos el script de traducción. Para empezar, definiremos el objeto que representa el esquema:
response_format={
"type": "json_schema",
"json_schema": {
"name": "translation_service",
"schema": {
"type": "object",
"required": [
"word",
"translation"
],
"properties": {
"word": {
"type": "string",
"description": "The word that needs to be translated."
},
"translation": {
"type": "string",
"description": "The translation of the word in the target language."
}
},
"additionalProperties": False
},
"strict": True
}
}
A continuación, podemos usar el objeto esquema en la función que realiza la solicitud:
def translate_property_llm(value, target_lang):
headers = {
'Content-Type': 'application/json',
'Authorization': f'Bearer {openai_key}',
}
url = 'https://api.openai.com/v1/chat/completions'
data = {
'model': 'gpt-4o-mini',
'response_format': response_format,
'messages': [
{'role': 'system',
'content': f'Translate "{value}" to {target_lang}. '
f'Only provide the translation without any additional text or explanation.'},
{'role': 'user', 'content': value}
],
}
response = requests.post(url, headers=headers, data=json.dumps(data))
try:
content_json = json.loads(response.json()["choices"][0]["message"]["content"])
return content_json["translation"]
except (json.JSONDecodeError, KeyError) as e:
raise ValueError(f"Failed to parse translation response: {str(e)}")
¡Eso es todo por la codificación! Para obtener más contexto, eres libre de explorar y experimentar con el proyecto en el repositorio de GitHub.
Comparación con el enfoque anterior
El proceso de traducción llevó un poco más de tiempo para la versión actualizada en comparación con la basada en servicio de traducción. Dicho esto, el código proporcionado es el ejemplo de trabajo más simple, y podríamos hacerlo mucho más rápido mediante el envío de las solicitudes de manera asincrónica.
En términos de gastos, traducir a 5 idiomas me costó menos de 0.01$, lo cual es más de 10 veces más barato que la versión anterior:

Al ojear los paquetes de mensajes resultantes, no noté nada sospechoso. Aunque no puedo leer ninguno de los idiomas recién añadidos, el formato se ve bien, y no me encontré con ninguna aparición de ‘Claro, lo traduciré para ti’🙂
¿Por qué es importante?
Aunque el experimento descrito aborda un problema de nicho, destaca un aspecto más amplio y significativo de los grandes modelos de lenguaje. Si bien pueden superar o no a los servicios especializados para resolver una tarea específica, lo realmente notable de los LLM es su capacidad para servir como una solución rápida y práctica y una alternativa de propósito general a herramientas más especializadas.
Como alguien con sólo una familiaridad básica con el aprendizaje automático, solía evitar clases enteras de tareas, porque incluso una solución mediocre requería un conocimiento que no tenía. Ahora, sin embargo, se ha vuelto increíblemente fácil unir clasificadores, servicios de traducción, LLMs como jueces y muchas otras aplicaciones.
Por supuesto, la calidad sigue siendo una consideración, pero en la realidad, no necesitamos el 100% de perfección para muchos problemas de todos modos. ¡Una razón menos para no hackear!
Conclusión
No pude pensar en una manera significativa de concluir este artículo, así que aquí tienes una foto del gato que conocí hoy (¡no generado por IA!):
