pretty code

2026年5月18日 星期一

PDF 翻譯 Part 02

前天使用自製工具翻譯了一份文件,大概快 900 頁,下面是 API 使用情況。

=== Summary ===
Paragraphs translated : 4186
Input tokens          : 211,790
Output tokens         : 302,468
Estimated cost        : $7.0905 USD

還不賴,比一本書便宜。

另外,PDF 翻譯工具需改進,一個是動態調整 RPM 限制,另一個則是要考慮如何保存中間結果。

因為我遇到一個 5000 頁的文件,做到約 2250 頁時,系統當機,應該是我同時做很多事,導致記憶體用到快 16 G。


AI 給的 RPM 修改建議方向

先做:查你目前的狀態(5 分鐘)

查 Tier 在哪裡

方法 1:Console 介面

  1. 登入 console.anthropic.com
  2. 左側選單 → SettingsLimits(或 Plans & Billing
  3. 會看到「Tier 1 / 2 / 3 / 4」字樣,以及對應的 RPM、ITPM、OTPM 數字

方法 2:用 curl 直接問 API(更準)

打開 cmd 或 PowerShell,把 YOUR_KEY 換成你的 API key:

curl https://api.anthropic.com/v1/organizations/rate_limits ^
  -H "anthropic-version: 2023-06-01" ^
  -H "x-api-key: YOUR_KEY"

回傳的 JSON 裡會直接告訴你每個模型的 RPM/ITPM/OTPM,對照下面的表就知道 Tier:

Tier Sonnet RPM Sonnet ITPM
1 50 30,000
2 1,000 80,000
3 2,000 800,000
4 4,000 2,000,000

查 prompt caching 有沒有開

直接搜你的翻譯工具程式碼,看 system 那段:

# 在你的翻譯工具資料夾
findstr /S "cache_control" *.py
  • 有輸出 → 有開 caching
  • 沒輸出 → 沒開 caching

或更簡單:跑一次翻譯,在你的程式裡 print 出 response.usage,看有沒有這兩個欄位:

  • cache_creation_input_tokens(第一次跑會有)
  • cache_read_input_tokens(第二次以後會有)

兩個都是 0 → 沒開 caching。


然後:直接用 Batch API 改造你的工具

既然你選「注重速度,可以掛著等」,Batch API 是完美解答。我直接幫你準備好可以丟給 Claude Code 的修改說明。

Batch API 對你的好處(再次確認)

  • 不受 RPM 限制 — 5,000 段一次提交也沒事
  • 價格 5 折(input 和 output 都打 5 折)
  • 24 小時保證,實測通常 1–2 小時
  • 與 prompt caching 可疊加(更便宜)
  • ⚠️ 結果是「全部一起回」,不能邊跑邊看
  • ⚠️ 中途無法取消個別請求

給 Claude Code 的修改 prompt

你可以新開一個 Claude Code session,把這份貼過去(同時也把你現有的翻譯工具程式碼放在資料夾裡):

請幫我把這個 PDF 翻譯工具的 Claude API 呼叫,從「同步單筆呼叫」改成「Batch API + Prompt Caching」模式。

## 背景
- 目前工具:用 Anthropic SDK 逐段呼叫 `client.messages.create()`
- 模型:claude-sonnet-4-6(或更新的 Sonnet)
- 痛點:800 頁論文要跑 4 小時,撞到 RPM 限制
- 工作流:可以掛著等,不需即時看結果

## 修改目標
1. 加上 prompt caching(針對 system prompt + glossary)
2. 改用 Batch API 一次提交全部段落
3. 加入 batch 狀態輪詢與結果取回
4. 保留現有的「翻譯完回貼成 PDF 圖層」功能不變

## 詳細修改項目

### 1. Prompt Caching

修改現有的 API 呼叫,把 system prompt 和 glossary 標記為可快取:

```python
# 從這樣:
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    system=SYSTEM_PROMPT,
    messages=[{"role": "user", "content": text_to_translate}]
)

# 改成這樣(system 改用 list 形式並加 cache_control):
response = client.messages.create(
    model="claude-sonnet-4-6",
    max_tokens=4096,
    system=[
        {
            "type": "text",
            "text": SYSTEM_PROMPT + "\n\n" + glossary_text,
            "cache_control": {"type": "ephemeral"}
        }
    ],
    messages=[{"role": "user", "content": text_to_translate}]
)
```

重要:cache_control 加在 system prompt 的「最後」一個元素。
注意:cache 至少需要 1024 tokens 才會生效,如果 SYSTEM_PROMPT + glossary 不到,
請先確認 token 數(用 client.messages.count_tokens()),不夠就提早 return「caching 不適用」並用原本的方式呼叫。

### 2. Batch API 流程

新增一個模式(用 CLI 參數 --batch 切換),流程如下:

#### Step 1: 抽取所有要翻譯的段落
從 PDF 抽出所有段落,建立一個 list of (segment_id, text) tuple。

#### Step 2: 建立 batch requests

```python
from anthropic.types.messages.batch_create_params import Request

requests = []
for seg_id, text in segments:
    requests.append(Request(
        custom_id=seg_id,  # 用來回對映,例如 "page-12-para-3"
        params={
            "model": "claude-sonnet-4-6",
            "max_tokens": 4096,
            "system": [
                {
                    "type": "text",
                    "text": SYSTEM_PROMPT + "\n\n" + glossary_text,
                    "cache_control": {"type": "ephemeral"}
                }
            ],
            "messages": [{"role": "user", "content": text}]
        }
    ))
```

注意:一次 batch 最多 100,000 個請求或 256 MB,請判斷是否需要分批提交。

#### Step 3: 提交 batch

```python
batch = client.messages.batches.create(requests=requests)
print(f"Batch ID: {batch.id}")
print(f"Status: {batch.processing_status}")

# 把 batch.id 存到本地檔案(例如 .batch_state.json),
# 這樣程式中斷後還能用同個 batch_id 繼續輪詢
```

#### Step 4: 輪詢狀態

```python
import time

while True:
    batch = client.messages.batches.retrieve(batch.id)
    counts = batch.request_counts
    print(f"Status: {batch.processing_status} | "
          f"Done: {counts.succeeded}/{counts.processing + counts.succeeded + counts.errored + counts.canceled + counts.expired}")
    
    if batch.processing_status == "ended":
        break
    
    time.sleep(60)  # 每分鐘查一次
```

#### Step 5: 取回結果

```python
results = {}
for result in client.messages.batches.results(batch.id):
    if result.result.type == "succeeded":
        translated = result.result.message.content[0].text
        results[result.custom_id] = translated
    elif result.result.type == "errored":
        print(f"Error on {result.custom_id}: {result.result.error}")
        results[result.custom_id] = None  # 標記失敗,後面用單筆 API 重跑
```

#### Step 6: 重跑失敗的段落
對 results 裡值為 None 的 custom_id,用原本的單筆 messages.create 重新翻譯。

#### Step 7: 把翻譯結果回貼成 PDF 圖層
沿用現有的回貼邏輯。

### 3. CLI 介面

請新增三個指令:

```bash
# 提交 batch 並等待完成(前景模式)
python translator.py --batch input.pdf output.pdf

# 提交 batch 立刻退出(背景模式)
python translator.py --batch-submit input.pdf
# 程式印出 Batch ID,使用者可以關掉終端機

# 輪詢狀態
python translator.py --batch-status BATCH_ID

# 取回結果並產出 PDF
python translator.py --batch-fetch BATCH_ID output.pdf
```

背景模式方便你睡覺前提交、隔天起床取結果。

### 4. 錯誤恢復

請建立一個 `.batch_state.json`,內容如下:

```json
{
  "batch_id": "msgbatch_xxx",
  "submitted_at": "2026-05-17T10:00:00",
  "input_pdf": "path/to/input.pdf",
  "output_pdf": "path/to/output.pdf",
  "segment_map": {
    "page-12-para-3": "...原文段落...",
    ...
  }
}
```

這樣即使程式或電腦中斷,重新執行 `--batch-status` 或 `--batch-fetch` 還能繼續。

### 5. 成本與時間預估

請在提交前印出:
- 預估 input tokens 總數(用 count_tokens API 或保守估算)
- 預估 cost(Sonnet 4.6 batch 價格:$1.50 / MTok input, $7.50 / MTok output)
- 預估完成時間(保守說「1-24 小時」)

讓使用者確認後才真正提交。

## 不要做的事
- 不要拿掉原本的「逐段呼叫」模式,保留它做為 fallback
- 不要在 batch 提交後鎖死 UI,使用者要能關掉終端機去做別的事
- 不要假設 batch 一定 1 小時內完成,要支援 24 小時等待

改完之後的預期效果

以你 800 頁論文為例:

方案 時間 你需要在電腦旁
現況 ~4 小時 全程
+ Prompt caching ~3 小時 全程
+ Tier 升級 (-> 2) ~30 分鐘 全程
改 Batch API 1–2 小時 0 分鐘(掛著等)
Batch + Tier 升級 1–2 小時 0 分鐘

Batch API 對你的價值不只是「快」,而是「解放你的時間」 — 提交完就可以關電腦睡覺,醒來結果就在那。

成本方面:800 頁論文(假設 50 萬 input tokens、20 萬 output tokens)大致估算:

方案 預估成本(粗估)
現況 ~$4.50
+ Caching ~$3.00
+ Batch API ~$2.25
Caching + Batch ~$1.50

數字僅供參考,實際看你的文件密度。改完最多省 2/3 成本,外加 4 小時變 1-2 小時、還不用顧。


給你的執行順序建議

  1. 先查 Tier(用上面 curl 或 console)
  2. 新開 Claude Code session,把你的翻譯工具資料夾打開,把上面那段 prompt 貼進去
  3. 試跑一份小檔案(10-20 頁)驗證 batch 流程沒問題
  4. 正式跑 800 頁那份,掛著去睡覺
  5. 醒來檢查結果,用我們之前規劃的那個 PDF Compare Viewer 並排看 Google Translate 版 vs Claude API 版

整個工作流就完整了。

沒有留言: