이 기사에서는 OpenAI의 GPT-4 언어 모델을 사용하여 블로그 게시물의 번역을 자동화하기 위해 개발된 Proof of Concept (POC) Python 스크립트를 공유합니다. 이 스크립트는 특히 제 Hugo 블로그 구조에서 Markdown 파일을 처리하도록 설계되어 다국어 관리가 용이합니다. 이들은 영어, 스페인어, 중국어로 제공됩니다.

프로젝트 시작: 내 블로그를 위한 AI와 자동화의 융합

이 블로그 게시물 번역 자동화 프로젝트는 인공지능에 대한 나의 증가하는 관심에서 시작되었습니다. OpenAI GPT-4와 Mistral AI API를 사용한 초기 경험에서 영감을 받아, 이러한 기술을 실질적인 프로젝트로 실현하여 내 블로그에 실질적인 가치를 제공하는 아이디어에 매료되었습니다. 이것은 단순히 AI 도구를 숙달하는 탐구가 아니라 내 디지털 공간을 풍부하게 하기 위한 자동화와 혁신을 융합하려는 열망이었습니다.

이 프로젝트는 AI가 단순한 글쓰기 주제가 아니라 개발의 적극적인 파트너 역할을 한 모험으로 변모했습니다. AI를 사용하여 내 게시물을 간편하고 효율적으로 번역하면서 자동화 기능을 탐험하는 아이디어는 매혹적인 전망을 열어주었습니다. 이는 언어 장벽을 초월하여 내 콘텐츠를 더 넓은 청중에게 접근 가능하게 만드는 기회이자, 끊임없이 진화하는 인공지능 분야를 탐험하는 기회이기도 했습니다.

도전

주요 도전 과제는 코드 블록, 링크 및 이미지를 포함하여 기사의 원래 형식을 유지하면서 정확하게 번역할 수 있는 스크립트를 작성하는 것이었습니다. 또 다른 도전 과제는 다른 언어를 쉽게 지원할 수 있도록 스크립트를 조정할 수 있는 것이었습니다. 이 구조도 고려해야 했습니다:

├── content
│   ├── about
│   │   └── a-propos-du-blog-jls42.md
│   ├── mentions
│   │   └── mentions-legales.md
│   ├── posts
│   │   ├── blog
│   │   │   └── nouveau-theme-logo.md
│   │   ├── ia
│   │   │   ├── poc-mistral-ai-mixtral.md
│   │   │   ├── poc-openai-api-gpt4.md
│   │   │   └── stable-difusion-aws-ec2.md
│   │   ├── infrastructure
│   │   │   └── infrastruture-as-code-serverless-ha-jls42-org.md
│   │   └── raspberry-pi
│   │       ├── glusterfs_distribue_replique_sur_raspberry_pi_via_ansible.md
│   │       ├── initialisation-auto-de-raspbian-sur-raspberry-pi.md
│   │       ├── installation-de-docker-sur-raspberry-pi-via-ansible.md
│   │       └── installation-de-kubernetes-sur-raspberry-pi-via-ansible.md

해법: 혁신적인 스크립트

저는 OpenAI GPT-4 API를 활용하여 텍스트를 번역하면서 비텍스트 요소를 보존하는 Python 스크립트를 설계했습니다. 일련의 처리 규칙과 플레이스홀더를 사용하여 스크립트는 코드 블록과 기타 번역할 수 없는 요소를 식별하고 제외할 수 있어 번역된 콘텐츠가 원본을 충실히 유지하도록 보장합니다.

