跳至主要內容

使用 mitmproxy 查看 AI 程式碼工具的網路通訊

透過將 mitmproxy 設定為中間人代理,可以即時查看 AI 程式碼工具在背後進行的 API 通訊內容。

運作原理​

許多 AI 程式碼工具是以 Node.js 開發的應用程式,透過 HTTPS 與外部 API 進行通訊。將 mitmproxy 作為中間人代理插入,並讓 Node.js 信任 mitmproxy 的 CA 憑證,即可解密加密的通訊內容並即時查看。

安裝​

使用 uv 安裝 mitmproxy 最為方便。

uv tool install mitmproxy

代理設定​

在啟動工具之前,先設定以下環境變數。

$env:HTTPS_PROXY = "http://127.0.0.1:8080"
$env:HTTP_PROXY = "http://127.0.0.1:8080"
$env:NODE_EXTRA_CA_CERTS = "$env:USERPROFILE\.mitmproxy\mitmproxy-ca-cert.pem"

關於 NODE_EXTRA_CA_CERTS​

只設定 HTTPS_PROXY 和 HTTP_PROXY 會導致 Node.js 的 TLS 驗證失敗而無法通訊。mitmproxy 在中繼 HTTPS 時使用自簽憑證,而 Node.js 預設會拒絕該憑證。

在 NODE_EXTRA_CA_CERTS 中指定 mitmproxy CA 憑證的路徑後,Node.js 便會信任該憑證,通訊也就能順利建立。

產生 CA 憑證​

mitmproxy 在首次啟動時會自動產生 CA 憑證,並儲存至 ~\.mitmproxy\。若尚未產生,啟動一次即可。

mitmweb

瀏覽器會自動開啟,在 http://127.0.0.1:8081 顯示代理管理介面。

啟動工具​

在另一個已設定環境變數的終端機中啟動工具。

開始操作工具後,mitmweb 畫面上就會出現請求流量。

擷取到的通訊內容​

端點​

工具會向以下端點發送請求。

POST https://api.example.com/v1/messages

請求標頭​

x-service-version: ...
content-type: application/json
x-api-key: sk-...

請求本文​

{
"model": "model-name",
"max_tokens": 16000,
"stream": true,
"system": [
{
"type": "text",
"text": "..."
}
],
"messages": [
{
"role": "user",
"content": "..."
}
],
"tools": [
{
"name": "Read",
"description": "...",
"input_schema": {}
}
]
}

設定 stream: true 後,會以 Server-Sent Events (SSE) 格式接收串流回應。

回應​

data: {"type":"content_block_start","index":0,"content_block":{"type":"text","text":""}}
data: {"type":"content_block_delta","index":0,"delta":{"type":"text_delta","text":"Hello"}}
...
data: {"type":"message_stop"}

可以了解到的資訊​

項目內容
API 端點api.example.com/v1/messages
認證方式API 金鑰 (x-api-key 標頭)
串流方式SSE 格式
工具定義每次請求都包含
系統提示詞數千至數萬 token 規模

系統提示詞中包含 AI 程式碼工具的運作原則、可用工具的說明及注意事項等內容。

總結​

  • 關鍵在於透過 NODE_EXTRA_CA_CERTS 讓 Node.js 信任 mitmproxy 的 CA 憑證
  • API 通訊採用 SSE 串流格式
  • 可以查看工具定義和系統提示詞等 AI 程式碼工具的內部結構

在 Amazon Cognito 設定密碼金鑰 (WebAuthn)

在使用 AWS SAM 建立的檔案儲存 API 的使用者認證中,我使用了 Amazon Cognito。最近我新增了透過密碼金鑰 (WebAuthn) 登入的功能,因此總結一下設定內容。

前提條件:使用密碼金鑰所需的 Cognito 設定​

要在 Cognito 中使用密碼金鑰,必須具備以下所有條件。

要件本次的架構
UserPool 層級ESSENTIALS 以上
管理式登入v2 (新的登入 UI)
自訂網域login.example.com (用於 Relying Party ID)

Cognito 的密碼金鑰必須從管理式登入 v2 UI 中註冊及使用。在 LITE 層級 (免費) 中無法使用 WebAuthn,因此需要 ESSENTIALS 層級。

認證流程​

密碼金鑰註冊流程​

第一次登入時使用密碼,然後從帳戶設定中註冊密碼金鑰。

密碼金鑰登入流程​

註冊後,可以透過「使用密碼金鑰登入」按鈕直接認證。

設定內容​

為了新增密碼金鑰,我在 template.yaml (SAM 模板) 上進行了僅 6 行的變更。

變更前​

UserPool:
Type: AWS::Cognito::UserPool
Properties:
# ...
Policies:
PasswordPolicy:
MinimumLength: 8
# ...
MfaConfiguration: "OFF"

變更後​

UserPool:
Type: AWS::Cognito::UserPool
Properties:
# ...
Policies:
PasswordPolicy:
MinimumLength: 8
# ...
SignInPolicy:
AllowedFirstAuthFactors:
- PASSWORD
- WEB_AUTHN # ← 新增密碼金鑰
MfaConfiguration: "OFF"
WebAuthnRelyingPartyID: login.example.com # ← 指定 RP ID
WebAuthnUserVerification: required # ← 強制要求生物認證

各參數的意義​

SignInPolicy.AllowedFirstAuthFactors​

初步認證步驟中可用的認證方式列表。只有 PASSWORD 時僅能使用密碼,若加上 WEB_AUTHN 則能選擇密碼金鑰。

WebAuthnRelyingPartyID​

WebAuthn 的 Relying Party ID (RP ID)。密碼金鑰將綁定到此網域生成和儲存,因此必須與 實際提供登入頁面的網域一致。

本次直接使用自訂網域 login.example.com。若使用 Cognito 的預設網域 (xxx.auth.ap-northeast-1.amazoncognito.com),則需指定該網域。

WebAuthnUserVerification​

使用密碼金鑰時的用戶確認等級。

值說明
required強制要求生物認證或 PIN 等的身份確認
preferred儘可能要求身份確認,但可不要求
discouraged省略身份確認 (不使用生物認證等)

為了提高安全性,選擇了 required。

管理式登入的 UI​

在管理式登入 v2 的界面中,密碼金鑰設定後,登入畫面將自動新增「使用密碼金鑰登入」按鈕。第一次註冊時需先使用密碼登入,然後可在帳戶設定中新增密碼金鑰。

部署​

sam build
sam deploy --no-confirm-changeset

由於 samconfig.toml 中已定義堆疊名稱、區域及參數,因此每次部署時不需要指定選項。

總結​

啟用 Cognito 密碼金鑰的重點總結如下:

  1. 設定為 ESSENTIALS 層級 (LITE 不支持 WebAuthn)
  2. 使用 管理式登入 v2
  3. 指定自訂網域 (或 Cognito 預設網域) 為 RP ID
  4. 在 SignInPolicy.AllowedFirstAuthFactors 中增加 WEB_AUTHN
  5. 使用 WebAuthnUserVerification: required 強制要求生物認證

僅透過 6 行的變更,即可使用密碼金鑰登入。此時仍保留密碼,逐步向密碼金鑰過渡,這正是 Cognito 的便利之處。

在 PowerShell 中呼叫 Vertex AI Gemini API

介紹如何從 PowerShell 經由 Google Cloud 的 Vertex AI 呼叫 Gemini 模型。涵蓋 OpenAI 相容端點與原生 Gemini 端點兩種方式。

認證方式​

gcloud auth(推薦)​

不需要 API 金鑰,可直接使用現有的 Google Cloud 認證資訊。

$accessToken = (gcloud auth print-access-token)

API 金鑰​

$apiKey = $env:VERTEX_API_KEY

端點​

OpenAI 相容端點(gcloud auth 認證)​

https://{region}-aiplatform.googleapis.com/v1beta1/projects/{projectId}/locations/{region}/endpoints/openapi/chat/completions

請求與回應格式與 OpenAI API 相同。模型名稱需加上 google/ 前綴(例:google/gemini-2.5-flash-lite)。

原生 Gemini 端點(API 金鑰認證)​

https://{region}-aiplatform.googleapis.com/v1/projects/{projectId}/locations/{region}/publishers/google/models/{model}:generateContent

若需要串流請使用 :streamGenerateContent。

基本呼叫範例​

OpenAI 相容(gcloud auth)​

$projectId = "your-project-id"
$region = "us-central1"
$model = "google/gemini-2.5-flash-lite"
$accessToken = (gcloud auth print-access-token)

$body = @{
model = $model
messages = @(
@{
role = "user"
content = "東京的人口是多少?"
}
)
} | ConvertTo-Json -Depth 10

$uri = "https://$region-aiplatform.googleapis.com/v1beta1/projects/$projectId/locations/$region/endpoints/openapi/chat/completions"

