跳至主要內容

使用 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 BedrockGoogle Vertex AIMicrosoft 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.tsthemeConfig.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,在文章頁輸出包含 headlinedatePublisheddateModifiedauthorBlogPosting JSON-LD。

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

WebSite JSON-LD

docusaurus.config.tsheadTags 加入 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/

AI 代理人的「技能」是什麼?淺顯說明其運作原理

將「技能」加入 AI 代理人,就像在應用程式安裝功能擴充一樣,可以增加它能做的事情。本文解說 Agent Skills 的運作方式,以及代理人在內部如何處理任務。

AI 代理人是什麼

首先作為前提,AI 代理人是指「接受指示並能自主執行任務的 AI 程式」。

與像 ChatGPT 那種「問了就回答」的 AI 不同,代理人可以做以下事情:

  • 讀寫檔案
  • 執行程式碼並檢查結果
  • 呼叫外部 API 或工具
  • 自行判斷並執行多步驟流程

技能是什麼

Agent Skills 是一種「用來為代理人新增能力或專業知識的機制」。

比喻成人類的話,就像把「新的工作手冊」交給對方一樣。閱讀了手冊(技能)的代理人就能理解該任務應如何進行,並據此行動。

無技能: 「寫一篇部落格」→ 代理人隨便寫
有技能: 「寫一篇部落格」→ 按照手冊步驟,以一定品質寫出文章

技能主要以 Markdown 檔案(SKILL.md)描述,通常包含以下要素:

  • 步驟說明:要做哪些事、按什麼順序做
  • 腳本:想自動化的處理流程
  • 範例與設定:代理人會參考的資源

為什麼需要技能

AI 代理人能力很強,但不會預先具備你專案特有的知識

例如:

  • 「這個團隊要如何撰寫提交訊息」
  • 「這個部落格的 front matter 要如何寫」
  • 「部署流程要用哪個指令」

這類資訊如果不以技能方式提供,代理人無從得知。有了技能,代理人就能在知道「正確做法」的前提下執行任務。

代理人使用技能時的處理流程

接下來看看代理人在內部如何運作。

重點如下:

1. 載入技能

代理人一開始會載入技能。技能內容成為輸入給 LLM 的一部分(提示)。LLM 讀到後會理解「這個任務的正確做法」。

2. 任務拆解

LLM 根據收到的步驟,將任務拆成小步驟。例如「先閱讀既有文章 3 篇」「接著決定檔名」「寫 front matter」等。

3. 呼叫工具

在每個步驟中,視需要呼叫工具:讀檔、搜尋網路、執行程式碼等,依照技能定義的流程執行。

4. 結果回饋

工具執行的結果會再次傳回給 LLM。LLM 根據結果判斷「下一步要做什麼」,並持續迴圈直到任務完成。

技能指令

技能可以透過斜線指令(/命令名)來呼叫。

呼叫指令時,對應的 Markdown 檔內容會被展開為提示,代理人就會開始依該步驟執行。

技能的應用範圍

Agent Skills 的格式是 由 Anthropic 開發並開放的,目前支援許多工具。

工具是否支援
Claude Code
GitHub Copilot
Cursor
Gemini CLI
OpenAI Codex
VS Code

同一套技能能在多個工具間重複使用,這是很大的優勢。

總結

  • 技能是把專業知識或操作步驟傳給代理人的機制
  • 只要撰寫 Markdown 檔(SKILL.md)描述步驟與規則,就能建立技能
  • 代理人把技能當成提示給 LLM,LLM 會解讀並按步驟執行
  • 這是個開放且跨工具的標準格式,如 Claude Code、Cursor、GitHub Copilot 等均可共用

善用技能可以省去「每次都要向 AI 解釋同一件事」的麻煩,讓代理人以一致的品質完成任務。

在 AWS 無伺服器環境下自建部落格留言 API

想要在這個部落格加入留言功能,因此在 AWS 無伺服器架構下自建了一個 API。介紹其設計與實作。

架構

請求以以下流程處理。

瀏覽器 (www.hikari-dev.com)
↓ HTTPS
API Gateway
├── GET /comment?postId=... → 取得留言
├── POST /comment → 發表留言
└── PATCH /comment/{id} → 管理(切換隱藏)

Lambda (Node.js 20 / arm64)

DynamoDB(儲存留言)
+ SES v2(通知管理者)