주요 기능

  1. GPT-4와 함께하는 정확한 번역: 스크립트는 OpenAI의 GPT-4 모델을 사용하여 프랑스어 텍스트를 영어로 번역하면서 원래 콘텐츠의 품질과 뉘앙스를 유지합니다.
  2. 형식 보존: 코드 블록, URL 및 이미지 경로를 식별하고 번역 중 그대로 유지하여 원래 형식을 보존합니다.
  3. 다국어 유연성: 스크립트는 다양한 소스 및 타겟 언어에 쉽게 적응할 수 있도록 설계되어 다양한 다국어 응용 프로그램에 사용할 수 있습니다.
  4. Markdown 파일 지원: Markdown으로 작성된 문서를 번역하면서 구조와 형식을 유지할 수 있습니다.
  5. 디렉토리 번역 자동화: 지정된 디렉토리와 하위 디렉토리에서 찾은 Markdown 파일을 자동으로 번역하여 대량의 콘텐츠 관리를 용이하게 합니다.
  6. 번역 주석 통합: 번역된 문서 끝에 사용된 GPT 모델을 표시하는 번역 주석을 자동으로 추가합니다.
  7. 쉽고 쉬운 설정 및 맞춤화: API 키, GPT 모델, 소스 및 타겟 언어, 파일 디렉토리에 대한 기본 설정을 사용자 지정할 수 있어 사용의 유연성을 제공합니다.
  8. 성능 보고서: 스크립트는 각 파일을 번역하는 데 걸리는 시간에 대한 피드백을 제공하여 성능을 모니터링할 수 있게 합니다.

스크립트 코드

코드는 여기에서도 확인할 수 있습니다: AI-Powered Markdown Translator

#!/usr/bin/env python3

import os
import argparse
import time
from openai import OpenAI
import re

# Initialisation de la configuration avec les valeurs par défaut
DEFAULT_API_KEY = 'votre-clé-api-par-défaut'
DEFAULT_MODEL = "gpt-4-1106-preview"
DEFAULT_SOURCE_LANG = 'fr'
DEFAULT_TARGET_LANG = 'en'
DEFAULT_SOURCE_DIR = 'content/posts'
DEFAULT_TARGET_DIR = 'traductions_en'

MODEL_TOKEN_LIMITS = {
    "gpt-4-1106-preview": 4096,
    "gpt-4-vision-preview": 4096,
    "gpt-4": 8192,
    "gpt-4-32k": 32768,
    "gpt-4-0613": 8192,
    "gpt-4-32k-0613": 32768
}

# Fonction de traduction
def translate_with_openai(text, client, args):
    """
    Traduit le texte donné du langage source au langage cible en utilisant l'API OpenAI.
    
    Args:
        text (str) : Le texte à traduire.
        client : L'objet client OpenAI.
        args : Les arguments contenant les informations sur le langage source, le langage cible et le modèle.
        
    Returns:
        str : Le texte traduit.
    """
    # Détecter et stocker les blocs de code
    code_blocks = re.findall(r'(^```[a-zA-Z]*\n.*?\n^```)', text, flags=re.MULTILINE | re.DOTALL)
    placeholders = [f"#CODEBLOCK{index}#" for index, _ in enumerate(code_blocks)]
    
    # Remplacer les blocs de code par des placeholders
    for placeholder, code_block in zip(placeholders, code_blocks):
        text = text.replace(code_block, placeholder)
    
    # Création du message pour l'API
    messages = [
        {"role": "system", "content": f"Translate the following text from {args.source_lang} to {args.target_lang}, ensuring that elements such as URLs, image paths, and code blocks (delimited by ```) are not translated. Leave these elements unchanged."},
        {"role": "user", "content": text}
    ]
    
    # Envoi de la demande de traduction
    response = client.chat.completions.create(
        model=args.model,
        messages=messages
    )
    
    # Obtenir le texte traduit et remplacer les placeholders par les blocs de code originaux
    translated_text = response.choices[0].message.content.strip()
    for placeholder, code_block in zip(placeholders, code_blocks):
        translated_text = translated_text.replace(placeholder, code_block)

    return translated_text

def add_translation_note(client, args):
    """
    Ajoute une note de traduction à un document.

    Args:
        client : Le client de traduction.
        args : Arguments supplémentaires.

    Returns:
        La note de traduction formatée.
    """
    # Note de traduction en français
    translation_note_fr = "Ce document a été traduit de la version française du blog par le modèle "
    # Traduire la note en langue cible
    translated_note = translate_with_openai(translation_note_fr + args.model, client, args)
    # Formatage de la note de traduction
    return f"\n\n**{translated_note}**\n\n"