$response = Invoke-RestMethod `
-Uri $uri `
-Method Post `
-ContentType "application/json" `
-Headers @{ Authorization = "Bearer $accessToken" } `
-Body $body

$response.choices[0].message.content

原生 Gemini(API 金鑰)​

$projectId = "your-project-id"
$region = "us-central1"
$model = "gemini-2.5-flash-lite"
$apiKey = $env:VERTEX_API_KEY

$body = @{
contents = @(
@{
role = "user"
parts = @(
@{ text = "東京的人口是多少?" }
)
}
)
} | ConvertTo-Json -Depth 10

$uri = "https://$region-aiplatform.googleapis.com/v1/projects/$projectId/locations/$region/publishers/google/models/${model}:generateContent?key=$apiKey"

$response = Invoke-RestMethod `
-Uri $uri `
-Method Post `
-ContentType "application/json" `
-Body $body

$response.candidates[0].content.parts[0].text

回應結構​

OpenAI 相容​

$response.choices[0].message.content # 產生的文字
$response.usage.total_tokens # 總 Token 數
$response.model # 使用的模型

原生 Gemini​

$response.candidates[0].content.parts[0].text # 產生的文字
$response.usageMetadata.totalTokenCount # 總 Token 數
$response.modelVersion # 使用的模型版本

串流(streamGenerateContent)會回傳多個 chunk 的陣列,需將文字串接取出。

$fullText = ($response | ForEach-Object {
$_.candidates[0].content.parts[0].text
}) -join ""

加入系統提示​

OpenAI 相容​

$body = @{
model = $model
messages = @(
@{
role = "system"
content = "您是用日語回答的 AI 助理,請簡潔回覆。"
}
@{
role = "user"
content = "光速是多少?"
}
)
} | ConvertTo-Json -Depth 10

原生 Gemini​

$body = @{
system_instruction = @{
parts = @(
@{ text = "您是用日語回答的 AI 助理,請簡潔回覆。" }
)
}
contents = @(
@{
role = "user"
parts = @(@{ text = "光速是多少?" })
}
)
} | ConvertTo-Json -Depth 10

多回合對話​

將歷史對話依序放入陣列即可實現多回合對話。

OpenAI 相容​

$body = @{
model = $model
messages = @(
@{ role = "user"; content = "你比較喜歡貓還是狗?" }
@{ role = "assistant"; content = "我喜歡貓。" }
@{ role = "user"; content = "為什麼?" }
)
} | ConvertTo-Json -Depth 10

原生 Gemini​

助理的角色需指定為 "model"。

$body = @{
contents = @(
@{
role = "user"
parts = @(@{ text = "你比較喜歡貓還是狗?" })
}
@{
role = "model"
parts = @(@{ text = "我喜歡貓。" })
}
@{
role = "user"
parts = @(@{ text = "為什麼?" })
}
)
} | ConvertTo-Json -Depth 10

可用模型​

模型OpenAI 相容名稱特性
gemini-2.5-flash-litegoogle/gemini-2.5-flash-lite輕量、快速、低成本
gemini-2.5-flashgoogle/gemini-2.5-flash平衡型
gemini-2.5-progoogle/gemini-2.5-pro高精度,適合複雜任務

選擇方式的判斷基準​

情況推薦方式
已有 GCP 認證的環境(開發、CI 等)OpenAI 相容 + gcloud auth
只能使用 API 金鑰原生 Gemini
從 OpenAI 移轉中OpenAI 相容(最小化程式碼變更)
需要串流原生 Gemini

注意事項​

  • 請勿將 API 金鑰硬寫在腳本中,應從環境變數($env:VERTEX_API_KEY)讀取。
  • gcloud auth 的 token 有效期限約為 1 小時,長時間執行的腳本需要定期重新取得。
  • 每個專案有各自的速率限制與配額,大量發送請求前請事先確認。

什麼是 CORS?從安全性角度為初學者解說

説明 Web 瀏覽器的安全功能 CORS (Cross-Origin Resource Sharing),涵蓋「為什麼需要」和「有什麼風險」,為初學者量身打造的解説。正確理解 CORS 能夠實現安全的 Web 開發。

CORS 出現的背景:同源政策 (Same-Origin Policy)​

在 1990 年代初期,JavaScript 被引入瀏覽器時,Web 安全的概念幾乎不存在。當時,惡意網站可以自由存取其他網站的資料,容易導致工作階段劫持 (Session Hijacking) 和資料竊取。

為了解決這個問題,引入了一項稱為同源政策 (Same-Origin Policy) 的限制。這是一個簡單而強大的規則:「從網頁加載的 JavaScript 無法存取該網頁不同源的資料」。

例如,從 https://www.example.com 加載的 JavaScript 無法存取 https://www.bank.com 的資料。這樣,即使使用者登入銀行網站後訪問惡意網站,銀行資訊也不會被盜取。

什麼是源 (Origin)​

源 (Origin) 由以下三個要素決定:

  • 協議 (Protocol):http:// 或 https://
  • 主機 (Host/網域):example.com 或 api.example.com
  • 連接埠 (Port):80 或 8080

例如,

URL協議主機連接埠源
https://www.example.com/pageHTTPSwww.example.com443 (預設)https://www.example.com
https://api.example.com/dataHTTPSapi.example.com443 (預設)https://api.example.com

由於主機不同,它們被視為不同的源。

同源政策防止的事項​

JavaScript 的 XHR (XMLHttpRequest) 或 Fetch API 對不同源的請求受到限制。

例:evil.com 上的惡意指令碼

fetch('https://bank.example.com/api/transfer', {
method: 'POST',
body: JSON.stringify({ amount: 1000000 })
});

沒有同源政策的話,惡意網站 (evil.com) 的 JavaScript 可以在使用者登入銀行網站的狀態下發送轉帳請求。防止這種情況就是同源政策的目的。

為什麼需要 CORS​

然而,現代 Web 開發經常涉及多個源的協作。

  • 前端:https://www.example.com
  • API 伺服器:https://api.example.com
  • CDN / 靜態文件:https://cdn.example.com

這些是由同一公司營運的正當通訊。但如果受到同源政策限制,應用程式就無法運作。

這時候 CORS (Cross-Origin Resource Sharing) 就派上用場了。

什麼是 CORS:明確允許存取​

CORS 是伺服器明確聲明「允許來自此源的請求」的機制。

伺服器只需返回以下回應標頭,瀏覽器就會放寬限制。

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: GET, POST, PUT
Access-Control-Allow-Headers: Content-Type, Authorization

除非伺服器表示「允許」,否則瀏覽器不會將請求結果傳回 JavaScript。這樣既能實現跨源存取,又能保持安全性。

CORS 的安全風險:常見的設定錯誤​

雖然 CORS 很便利,但設定不當會造成安全漏洞。

錯誤:允許所有源​

Access-Control-Allow-Origin: *

這表示「世界上任何人都可以存取此伺服器」。

// https://evil.com 的 JavaScript
fetch('https://api.example.com/user/profile')
.then(r => r.json())
.then(data => {
// 竊取使用者個人資料的處理
console.log(data);
});

對於包含認證憑證(如 Cookie)的請求特別危險,使用者在登入 api.example.com 後訪問 evil.com 時,個人資訊可能被盜取。

fetch('https://api.example.com/user/profile', {
credentials: 'include' // 包含 Cookie
})

包含認證資訊時,不能使用 Access-Control-Allow-Origin: *。必須明確指定特定的源。

Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Credentials: true

錯誤:直接允許使用者指定的 URL​

// 危險的實現範例(伺服器端)
const origin = request.headers.get('Origin');
response.headers.set('Access-Control-Allow-Origin', origin); // 原樣返回!

這樣做會允許來自 https://evil.com 的請求,回應會帶有 Access-Control-Allow-Origin: https://evil.com,容易被濫用。

正確做法是準備白名單,只允許列表中的源。

const allowedOrigins = [
'https://www.example.com',
'https://admin.example.com'
];

if (allowedOrigins.includes(origin)) {
response.headers.set('Access-Control-Allow-Origin', origin);
}

錯誤:允許所有標頭​

Access-Control-Allow-Headers: *

這表示「接受任何標頭」,允許透過自訂標頭注入惡意資料的攻擊。

只列出必要的標頭。

Access-Control-Allow-Headers: Content-Type, Authorization

CORS 預檢請求:瀏覽器的事前確認​

對於簡單請求以外的請求(GET、HEAD、POST),瀏覽器會自動發送 OPTIONS 方法的請求來確認「這樣可以嗎?」這被稱為預檢請求 (Preflight Request)。

1. JavaScript 嘗試發送 PUT 請求

2. 瀏覽器自動發送 OPTIONS 預檢請求
OPTIONS /api/resource HTTP/1.1
Origin: https://www.example.com
Access-Control-Request-Method: PUT
Access-Control-Request-Headers: Content-Type

3. 伺服器回應「OK」
HTTP 200 OK
Access-Control-Allow-Origin: https://www.example.com
Access-Control-Allow-Methods: PUT
Access-Control-Allow-Headers: Content-Type

4. 瀏覽器發送實際的 PUT 請求

如果伺服器不支援 OPTIONS 方法,預檢就會失敗,實際請求也不會被發送。

要點:

  • 在白名單中明確指定允許的源
  • 使用 credentials: true 處理包含 Cookie 認證的請求
  • 只允許必要的方法和標頭
  • OPTIONS 預檢請求

常見疑問​

Q. 遇到 CORS 錯誤。可以允許所有源來解決嗎?​

A: 不可以。可能會暫時有效,但在生產環境中允許 * 是安全風險。需要重新檢查伺服器端設定或重新設計 API。

Q. 想在本地開發時存取不同連接埠的源。可以嗎?​

A: 在本地開發環境中只停用 CORS 是可以的。

Q. 從行動應用程式呼叫 API 時,CORS 有影響嗎?​

A: CORS 是瀏覽器的安全功能,所以對行動應用程式沒有影響。取而代之,需要使用 API 金鑰或 OAuth 實作認證和授權。

總結​

重點說明
CORS 的目的在保持瀏覽器安全性的同時允許跨源存取
危險的設定對需要認證的 API 使用 Access-Control-Allow-Origin: *
正確的設定在白名單中明確指定允許的源
包含 Cookie 時必須指定 Access-Control-Allow-Credentials: true 和具體的源
預檢PUT/DELETE 等複雜請求需要瀏覽器用 OPTIONS 進行事前確認

CORS 不只是「導致錯誤的原因」,而是 Web 安全的重要機制。設定錯誤可能導致安全事故,因此需要謹慎處理。

參考資料​

使用 OpenAI API 開發支援多語言的翻譯 CLI 工具 translate-mcp

translate-mcp 是一個使用 OpenAI API 的翻譯工具。它支援 CLI 模式和 MCP 伺服器的兩種使用方式,能夠應對從想要翻譯整個文件到希望與 AI 工具整合的各種場合。

translate-mcp 是什麼​

translate-mcp 是一款 專為翻譯而設的工具,使用 OpenAI API。它是用 Python 實現的,具有以下兩種使用方法。

  1. CLI 模式: 可以直接從命令行翻譯文件
  2. MCP 伺服器模式: 充當 Model Context Protocol (MCP) 伺服器,整合到 AI 工具中

特徵​

  • 多語言支援: 支援多種語言
  • 簡單易用: 只需一個 API 金鑰便可開始
  • 兩種使用方式: 既可作為 CLI 腳本,也能作為 MCP 伺服器運行
  • 輕量級: 無需依賴外部庫,僅依賴 OpenAI API
  • 錯誤處理: CLI 模式以 stderr 輸出,MCP 模式以 JSON 格式回傳

安裝​

前提條件​

  • 已安裝 Python
  • 已獲取 OpenAI API 金鑰

安裝 uv​

由於 translate-mcp 由 uv 管理,因此需要先安裝 uv。

安裝 uv 的方法參見 Installation | uv。

# macOS / Linux
curl -LsSf https://astral.sh/uv/install.sh | sh

# Windows (PowerShell)
powershell -ExecutionPolicy ByPass -c "irm https://astral.sh/uv/install.ps1 | iex"

安裝 translate-mcp​

如果使用 uv,可以使用以下命令進行安裝。

uv tool install git+https://github.com/Himeyama/translate-mcp

如果使用 uvx,則無需安裝,直接執行即可。

uvx git+https://github.com/Himeyama/translate-mcp --help

使用方法​

CLI 模式​

翻譯文件時,可以使用以下命令執行。

translate --input blog/2026-04-02-example.md --from Japanese --to English

結果將輸出至標準輸出,並可透過重定向保存到文件中。

translate \
--input blog/2026-04-02-example.md \
--from Japanese \
--to English \
--output i18n/en/blog/2026-04-02-example.md

參數​

  • --mcp: MCP 模式
  • --input: 待翻譯文件的路徑
  • --from: 原始語言(例如: Japanese、English)
  • --to: 目標語言(例如: English、Taiwanese)
  • --output (可選): 儲存翻譯後文本的位置
  • --model (可選): OpenAI 模型(例如: gpt-5-mini)
  • --debug (可選): 調試模式

錯誤處理​

若發生錯誤,將輸出至 stderr。

MCP 伺服器模式​

啟動為 MCP 伺服器,並可供 Claude Code 或其他 AI 工具使用。

translate --mcp

實例​

以下說明如何將部落格文章從日文翻譯成英文和繁體中文(台灣)。

日文版本(原文)​

# blog/2026-04-02-example.md 中有日文的文章

生成英文版本​

translate \
--input blog/2026-04-02-example.md \
--from Japanese \
--to English > i18n/en/docusaurus-plugin-content-blog/2026-04-02-example.md

生成台灣版本(繁體中文)​

translate \
--input blog/2026-04-02-example.md \
--from Japanese \
--to Taiwanese > i18n/zh-TW/docusaurus-plugin-content-blog/2026-04-02-example.md

優缺點​

優點​

  • 高精度: 使用 OpenAI 的高品質模型(如 GPT-4)
  • 技能支援: 可整合至 ChatGPT 和 Claude 的多種工具
  • 簡單: 設置和使用都非常簡單
  • 可定制: 由於源代碼公開,因此可進行自定義

缺點​

  • 需支付 API 費用: 根據翻譯量,OpenAI API 需要收費
  • 必須連網: 需調用 API,因此無法離線使用
  • 速率限制: 受限於 OpenAI API 的速率限制

總結​

translate-mcp 是一個 簡單且高品質的翻譯工具,利用 OpenAI API 開發。它在部落格文章的多語言支援、文件翻譯以及與 AI 工具整合等多種場合中均能發揮作用。

特別是在想要使用 Docusaurus 等靜態網站生成器支援多語言時,作為自動化腳本的用途效果顯著。

參考資料​

比較 Anthropic API 與 AWS Bedrock 的費用

Claude 若要透過 API 使用,除了直接使用 Anthropic API 外,也能經由 AWS Bedrock、Google Vertex AI、Microsoft Azure (Azure AI Foundry) 使用。基本價格各路徑幾乎相同,但在批次處理與與雲端生態系統整合面會有差異。

單位:USD / 1M 代幣 (MTok)。資料截至 2026 年 3 月。

基本價格(按需)​

模型項目Anthropic APIBedrockVertex AIAzure
Claude Opus 4.6輸入$5.00$5.00$5.00$5.00
輸出$25.00$25.00$25.00$25.00
Claude Sonnet 4.6輸入$3.00$3.00$3.00$3.00
輸出$15.00$15.00$15.00$15.00
Claude Haiku 4.5輸入$1.00$1.00$1.00$1.00
輸出$5.00$5.00$5.00$5.00
Claude Sonnet 4.5輸入$3.00$3.00$3.00$3.00
輸出$15.00$15.00$15.00$15.00

基本價格在各路徑相同。

不過在 Vertex AI 若不是使用全球端點而指定區域端點(regional endpoint),標準價格會額外加收 10%。Bedrock 有 Long Context 變體(另有 SKU),但價格相同。Anthropic API 則已將 Long Context 整合在一般模型中。

快取費用​

Prompt 快取(Prompt Caching)的費用在各路徑也相同。

模型快取類型Anthropic APIBedrockVertex AIAzure
Claude Opus 4.65 分快取寫入$6.25$6.25$6.25$6.25
1 小時快取寫入$10.00$10.00$10.00$10.00
快取讀取$0.50$0.50$0.50$0.50
Claude Sonnet 4.65 分快取寫入$3.75$3.75$3.75$3.75
1 小時快取寫入$6.00$6.00$6.00$6.00
快取讀取$0.30$0.30$0.30$0.30
Claude Haiku 4.55 分快取寫入$1.25$1.25$1.25$1.25
1 小時快取寫入$2.00$2.00$2.00$2.00
快取讀取$0.10$0.10$0.10$0.10

快取寫入依 TTL 分為 5 分(短期)和 1 小時(長期)兩種。長期快取的寫入成本較高,但如果系統提示(system prompt)很長且會被重複參考,透過節省讀取費用通常是划得來的。

批次處理費用​

Bedrock、Vertex AI、Anthropic API 三者的非同步批次 API 可享按需價格的 50% 折扣。Azure 目前未明示。

模型批次輸入批次輸出
Claude Opus 4.6$2.50$12.50
Claude Sonnet 4.6$1.50$7.50
Claude Haiku 4.5$0.50$2.50
Claude Sonnet 4.5$1.50$7.50

若要大量處理資料(例如日誌分析、向量嵌入生成等)並頻繁使用批次處理,無論走哪個路徑都能把成本砍半。

生態系比較​

比較重點Anthropic APIBedrockVertex AIAzure
基本價格相同相同相同相同
區域加價——+10%(區域端點)—
批次處理(50% OFF)○○○未明示
東京區域—○○—
IAM / 審計日誌整合—AWSGoogle CloudAzure
VPC / PrivateLink—○○○
計費整合Anthropic 直接AWSGoogle CloudAzure
新功能推出速度最快會延遲會延遲會延遲

新功能(例如 Extended Thinking)通常會先在 Anthropic API 上推出,向 Vertex AI、Bedrock、Azure 的推展有時會晚幾週。

如何選擇​

  • 單純使用/原型開發:Anthropic API,只要一組 API 金鑰即可啟動,新功能也能最先使用。
  • 已整合到 AWS:若需 IAM、CloudWatch、VPC,選 Bedrock。支援東京區域。
  • 已整合到 Google Cloud:Vertex AI 是自然的選擇,但注意區域端點會加收 10%。
  • 已整合到 Azure:可透過 Azure AI Foundry 使用,能整合到 Azure 的計費與管理。
  • 大量使用批次以節省成本:Bedrock、Vertex AI、Anthropic API 都有 50% OFF 的批次 API 可用。

參考​

即使是明朝體的文章,加粗常會變成黑體的理由

即使是以明朝體或襯線字體書寫的文章,加粗時常以黑體(無襯線字體)呈現。這是設計上刻意的選擇,主要有可讀性、視認性、與歷史慣習三個理由。

維持可讀性​

若單純把明朝體加粗,襯線(裝飾筆畫)與主筆畫的對比會變得過大,字形內部的空白(counter)容易消失。這在 Web 或螢幕上的小尺寸顯示尤其明顯,字形會潰掉而難以辨認。

黑體本身沒有襯線,因此即使提高字重也能保留字形內的空白,不會明顯降低可讀性。

明確傳達強調意圖​

直接改變字體家族,可以讓「這裡是強調」的意圖在視覺上被更即刻地傳達。與單純把明朝體變粗相比,切換到黑體更容易被人眼當作「不同資訊」來處理,強調效果更明確。

歷史與文化背景​

自日本活字印刷時代起,就有「強調使用不同字體」的慣例。將明朝體做得更粗在活字鑄造上會產生問題,因此會用完全不同的黑體來表示強調。這個慣習被沿用到數位排版與 Web。

在 CSS 中的實作​

若在 Web 以明朝體作為基底字體,可以用以下樣式將加粗切換為黑體。

body {
font-family: "Noto Serif JP", serif;
}

strong, b, h1, h2, h3, h4, h5, h6 {
font-family: "Noto Sans JP", sans-serif;
font-weight: 700;
}

這個做法也有實作上的優點。無須載入明朝體的多個字重,只要為本文提供 Regular,以及為強調提供黑體的 Bold,就能減少頁面載入成本。

總結​

日文(以及台灣中文排版時常類比的情況)中,加粗時使用黑體的理由可歸納為三點:

  1. 可讀性:黑體提高字重仍能保留字形內部空白,維持可讀性
  2. 視認性:改變字體家族比單純加粗更清楚地傳達強調
  3. 慣習:自活字印刷時代起的「強調用別書體」的做法被沿用

在 Web 上也因為相同理由採用這種組合,是設計與實作上都合理的選擇。

在 Docusaurus 部落格把 PageSpeed Insights 幾乎做滿分的方法 — SEO、效能、可及性

我把這個部落格的行動版 PageSpeed Insights 分數改善到 Performance 99、Accessibility 100、Best Practices 100、SEO 100。從 SEO、效能、可及性 三個面向整理我做過的改動。

改善前的問題​

在原生的 Docusaurus 部落格跑 PageSpeed Insights 時,發現了幾個問題。

  • SEO:沒有 meta description、沒有 OGP/Twitter Cards、沒有結構化資料、網站地圖的 priority 未設定
  • 效能:因為 Google Tag Manager (GTM) 同步載入導致未使用的 JS 過大、從外部 CDN 取得 avatar 成為瓶頸
  • 可及性:主要顏色的對比度未達 WCAG AA 標準

SEO 的改善​

新增 meta description、OGP、Twitter Cards​

在 docusaurus.config.ts 的 themeConfig.metadata 中加入站點共通的 meta 資訊。

themeConfig: {
metadata: [
{ name: 'description', content: "ひかりの技術メモ..." },
{ property: 'og:locale', content: 'ja_JP' },
{ name: 'twitter:card', content: 'summary_large_image' },
{ name: 'twitter:site', content: '@ptrqr' },
],
}

另外,透過 Swizzle 覆寫 src/theme/Layout/index.tsx,為沒有自訂 description 的頁面(例如部落格列表、標籤頁)設定以語系為單位的備用 description。

新增 robots.txt​

加入 static/robots.txt,明確告訴爬蟲網站地圖的位置。

BlogPosting JSON-LD(結構化資料)​

透過 Swizzle 覆寫 src/theme/BlogPostItem/index.tsx,在文章頁輸出包含 headline、datePublished、dateModified、author 的 BlogPosting JSON-LD。

不過後來發現 Docusaurus 內建的 BlogPostPage/StructuredData 已經會輸出相同的資料。把自訂的 JSON-LD 移除,改為在內建元件上加上 keywords 的備援(frontMatter.keywords → tags)。結構化資料重複會對 SEO 造成負面影響,整理這點相當重要。

WebSite JSON-LD​

在 docusaurus.config.ts 的 headTags 加入 WebSite 類型的 JSON-LD,讓 Google 能正確辨識站名。

headTags: [
{
tagName: 'script',
attributes: { type: 'application/ld+json' },
innerHTML: JSON.stringify({
'@context': 'https://schema.org',
'@type': 'WebSite',
name: 'ひかりの備忘録',
url: 'https://www.hikari-dev.com/',
}),
},
],

自動為所有文章生成 OGP 圖片​

建立 scripts/generate-ogp.js,自動根據標籤產生漸層背景的 OGP 圖片。如此一來,所有文章在社群分享時都會帶有吸睛的圖片。為每篇文章的 frontMatter 設定 image: 欄位,若文章已有專屬圖片則優先使用。

改善網站地圖​

使用 createSitemapItems 回呼把首頁 priority 設為 1.0、部落格文章設為 0.8。另外從 URL 含有的日期自動抽出 lastmod。

hreflang x-default​

在 src/theme/Root.tsx 注入帶有 hreflang="x-default" 的 <link>,將英文頁面(/en/...)對應到預設(日文) URL,讓搜尋引擎能正確辨識語言變體。

const defaultPath = pathname.replace(/^\/en(?=\/|$)/, '') || '/';
const xDefaultUrl = `${siteConfig.url}${defaultPath}`;

<Head>
<link rel="alternate" hreflang="x-default" href={xDefaultUrl} />
</Head>

效能的改善​

GTM 延遲載入​

移除 @docusaurus/plugin-google-gtag,改在 src/clientModules/gtag.js 裡於 window.load 事件之後動態注入 GTM 腳本。如此一來,可大幅減少阻塞初始渲染的未使用 JS。

function loadGtag() {
const script = document.createElement('script');
script.async = true;
script.src = `https://www.googletagmanager.com/gtag/js?id=${GA_ID}`;
document.head.appendChild(script);
}

