用OpenAI將我的博客翻譯成7種語言,僅需幾分錢

我如何向多元化的受眾提供內容

9 min 1631 words
Cameron Roots Cameron Roots's profile picture

此帖子由人工智能翻译,并仅通过反向翻译进行了表面检查。

為什麼要進行多語言博客

當我上次更新我的博客時,我特意重新設計了它,以便能夠提供多語言的內容。因為我的大多數內容將與合成生物學、編程以及偶爾的美國研究生院有關,我預計我的大多數讀者可能已經對英語有一定的熟練度,這是我的母語。大約90%的學術文獻是用英語發表的。編程的情況也類似,主要語言如Java、C和Python大多(但不僅限於)用英語編寫。我不知道美國有哪個生物學研究生項目提供英語以外的課程。

然而,我的一部分讀者在英語方面的舒適度可能不如他們的母語。例如,許多哥倫比亞研究人員報告因語法問題而被拒絕的論文。確實有一些潛在的讀者根本無法閱讀英語。他們可能是本科生或好奇的非科學讀者。為了接觸更廣泛的受眾並使我的內容對這些社群更具可及性,我需要一種快速且乾淨地將我的內容翻譯成其他語言的方法。

選擇目標語言

我希望我能說我選擇的語言是出於某種高深的理由,但事實上這主要是關於覆蓋範圍。英語是我的母語,也是我寫作的語言,所以這個是自動的。我還想包括德語、法語和西班牙語,這些語言與英語相近,提供了一個不錯的比較基準。印地語和簡體中文則因為說這些語言的人數眾多而被納入。我添加了阿拉伯語,因為我認為添加一種從右到左的語言會很有趣,最終打算將其編程對齊到右側。我還有同事會說這些語言,我可以從他們那裡獲得反饋。

建立管道

利用人工智慧進行翻譯

有很多文獻探討為什麼人們可能會選擇生成式人工智慧(例如 OpenAI)而不是基於規則的系統(例如 Google 翻譯)來建立自動翻譯。 我選擇 OpenAI 的主要原因是個人經驗。 在將其他語言的內容翻譯成英語時,我注意到基於規則的翻譯經常會給我帶來尷尬的語法。 我無法想像其他語言會有更好的體驗。 通過使用人工智慧,我希望能獲得更自然的翻譯。

設置 OpenAI 管道

OpenAI 已經有一個簡單且文檔完善的 Python 包來包裝他們的 API。 我對 Python 已經有了豐富的經驗,因此使用它來設置我的翻譯是一個簡單的決定。

首先,您需要確保已設置 OpenAI 密鑰。 這還需要您向帳戶中加載資金,然而,我發現以這種方式翻譯博客文章的成本不到一美分,這是基於當前價格和 4o-mini 模型。 這個 API 顯然更適合於需要隨時間進行大量 API 調用的持續服務,而不是我們偶爾調用它進行翻譯的應用。

要設置您的密鑰,請訪問 OpenAI 的開發者平台,導航到您的儀表板,然後是 API 密鑰,並創建一個新的秘密密鑰。 您需要將此密鑰保存以備後用。 將其保存在安全的私人地方,因為擁有此密鑰的人可以隨意使用您的 OpenAI 資金。

我們可以用一個簡單的 Python 腳本來測試這個金鑰,以確保我們正確設置了它。

"""測試 OpenAI API 金鑰是否有效。"""
   key = get_openapi_key()
   client = OpenAI(api_key=key)

   completion = client.chat.completions.create(
   	model="gpt-4o-mini",
   	messages=[
       	{
           	"role": "system",
           	"content": "你是一個健康檢查 API。只需回覆 'OK'",
       	},
       	{"role": "user", "content": "檢查"},
   	],
   )
   if completion.choices[0].message.content == "OK":
   	print("OpenAI 金鑰有效且正常運作")
   else:
   	print(completion.choices[0].message)

現在我們已經驗證了金鑰的運作,我們可以設置管道。我們不提供即時服務,因此可以使用 OpenAI 的批量 API 來節省大量金錢。批量 API 設計用於大量請求,這些請求不需要立即回應,因此它們可能會較慢。然而,它實際上不在乎你發送的請求數量,並且在撰寫時價格只有一半。這使得將一篇博客文章翻譯成我們感興趣的每種語言的成本降到幾分之一美分。

OpenAPI 要求它們的請求必須以特定格式發送。我們需要告訴 AI 這是什麼(翻譯工具)、我們使用的模型以及我們要翻譯的語言。我讀過告訴它使用特定方言也有幫助,因此我們也會添加這個選項。

def openai_format(
	id: str, model: str, language: str, dialect: str, content: str
) -> dict:
	if dialect:
    	system_message = f"""你是一個翻譯後端。
    	將用戶輸入翻譯成 {language},使用 {dialect}
    	方言。僅返回翻譯,保留
    	markdown 結構。你可能會在代碼塊中間開始。"""
	else:
    	system_message = f"""你是一個翻譯後端。
    	將用戶輸入翻譯成 {language}。僅返回
    	匹配的 markdown 格式的翻譯。你可能會在代碼塊中間開始。"""
	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

現在我們有了一個幫助我們格式化請求的函數,我們可以使用 OpenAI 的 Python 套件將它們打包在一起。API 喜歡以 JSON 格式的請求,因此我們將使用臨時目錄將請求寫入文件,然後使用 Python 套件將請求發送到 API。這將給我們一個批次 ID,我們必須跟蹤它,因為這將是我們檢索結果的方式。

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 = "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

現在我們已經發送了請求,我們需要等待批次完成。即使我們使用的是較慢的批次 API,這個過程通常只需幾分鐘。如果我們在完成之前嘗試獲取結果,我們將會出錯。我們可以使用 ID 在循環中檢查批次,然後再提取結果。

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"等待 OpenAI(批次狀態為 {result.status}")
        	sleep(5)
        	continue
    	else:
        	break
	if result.status in ["failed", "expired",
                 	"canceling", "canceled"]:
    	raise ValueError(f"OpenAI 批次 {batch_id} 失敗")

獲得實際結果非常簡單。一旦我們獲得綠燈,我們可以直接從結果對象中提取文本。返回的值是一系列以 JSON 格式的字符串,格式與我們用於請求的類似。一旦我們加載 JSON,OpenAI 的回應就埋藏在 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

結論

就這樣!我們現在可以使用 OpenAI 將我們的博客文章(或任何其他內容)翻譯成我們想要的任何語言。在運行翻譯之前,請確保提前進行任何編輯,因為我無法保證模型的行為。我已經進一步自動化了這個過程。我的系統自動接收 Markdown 文件。它還將文件拆分成較小的塊,因為我注意到如果給 AI 一個大型文件進行翻譯,它可能會有點分心。我已經將這個放入一個單獨的庫,你可以在 這裡 找到。這段代碼是 MIT 許可的,所以隨意使用。