# Traitement des fichiers Markdown
def translate_markdown_file(file_path, output_path, client, args):
    """
    Traduit le contenu d'un fichier markdown en utilisant l'API de traduction OpenAI et écrit le contenu traduit dans un nouveau fichier.

    Args:
        file_path (str): Chemin vers le fichier markdown d'entrée.
        output_path (str): Chemin vers le fichier de sortie où le contenu traduit sera écrit.
        client: Client de traduction OpenAI.
        args: Arguments supplémentaires pour le processus de traduction.

    Returns:
        None
    """
    print(f"Traitement du fichier : {file_path}")
    start_time = time.time()

    with open(file_path, 'r', encoding='utf-8') as f:
        content = f.read()

    translated_content = translate_with_openai(content, client, args)
    
    # Ajouter la note de traduction à la fin du contenu traduit
    translation_note = add_translation_note(client, args)
    translated_content_with_note = translated_content + translation_note

    with open(output_path, 'w', encoding='utf-8') as f:
        f.write(translated_content_with_note)

    end_time = time.time()
    print(f"Traduction terminée en {end_time - start_time:.2f} secondes.")

def translate_directory(input_dir, output_dir, client, args):
    """
    Traduit tous les fichiers markdown dans le répertoire d'entrée et ses sous-répertoires.

    Args:
        input_dir (str): Chemin vers le répertoire d'entrée.
        output_dir (str): Chemin vers le répertoire de sortie.
        client: Objet client de traduction.
        args: Arguments supplémentaires pour la traduction.

    Returns:
        None
    """
    for root, dirs, files in os.walk(input_dir, topdown=True):
        # Exclure les dossiers qui commencent par "traductions_"
        dirs[:] = [d for d in dirs if not d.startswith("traductions_")]

        for file in files:
            if file.endswith('.md'):
                file_path = os.path.join(root, file)
                base, _ = os.path.splitext(file)
                # Ajouter le nom du modèle utilisé dans le nom du fichier de sortie
                output_file = f"{base}-{args.model}-{args.target_lang}.md"
                relative_path = os.path.relpath(root, input_dir)
                output_path = os.path.join(output_dir, relative_path, output_file)

                os.makedirs(os.path.dirname(output_path), exist_ok=True)

                if not os.path.exists(output_path):
                    translate_markdown_file(file_path, output_path, client, args)
                    print(f"Fichier '{file}' traité.")


def main():
    """
    Fonction principale pour traduire les fichiers Markdown.

    Args:
        --source_dir (str): Répertoire source contenant les fichiers Markdown.
        --target_dir (str): Répertoire cible pour sauvegarder les traductions.
        --model (str): Modèle GPT à utiliser.
        --target_lang (str): Langue cible pour la traduction.
        --source_lang (str): Langue source pour la traduction.
    """
    parser = argparse.ArgumentParser(description="Traduit les fichiers Markdown.")
    parser.add_argument('--source_dir', type=str, default=DEFAULT_SOURCE_DIR, help='Répertoire source contenant les fichiers Markdown')
    parser.add_argument('--target_dir', type=str, default=DEFAULT_TARGET_DIR, help='Répertoire cible pour sauvegarder les traductions')
    parser.add_argument('--model', type=str, default=DEFAULT_MODEL, help='Modèle GPT à utiliser')
    parser.add_argument('--target_lang', type=str, default=DEFAULT_TARGET_LANG, help='Langue cible pour la traduction')
    parser.add_argument('--source_lang', type=str, default=DEFAULT_SOURCE_LANG, help='Langue source pour la traduction')

    args = parser.parse_args()

    openai_api_key = os.getenv('OPENAI_API_KEY', DEFAULT_API_KEY)
    with OpenAI(api_key=openai_api_key) as client:
        translate_directory(args.source_dir, args.target_dir, client, args)

if __name__ == "__main__":
    main()

스크립트 개요

모듈 임포트

먼저, os, argparse, time, re 등 필요한 모듈 몇 가지를 임포트합니다. 이 모듈들은 파일 시스템 작업, 명령줄 인수 분석, 실행 시간 측정 및 텍스트 검색 및 교체 작업에 사용됩니다.

상수

다음으로, DEFAULT_API_KEY, DEFAULT_MODEL, DEFAULT_SOURCE_LANG, DEFAULT_TARGET_LANG, DEFAULT_SOURCE_DIR, DEFAULT_TARGET_DIR와 같은 상수들이 정의되어 있습니다. 이 상수들은 스크립트에서 사용되는 기본값을 나타내지만, 명령줄 인수를 지정하여 변경할 수 있습니다.