window.addEventListener('load', loadGtag, { once: true });

在 SPA 的頁面切換時使用 Docusaurus 的 onRouteDidUpdate hook 手動呼叫 window.gtag。另外也使用 requestIdleCallback 在閒置時載入加以優化。

將 avatar 自行託管並轉為 WebP​

把 avatar 圖片從 GitHub CDN(avatars.githubusercontent.com)移到自行託管。GitHub CDN 的快取 TTL 很短(5 分鐘),在 PageSpeed Insights 上每次都會被視為外部請求。

把 avatar 轉為 WebP,檔案從 34 KB(PNG)降到 3.5 KB,約減少 90%。

圖片尺寸優化與修正 CLS​

  • 在 GitHub avatar URL 加上 ?size=64,把 460 px 縮到 64 px(減少 33 KB)
  • 在導航列 logo 加上 width/height 屬性以修正 CLS(Cumulative Layout Shift)
  • 在 <img> 標籤加入 loading="lazy"

導入 rspack / SWC​

安裝 @docusaurus/faster,把 webpack 換成 rspack + SWC + lightningCSS。

future: {
v4: true,
experimental_faster: true,
},

如此一來,建置速度和打包體積都改善了。

停用未使用的插件​

停用未使用的 docs 外掛,避免不必要的 JS 被傳到用戶端。

