Traduciendo mi Blog a 7 Idiomas por Centavos con OpenAI
Cómo estoy ofreciendo contenido a una audiencia diversa
Categories: Professional Development Computational
Esta publicación fue traducida por una IA y solo se verificó superficialmente mediante traducción inversa.
¿Por qué molestarse con el Blogging Multilingüe?
Cuando actualicé mi blog por última vez, lo rediseñé específicamente con la capacidad de proporcionar contenido en múltiples idiomas. Dado que la mayor parte de mi contenido estará relacionado con la biología sintética, la programación y, ocasionalmente, la escuela de posgrado en Estados Unidos, anticipo que la mayoría de mis lectores probablemente ya sean algo competentes en inglés, mi lengua materna. Alrededor del 90% de la literatura académica se publica en inglés. La programación se encuentra en una situación similar, con lenguajes principales como Java, C y Python programados en gran medida (pero no exclusivamente) en inglés. No tengo conocimiento de programas de posgrado en biología en Estados Unidos que ofrezcan cursos fuera del inglés.
Sin embargo, un subconjunto de mis lectores se sentirá menos cómodo en inglés que en su lengua nativa. Por ejemplo, un gran número de investigadores colombianos reportan haber tenido artículos rechazados debido a la gramática. Ciertamente, hay algunos lectores potenciales que no podrán leer inglés en absoluto. Estos podrían ser estudiantes de pregrado o lectores curiosos no científicos. Para alcanzar una audiencia más amplia y hacer que mi contenido sea más accesible para estas comunidades, necesito una forma de traducir rápidamente y de manera limpia mi contenido a otros idiomas.
Elegir Idiomas Objetivo
Desearía poder decir que elegí los idiomas que elegí por una razón sofisticada, pero la verdad es que se trata principalmente de alcance. El inglés es mi lengua materna y el idioma en el que escribo, así que ese es automático. También quería incluir alemán, francés y español como idiomas cercanos al inglés que proporcionan un buen punto de referencia para comparación. Hindi y chino simplificado están incluidos debido al gran número de personas que hablan esos idiomas. Agregué árabe porque pensé que sería interesante añadir un idioma que se lee de izquierda a derecha, con la intención eventual de programarlo para que se alinee a la derecha. También tengo colegas que hablan cada uno de estos idiomas de los que puedo recibir retroalimentación.
Construyendo el pipeline
Aprovechando la IA para la Traducción
Hay mucha literatura sobre por qué uno podría elegir IA generativa (ej. OpenAI) en lugar de un sistema basado en reglas (ej. Google Translate) para construir traducción automatizada. La razón principal por la que elegí OpenAI es por experiencia personal. Al traducir contenido de otros idiomas al inglés, he notado que las traducciones basadas en reglas a menudo me devuelven una gramática incómoda. No puedo imaginar que otros idiomas tengan una mejor experiencia. Al usar IA, espero obtener traducciones que suenen más naturales.
Configurando el Pipeline de OpenAI
OpenAI ya tiene un paquete de python sencillo y bien documentado que envuelve su API. Al tener ya experiencia en python, usarlo para mi configuración de traducción fue una decisión simple.
Primero, necesitarás asegurarte de tener una clave de OpenAI configurada. Esto también requerirá que cargues fondos en tu cuenta, sin embargo, he encontrado que traducir publicaciones de blog de esta manera me cuesta menos de un centavo con los precios actuales y los modelos 4o-mini. Esta API está claramente dirigida más hacia servicios continuos que harían muchas llamadas a la API con el tiempo, en lugar de nuestra aplicación de solo llamarla ocasionalmente para traducción.
Para configurar tu clave, visita la plataforma de desarrolladores de OpenAI, navega a tu panel de control, luego a claves de API, y crea una nueva clave secreta. Necesitarás guardar esta clave para más tarde. Mantenla en un lugar seguro y privado, ya que permitirá a cualquiera que la tenga usar tus fondos de OpenAI para cualquier propósito que desee.
Podemos probar la clave con un simple script de python para asegurarnos de que la configuramos correctamente.
"""Prueba si la clave de la API de OpenAI es válida."""
key = get_openapi_key()
client = OpenAI(api_key=key)
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "Eres una API de verificación de salud. Simplemente responde 'OK'",
},
{"role": "user", "content": "Verifica"},
],
)
if completion.choices[0].message.content == "OK":
print("La clave de OpenAI es válida y está funcionando")
else:
print(completion.choices[0].message)
Ahora que hemos verificado que la clave está funcionando, podemos configurar el pipeline. No estamos ofreciendo un servicio en vivo, así que podemos usar la API por lotes de OpenAI para ahorrar mucho dinero. La API por lotes está diseñada para ser utilizada para grandes cantidades de solicitudes que no requieren respuestas inmediatas, por lo que pueden ser más lentas. Sin embargo, en realidad no le importa el número de solicitudes que envíes y cuesta la mitad del precio al momento de escribir esto. Esto reduce el costo a una fracción de un centavo para traducir una publicación de blog a cada idioma que nos interesa.
OpenAPI requiere que sus solicitudes estén en un formato específico. Necesitamos decirle a la IA qué es (una herramienta de traducción), el modelo que estamos usando y el idioma al que estamos traduciendo. He leído que también ayuda decirle que use un dialecto específico, así que añadiremos esa opción también.
def openai_format(
id: str, model: str, language: str, dialect: str, content: str
) -> dict:
if dialect:
system_message = f"""Eres un backend de traducción.
Traduce la entrada del usuario al {language} usando un
dialecto {dialect}. Solo devuelve la traducción
preservando la estructura de markdown. Es posible que
estés comenzando a mitad de un bloque de código."""
else:
system_message = f"""Eres un backend de traducción.
Traduce la entrada del usuario al {language}. Solo devuelve
la traducción en el formato de markdown correspondiente. Es
posible que estés comenzando a mitad de un bloque de código."""
formatted_prompt = {
"custom_id": id,
"method": "POST",
"url": "/v1/chat/completions",
"body": {
"model": model,
"messages": [
{"role": "system", "content": system_message},
{"role": "user", "content": content},
],
"temperature": 0.1,
},
}
return formatted_prompt
Ahora que tenemos una función para ayudarnos a formatear nuestras solicitudes, podemos agruparlas usando el paquete de Python de OpenAI. La API prefiere sus solicitudes en formato JSON, así que usaremos un directorio temporal para escribir nuestras solicitudes en un archivo, y luego utilizaremos el paquete de Python para enviar las solicitudes a la API. Esto nos dará un ID de lote que debemos rastrear, ya que será la forma en que recuperaremos los resultados.
import json
from tempfile import TemporaryDirectory
from openai import OpenAI
def send_openai_batch(formatted_prompts: list, description=None):
key = get_openapi_key()
client = OpenAI(api_key=key)
if not description:
description = "Lote de traducción en Markdown"
with TemporaryDirectory() as tmpdirname:
with open(f"{tmpdirname}/batchinput.json", "a") as f:
for prompt in formatted_prompts:
f.write(json.dumps(prompt) + "\n")
batch_input_file = client.files.create(
file=open(f"{tmpdirname}/batchinput.json", "rb"),
purpose="batch"
)
batch_input_file_id = batch_input_file.id
batch = client.batches.create(
input_file_id=batch_input_file_id,
endpoint="/v1/chat/completions",
completion_window="24h",
metadata={"description": description},
)
return batch
Ahora que hemos enviado nuestra solicitud, necesitamos esperar a que el lote termine. Aunque estamos usando la API de lotes más lenta, este proceso generalmente solo toma unos minutos. Si intentamos obtener los resultados antes de que termine, obtendremos un error. Podemos usar el ID para verificar el lote en un bucle y luego obtener los resultados después.
from time import sleep
def openai_check_results(batch_id: str):
key = get_openapi_key()
client = OpenAI(api_key=key)
result = client.batches.retrieve(batch_id)
return result
def wait_for_openai(batch_id: str):
while True:
result = openai_check_results(batch_id)
if result.status in ["validating", "in_progress"]:
print(f"Esperando a OpenAI (el lote está {result.status})")
sleep(5)
continue
else:
break
if result.status in ["failed", "expired",
"canceling", "canceled"]:
raise ValueError(f"El lote de OpenAI {batch_id} falló")
Obtener el resultado real es muy sencillo. Una vez que tengamos luz verde, solo podemos tomar el texto del objeto de resultado. El valor devuelto es una serie de cadenas en formato JSON en un formato similar al que usamos para la solicitud. Una vez que cargamos el JSON, la respuesta de OpenAI está enterrada en result["response"]["body"]["choices"][0]["message"]["content"]
.
from collections import defaultdict
def build_openai_results(batch_id: str):
key = get_openapi_key()
client = OpenAI(api_key=key)
result = openai_check_results(batch_id)
file_response = client.files.content(result.output_file_id
).text.split("\n")
result_dict = defaultdict(dict)
for json_str in file_response:
if json_str == "":
continue
result = json.loads(json_str)
id = result["custom_id"]
result_dict[id] = result["response"]["body"][
"choices"][0]["message" ]["content"]
return result_dict
Conclusión
¡Y eso es todo! Ahora podemos usar OpenAI para traducir nuestras publicaciones de blog (o cualquier otro contenido) a cualquier idioma que queramos. Asegúrate de hacer cualquier edición de texto con anticipación antes de ejecutar las traducciones, ya que no puedo garantizar cómo se comportará el modelo. He llevado esto un poco más allá al automatizar más del proceso. Mi sistema toma automáticamente archivos Markdown. También divide los archivos en partes más pequeñas, ya que he notado que la IA puede distraerse un poco si se le da un archivo grande para traducir. He puesto esto en un repositorio separado que puedes encontrar aquí. El código tiene licencia MIT, así que siéntete libre de usarlo como quieras.