translate_with_openai 함수

다음으로, translate_with_openai 함수가 있습니다. 이 함수는 텍스트, OpenAI 클라이언트 객체 및 인수를 매개변수로 받아들입니다. 이 함수는 OpenAI API를 사용하여 텍스트를 원본 언어에서 대상 언어로 번역합니다. 작동 방식은 다음과 같습니다:

  1. 함수는 정규 표현식을 사용하여 텍스트 내의 코드 블록을 감지하고 저장합니다. 이 코드 블록들은 백틱 세 개()로 구분됩니다. 코드 블록은 code_blocks`라는 리스트에 저장됩니다.
  2. 그런 다음 함수는 텍스트 내의 코드 블록을 플레이스홀더로 교체합니다. 플레이스홀더는 #CODEBLOCK{index}# 형식의 문자열이며, 여기서 indexcode_blocks 리스트 내의 코드 블록의 인덱스입니다.
  3. 함수는 OpenAI API에 보낼 메시지를 생성합니다. 이 메시지에는 시스템 메시지와 사용자 메시지의 두 부분이 포함됩니다. 시스템 메시지는 API에게 URL, 이미지 경로 및 코드 블록 등은 변경하지 않고 텍스트를 번역하라는 지시를 포함하며, 사용자 메시지는 번역할 텍스트를 포함합니다.
  4. 함수는 client.chat.completions.create() 메소드를 사용하여 API에게 번역 요청을 보냅니다. 여기서 사용할 모델과 번역할 메시지를 지정합니다.
  5. API 응답에는 번역된 텍스트가 포함됩니다. 함수는 이 번역된 텍스트를 가져와 플레이스홀더를 원본 코드 블록으로 교체합니다.
  6. 마지막으로 함수는 번역된 텍스트를 반환합니다.

add_translation_note 함수

다음으로, add_translation_note 함수가 있습니다. 이 함수는 문서에 번역 노트를 추가합니다. 이 함수는 OpenAI 클라이언트 객체 및 인수를 매개변수로 받아들입니다. 작동 방식은 다음과 같습니다:

  1. 함수는 translation_note_fr 변수를 사용하여 프랑스어로 번역 노트를 만듭니다.
  2. 그런 다음 함수는 translate_with_openai 함수를 사용하여 OpenAI API를 통해 번역 노트를 번역합니다. translate_with_openai에 전달되는 인수에는 프랑스어 번역 노트 및 기타 인수가 포함됩니다.
  3. 함수는 서식 문자를 추가하여 번역된 번역 노트를 포맷합니다.
  4. 마지막으로 함수는 포맷된 번역 노트를 반환합니다.

translate_markdown_file 함수

다음으로, translate_markdown_file 함수가 있습니다. 이 함수는 입력 마크다운 파일 경로, 출력 파일 경로, OpenAI 클라이언트 객체 및 인수를 매개변수로 받아들입니다. 이 함수는 OpenAI 번역 API를 사용하여 마크다운 파일의 내용을 번역하고, 번역된 내용을 출력 파일에 씁니다.

이 스크립트는 제 블로그 게시물의 접근성을 개선했을 뿐만 아니라 다국어 콘텐츠 제작 분야에서 새로운 자동화 가능성을 열어주었습니다. 이는 더 넓고 더 포용적인 지식 공유를 위한 한 걸음입니다. ## 사용 경험 및 처리 시간

사용 예제

# Création des répertoires cibles
jls42@Boo:~/blog/jls42$ mkdir content/traductions_en content/traductions_es

###############################################
# Demande de traduction à l'IA vers l'anglais #
###############################################
jls42@Boo:~/blog/jls42$ python3 translate.py --source_dir content/ --target_dir content/traductions_en
Traitement du fichier : content/posts/ia/stable-difusion-aws-ec2.md
Traduction terminée en 21.57 secondes.
Fichier 'stable-difusion-aws-ec2.md' traité.
Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.md
Traduction terminée en 34.87 secondes.
Fichier 'poc-openai-api-gpt4.md' traité.
Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.md
Traduction terminée en 62.47 secondes.
Fichier 'poc-mistral-ai-mixtral.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-kubernetes-sur-raspberry-pi-via-ansible.md
Traduction terminée en 46.37 secondes.
Fichier 'installation-de-kubernetes-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-docker-sur-raspberry-pi-via-ansible.md
Traduction terminée en 10.08 secondes.
Fichier 'installation-de-docker-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/initialisation-auto-de-raspbian-sur-raspberry-pi.md
Traduction terminée en 17.17 secondes.
Fichier 'initialisation-auto-de-raspbian-sur-raspberry-pi.md' traité.
Traitement du fichier : content/posts/blog/nouveau-theme-logo.md
Traduction terminée en 12.91 secondes.
Fichier 'nouveau-theme-logo.md' traité.
Traitement du fichier : content/posts/infrastructure/infrastruture-as-code-serverless-ha-jls42-org.md
Traduction terminée en 12.64 secondes.
Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.
Traitement du fichier : content/mentions/mentions-legales.md
Traduction terminée en 11.90 secondes.
Fichier 'mentions-legales.md' traité.
Traitement du fichier : content/about/a-propos-du-blog-jls42.md
Traduction terminée en 18.72 secondes.
Fichier 'a-propos-du-blog-jls42.md' traité.

################################################
# Demande de traduction à l'IA vers l'espagnol #
################################################
jls42@Boo:~/blog/jls42$ python3 translate.py --source_dir content/ --target_dir content/traductions_es --target_lang es
Traitement du fichier : content/posts/ia/stable-difusion-aws-ec2.md
Traduction terminée en 33.19 secondes.
Fichier 'stable-difusion-aws-ec2.md' traité.
Traitement du fichier : content/posts/ia/poc-openai-api-gpt4.md
Traduction terminée en 25.24 secondes.
Fichier 'poc-openai-api-gpt4.md' traité.
Traitement du fichier : content/posts/ia/poc-mistral-ai-mixtral.md
Traduction terminée en 58.78 secondes.
Fichier 'poc-mistral-ai-mixtral.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-kubernetes-sur-raspberry-pi-via-ansible.md
Traduction terminée en 17.64 secondes.
Fichier 'installation-de-kubernetes-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/installation-de-docker-sur-raspberry-pi-via-ansible.md
Traduction terminée en 19.60 secondes.
Fichier 'installation-de-docker-sur-raspberry-pi-via-ansible.md' traité.
Traitement du fichier : content/posts/raspberry-pi/initialisation-auto-de-raspbian-sur-raspberry-pi.md
Traduction terminée en 37.12 secondes.
Fichier 'initialisation-auto-de-raspbian-sur-raspberry-pi.md' traité.
Traitement du fichier : content/posts/blog/nouveau-theme-logo.md
Traduction terminée en 18.91 secondes.
Fichier 'nouveau-theme-logo.md' traité.
Traitement du fichier : content/posts/infrastructure/infrastruture-as-code-serverless-ha-jls42-org.md
Traduction terminée en 30.73 secondes.
Fichier 'infrastruture-as-code-serverless-ha-jls42-org.md' traité.
Traitement du fichier : content/mentions/mentions-legales.md
Traduction terminée en 13.14 secondes.
Fichier 'mentions-legales.md' traité.
Traitement du fichier : content/about/a-propos-du-blog-jls42.md
Traduction terminée en 11.24 secondes.
Fichier 'a-propos-du-blog-jls42.md' traité.

처리 시간

  • 영어: 약 4분 (248.70초)
  • 스페인어: 약 4.7분 (284.05초)
  • 총 합계: 약 8.7분 (532.75초) 이 시간들은 스크립트의 효율성과 속도를 보여줍니다.

결과

다음 링크에서 번역된 콘텐츠의 결과를 지금 바로 확인할 수 있습니다:

이 블로그 포스트는 AI를 사용한 번역 자동화에 대한 제 경험을 간략히 정리한 것입니다. 이는 프로그래밍과 인공지능을 결합하면 거의 무한한 가능성을 가지고 있으며 지식 공유와 콘텐츠 접근성 분야에서 새로운 흥미로운 지평을 열 수 있음을 입증합니다.

이 문서는 gpt-4o 모델을 사용하여 fr 버전에서 ko 언어로 번역되었습니다. 번역 과정에 대한 자세한 내용은 https://gitlab.com/jls42/ai-powered-markdown-translator 를 참조하십시오.