僅在行動裝置載入 Google Fonts​

Noto Sans JP 在我的站只在行動版需要。利用 matchMedia 判斷行動裝置,僅在行動裝置動態注入字型樣式表,桌面版因此減少約 130 KB 的未使用 CSS。

可及性的改善​

修正對比度​

把主要色從 #F15EB4 改為 #C82273,在白底下達到對比度 5.3:1(符合 WCAG AA)。深色模式則使用 #F36AB2(在暗底下為 7.0:1)。

文章日期文字的顏色改用 CSS 變數 --post-date-color 管理,淺色模式為 #595959(7.0:1),深色模式為 #9e9e9e。

統一字型​

把標題與 <strong> 的字型從 Noto Serif JP 改為 Noto Sans JP,以與內文保持一致性。

結果​

類別分數
Performance99
Accessibility100
Best Practices100
SEO100

在行動裝置上幾乎達到滿分。

總結​

對我影響最大的三項變更如下:

  1. GTM 的延遲載入:大幅減少未使用的 JS,效能分數大幅提升
  2. OGP 與結構化資料的整理:達成 SEO 100,社群分享時的呈現也改善
  3. 修正對比度:符合 WCAG AA 讓可及性達到 100

Docusaurus 預設就能生成高品質網站,但要在 PageSpeed Insights 上追求幾乎滿分,仍需針對 GTM 的載入策略、meta 資訊與可及性的細節做調整。希望對也在做同類改善的人有所幫助。

