# 和 AI 智能體花五個回合做出部落格 logo：留下來的是一個 /logo skill 與三支指令碼

> 用 AI 智能體重做這個部落格的 logo 與程式碼字型：壞掉的 SVG 字形、用正規表達式算出的邊界框、猜錯的漢字，以及最後長出來的 /logo skill。

- Source: https://oharu121.com/zh-tw/blog/ai-agent-logo-design-fonttools-svg-path-bugs-jetbrains-mono/
- Published: 2026-08-10T14:33:43+09:00
- Tags: 生成式 AI, 開發工具, Claude Code

---
## 引言

我坐下來，打算修掉一個我不滿意很久的 logo：一個純文字的標記，是這個專案開工第一天自己生出來的，之後沒有人為它做過任何決定。

用 AI 智能體設計 logo 不是一次就能收工的事，我也不希望它是。我真正想知道的，是這個迴圈本身值不值得寫成一個 Claude Code skill：智能體給出候選，我看、我砍、我說明理由，然後再跑一輪，直到我滿意為止。**智能體在這幾個回合裡有五次自信滿滿地弄錯，而我不認為其中任何一次是浪費。** 每一次走錯路都長出一條規則，這些規則現在成了一個 /logo skill 與三支指令碼，下一個標記可以直接從那裡開始。

最後出貨的，是一枚寫著 桜 的朱紅印章，旁邊是用 Fraunces 排的「oharu」，再加上只在文章頁生效的 JetBrains Mono。本文會走完這五個回合、每一個回合各自花了什麼代價才被發現，以及它們最後變成的那個 skill。

## 這個部落格沒有設計，而我一直將就著用

當時的 favicon 是這樣：

*Figure — OldFavicon: 一個圓角的 `rect`，加上一個指定 `Helvetica,Arial,sans-serif` 的 `<text>`，以 favicon 實際會被用到的三種尺寸並列。那個字母是訪客的作業系統給什麼就是什麼。*

那不是 logo，是一個我一直將就著用的佔位符。頁首連標記都沒有，只有純文字的網站標題。

排版也是同一個故事：

```css title="src/styles/global.css"
--font-sans: ui-sans-serif, system-ui, -apple-system, 'Segoe UI', Roboto, …;
--font-mono: ui-monospace, 'SF Mono', SFMono-Regular, Menlo, Consolas, …;
```

純系統堆疊。所以字型並不是無聊。**根本沒有字型。** 每一位訪客看到的都是自己作業系統的臉。這是一個正當的取捨（零位元組、零版面位移），但它不是任何人做出的決定。

## 錯誤一：同一個想法的二十四種變化

我先要的是廣度：跨越純圖示、文字商標與組合標記的二十四個概念，並算繪成一張同時以 128px、32px 與 16px 呈現的清單。

它產出了二十四個。**它們全都是同一個 logo。**

*Image: 八個圖示概念排成格狀，每一個都是同樣線寬、同樣圓端點的細藍色圓形或環形線稿*

這是整張表的圖示那一半。光圈、軌道、終端機、圓弧、鳥居、方圓形、提示符、分裂。八個名字，指向同一張圖。每一個都是單線的幾何筆畫：圓、弧、均勻線寬、圓形端點。不管是圖示還是文字商標，骨架都一樣。那副骨架正是語言模型最先伸手拿的預設值，也正是網路上每一個 AI logo 產生器的招牌風格。我要的是幅度，拿到的是戴了八頂帽子的重複。

解法不是更好的提示詞，而是先去看真正的作品。六個識別，逐一截圖並實際端詳：

| 參考對象 | 它做了什麼 | 第一輪做了什麼 |
| --- | --- | --- |
| [Linear](https://linear.app)、[Astro](https://astro.build) | **實心填色**，標記小而安靜 | 全是線條，到 16px 就糊成一團 |
| [Rauno Freiberg](https://rauno.me)、[Josh Comeau](https://www.joshwcomeau.com) | **完全沒有標記。** 只有字，加一個下定決心的顏色 | 預設標記是必要的 |
| [Maggie Appleton](https://maggieappleton.com) | **展示用襯線體。** 因為那是一個寫作的網站 | 幾何無襯線，範本的預設值 |
| [Anthony Fu](https://antfu.me) | 個人**字母組合**，手寫的字 | 純幾何長不出「個人」 |

第二輪回來的東西是真的不一樣了，因為這次的限制來自真實存在的地方：

*Image: 十六個概念被分成編輯風襯線、grotesk、朱紅印章、帶顏色的字，以及字母組合五組，看得出是五個不同的想法而不是一個*

這個教訓不只適用於 logo：**向 AI 要 *N* 個選項，你拿到的是同一個分布裡的 *N* 個樣本。** 幅度只能從外部強加進去，而去看真正的作品，就是找出「外部」長什麼樣子的方法。

## 錯誤二：從來沒有被算繪過的字形

第一輪是手工搭出來的幾何，而且一次都沒有算繪就直接進了清單。三個 bug 就攤在那裡。

最糟的是字母 `u`：

```
M9,49 L9,99 A41,41 0 0 0 91,99 L91,140
```

把它當成一支筆來讀。左邊字幹從 x-height 往下畫到 `y=99`。碗狀弧線橫過去到 `(91,99)`。然後一條線往下到基線。弧線*結束*在 `y=99`，所以右邊的字幹只從 `99` 走到 `140`：**整個上半截不見了。** 這個字會被算繪成右側發育不良的 `u`。

修法是拆成獨立的子路徑：

```
M9,49 L9,99 A41,41 0 0 0 91,99    ← bowl
M91,49 L91,140                     ← full-height right stem
```

*Image: 「oharu」這個字出現兩次：上面那個最後的 u，右邊筆畫停在一半的高度；下面同一個字，筆畫升到與鄰居相同的 x-height*

另外兩個是同一類錯誤。一行放在 `y=160`、縮放 `0.26` 的副標題，上伸部會落在 `160 + 0.26 × (−700) = −22`，比它底下那個字的基線還高，於是 `t`、`h`、`b`、`l` 直接刺穿了名字。還有一個標記畫成 220 單位高，旁邊的字母卻是 700 單位高，看起來不像 logo，比較像一個走失的項目符號。

這些在瀏覽器裡都一目了然，在路徑資料裡則完全看不見。**這些幾何被推理過，卻從來沒有被看過。**

## 錯誤三：智能體自己假設的一個字

第二輪跑出來最強的概念是日式的*判子*（hanko），也就是個人印章，這和部落格的本質相當接近。朱紅、一個字、白色反白。

名字是「oharu」，所以智能體用了 春（*haru*，春天）。看起來不錯，差一點就做下去了。

然後它問了，因為把一個字放進別人的識別裡，不是可以憑假設默默做掉的事。我更正它：「oharu」是 **桜明**，完全不是 春。

這個答案改動的不只是一個字符，而是整個設計。桜明 是兩個字，而**兩個字撐不過 16px 的 favicon**：在那個尺寸下，十畫的漢字是紋理，不是文字。

*Image: 六個朱紅印章方案，每個都先大圖呈現，再以 32 與 16 像素並列。在 16 像素時，兩字版本都成了看不出輪廓的色塊，只有單字圓印還讀得出來*

16px 的算繪結果就把事情定了。直排的 桜明 是這個名字正確的判子，卻是瀏覽器標籤頁裡表現最差的一個；朱文外框版本更是整個消失。所以印章變成圓形、單字的 桜，而**這個選擇是由筆畫數決定的，不是品味。**

一般化的版本是：**AI 很樂意在一個關於你的猜測之上，做出自信、合理而且完成度很高的成品。** 那個猜測在輸出裡看不出來，只能靠問才會浮現。

## 錯誤四：用正規表達式算出來的邊界框

到了真正要用的資產，手繪字母讓位給從真實字型烘焙出來的外框：文字商標用 Fraunces，桜 用 Noto Serif JP，透過 fontTools 與 HarfBuzz 處理。正確的字距與對比被凍結成路徑資料，所以文字商標是以幾何形狀出貨的，沒有網頁字型請求，也沒有備用字型閃現。

要擺放一個字符，就需要它的實際墨水邊界。那支程式把路徑字串裡的每一個數字都抓出來，當成 x、y 交替的座標對。

這是錯的，而且錯得很安靜。`SVGPathPen` 會輸出 `H` 與 `V` 這兩個簡寫，而它們攜帶的座標只有**一個**。**只要出現一條水平線，後面每一個數字的奇偶位置都會翻掉。**

這個 bug 以「看起來只是有點怪」的樣子撐過了兩輪清單。它變得無法否認，是在**一個應該大約 3200 單位寬的組合標記算出來只有 1802 的時候**。

修法是別再解析文字，讓函式庫去量：

```python title="outline.py"
from fontTools.pens.boundsPen import BoundsPen

bounds = BoundsPen(glyph_set)
for info, pos in zip(buf.glyph_infos, buf.glyph_positions):
    xform = (scale, 0, 0, -scale, (x + pos.x_offset) * scale, 0)
    glyph_set[name].draw(TransformPen(pen, xform))
    glyph_set[name].draw(TransformPen(bounds, xform))   # same transform
```

文字商標真正的邊界是 `2456.4 × 758.6`。正規表達式從第一張清單開始，就一直很有自信地回報別的數字。

**這是最值得單獨拿出來講的失敗形式：它沒有崩潰。** 它產出了看起來合理的結果，而人類瞄了一眼就通過，還通過了兩次。

## 錯誤五：每一頁都掛著的 preload

這次工作階段的另一半，是給程式碼自己的字型。程式碼區塊是技術部落格真正被閱讀的表面，而我的程式碼區塊當時是用 Menlo 算繪的，和內文一樣處在「沒有人做過決定」的狀態。

JetBrains Mono Variable，只取 Latin 子集，40KB，自架在固定路徑上，好讓它可以用 URL 預先載入。**這在日文頁與中文頁上完全不花成本**：程式碼是 Latin，所以 CJK 堆疊完全不動，也不會有任何 CJK 字型被抓下來。

preload 被放進了共用版面。瀏覽器直接說明了那為什麼是錯的：

```
The resource http://localhost:4321/fonts/jetbrains-mono-latin.woff2 was
preloaded using link preload but not used within a few seconds from the
window's load event. Please make sure it has an appropriate `as` value and
it is preloaded intentionally.
```

首頁、標籤列表頁，以及兩個 CJK 索引頁上一行程式碼都沒有。**它們每一頁都在抓 40KB 來算繪出零。** 把它收在只有文章頁會設定的 prop 後面：

| 頁面 | `rel="preload"` 連結數 |
| --- | --- |
| `dist/index.html` | 0 |
| `dist/ja/index.html` | 0 |
| `dist/tags/index.html` | 0 |
| `dist/blog/<article>/index.html` | 1 |

一則主控台警告，換到一份真實的節省。**這是五次裡唯一一次工具主動說出來的錯誤。** 其餘全部都是因為有人跑去看，才浮上檯面。

## 這些回合留下的東西：一個 /logo skill

每一次走錯路，都長出一條下一次不能跳過的規則。那些規則就是這個 skill 的不可妥協事項，並且照著「跳過它會造成多大損害」由大到小排列：

| 規則 | 它從哪裡來 |
| --- | --- |
| 在生成任何東西之前，先研究真正的作品 | 同一個分布裡的二十四個概念 |
| 拿給任何人看之前，先算繪出來親眼看 | 只剩一半字幹的 `u` |
| 用 16px 來判斷 | 在瀏覽器標籤頁裡糊成一團的 桜明 |
| 絕不把關於這個人的假設烘進去 | 春 |
| 用真實字型的外框，不要手工畫字形 | 手工搭出來的幾何，以及量它的那段正規表達式 |
| 顏色靠比較決定，不靠理論 | 紅色印章旁邊的藍色文字商標 |
| 每一道關卡都由使用者決定 | 迴圈本身 |

最後一條讓其餘幾條成立。這個 skill 被要求：提出方案、附理由推薦，然後停下來，因為*意見有用，決定不是你的*。**本文裡的每一個回合，都結束在一道由我來選的關卡上。** 這也是為什麼五次走錯路的代價是半天，而不是一個建立在猜測之上的完成品。

機械的那一半由三支指令碼負責。`gallery.mjs` 組出對照清單並在本機連接埠上提供服務，`outline.py` 透過 fontTools 與 HarfBuzz 把字型外框烘成路徑資料，`build-brand-lockup.mjs` 則是產出 favicon 與頁首元件的示範。概念與清單都寫進暫存目錄，絕不寫進儲存庫：**一輪設計產出的大多是被淘汰的東西，而儲存庫不是放淘汰品的地方。**

錯誤五沒有在這裡留下規則。在沒有程式碼的頁面上發動的 preload 是版面的 bug，不是設計的 bug，它屬於這次工作階段的字型那一半，而不是標記。

## 出貨的東西

一枚寫著 桜 的朱紅圓印，旁邊是用 Fraunces 排的「oharu」。SVG 是兩個而不是一個，因為印章刻意做得比文字商標的墨水高度矮，所以沒辦法靠把整個組合標記裁成正方形來生出窄螢幕版本。低於 50rem 時只出現印章，這順手解決了舊的純文字商標留下的版面問題：在手機上，標題會和搜尋控制項互相搶位置。

有一個顏色決定是靠看、不是靠推論做出來的。網站的強調色是 `#1d4ed8`，印章是 `#d8452a`。這兩個在色相上幾乎正對面，藍色文字商標擺在紅色印章旁邊會明顯打架。**解法不是換一個紅色。** 而是讓文字商標吃主題的文字色，把藍色留給連結，這樣兩者永遠不會並排。

成本是量出來的，不是估的：

| | |
| --- | --- |
| 文字商標路徑資料 | 3569 位元組（從 5145 取整數） |
| 印章路徑資料 | 1677 位元組 |
| `favicon.svg` | 1878 位元組 |
| JetBrains Mono，Latin 子集 | 40KB，只在文章頁 |
| `/ja/` 上的網頁字型請求 | 1 個，且沒有 CJK 字型 |

`astro check` 對 53 個檔案回報 0 errors、0 warnings、0 hints。

## 總結

五次走錯路。**沒有任何一次是靠對程式碼進行推理抓到的。**

那些通用到不行的概念，需要真實的參考對象才會顯出它有多通用。壞掉的字形需要一個瀏覽器。猜錯的漢字需要一個提問。邊界框的 bug 需要一個荒謬到足以被注意的數字。多餘的 preload 需要一個主控台。

這份清單正是把它當成迴圈、而不是當成一句提示詞來跑的理由。產出二十四個打磨過的概念現在只要幾分鐘，這讓人非常容易把數量誤認成幅度，也非常容易接受一個建立在猜測之上的自信成品。抵銷它的不是更好的提示詞，**而是把東西算繪出來、把東西量出來，並且在它取決於一個我掌握、而智能體並不掌握的事實時，開口問。**

這五次在當時不像浪費，現在讀起來也不像，因為每一次都以一條規則的形式回來了。**下一個標記不是從一張空白提示詞開始，而是從七條規則與三支指令碼開始**，那才是值得留下的部分。
