Traduire mon blog en 7 langues pour des centimes avec OpenAI
Comment j'offre du contenu à un public diversifié
Categories: Professional Development Computational
Ce post a été traduit par une IA et seulement revu de manière sommaire par rétrotraduction.
Pourquoi se donner la peine de bloguer en plusieurs langues
Lorsque j'ai mis à jour mon blog pour la dernière fois, je l'ai redessiné spécifiquement avec la capacité de fournir du contenu en plusieurs langues. Comme la plupart de mon contenu sera lié à la biologie synthétique, à la programmation et occasionnellement à l'école supérieure américaine, je prévois que la plupart de mes lecteurs seront probablement déjà assez compétents en anglais, ma langue maternelle. Environ 90 % de la littérature académique est publiée en anglais. La programmation est dans une situation similaire, avec des langages majeurs comme Java, C et Python largement (mais pas exclusivement) programmés en anglais. Je ne suis pas au courant de programmes de maîtrise en biologie aux États-Unis qui offrent des cours en dehors de l'anglais.
Cependant, un sous-ensemble de mes lecteurs sera moins à l'aise en anglais que dans leur langue maternelle. Par exemple, un grand nombre de chercheurs colombiens rapportent avoir des articles rejetés en raison de la grammaire. Il est certain que certains lecteurs potentiels ne pourront pas lire l'anglais du tout. Cela pourrait être des étudiants de premier cycle ou des lecteurs non scientifiques curieux. Afin d'atteindre un public plus large et de rendre mon contenu plus accessible à ces communautés, j'ai besoin d'un moyen de traduire rapidement et proprement mon contenu dans d'autres langues.
Choisir les langues cibles
Je souhaiterais pouvoir dire que j'ai choisi les langues que j'ai choisies pour une raison sophistiquée, mais en vérité, c'est surtout une question de portée. L'anglais est ma langue maternelle et celle dans laquelle j'écris, donc celle-ci est automatique. Je voulais également inclure l'allemand, le français et l'espagnol comme langues proches de l'anglais qui fournissent un bon point de référence pour la comparaison. L'hindi et le chinois simplifié sont inclus en raison du grand nombre de personnes qui parlent ces langues. J'ai ajouté l'arabe parce que je pensais qu'il serait intéressant d'ajouter une langue qui se lit de gauche à droite, avec l'intention éventuelle de la programmer pour s'aligner à droite. J'ai également des collègues qui parlent chacune de ces langues et de qui je peux recevoir des retours.
Construire le pipeline
Tirer parti de l'IA pour la traduction
Il existe beaucoup de littérature sur les raisons pour lesquelles on pourrait choisir l'IA générative (ex. OpenAI) plutôt qu'un système basé sur des règles (ex. Google Translate) pour mettre en place une traduction automatisée. La principale raison pour laquelle j'ai choisi OpenAI est mon expérience personnelle. En traduisant du contenu d'autres langues vers l'anglais, j'ai remarqué que les traductions basées sur des règles me renvoient souvent une grammaire maladroite. Je ne peux pas imaginer que d'autres langues bénéficieraient d'une meilleure expérience. En utilisant l'IA, j'espère obtenir des traductions qui sonnent plus naturellement.
Configuration du pipeline OpenAI
OpenAI dispose déjà d'un package python simple et bien documenté qui enveloppe leur API. Étant déjà bien expérimenté en python, l'utiliser pour ma configuration de traduction était une décision simple.
Tout d'abord, vous devrez vous assurer que vous avez une clé OpenAI configurée. Cela nécessitera également de charger des fonds sur votre compte, cependant, j'ai constaté que traduire des articles de blog de cette manière me coûte moins d'un centime avec les prix actuels et les modèles 4o-mini. Cette API est évidemment plus orientée vers des services continus qui effectueraient de nombreux appels API au fil du temps, plutôt que notre application qui consiste simplement à l'appeler occasionnellement pour la traduction.
Pour configurer votre clé, visitez la plateforme des développeurs d'OpenAI, naviguez vers votre tableau de bord, puis vers les clés API, et créez une nouvelle clé secrète. Vous devrez sauvegarder cette clé pour plus tard. Gardez-la dans un endroit sûr et privé, car elle permettra à quiconque la possède d'utiliser vos fonds OpenAI pour n'importe quel but qu'il souhaite.
Nous pouvons tester la clé avec un simple script python pour nous assurer que nous l'avons configurée correctement.
"""Tester si la clé API OpenAI est valide."""
key = get_openapi_key()
client = OpenAI(api_key=key)
completion = client.chat.completions.create(
model="gpt-4o-mini",
messages=[
{
"role": "system",
"content": "Vous êtes une API de vérification de santé. Répondez simplement 'OK'",
},
{"role": "user", "content": "Vérifier"},
],
)
if completion.choices[0].message.content == "OK":
print("La clé OpenAI est valide et fonctionne")
else:
print(completion.choices[0].message)
Maintenant que nous avons vérifié que la clé fonctionne, nous pouvons configurer le pipeline. Nous n'offrons pas de service en direct, donc nous pouvons utiliser l'API par lot d'OpenAI pour économiser beaucoup d'argent. L'API par lot est conçue pour être utilisée pour de grandes quantités de demandes qui ne nécessitent pas de réponses immédiates, donc elles peuvent être plus lentes. Cependant, elle ne se soucie pas réellement du nombre de demandes que vous envoyez et elle coûte moitié prix au moment de l'écriture. Cela réduit le coût à une fraction de centime pour traduire un article de blog dans chaque langue qui nous intéresse.
OpenAPI exige que leurs demandes soient dans un format spécifique. Nous devons dire à l'IA ce que c'est (un outil de traduction), le modèle que nous utilisons et la langue dans laquelle nous traduisons. J'ai lu qu'il est également utile de lui dire d'utiliser un dialecte donné, donc nous ajouterons cette option aussi.
def openai_format(
id: str, model: str, language: str, dialect: str, content: str
) -> dict:
if dialect:
system_message = f"""Vous êtes un backend de traduction.
Traduisez l'entrée de l'utilisateur en {language} en utilisant un dialecte {dialect}.
Renvoie uniquement la traduction en préservant la
structure markdown. Il est possible que vous commenciez
mid-code block."""
else:
system_message = f"""Vous êtes un backend de traduction.
Traduisez l'entrée de l'utilisateur en {language}. Renvoie uniquement la
traduction dans le format markdown correspondant. Il est
possible que vous commenciez mid-code block."""
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
Maintenant que nous avons une fonction pour nous aider à formater nos requêtes, nous pouvons les regrouper en utilisant le package python d'OpenAI. L'API préfère ses requêtes au format JSON, nous allons donc utiliser un répertoire temporaire pour écrire nos requêtes dans un fichier, puis utiliser le package python pour envoyer les requêtes à l'API. Cela nous donnera un ID de lot que nous devons garder à l'esprit, car c'est ainsi que nous récupérerons les résultats.
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 = "Lot de traduction 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
Maintenant que nous avons envoyé notre requête, nous devons attendre que le lot soit terminé. Même si nous utilisons l'API de lot plus lente, ce processus ne prend généralement que quelques minutes. Si nous essayons de récupérer les résultats avant qu'il ne soit terminé, nous obtiendrons une erreur. Nous pouvons utiliser l'ID pour vérifier le lot dans une boucle à la place, puis récupérer les résultats par la suite.
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"En attente d'OpenAI (le lot est {result.status})")
sleep(5)
continue
else:
break
if result.status in ["failed", "expired",
"canceling", "canceled"]:
raise ValueError(f"Le lot OpenAI {batch_id} a échoué")
Obtenir le résultat réel est très simple. Une fois que nous avons le feu vert, nous pouvons simplement récupérer le texte de l'objet de résultat. La valeur retournée est une série de chaînes formatées en JSON dans un format similaire à celui que nous avons utilisé pour la demande. Une fois que nous avons chargé le JSON, la réponse d'OpenAI est enfouie dans 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
Conclusion
Et c'est tout ! Nous pouvons maintenant utiliser OpenAI pour traduire nos articles de blog (ou tout autre contenu) dans n'importe quelle langue que nous voulons. Assurez-vous de faire toute la révision nécessaire avant d'exécuter les traductions, car je ne peux pas garantir comment le modèle pourrait se comporter. J'ai poussé cela un peu plus loin en automatisant davantage le processus. Mon système prend automatiquement des fichiers Markdown. Il divise également les fichiers en morceaux plus petits, car j'ai remarqué que l'IA peut être un peu distraite si elle reçoit un gros fichier à traduire. J'ai mis cela dans un dépôt séparé que vous pouvez trouver ici. Le code est sous licence MIT, donc n'hésitez pas à l'utiliser comme bon vous semble.