使用 pLaTeX 製作日文 PDF 的方法

整理了使用 pLaTeX 製作日文 PDF 文件的步驟。從範本的構成到編譯,以及常見錯誤的處理都會說明。

pLaTeX 是什麼​

pLaTeX 是支援日文組版的 LaTeX 實作。使用 platex 指令編譯 .tex 檔,然後用 dvipdfmx 轉成 PDF。

因為包含在 TeX Live 中,如果已安裝 TeX Live 就可以立即使用。TeX Live 的安裝步驟請參考「在 Linux 上安裝 TeX Live 2026」。

選擇文件類別​

在日文文件中可使用以下類別。

類別用途
jarticle論文、報告等短篇文件
jbook書籍、有章節的長篇文件
jreport技術報告(含 abstract + chapter 結構)
beamer投影片(簡報)

範本(jarticle)​

使用 jarticle 的基本範例如下。

顯示範例程式碼
% pLaTeX 範例文件 — 主要功能概覽
\documentclass[a4paper,12pt]{jarticle}

%% ========== 套件 ==========
\usepackage{amsmath, amssymb} % 數學式強化
\usepackage{graphicx} % 插入圖像
\usepackage{color} % 顏色設定
\usepackage{fancyhdr} % 頁首/頁尾
\usepackage{geometry} % 頁面版面
\usepackage{enumerate} % 列舉自訂
\usepackage{url} % URL 顯示
\usepackage{multicol} % 分欄
\usepackage{booktabs} % 高品質表格線
\usepackage{array} % 表格欄格式擴充
\usepackage{verbatim} % verbatim 環境
\usepackage{ascmac} % 框線環境(screen, itembox 等)
\usepackage{okumacro} % Ruby・圓點等(pLaTeX 常用)
\usepackage{setspace} % 行距調整
\usepackage{listings} % 原始碼清單
\usepackage{xcolor} % 顏色(listings 用)
\usepackage{caption} % 標題樣式

%% ========== 頁面版面 ==========
\geometry{top=25mm, bottom=25mm, left=25mm, right=25mm}
\setlength{\headheight}{17pt}
\addtolength{\topmargin}{-5pt}

%% ========== 頁首/頁尾 ==========
\pagestyle{fancy}
\fancyhf{}
\lhead{pLaTeX 範例文件}
\rhead{\today}
\cfoot{\thepage}
\renewcommand{\headrulewidth}{0.4pt}

%% ========== listings 設定 ==========
\lstset{
basicstyle=\ttfamily\small,
keywordstyle=\color{blue}\bfseries,
commentstyle=\color{green!50!black},
stringstyle=\color{red!70!black},
numbers=left,
numberstyle=\tiny\color{gray},
frame=single,
breaklines=true,
tabsize=4,
}

%% ========== 標題資訊 ==========
\title{\textbf{pLaTeX 主要功能範例}\\[5pt]
\large --- 日文 \LaTeX 的典型用法 ---}
\author{範例太郎\thanks{範例大學 資訊工程系}
\and 範例花子}
\date{\today}

%% ================================================================
\begin{document}
%% ================================================================

\maketitle
\thispagestyle{fancy}

\begin{abstract}
本文檔是示範 pLaTeX 主要功能的範例。
依序說明文件類別與套件的讀入、日文組版特有的功能、
數學式、表格、圖形、交叉參照、腳註、Ruby、圓點、
原始碼清單、分欄、自訂指令等內容。
\end{abstract}

\tableofcontents
\newpage

%% ================================================================
\section{日文組版的基本}
%% ================================================================

\subsection{和文・歐文的混合}

pLaTeX 可以自然地混合日文與歐文。
例如「Hello, World!」或 \texttt{LaTeX2e} 類的
ASCII 文字會自動插入適當的空白。

全形字與半形字之間的間距調整由 pLaTeX 自動處理:
日本語English日本語、數字123日本語、記號\%日本語。

\subsection{Ruby(振假名)}

使用 \texttt{okumacro} 套件的 \verb|\ruby| 指令可以加上 Ruby(振假名)。

\begin{center}
\ruby{漢字}{かんじ}、\ruby{情報処理}{じょうほうしょり}、
\ruby{自然言語}{しぜんげんご}\ruby{処理}{しょり}
\end{center}

\subsection{圓點(傍點)}

使用 \texttt{okumacro} 的 \verb|\kenten| 可以加上圓點(傍點)。

\begin{center}
\kenten{重要な箇所}には圏点を打つことができる。
\end{center}

\subsection{字體大小}

{\tiny 極小} {\scriptsize 腳註} {\footnotesize 小} {\small 稍小}
{\normalsize 標準} {\large 稍大} {\Large 大} {\LARGE 很大}
{\huge 超大} {\Huge 最大}

\subsection{文字裝飾}

\begin{itemize}
\item \textbf{粗體(Bold)}
\item \textit{斜體(英文字)}
\item \textsl{斜向}
\item \textsc{Small Caps}
\item \texttt{等寬字體(Typewriter)}
\item \underline{底線文字}
\item \textcolor{red}{紅色文字}
\item \textcolor{blue!70!black}{藍色文字}
\item \colorbox{yellow}{標示底色}
\end{itemize}

%% ================================================================
\section{文件結構}
%% ================================================================

\subsection{標題層級}

在 pLaTeX(\texttt{jarticle})中可以使用以下標題層級:
\verb|\section|、\verb|\subsection|、\verb|\subsubsection|、
\verb|\paragraph|、\verb|\subparagraph|。

\subsubsection{子子節範例}
這是子子節的範例。

\paragraph{段落標題}
這是段落標題的範例。接續的內文沒有縮排。

\subsection{腳註}

可以在內文中加入腳註\footnote{這是腳註的文字。會自動配置在頁底。}。
也可以使用多個腳註\footnote{第二個腳註。編號會自動遞增。}。

\subsection{交叉參照}

加上 \verb|\label| 之後,
可以用 \verb|\ref| 或 \verb|\pageref| 來參照。
例:下一節是第~\ref{sec:math}~節(第~\pageref{sec:math}~頁)。

%% ================================================================
\section{數學式環境}
\label{sec:math}
%% ================================================================

\subsection{行內數學式與顯示數學式}

行內數學式:$E = mc^2$、$\alpha + \beta = \gamma$、
$\sum_{i=1}^{n} i = \frac{n(n+1)}{2}$。