程式碼以 TypeScript 撰寫,並以 SAM(Serverless Application Model)管理基礎設施即程式碼(IaC)。Lambda 採用 arm64(Graviton2)以稍微節省成本。

DynamoDB 的資料表設計

資料表名稱為 blog-comments,分區鍵為 postId,排序鍵為 commentId

型別說明
postIdString文章的識別值(例: /blog/2026/03/20/hime
commentIdStringULID(可時序排序的 ID)

因為在排序鍵使用 ULID,使用 QueryCommand 取得的留言會自動按發表順序排列。這也是為什麼沒有使用 UUID 的原因。

垃圾留言過濾

在將留言寫入 DynamoDB 之前,會與 keywords.json 中定義的關鍵字比對。

若命中關鍵字,會以 isHidden: true 自動隱藏,並附加 isFlagged: "1"。若未命中則立即公開。

isFlagged 作為稀疏 GSI(Sparse GSI)的鍵使用。未命中的留言不會帶入此屬性,因此不會在 GSI 中增加多餘的分區,對成本與效能都有利。只要在 DynamoDB Document Client 設定 removeUndefinedValues: true 就能達成。

通知管理者的郵件

每次有留言發表時,會用 SES v2 發信通知自己。郵件內容包含發言者名稱、內容、評分、IP 位址以及是否被標記(flag)等資訊。

郵件發送採非同步進行,即使發送失敗也會忽略錯誤(不影響使用者流程)。這是為了避免影響留言發表的回應速度。

隱私考量

雖然會在 DynamoDB 中儲存 IP 位址與 User-Agent,但不會把它們包含在 GET 端點的回應中。我們在型別定義層就進行分離。

安全性

層級對策
網路使用 AWS WAF 設定每個 IP 每 5 分鐘 100 次的速率限制
CORS僅允許 https://www.hikari-dev.com
管理 APIAPI Gateway 的 API Key 認證(X-Api-Key 標頭)
垃圾留言關鍵字過濾自動隱藏

管理端點(PATCH /comment/{id})只要在 SAM 模板設定 ApiKeyRequired: true 即可啟用 API 金鑰認證。無需自行實作 Lambda Authorizer,較為簡單。

總結

採用無伺服器架構後不需管理伺服器,DynamoDB 的按需付費讓流量低的個人部落格也能把成本降到最低。

程式碼以 SAM + TypeScript + esbuild 打包,僅需執行 sam build && sam deploy 即可部署。

製作可使用多個生成式 AI 代理人的 VSCode 聊天擴充套件 Hime

我做了一個可以和多個 AI 供應商聊天的 VSCode 擴充套件 Hime (HikariMessage)

採用 BYOK(Bring Your Own Key),只要有各 API 提供者的 API 金鑰即可使用。

什麼是 Hime

Hime 是一個整合在 VSCode 側邊欄的生成式 AI 聊天擴充套件。支援 Anthropic、OpenAI、Azure OpenAI、OpenRouter、Ollama,並可透過下拉選單輕鬆切換提供者。

主要功能

支援多個 AI 提供者

支援以下提供者:

  • Anthropic (Claude)
  • OpenAI
  • Azure OpenAI
  • OpenRouter
  • Ollama

即時串流顯示

回應會即時串流顯示,因此即使回覆很長也不會感覺到太久的等待。

MCP

透過設定以 JSON 格式指定以下參數即可使用 MCP。

範例

{
"filesystem": {
"command": "npx",
"args": ["-y", "@modelcontextprotocol/server-filesystem", "C:\\Users"]
}
}

豐富的 UI

  • Markdown 呈現
  • 程式碼區塊語法高亮
  • 程式碼區塊複製按鈕
  • 顯示 MCP 的輸出結果

聊天記錄持久化

對話記錄會以 JSON 格式儲存在 ~/.hime/chats/。即使重新啟動 VSCode 也能從上次繼續對話。

自動系統提示

工作區資訊、作業系統資訊與目前開啟的編輯器上下文會自動包含在系統提示中。只要說「請修正這個檔案」,AI 就能知道你正在查看哪個檔案。

安裝

需要 Node.js 20 以上與 VSCode 1.96 以上。

git clone https://github.com/Himeyama/hime
cd hime
npm install
npm run watch # 開發時: Extension Host 與 Webview 同時監控

之後在 VSCode 按 F5 即可啟動擴充套件主機。API 金鑰可從側邊欄的設定面板輸入,並會使用 VSCode 的 SecretStorage 加密儲存。

總結

Hime 的強項是在不離開編輯器的情況下即可與 AI 互動,並且可透過 MCP 執行工具。歡迎試用。

程式碼倉庫: https://github.com/Himeyama/hime

用 AWS 無伺服器打造雲端儲存服務

はじめに

自分専用のファイル共有システムが欲しいと思い、AWS のサーバーレスサービスだけでファイルストレージサービスを作りました。

この記事では、設計で意識したポイントと、実際のアーキテクチャを紹介します。

何を作ったか

制作した Web システムは、Web ブラウザからファイルのアップロード・ダウンロード・フォルダ管理ができるクラウドストレージサービスです。

主な機能

  • ファイルのアップロード / ダウンロード
  • フォルダの作成・階層管理
  • 複数ファイル / フォルダの一括 ZIP ダウンロード
  • ユーザー認証(サインアップ・ログイン・パスワードリセット)
  • ユーザープロフィール管理

アーキテクチャ

以下、構成図です。

認証の大部分は Cognito で行い、 ファイル転送は S3 の Presigned URL を Lambda で発行してクライアントと S3 が直接やり取りする仕組みです。

技術スタック

レイヤー技術
バックエンドC# (.NET 8) / AWS Lambda
認証Amazon Cognito + Managed Login v2
APIAPI Gateway (REST) + Cognito Authorizer
ストレージAmazon S3

設計判断とその理由

認証を Cognito で行う

Cognito の OAuth 2.0 エンドポイントと Managed Login を活用し、認証機能を実現しました。

最終的に認証系の Lambda は TokenFunction 1 つだけになりました。

機能的にもセキュリティ的にも減らせるコードは減らすのが吉です。

AWS のサービスがやってくれることを自前で書く必要はありません。

Presigned URL によるファイル転送

ファイルのアップロード・ダウンロードで Lambda を経由すると、いくつかの問題が生じます:

  • Lambda のペイロード上限に引っかかる
  • 大きなファイルを Lambda のメモリに載せるとコストがかかる
  • 転送時間が Lambda の実行時間としてカウントされる

Presigned URL なら、Lambda は URL を発行するだけで、実際のファイル転送はブラウザと S3 が直接行います。

Lambda の実行時間は数十ミリ秒で済み、ファイルサイズの制約も S3 の上限までとなります。

アップロードの流れ:
1. ブラウザ → Lambda: 「file.pdf をアップロードしたい!アップロード先の URL を送れ~」
2. Lambda → ブラウザ: 「アップロード先の Presigned URL だよ。ここに PUT してね~」
3. ブラウザ → S3: 「S3 に PUT するよ~」
4. ブラウザ → Lambda: 「アップロード完了したよ~」

4. フォルダの ZIP ダウンロード

S3 にはフォルダごとダウンロードする機能がありません。

複数ファイルの一括ダウンロードは、Lambda 上で ZIP を生成して一時的に S3 に置き、その Presigned URL を返す方式にしました。

一時 ZIP ファイルは S3 のライフサイクルルールで 1 日後に自動削除されるので、ゴミが溜まることはありません。

セキュリティ

対策実装
ブルートフォース防止Cognito 標準のロック機能 (5 回失敗: 15 分ロック)
API 保護Cognito Authorizer による JWT 検証
CORSAllowedOrigin を特定のドメインに限定
一時ファイル管理S3 ライフサイクルで不要なファイルを 1 日で自動削除

コスト

サーバーレス構成なので、利用がなければコストはほぼゼロです。

  • Cognito: ESSENTIALS Tier は MAU 10,000 まで無料
  • Lambda: 月 100 万リクエストまで無料
  • S3: 保存量に応じた従量課金(GB あたり約 $0.025/月)
  • API Gateway: 100 万リクエストあたり $3.50

個人利用なら月額数十円〜数百円程度に収まります。

インフラのコード化

インフラ全体を 1 つの template.yaml (AWS SAM) で定義しています。

Cognito User Pool、API Gateway、Lambda 3 関数、S3 バケット、CloudWatch アラーム、SNS — すべてのリソースを 600 行程度の YAML で定義しています。