顯示數學式:
\[
\int_{-\infty}^{\infty} e^{-x^2}\,dx = \sqrt{\pi}
\]

\subsection{equation・align 環境}

帶編號的數學式(\texttt{equation}):
\begin{equation}
\label{eq:euler}
e^{i\pi} + 1 = 0
\end{equation}
式~\eqref{eq:euler} 為歐拉恆等式。

多行對齊(\texttt{align}):
\begin{align}
(a+b)^2 &= a^2 + 2ab + b^2 \\
(a-b)^2 &= a^2 - 2ab + b^2 \\
(a+b)(a-b) &= a^2 - b^2
\end{align}

\subsection{矩陣・向量}

\begin{equation}
A = \begin{pmatrix} a_{11} & a_{12} \\ a_{21} & a_{22} \end{pmatrix},\quad
\det(A) = a_{11}a_{22} - a_{12}a_{21}
\end{equation}

\subsection{分段、分數、極限}

\begin{equation}
f(x) = \begin{cases}
x^2 & (x \geq 0) \\
-x^2 & (x < 0)
\end{cases}, \qquad
\lim_{n\to\infty} \left(1 + \frac{1}{n}\right)^n = e
\end{equation}

%% ================================================================
\section{表格(tabular 環境)}
%% ================================================================

\subsection{基本表格}

\begin{table}[h]
\centering
\caption{各城市氣溫資料(示意)}
\label{tab:temp}
\begin{tabular}{lrrr}
\toprule
城市 & 最高氣溫 (°C) & 最低氣溫 (°C) & 平均氣溫 (°C) \\
\midrule
東京 & 35.2 & 25.1 & 29.8 \\
大阪 & 36.4 & 26.3 & 31.0 \\
札幌 & 28.7 & 19.5 & 23.9 \\
那覇 & 32.1 & 27.4 & 29.8 \\
\bottomrule
\end{tabular}
\end{table}

\subsection{複雜表格(\texttt{array} 擴充・儲存格合併)}

\begin{table}[h]
\centering
\caption{程式語言比較}
\label{tab:lang}
\begin{tabular}{|l|c|c|c|}
\hline
語言 & 型別 & 範式 & 主要用途 \\
\hline\hline
Python & 動態 & 多範式 & AI/資料分析 \\
Rust & 靜態 & 系統程式 & 系統開發 \\
Haskell & 靜態 & 函數式 & 研究/金融 \\
JavaScript & 動態 & 多範式 & Web 前端 \\
\hline
\end{tabular}
\end{table}

像表~\ref{tab:temp} 與表~\ref{tab:lang} 可以透過 \verb|\label/\ref| 進行參照。

%% ================================================================
\section{列表環境}
%% ================================================================

\subsection{項目符號列表(itemize)}

\begin{itemize}
\item 第一個項目
\item 第二個項目
\begin{itemize}
\item 嵌套項目 A
\item 嵌套項目 B
\end{itemize}
\item 第三個項目
\end{itemize}

\subsection{編號列表(enumerate)}

\begin{enumerate}[(1)] % enumerate 套件指定格式
\item 最初的步驟
\item 下一個步驟
\item 最後的步驟
\end{enumerate}

\subsection{說明列表(description)}

\begin{description}
\item[pLaTeX] 支援日文的 \LaTeX 系統
\item[upLaTeX] 支援 Unicode 的 pLaTeX 後繼版
\item[LuaLaTeX] 可使用 Lua 腳本的 \LaTeX 系統
\end{description}

%% ================================================================
\section{verbatim・原始碼}
%% ================================================================

\subsection{verbatim 環境}

\begin{verbatim}
#include <stdio.h>
int main(void) {
printf("Hello, pLaTeX!\n");
return 0;
}
\end{verbatim}

\subsection{用 listings 做語法高亮}

\begin{lstlisting}[language=Python, caption={費波那契數列(Python)}]
def fibonacci(n: int) -> int:
"""使用遞迴計算費波那契數列"""
if n <= 1:
return n
return fibonacci(n - 1) + fibonacci(n - 2)

# 輸出前 10 項
for i in range(10):
print(f"F({i}) = {fibonacci(i)}")
\end{lstlisting}

%% ================================================================
\section{框線・強調框}
%% ================================================================

可以使用 \texttt{ascmac} 套件的 \texttt{itembox}、\texttt{screen} 等。

\begin{itembox}[l]{重要要點}
\begin{itemize}
\item pLaTeX 使用 \texttt{jarticle} / \texttt{jbook} 文件類別
\item 使用 \texttt{okumacro} 可以用 Ruby 與圓點
\item 以 \texttt{platex + dvipdfmx} 產生 PDF
\end{itemize}
\end{itembox}

\vspace{5pt}

\begin{screen}
\texttt{screen} 環境:終端機風格的框線。用來顯示原始碼範例或輸出範例。
\end{screen}

%% ================================================================
\section{分欄}
%% ================================================================

可用 \texttt{multicol} 套件從某處開始切成兩欄。

\begin{multicols}{2}
\noindent
這是兩欄版面的左欄。
pLaTeX 也能自然處理日文的分欄。
即使是長文也會自動均分,
適合做報章雜誌風格的排版。

\columnbreak

\noindent
這是右欄。
可以用 \verb|\columnbreak| 強制換欄。
數學式 $x = \frac{-b \pm \sqrt{b^2 - 4ac}}{2a}$
也可以行內使用。
\end{multicols}

%% ================================================================
\section{行距・間距調整}
%% ================================================================

\subsection{水平間距}

字與字之間的空格:a\,b(\verb|\,| 細空白)、
a\enspace b(\verb|\enspace|)、
a\quad b(\verb|\quad|)、
a\qquad b(\verb|\qquad|)。

\subsection{垂直間距}

可以用 \verb|\vspace| 新增垂直間距。

\vspace{5mm}
在此插入自上方 5mm 的垂直間距後的文字。
\vspace{5mm}

\subsection{行距}

{\setstretch{1.8}
此段落用 \texttt{setspace} 套件的
\texttt{setstretch} 將行距設為 1.8 倍。
在日文文件中,行距約 1.5〜2.0 較為易讀。
}

%% ================================================================
\section{自訂指令與環境}
%% ================================================================

\subsection{自訂指令定義}

\newcommand{\R}{\mathbb{R}}
\newcommand{\N}{\mathbb{N}}
\newcommand{\highlight}[1]{\colorbox{yellow!60}{#1}}
\newcommand{\term}[1]{\textbf{\textit{#1}}} % 用語強調

範例:$\R$(實數集)、$\N$(自然數)。
\highlight{被標示的文字}。
\term{機器學習}(Machine Learning)是人工智慧的一個領域。

\subsection{自訂環境定義}

\newenvironment{mybox}[1]{%
\begin{center}\begin{tabular}{|p{0.85\linewidth}|}
\hline\vspace{1pt}
\textbf{#1}\\[2pt]
}{%
\\\hline\end{tabular}\end{center}
}

\begin{mybox}{自製的框線}
使用 \verb|\newenvironment| 可以定義自訂環境。
可以接受參數以利彈性重複使用。
\end{mybox}

%% ================================================================
\section{參考文獻}
%% ================================================================

文獻引用可用 \verb|\cite| 指令進行~\cite{knuth1984,lamport1994,okumura2020}。
文件末會輸出參考文獻清單。

%% ================================================================
%% 參考文獻清單(thebibliography 環境)
%% ================================================================
\begin{thebibliography}{99}

\bibitem{knuth1984}
D.~E. Knuth,
\textit{The \TeX book},
Addison-Wesley, 1984.

\bibitem{lamport1994}
L.~Lamport,
\textit{\LaTeX: A Document Preparation System}, 2nd ed.,
Addison-Wesley, 1994.

\bibitem{okumura2020}
奥村晴彦・黒木裕介,
『\LaTeXe 美文書作成入門』第 8 版,
技術評論社, 2020.

\end{thebibliography}

\end{document}

編譯流程​

基本(platex + dvipdfmx)​

為了正確解決交叉參照與目錄,請先執行 platex 兩次,然後用 dvipdfmx 轉成 PDF。

platex -interaction=nonstopmode -kanji=utf8 document.tex
platex -interaction=nonstopmode -kanji=utf8 document.tex
dvipdfmx document.dvi

ptex2pdf​

也可以用將 platex + dvipdfmx 二步驟合併成一個指令的 ptex2pdf。

基本指令​

ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex
選項含意
-l使用以 latex 為基礎的引擎(platex)
-ot "..."傳給 platex 的選項
-kanji=utf8指定輸入編碼為 UTF-8
-interaction=nonstopmode發生錯誤時不停止繼續處理

步驟​

  1. 執行 ptex2pdf 兩次(為了解決交叉參照與目錄)

    ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex
    ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex
  2. 確認結果

    • 成功:會產生 document.pdf
    • 發生錯誤:檢查 .log 中以 ! 開頭的行及其前後 3 行
    • 只有警告:檢查 Overfull/Underfull hbox、未定義參照等
  3. 若需要 BibTeX(當有 .bib 檔或包含 \bibliography 指令時)

    ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex
    bibtex document
    ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex
    ptex2pdf -l -ot "-kanji=utf8 -interaction=nonstopmode" document.tex

    在執行 bibtex 後再次執行 ptex2pdf 兩次是為了解決參考文獻清單與內文引用編號的交叉參照。

常見錯誤與處理​

Undefined control sequence​

! Undefined control sequence.
l.42 \somecommand

代表使用了未定義的指令。通常是忘了載入某個套件或是打錯字造成的。

Missing $ inserted​

! Missing $ inserted.

表示在數學模式外使用了 _(下標)或 ^(上標)等數學符號。請用 $...$ 將其包起來。

File not found​

! LaTeX Error: File `image.png' not found.

表示 \includegraphics 指定的檔案路徑錯誤。請確認相對於 .tex 的路徑是否正確。

Overfull \hbox​

Overfull \hbox (12.3pt too wide) in paragraph at lines 55--60

這是警告不是錯誤。表示行超出指定寬度。常見原因為長 URL 或長英文字,通常可用 \allowbreak 或用 \url{}(url 套件)來解決。

檢查日誌​

編譯後產生的 .log 檔會記錄錯誤與警告的詳細資訊。錯誤行通常以 ! 開頭,警告則包含 Warning 字樣。

grep -n "^!" document.log # 抽出錯誤行
grep -n "Warning" document.log # 抽出警告行

刪除中間檔案​

編譯會產生許多中間檔。若想保留 PDF 並清理其他檔案,可執行下列指令。

rm -f document.aux document.log document.dvi \
document.toc document.lof document.lot \
document.out document.bbl document.blg \
document.synctex.gz

若要全部刪除(包含 PDF),請再加上 .pdf。

在 Linux 安裝 TeX Live 2026

將使用 ISO 映像在 Linux(RHEL 系)上安裝 TeX Live 2026 的步驟整理如下,適用於 RHEL 系統。

前置條件​

  • 具有 sudo 權限
  • 磁碟空間至少 10 GB(ISO 約 6.4 GB + 安裝目標約 8 GB)
  • 有網路連線

Step 1: 下載 ISO 映像檔​

從理化學研究所的鏡像站下載 ISO。

cd
curl -C - -O --progress-bar https://ftp.riken.jp/CTAN/systems/texlive/Images/texlive2026.iso

-C - 是在下載中斷時繼續下載的選項。

下載完成後,確認檔案大小(約 6.4 GiB):

ls -lh ~/texlive2026.iso

Step 2: 掛載 ISO 映像​

建立掛載點,並將 ISO 以 loop 裝置掛載為唯讀:

sudo mkdir -p /mnt/texlive
sudo mount -o loop,ro ~/texlive2026.iso /mnt/texlive

確認是否掛載:

ls /mnt/texlive

出現如下檔案即可視為成功:

install-tl archive tlpkg README ...

Step 3: 執行安裝程式​

sudo /mnt/texlive/install-tl

會啟動文字介面互動選單。

主要操作按鍵​

鍵操作
S選擇安裝方案
D變更安裝目錄
I開始安裝
Q結束(取消)

建議設定​

  • 方案: scheme-full(全部套件,包含日本語 LaTeX 所需的 collection-langjapanese)
  • 安裝路徑: /usr/local/texlive/2026(預設)

確認設定後輸入 I 開始安裝。安裝需時數十分鐘。

Step 4: 設定 PATH​

將 TeX Live 的執行檔路徑加入 ~/.bashrc:

echo 'export PATH="/usr/local/texlive/2026/bin/x86_64-linux:$PATH"' >> ~/.bashrc
source ~/.bashrc

Step 5: 修正語系 (RHEL 系)​

在 RHEL 系統若未設定 locale,執行 lualatex 時會發生錯誤。請執行:

sudo dnf install -y glibc-langpack-en

另外,在 shell 啟動時設定 locale:

echo 'export LANG=C.UTF-8' >> ~/.bashrc
source ~/.bashrc

Step 6: 檢查是否可正常執行​

確認各指令的版本:

tex --version
lualatex --version
platex --version

預期輸出範例:

TeX 3.141592653 (TeX Live 2026)
LuaHBTeX, Version 1.24.0 (TeX Live 2026)
e-upTeX 3.141592653-p4.1.2-u2.02 (TeX Live 2026)

Step 7: 測試日文編譯​

建立測試檔案:

cat > /tmp/test.tex << 'EOF'
\documentclass{jlreq}
\begin{document}
日本語のテスト。TeX Live 2026 による日本語組版のサンプルです。
\end{document}
EOF

編譯:

cd /tmp && lualatex test.tex

若生成 test.pdf 即為成功。

Step 8: 清理(可選)​

安裝完成後,可刪除 ISO 與掛載點:

sudo umount /mnt/texlive
rm ~/texlive2026.iso

鏡像站列表​

若下載速度慢,可嘗試其他鏡像站:

鏡像站URL
理化學研究所(建議)https://ftp.riken.jp/CTAN/systems/texlive/Images/
JAISThttps://ftp.jaist.ac.jp/pub/CTAN/systems/texlive/Images/
山形大學https://ftp.yz.yamagata-u.ac.jp/pub/CTAN/systems/texlive/Images/
CTAN 鏡像https://mirror.ctan.org/systems/texlive/Images/