<?xml version="1.0" encoding="utf-8"?>
<feed xmlns="http://www.w3.org/2005/Atom">
  <title>是 Ray 不是 Array</title>
  <icon>https://israynotarray.com/icon.png</icon>
  <subtitle>Software Engineer</subtitle>
  <link href="https://israynotarray.com/atom.xml" rel="self"/>
  
  <link href="https://israynotarray.com/"/>
  <updated>2026-06-23T02:00:00.000Z</updated>
  <id>https://israynotarray.com/</id>
  
  <author>
    <name>Ray</name>
    
  </author>
  
  <generator uri="https://hexo.io/">Hexo</generator>
  
  <entry>
    <title>Mac 系統監控工具就推薦用 Stats！一款免費又開源，還能看 CPU、GPU、溫度、網速的軟體</title>
    <link href="https://israynotarray.com/other/20260623/2826413095/"/>
    <id>https://israynotarray.com/other/20260623/2826413095/</id>
    <published>2026-06-23T02:00:00.000Z</published>
    <updated>2026-06-23T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8823%E6%97%A5%20%E4%B8%8B%E5%8D%8802_23_41.png" data-fancybox="gallery" data-caption="Mac 系統監控工具就推薦用 Stats！一款免費又開源，還能看 CPU、GPU、溫度、網速的軟體"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8823%E6%97%A5%20%E4%B8%8B%E5%8D%8802_23_41.png" alt="Mac 系統監控工具就推薦用 Stats！一款免費又開源，還能看 CPU、GPU、溫度、網速的軟體"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Mac 系統監控工具就推薦用 Stats！一款免費又開源，還能看 CPU、GPU、溫度、網速的軟體</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這一篇來分享一下我自己 Mac 上最愛用的 <a href="https://github.com/exelban/stats">Stats</a> 吧。剛好最近從 M1 跳到 M5 了，雖然轉移工具幫我無痛轉移，但不知道為什麼 Stats 每次都轉移失敗，所以這套軟體幾乎是我新 Mac 第一個會去裝的軟體，所以這邊就來分享一下它能做什麼、怎麼裝、還有我自己的一些設定習慣。</p><p>如果你之前有看過我寫的 <a href="https://israynotarray.com/other/20260421/3921847205/">Mac 上裝軟體到底該用哪種方式？Homebrew、官網下載、curl 完整比較</a> 或 <a href="https://israynotarray.com/other/20250114/4149194649/">關於我推的 Raycast 擴充套件，強化你的 Raycast！</a>，那這一篇基本上算是同一個系列 — 都是我自己長期掛在 Mac 上的東西。</p><h2 id="所以-Stats-是什麼？"><a href="#所以-Stats-是什麼？" class="headerlink" title="所以 Stats 是什麼？"></a>所以 Stats 是什麼？</h2><p>照慣例，先簡單介紹一下 Stats 是什麼東西。</p><p>簡單來講，Stats 是一款 macOS 的系統監控工具，相較於活動監視器，它更完善且直覺一次性把你需要看的東西列出來，像是我就會三不五時注意一下 Mac 溫度、網路流量、記憶體用量等等。</p><p>而這一款軟體本身是 MIT 授權的開源專案，所以完全免費使用，沒有任何功能限制，也沒有廣告或付費升級的煩惱，基本上你也不用太擔心它在背後偷做什麼奇怪的事情，也不會只有你遭殃，畢竟超多人用的。</p><p>這邊也快速列出一下它能夠看的範圍：</p><div class="table-wrapper"><table><thead><tr><th>模組</th><th>說明</th></tr></thead><tbody><tr><td>CPU</td><td>處理器使用率、各核心狀態、頻率</td></tr><tr><td>GPU</td><td>顯示卡使用率（Apple Silicon 跟外接 GPU 都支援）</td></tr><tr><td>記憶體</td><td>記憶體使用率、Swap 狀態</td></tr><tr><td>硬碟</td><td>各個磁碟的讀寫速度、剩餘空間</td></tr><tr><td>感測器</td><td>各種感測器數值（CPU 溫度、GPU 溫度、電壓、功耗等）</td></tr><tr><td>網路</td><td>即時上下載流量、公開 IP、Wi-Fi 訊號強度</td></tr><tr><td>電池</td><td>電池電量、健康度、充放電狀態</td></tr><tr><td>藍牙</td><td>已連線的藍牙裝置電量</td></tr><tr><td>時鐘</td><td>可以掛多個時區的時鐘</td></tr><tr><td>Remote</td><td>遠端監控其他 Mac 的狀態</td></tr></tbody></table></div><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623141524.png" data-fancybox="gallery" data-caption="Stats 的模組列表"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623141524.png" alt="Stats 的模組列表"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Stats 的模組列表</figcaption></figure><p>神奇吧？這 Stats 還有提供 Remote 呢！對於專門放一台 Mac mini 在家裡跑 OpenClaw、Hermes 使用者來講，這個功能就非常實用，可以直接在 Menu Bar 上看到遠端機器的狀態，不用再開一個遠端桌面軟體去看。</p><p>雖然我個人還沒用過 Remote 就是了，但看了一下 <a href="https://system-stats.com/">Stats 官網</a> 介紹是覺得滿棒的。</p><h2 id="安裝-Stats"><a href="#安裝-Stats" class="headerlink" title="安裝 Stats"></a>安裝 Stats</h2><p>講了那麼多，所以該怎麼安裝呢？這邊我依然推薦 Homebrew 的方式。</p><p>只需要在終端機輸入以下指令就可以安裝 Stats：</p><figure class="highlight cmd"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs cmd">brew install --cask stats<br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>Stats 是一個 GUI 應用程式，所以要用 <code>--cask</code> 來裝，不能用 <code>brew install stats</code>（不加 cask 的話 Homebrew 會去找命令列工具的 formula）。</p></blockquote><p>如果你不想用 Homebrew 也沒關係，直接去 <a href="https://github.com/exelban/stats/releases/latest">GitHub 的 Releases 頁</a> 抓最新版的 <code>Stats.dmg</code>。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142344.png" data-fancybox="gallery" data-caption="Stats 跑在 Menu Bar 上的示意圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142344.png" alt="Stats 跑在 Menu Bar 上的示意圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Stats 跑在 Menu Bar 上的示意圖</figcaption></figure><h2 id="我最常開的幾個模組"><a href="#我最常開的幾個模組" class="headerlink" title="我最常開的幾個模組"></a>我最常開的幾個模組</h2><p>接下來就來分享每一個模組啦～</p><h3 id="CPU-模組"><a href="#CPU-模組" class="headerlink" title="CPU 模組"></a>CPU 模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142612.png" data-fancybox="gallery" data-caption="CPU 模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142612.png" alt="CPU 模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">CPU 模組</figcaption></figure><p>CPU 模組是我長期保持開著的模組，畢竟可以讓我即時看到 CPU 的使用率，讓我可以快速判斷「電腦現在在幹嘛」，特別是當電腦突然變慢的時候，第一個會想看的就是 CPU 是不是被吃滿了。</p><h3 id="GPU-模組"><a href="#GPU-模組" class="headerlink" title="GPU 模組"></a>GPU 模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142809.png" data-fancybox="gallery" data-caption="GPU 模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142809.png" alt="GPU 模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">GPU 模組</figcaption></figure><p>我如果有跑 LLM 的話，我就會打開來看一下，畢竟 GPU 的使用率在這種情況下會非常明顯地飆高，這樣就可以知道是不是模型正在運算中，還是說其實它卡在某個步驟沒動。</p><h3 id="記憶體模組"><a href="#記憶體模組" class="headerlink" title="記憶體模組"></a>記憶體模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142924.png" data-fancybox="gallery" data-caption="記憶體模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623142924.png" alt="記憶體模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">記憶體模組</figcaption></figure><p>記憶體模組也是我長期會關注的點，畢竟記憶體不夠用的話，系統就會開始把一些東西丟到硬碟上去來做記憶體交換，這時候整體效能就會大幅下降。</p><blockquote><p><strong>Note</strong><br>稍微講一下，macOS 的記憶體管理跟 Windows 不太一樣，它會把沒用到的記憶體拿去當 cache，所以你會看到 <code>Memory used</code> 很高，可是其實系統還有很多可用的空間。</p></blockquote><h3 id="硬碟模組"><a href="#硬碟模組" class="headerlink" title="硬碟模組"></a>硬碟模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/disk-module-screenshot.png" data-fancybox="gallery" data-caption="硬碟模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/disk-module-screenshot.png" alt="硬碟模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">硬碟模組</figcaption></figure><p>這我自己就很少看了，所以基本上都是關閉，除非我剛好要做一些需要大量讀寫硬碟的工作，才會打開來看一下。</p><h3 id="感測器模組"><a href="#感測器模組" class="headerlink" title="感測器模組"></a>感測器模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143143.png" data-fancybox="gallery" data-caption="感測器模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143143.png" alt="感測器模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">感測器模組</figcaption></figure><p>感測器模組對於我來講非常的重要，畢竟我是拿 Air 居多，如果溫度沒有控制或關注的話，Mac 就會進入 Thermal Throttle（降頻）的狀態，這時候不管做什麼都會卡卡的…</p><p>而 Stats 的感測器模組可以讓我直接在 Menu Bar 上看到一些重要的溫度資訊，例如：</p><ul><li>CPU 各核心的溫度</li><li>GPU 溫度</li><li>SSD 溫度</li><li>電池溫度</li><li>主機板各區的溫度</li><li>整體功耗</li></ul><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143302.png" data-fancybox="gallery" data-caption="感測器模組的詳細資訊"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143302.png" alt="感測器模組的詳細資訊"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">感測器模組的詳細資訊</figcaption></figure><p>比較需要注意的是，這個模組溫度只會在 Menu Bar 上看到。</p><h3 id="網路模組"><a href="#網路模組" class="headerlink" title="網路模組"></a>網路模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143540.png" data-fancybox="gallery" data-caption="網路模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143540.png" alt="網路模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">網路模組</figcaption></figure><p>這也是我長期開啟的功能之一，畢竟網路有問題、跑的快不快，這個就是最重要的模組惹。</p><blockquote><p><strong>Note</strong><br>公開 IP 的部分 Stats 是去打一個外部 API 才拿得到，所以如果你很在意隱私，可以在網路模組的設定裡面把這個關掉。</p></blockquote><h3 id="電池模組"><a href="#電池模組" class="headerlink" title="電池模組"></a>電池模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143731.png" data-fancybox="gallery" data-caption="電池模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143731.png" alt="電池模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">電池模組</figcaption></figure><p>嗯…如果你是 Intel 時期的 Mac，我認為這功能滿重要的，因為 Intel 時期的 Mac 相較於 Apple Silicon 的 Mac 來講，電池的表現跟健康度都比較不穩定，所以如果你是 Intel 的話，這個模組就很建議開著看一下。</p><h3 id="藍牙模組"><a href="#藍牙模組" class="headerlink" title="藍牙模組"></a>藍牙模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143838.png" data-fancybox="gallery" data-caption="藍牙模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143838.png" alt="藍牙模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">藍牙模組</figcaption></figure><p>藍牙模組我就幾乎沒用過了，所以就…如果你發現藍牙不穩時，可以拿來看看。</p><h3 id="時間模組"><a href="#時間模組" class="headerlink" title="時間模組"></a>時間模組</h3><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143832.png" data-fancybox="gallery" data-caption="時間模組"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623143832.png" alt="時間模組"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">時間模組</figcaption></figure><p>時間模組我自己也沒什麼使用過，但方便在於可以設定多個時區，這個對於需要跟海外時差合作的人來講會比較實用（吧）。</p><p>最多，我自己是會掛一個美西時間，方便看一些國外服務的維護時間。</p><h2 id="額外小技巧分享"><a href="#額外小技巧分享" class="headerlink" title="額外小技巧分享"></a>額外小技巧分享</h2><p>有時候因為 menu bar 的空間有限，所以我會把一些我核心注重的細節放在桌面上，你只需要點桌面右鍵 &#x3D;&gt; 編輯小工具</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623144158.png" data-fancybox="gallery" data-caption="在桌面上編輯 Stats 小工具"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623144158.png" alt="在桌面上編輯 Stats 小工具"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">在桌面上編輯 Stats 小工具</figcaption></figure><p>接著找到你要拖曳放到桌面上的工具，然後直接拖曳到桌面上就可以了。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623144259.png" data-fancybox="gallery" data-caption="把 Stats 小工具拖曳到桌面上"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623144259.png" alt="把 Stats 小工具拖曳到桌面上"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">把 Stats 小工具拖曳到桌面上</figcaption></figure><p>這樣就可以在桌面上看到一些更詳細的資訊了，像是我自己就會把 CPU、GPU、記憶體的使用率放在桌面上。</p><h2 id="常見問題"><a href="#常見問題" class="headerlink" title="常見問題"></a>常見問題</h2><p>接下來我也列出一下一些問題。</p><h3 id="桌面小工具總是出現「No-data-available」的問題？"><a href="#桌面小工具總是出現「No-data-available」的問題？" class="headerlink" title="桌面小工具總是出現「No data available」的問題？"></a>桌面小工具總是出現「No data available」的問題？</h3><p>這個問題是因為你需要打開「macOS widgets」這個功能才有辦法顯示，因為 Stats 的桌面小工具是用 macOS 原生的 Widget 功能來實現的，所以如果你沒有打開這個功能的話，就會一直看到「No data available」的提示。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/mac-state-tools/20260623145345.png" data-fancybox="gallery" data-caption="Stats 桌面小工具出現 No data available"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/mac-state-tools/20260623145345.png" alt="Stats 桌面小工具出現 No data available"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Stats 桌面小工具出現 No data available</figcaption></figure><p>但這邊也要注意一下，開啟這個功能是會導致 Stats 一直針對硬碟做讀寫的，這個問題在官方 Issues 也有人提出過：<a href="https://github.com/exelban/stats/issues/2733">Widgets do not update or high disk writes</a></p><h3 id="桌面工具一直出現「Enable-in-Settings」的問題？"><a href="#桌面工具一直出現「Enable-in-Settings」的問題？" class="headerlink" title="桌面工具一直出現「Enable in Settings」的問題？"></a>桌面工具一直出現「Enable in Settings」的問題？</h3><p>這方面其實我覺得一直都會發生，所以只能等官方修正了。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>最後就來總結一下吧。</p><p>所以我個人推薦使用的原因有以下：</p><ul><li>開源、免費、MIT 授權</li><li>模組化設計，要看的才開，不要的關掉</li><li>畫面乾淨、支援中文</li><li>可以看到 macOS 原生不太願意給你看的東西，例如 CPU 溫度</li></ul><p>所以希望這一篇對你有幫助哩～</p>]]></content>
    
    
    <summary type="html">這一篇分享我自己 Mac 每天一開機就會跑的系統監控工具 Stats — 一款放在 Menu Bar 上的開源 macOS 系統監控工具，可以即時看 CPU、GPU、記憶體、硬碟、網路、電池、感測器溫度、藍牙裝置狀態甚至多時區時鐘。 內文包含 brew install --cask stats 安裝方式、各個主要模組的功能介紹、 以及我自己的 Menu Bar 排列習慣跟省電設定建議。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="Mac" scheme="https://israynotarray.com/tags/Mac/"/>
    
    <category term="macOS" scheme="https://israynotarray.com/tags/macOS/"/>
    
    <category term="Stats" scheme="https://israynotarray.com/tags/Stats/"/>
    
    <category term="系統監控" scheme="https://israynotarray.com/tags/%E7%B3%BB%E7%B5%B1%E7%9B%A3%E6%8E%A7/"/>
    
    <category term="Menu Bar" scheme="https://israynotarray.com/tags/Menu-Bar/"/>
    
    <category term="開源工具" scheme="https://israynotarray.com/tags/%E9%96%8B%E6%BA%90%E5%B7%A5%E5%85%B7/"/>
    
    <category term="生產力工具" scheme="https://israynotarray.com/tags/%E7%94%9F%E7%94%A2%E5%8A%9B%E5%B7%A5%E5%85%B7/"/>
    
  </entry>
  
  <entry>
    <title>讓 gh-pages 的 /llms.txt 回傳 text/markdown：Cloudflare 回應標頭轉換規則教學</title>
    <link href="https://israynotarray.com/ai/20260622/4283716095/"/>
    <id>https://israynotarray.com/ai/20260622/4283716095/</id>
    <published>2026-06-22T06:00:00.000Z</published>
    <updated>2026-06-22T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8822%E6%97%A5%20%E4%B8%8B%E5%8D%8802_33_15.png" data-fancybox="gallery" data-caption="讓 &#x2F;llms.txt 回傳 text&#x2F;markdown：Cloudflare 回應標頭轉換規則教學"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8822%E6%97%A5%20%E4%B8%8B%E5%8D%8802_33_15.png" alt="讓 &#x2F;llms.txt 回傳 text&#x2F;markdown：Cloudflare 回應標頭轉換規則教學"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">讓 &#x2F;llms.txt 回傳 text&#x2F;markdown：Cloudflare 回應標頭轉換規則教學</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>之前我有寫過一篇 <a href="https://israynotarray.com/ai/20260408/3734112946/">SEO 已經不夠了嗎？認識 AEO 答案引擎優化</a> 跟 <a href="https://israynotarray.com/ai/20260421/2847193056/">從 3 分到滿分！讓你的網站通過 isitagentready</a>，裡面有提到 <code>/llms.txt</code> 是用來告訴 AI 搜尋引擎你的網站有哪些東西的索引檔，然後這邊有個關鍵是你必須要把它的 Content-Type 標成 <code>text/markdown</code>。</p><p>那這邊我自己也乖乖把 <code>/llms.txt</code> 跟 <code>/llms-full.txt</code> 丟到 gh-pages 上，可是如果你這時候順便 <code>curl -I</code> 一下你會發現回傳的是…</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">HTTP/2 200<br>content-type: text/plain<br></code></pre></td></tr></table></figure><p>所以這一篇就是要來分享我怎麼用 Cloudflare 的回應標頭轉換規則，把 <code>content-type: text/plain</code> 改成 <code>text/markdown; charset=utf-8</code>，而這整個過程不用寫 Cloudflare Worker，免費方案就夠用哩～</p><h2 id="為什麼-llms-txt-會跑成-text-plain？"><a href="#為什麼-llms-txt-會跑成-text-plain？" class="headerlink" title="為什麼 &#x2F;llms.txt 會跑成 text&#x2F;plain？"></a>為什麼 &#x2F;llms.txt 會跑成 text&#x2F;plain？</h2><p>這個問題其實很簡單，因為它的副檔名是 <code>.txt</code>（對，就是這樣而已）。</p><p>每個 web server 在回應靜態檔的時候，會根據副檔名查一張對應表（叫做 MIME 對應表）來決定 Content-Type 要寫什麼。GitHub Pages 用的是業界很通用的 <a href="https://github.com/jshttp/mime-db">mime-db</a> 那一套，對應規則大概長這樣：</p><div class="table-wrapper"><table><thead><tr><th>副檔名</th><th>對應的 Content-Type</th></tr></thead><tbody><tr><td><code>.txt</code></td><td><code>text/plain</code></td></tr><tr><td><code>.md</code></td><td><code>text/markdown</code></td></tr><tr><td><code>.html</code></td><td><code>text/html</code></td></tr><tr><td><code>.xml</code></td><td><code>application/xml</code></td></tr></tbody></table></div><p>所以 <code>.txt</code> 結尾的檔案就會被標注成 <code>text/plain</code>，即使你裡面寫的是 markdown 格式，web server 也不會幫你看內容，它只認副檔名。</p><p>這時候應該又有一個疑問了：<code>text/plain</code> AI 還是讀得到啊，反正內容是 markdown，給它讀就好不是嗎？</p><p>對，AI 確實還是讀得到，可是這邊有兩個小問題：</p><ol><li><strong>少了明確的格式信號：</strong> AI 拿到 <code>text/plain</code> 之後會把它當純文字處理，不一定會啟動 markdown 的解析邏輯，這導致標題、列表、連結這些結構就比較容易被當成一般文字看待，那就沒辦法發揮 markdown 格式的優勢了。</li><li><strong>有些工具會看 Content-Type 決定要不要拿：</strong> 像 Anthropic、OpenAI 的文件裡都有提到，建議站點在提供 markdown 內容時把 Content-Type 標成 <code>text/markdown</code>，這樣 AI Agent 看到的時候會更明確知道這是要給它看的 markdown 內容，會比較友善地把它當成 markdown 來處理。</li></ol><p>所以 Content-Type 的調整就變得格外重要。</p><h2 id="為什麼選-Cloudflare-回應標頭轉換規則？"><a href="#為什麼選-Cloudflare-回應標頭轉換規則？" class="headerlink" title="為什麼選 Cloudflare 回應標頭轉換規則？"></a>為什麼選 Cloudflare 回應標頭轉換規則？</h2><p>我認為對「就只是想改 Content-Type」這種相對單純的需求來講，用 Cloudflare 的回應標頭轉換規則最簡單，更不用說我自己的網域本來就是託管在 Cloudflare 上（畢竟 gh-pages 本身也沒有地方讓你動 header）。</p><p>所以接下來，就來介紹一下該如何設定吧。</p><h2 id="設定步驟"><a href="#設定步驟" class="headerlink" title="設定步驟"></a>設定步驟</h2><p>整個流程非常簡單，只需要幾分鐘就可以設定完了。</p><h3 id="Step-1：進到轉換規則頁面"><a href="#Step-1：進到轉換規則頁面" class="headerlink" title="Step 1：進到轉換規則頁面"></a>Step 1：進到轉換規則頁面</h3><ol><li>登入 <a href="https://dash.cloudflare.com/">Cloudflare Dashboard</a></li><li>選你要設定的網域（這邊以我的部落格 israynotarray.com 為例）</li><li>左側選單點 <strong>規則 → 轉換規則</strong></li><li>找到 <strong>回應標頭轉換規則</strong> 那一欄，點右上角 <strong>建立規則</strong></li></ol><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133415.png" data-fancybox="gallery" data-caption="轉換規則頁面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133415.png" alt="轉換規則頁面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">轉換規則頁面</figcaption></figure><p>這邊選擇「回應標頭轉換規則」</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133425.png" data-fancybox="gallery" data-caption="建立規則"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133425.png" alt="建立規則"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">建立規則</figcaption></figure><h3 id="Step-2：填規則名稱"><a href="#Step-2：填規則名稱" class="headerlink" title="Step 2：填規則名稱"></a>Step 2：填規則名稱</h3><p>規則名稱我這邊取 <code>Set markdown content-type</code>，方便我之後一眼看出來這個規則在幹嘛。</p><p>你也可以叫 <code>llms.txt markdown header</code> 之類的，反正隨你喜歡。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622142309.png" data-fancybox="gallery" data-caption="填規則名稱"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622142309.png" alt="填規則名稱"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">填規則名稱</figcaption></figure><h3 id="Step-3：寫比對條件"><a href="#Step-3：寫比對條件" class="headerlink" title="Step 3：寫比對條件"></a>Step 3：寫比對條件</h3><p>接下來這邊稍微有一點坑 XD</p><p>所以我個人建議直接使用 <strong>編輯運算式</strong> 模式比較好，然後把整段貼進去：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">(http.request.uri.path eq &quot;/llms.txt&quot;) or (http.request.uri.path eq &quot;/llms-full.txt&quot;) or (ends_with(http.request.uri.path, &quot;.md&quot;))<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622142529.png" data-fancybox="gallery" data-caption="貼上比對運算式"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622142529.png" alt="貼上比對運算式"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">貼上比對運算式</figcaption></figure><p>這段運算式做了三件事情：</p><ul><li>抓 <code>/llms.txt</code></li><li>抓 <code>/llms-full.txt</code></li><li>抓所有 <code>.md</code> 結尾的檔案（如果你有開「每篇文章對應一份 .md」這種設計的話）</li></ul><p>所以你的畫面按完成之後應該會變成這樣：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133032.png" data-fancybox="gallery" data-caption="完成後的運算式預覽"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133032.png" alt="完成後的運算式預覽"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">完成後的運算式預覽</figcaption></figure><h3 id="Step-4：設定要塞的-header"><a href="#Step-4：設定要塞的-header" class="headerlink" title="Step 4：設定要塞的 header"></a>Step 4：設定要塞的 header</h3><p>接下來是最重要的一個步驟，請你往下到 <strong>則…</strong> 區塊，點 <strong>設定新標頭</strong> 按鈕，會展開三個欄位讓你填：</p><ul><li><strong>動作：</strong> 選「設定靜態」（Set static）</li><li><strong>標頭名稱：</strong> 填 <code>Content-Type</code></li><li><strong>值：</strong> 填 <code>text/markdown; charset=utf-8</code></li></ul><p><code>charset=utf-8</code> 這段建議加上去，不然中文內容在某些 client 上可能會出現編碼錯亂。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133158.png" data-fancybox="gallery" data-caption="Content-Type"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133158.png" alt="Content-Type"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Content-Type</figcaption></figure><p><strong>放置於</strong> 保持預設的「最後」就好，按右下角 <strong>部署</strong> 就完成啦～</p><h3 id="Step-5：用-curl-驗證"><a href="#Step-5：用-curl-驗證" class="headerlink" title="Step 5：用 curl 驗證"></a>Step 5：用 curl 驗證</h3><p>部署完之後打開 terminal，跑一下 <code>curl -I</code> 看回應的 header：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">curl -I https://你的網域/llms.txt<br></code></pre></td></tr></table></figure><p>預期看到的回應大概長這樣：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs txt">HTTP/2 200<br>content-type: text/markdown; charset=utf-8<br>server: cloudflare<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133228.png" data-fancybox="gallery" data-caption="curl"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/cloudflare-transform-rules/20260622133228.png" alt="curl"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">curl</figcaption></figure><p><code>content-type</code> 變成 <code>text/markdown; charset=utf-8</code> 就代表規則生效了。</p><blockquote><p><strong>Note</strong><br>如果你看到 <code>content-type</code> 還是 <code>text/plain</code>，可能是 Cloudflare 邊緣快取還沒更新。可以加 <code>-H &quot;Cache-Control: no-cache&quot;</code> 重抓一次，或者直接去 Cloudflare Dashboard 的快取頁面點「清除快取」一下。</p></blockquote><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>最後總結一些重點吧。</p><ol><li>如果部署在 gh-pages 上，<code>.txt</code> 結尾的檔案預設會被標成 <code>text/plain</code>，這時候 AI 爬蟲就不太會把它當 markdown 來處理了。</li><li>用 Cloudflare 的回應標頭轉換規則可以很簡單地把 <code>/llms.txt</code>、<code>/llms-full.txt</code> 和所有 <code>.md</code> 檔的 Content-Type 改成 <code>text/markdown; charset=utf-8</code>。</li><li>設定的時候要注意比對條件的寫法，建議直接用編輯運算式的方式，這樣比較不會踩坑（要選「URI 路徑」不是「完整 URI」）。</li><li>設定完之後可以用 <code>curl -I</code> 來驗證 header 是否有正確被改寫。</li></ol><p>這條規則做完之後，下次 AI 爬蟲來抓 <code>/llms.txt</code> 就會拿到明確的 markdown 格式信號。</p><p>那如果你想做更進階的 Markdown Negotiation（也就是 AI 帶 <code>Accept: text/markdown</code> header 時，把整篇 HTML 文章即時轉成 markdown 回給它），那就要動到 Cloudflare Worker 了，這部分我之前在 <a href="https://israynotarray.com/ai/20260421/2847193056/">從 3 分到滿分！讓你的網站通過 isitagentready</a> 那篇有完整的 Worker 寫法可以參考。</p><p>到這邊整個 AEO 的最後一哩路就接起來了。希望這一篇有讓你對 Cloudflare 轉換規則有更近一步的認識哩～</p>]]></content>
    
    
    <summary type="html">這篇分享怎麼用 Cloudflare 的回應標頭轉換規則，一條規則就把 GitHub Pages 上的 /llms.txt、/llms-full.txt 與所有 .md 檔的 Content-Type 改回 text/markdown，不用另外寫 Cloudflare Worker。內容涵蓋為什麼 .txt 副檔名預設會被 mime-db 對應成 text/plain、Transform Rules 的免費方案額度（10 條）、Cloudflare Dashboard 的完整設定步驟、編輯運算式怎麼寫，以及用 curl -I 驗證的方式。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="AEO" scheme="https://israynotarray.com/tags/AEO/"/>
    
    <category term="llms.txt" scheme="https://israynotarray.com/tags/llms-txt/"/>
    
    <category term="Cloudflare" scheme="https://israynotarray.com/tags/Cloudflare/"/>
    
    <category term="Cloudflare Transform Rules" scheme="https://israynotarray.com/tags/Cloudflare-Transform-Rules/"/>
    
    <category term="回應標頭轉換規則" scheme="https://israynotarray.com/tags/%E5%9B%9E%E6%87%89%E6%A8%99%E9%A0%AD%E8%BD%89%E6%8F%9B%E8%A6%8F%E5%89%87/"/>
    
    <category term="GitHub Pages" scheme="https://israynotarray.com/tags/GitHub-Pages/"/>
    
    <category term="Content-Type" scheme="https://israynotarray.com/tags/Content-Type/"/>
    
  </entry>
  
  <entry>
    <title>AI 是不是偷偷變笨了？AIStupidLevel：24 小時監測 AI 模型退化的評測網站</title>
    <link href="https://israynotarray.com/ai/20260616/3947261580/"/>
    <id>https://israynotarray.com/ai/20260616/3947261580/</id>
    <published>2026-06-16T02:00:00.000Z</published>
    <updated>2026-06-16T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/ai-stupid-level/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8818%E6%97%A5%20%E4%B8%8A%E5%8D%8811_34_19.png" data-fancybox="gallery" data-caption="AI 是不是偷偷變笨了？AIStupidLevel：24 小時監測 AI 模型退化的評測網站"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/ai-stupid-level/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%8818%E6%97%A5%20%E4%B8%8A%E5%8D%8811_34_19.png" alt="AI 是不是偷偷變笨了？AIStupidLevel：24 小時監測 AI 模型退化的評測網站"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AI 是不是偷偷變笨了？AIStupidLevel：24 小時監測 AI 模型退化的評測網站</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>不知道大家有沒有遇過這種狀況，也就是昨天使用 AI 還感覺它很聰明，不用多講什麼就會順便把一些 Bug 給解掉，但隔天卻同樣的問題它打死都不回去主動順便把一些明顯的 Bug 給解掉，反而都要等你發現他才會處理，甚至它卻開始鬼打牆、答非所問，讓你開始懷疑「AI 是不是偷偷變笨了？」。</p><p>我相信這種感覺應該滿多人都有，但就是很難講出所以一個然。到底是 AI 廠商搞鬼，還是只是你今天的問題剛好比較難、或單純是心理作用？沒有一個客觀的數字，這就只是一個都市傳說。</p><p>所以這一篇就要來介紹一個網站，它就叫做<a href="https://aistupidlevel.info/">AIStupidLevel</a>，光看名字（直翻大概是「AI 智障程度」）就知道它走的是很直白的路線。它做的事情就是 24 小時不間斷地測試各家 AI 模型，幫你把「AI 是不是變笨了」這件事變成一條可以攤開來看的曲線。</p><p>所以我才寫一下這一篇來介紹啦～</p><h2 id="所以-AIStupidLevel-到底是什麼？"><a href="#所以-AIStupidLevel-到底是什麼？" class="headerlink" title="所以 AIStupidLevel 到底是什麼？"></a>所以 AIStupidLevel 到底是什麼？</h2><p>簡單來講，AIStupidLevel 是一個「持續監測 AI 模型表現有沒有退步」的第三方評測平台，就跟我們系統在做健康檢查一樣，而它則是專門盯著 AI 模型的「健康狀態」，幫你抓出哪個模型最近偷偷變笨了。全年無休 24h 幫你監控 OpenAI、Anthropic、Google、xAI、DeepSeek、Kimi、GLM 共 7 家供應商旗下 21 個現役 AI 模型的表現，然後把每個模型當下的表現換算成一個分數攤在儀表板上。</p><p>這時候有些人可能會好奇，這個專案是不是某個 AI 監控公司做的，其實不是，這是一個開源專案。所以立場會比較偏向「第三方監督」角色，比較不容易跟廠商牽扯上關係，這點我覺得滿重要的，畢竟比較不會發生球員兼裁判的問題哩～</p><p>那麼了解了基本 AIStupidLevel 是什麼之後，讓我們繼續往下看吧。</p><h2 id="恩？AI-為什麼會「變笨」？"><a href="#恩？AI-為什麼會「變笨」？" class="headerlink" title="恩？AI 為什麼會「變笨」？"></a>恩？AI 為什麼會「變笨」？</h2><p>我想這應該是滿多人會有的疑問，也就是…</p><blockquote><p>模型出廠不是就訓練好了嗎？固定了嗎？</p></blockquote><p>這個問題的答案其實原因有以下：</p><ol><li><strong>無聲更新（silent update）：</strong> 你如果有接過 API 的話，你可能會看到這種版本號 （例如 <code>gpt-4.0-20240924</code>），其實這個版本號裡面那串日期（20240924）但實際上你在使用 Codex 選模型時，你並不會看到這麼細的版本號，甚至有些模型根本就沒有版本號，只有一個 model ID（例如 <code>gpt-4</code>），所以你根本不知道自己實際使用的是哪一版模型，被偷偷更新也很難察覺。</li><li><strong>量化（quantization）</strong>：許多時候，為了處理來自全世界的尖峰流量、算力，模型商可能會把模型壓縮過（量化）。</li><li><strong>算力限流</strong>：當使用人數超過一定之後的時候，模型商會把你能夠使用的算力限制掉，這樣就會讓模型的表現變差。</li><li><strong>算力移轉</strong>：有時候，當一個新模型即將推出的時候，模型商可能會把算力從舊模型轉移到新模型上，這樣就會讓舊模型的表現變差。以我自己長期接 API 來講，就很常遇到這種狀況，相同 Prompt 突然某一天彷彿降級到變成智障，接著沒多久模型商就推出更強的版本，接著舊版本才恢復正常。</li></ol><p>其中「量化」這件事我之前在 <a href="https://israynotarray.com/ai/1749382056/">想自己跑 LLM？搞懂模型參數與量化讓你不再選錯模型</a> 那篇有比較完整聊過，簡單來講就是把模型壓縮的技術，壓縮過後雖然省資源，可是細節會少一點。</p><p>所以來畫個重點吧：</p><blockquote><p>AI 模型不是一個固定不變的東西，廠商可能會在背後偷偷更新、偷偷量化、偷偷限流、偷偷轉移算力，這些都會讓模型的表現變差，但你卻很難察覺，所以才會有「AI 是不是偷偷變笨了」這種感覺。</p></blockquote><h2 id="它是怎麼測的？"><a href="#它是怎麼測的？" class="headerlink" title="它是怎麼測的？"></a>它是怎麼測的？</h2><p>講了那麼多，但似乎沒聊到 AIStupidLevel 到底怎麼測試的，其實核心測試方式很簡單，分別是：</p><ol><li><strong>固定題目反覆考</strong>：它準備了一套固定的題庫，定時定期丟給各個模型去考，然後把成績記錄下來。</li><li><strong>同一題問 N 次</strong>：因為 AI 模型的輸出有隨機性，所以它同一題會跑 N 次，取中位數來當分數，並且算出 95% 的信心區間。</li></ol><p>看起來很簡單吧？但魔鬼就藏在細節裡。</p><h3 id="同一題問-N-次，因為-AI-每次答案都不一樣"><a href="#同一題問-N-次，因為-AI-每次答案都不一樣" class="headerlink" title="同一題問 N 次，因為 AI 每次答案都不一樣"></a>同一題問 N 次，因為 AI 每次答案都不一樣</h3><p>「AI 每次產出都有隨機性不是嗎？」</p><p>沒錯，就是利用這個特性來設計測試的，如果只問一次就給分數，那絕對可信度是非長堤的，因此同一個題目至少會跑 N 次（但 N 是多少呢？AIStupidLevel 的做法是 5 次），然後取中位數來當分數，這樣就能比較穩定地反映出模型的「平均表現」，而不是被偶爾的好運或壞運給誤導。</p><h3 id="四種測試輪流跑"><a href="#四種測試輪流跑" class="headerlink" title="四種測試輪流跑"></a>四種測試輪流跑</h3><p>AIStupidLevel 可不是只考一種題目，它可是準備了好幾套不同的測試輪流上場，每一套盯的東西不太一樣。整理成表格會比較清楚：</p><div class="table-wrapper"><table><thead><tr><th>測試套組</th><th>多久跑一次</th><th>在測什麼</th></tr></thead><tbody><tr><td>速度測試（程式碼解題）</td><td>每 4 小時</td><td>147 道程式題，看模型解程式題的綜合表現</td></tr><tr><td>深度推理</td><td>每天一次</td><td>5～7 turn 的多輪對話，看長對話下邏輯會不會爆掉</td></tr><tr><td>工具呼叫</td><td>每天一次</td><td>開真實的 Docker 沙箱讓 AI 跑 execute-command、read-file、write-file 之類的多步驟流程</td></tr><tr><td>快速漂移偵測</td><td>每小時</td><td>12 題輕量快篩，第一時間抓出有沒有異常</td></tr></tbody></table></div><p>從這張表可以看出來，它的設計是「高頻快篩 + 低頻深測」搭配著用。每小時的快篩負責當哨兵，一發現苗頭不對就拉警報；每天那套比較重的深度推理跟工具呼叫，則是拿來做比較紮實的全面體檢。</p><p>其中工具呼叫那套我覺得特別有意思，它是真的開一個 Docker 沙箱（你可以想像成一個跟外界隔離的乾淨小電腦）讓 AI 在裡面實際跑指令，而不是只看它「嘴上說會不會」。這樣測出來的結果會比較貼近你實際拿 AI 來寫程式時的體感。</p><h3 id="評分不是只看「對不對」"><a href="#評分不是只看「對不對」" class="headerlink" title="評分不是只看「對不對」"></a>評分不是只看「對不對」</h3><p>那一題到底怎麼打分數？這邊也是它做得比較細的地方。它不是單純看答案對不對，而是從好幾個面向一起評。</p><p>以程式碼解題來講，AIStupidLevel 目前是用 9 個面向加權算出來的，分別是正確性（40%）、複雜度（20%）、程式碼品質（15%）、穩定性（10%）、效率（5%）、邊界狀況處理（3%）、除錯能力（3%）、輸出格式（2%）、安全性（2%）。可以看到正確性佔了最大宗，但即使答案能跑，寫得又亂、邊界沒處理好、格式亂噴，分數還是會被扣。</p><h3 id="怎麼抓出「變笨的那一刻」"><a href="#怎麼抓出「變笨的那一刻」" class="headerlink" title="怎麼抓出「變笨的那一刻」"></a>怎麼抓出「變笨的那一刻」</h3><p>光是有分數還不夠，因為分數本來就會上上下下的跳動（前面講過 AI 有隨機性）。真正的難題是：怎麼判斷這次掉分是「真的退化」，還是「正常的隨機跳動」？</p><p>這就是它最核心的技術了。它用了一個叫做 <strong>CUSUM</strong> 的演算法來偵測這件事。這個東西其實不是 AI 領域發明的，它本來是工廠在做品質管制時用的老方法，概念上就是持續累積「實際表現」跟「基準水準」之間的落差，當落差累積到一個程度，就判定「這不是抖動了，是真的出事了」。</p><p>除了 CUSUM 之外，它還會搭配統計檢定（去算這個差異在統計上是不是顯著、p 值有沒有低於 0.05）來再確認一次，盡量壓低「明明沒事卻亂報」的誤判率。</p><p>這邊真正的關鍵是：靠這套統計方法，它可以在模型開始退化的<strong>幾個小時內</strong>就抓到，而不是等到大家都在社群上哀嚎「AI 變笨了」才後知後覺。整個系統內建了 29 種不同的警告類別，對應各種不同的異常狀況。</p><h2 id="那分數到底怎麼看？（Stupid-Meter）"><a href="#那分數到底怎麼看？（Stupid-Meter）" class="headerlink" title="那分數到底怎麼看？（Stupid Meter）"></a>那分數到底怎麼看？（Stupid Meter）</h2><p>其實你打開網站就可以馬上看到儀表板了</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/ai-stupid-level/20260618101231.png" data-fancybox="gallery" data-caption="AIStupidLevel 的儀表板示意圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/ai-stupid-level/20260618101231.png" alt="AIStupidLevel 的儀表板示意圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AIStupidLevel 的儀表板示意圖</figcaption></figure><p>每個模型旁邊都會有一個 0～100 的即時分數，分數越高代表當下表現越好、越「聰明」，你可以把它想像成 AI 模型的即時戰力錶。然後分數旁邊會掛上一個狀態標籤，告訴你它現在是 STABLE（穩定）、VOLATILE（在抖）、DEGRADED（已經降級）、還是 RECOVERING（正在回血）這四種狀態的哪一種。</p><p>除了當下的分數，它還會把每個模型的歷史曲線攤開，讓你比較「上週很穩的模型，這幾天是不是開始亂跳」這種情境，再搭配前面那 29 種警告類別，可以很快判斷現在這個時間點，哪幾個模型狀態正常、哪幾個最近怪怪的。</p><p>所以實際用起來，它比較像是看盤軟體，你不是來看一個死的排名，而是來看「此時此刻哪個模型最值得用」。如果你正在煩惱要選哪個 AI 工具，我之前寫的 <a href="https://israynotarray.com/ai/73911729/">2026 年 AI Coding 工具怎麼選？</a> 那篇可以搭配著看哩～</p><h2 id="順便講一下-Smart-Router"><a href="#順便講一下-Smart-Router" class="headerlink" title="順便講一下 Smart Router"></a>順便講一下 Smart Router</h2><p>除了監測之外，AIStupidLevel 還附帶了一個叫做 <strong>Smart Router</strong> 的功能，這個我覺得是這個專案蠻有意思的延伸。</p><p>簡單來講，它就是一個 OpenAI 相容的 API endpoint（也就是你原本打 OpenAI 的程式碼幾乎不用改），你只要把各家 provider 的 API Key 存進去（會用 AES-256 加密），再把 base URL 換成它的網址，它就會根據當下監測到的模型狀態，自動幫你把 request 丟給最適合的模型。</p><p>它目前提供六種路由策略，你只要在打 API 的時候把 <code>model</code> 欄位指定成下面任何一個，它就會幫你動態挑模型：</p><ul><li><code>auto-best</code>：通盤考慮，挑當下綜合分數最高的</li><li><code>auto-coding</code>：專挑當下程式題表現最好的模型</li><li><code>auto-reasoning</code>：專挑推理任務最強的</li><li><code>auto-creative</code>：偏向創意輸出</li><li><code>auto-cheapest</code>：在合格門檻內挑最便宜的</li><li><code>auto-fastest</code>：在合格門檻內挑回應最快的</li></ul><p>如果你只填 <code>auto</code>，它會用你預設的策略；如果你填具體的模型名（例如 <code>claude-opus-4-7</code>），它就會直接打那個模型不走路由。換句話說，當某個模型偷偷變笨被它抓到時，Smart Router 會自動避開那個模型，把流量導去當下狀態還正常的兄弟模型，這對於要把 AI 真的接進產品的人來說滿實用的。</p><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>聊那麼多，最後總結一下吧。</p><p>所以 AIStupidLevel 到底是什麼？</p><ul><li>它是一個<strong>獨立、開源</strong>（MIT License）的第三方評測網站，24 小時監測 OpenAI、Anthropic、Google、xAI、DeepSeek、Kimi、GLM 共 7 家供應商旗下 21 個現役 AI 模型</li><li>測法是<strong>固定題目反覆考</strong>，同一題跑 5 次取中位數 + 95% 信心區間，分高頻快篩跟低頻深測四種套組輪流跑</li><li>它用 <strong>CUSUM 變點偵測</strong>搭配統計檢定，能在幾小時內抓出某個模型是不是偷偷退化了</li><li>另外還附帶 <strong>Smart Router</strong>，可以根據監測結果自動把 API 流量導去當下狀態最好的模型</li></ul><p>下次你再覺得 AI 突然變笨的時候，先別急著懷疑是不是自己的問題，可以打開這個網站看一眼，搞不好真的能找到證據哩～</p>]]></content>
    
    
    <summary type="html">這篇來聊一個叫做 AIStupidLevel 的評測網站，它 24 小時盯著 OpenAI、Anthropic、Google、xAI、DeepSeek、Kimi、GLM 共 7 家供應商旗下 21 個現役 AI 模型，每隔幾小時就丟一批編碼題、推理題、工具呼叫任務給它們跑，再用 5 次取中位數 + 95% 信心區間打分數，搭配 CUSUM 變點偵測演算法揪出模型有沒有偷偷變笨。文中會講為什麼 AI 會變笨（無聲更新、量化）、它的四種測試怎麼跑、Stupid Meter 分數怎麼看、怎麼拿自己的 API Key 測，以及附帶的 Smart Router 自動選模型功能。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude" scheme="https://israynotarray.com/tags/Claude/"/>
    
    <category term="AIStupidLevel" scheme="https://israynotarray.com/tags/AIStupidLevel/"/>
    
    <category term="AI Benchmark" scheme="https://israynotarray.com/tags/AI-Benchmark/"/>
    
    <category term="LLM" scheme="https://israynotarray.com/tags/LLM/"/>
    
    <category term="AI 模型評測" scheme="https://israynotarray.com/tags/AI-%E6%A8%A1%E5%9E%8B%E8%A9%95%E6%B8%AC/"/>
    
    <category term="ChatGPT" scheme="https://israynotarray.com/tags/ChatGPT/"/>
    
    <category term="Gemini" scheme="https://israynotarray.com/tags/Gemini/"/>
    
  </entry>
  
  <entry>
    <title>AI 搜尋實際撈走你文章的多少內容？拆 Claude 的 WebSearch 與 WebFetch 規格</title>
    <link href="https://israynotarray.com/ai/20260609/4628193507/"/>
    <id>https://israynotarray.com/ai/20260609/4628193507/</id>
    <published>2026-06-09T02:00:00.000Z</published>
    <updated>2026-06-09T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/ai-websearch-webfetch/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%889%E6%97%A5%20%E4%B8%8A%E5%8D%8810_09_47.png" data-fancybox="gallery" data-caption="AI 搜尋實際撈走你文章的多少內容？拆 Claude 的 WebSearch 與 WebFetch 規格"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/ai-websearch-webfetch/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%889%E6%97%A5%20%E4%B8%8A%E5%8D%8810_09_47.png" alt="AI 搜尋實際撈走你文章的多少內容？拆 Claude 的 WebSearch 與 WebFetch 規格"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AI 搜尋實際撈走你文章的多少內容？拆 Claude 的 WebSearch 與 WebFetch 規格</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>之前寫過一篇 <a href="https://israynotarray.com/ai/3734112946/">SEO 已經不夠了嗎？認識 AEO 答案引擎優化</a> ，寫完之後我突然有一個想法，也就是：AI 在做網路搜尋的時候，到底會撈走我部落格多少內容？整篇一字不漏？前面 500 字？還是只看到標題就跳走？所以就稍微研究一下，然後這篇就把 Anthropic 官方的 <code>web_search</code> 跟 <code>web_fetch</code> tool 文件翻一遍，順便拿自己的部落格實測一輪，最後整理這對怎麼寫文章、寫文案有什麼具體的影響吧。</p><h2 id="先講清楚，「搜尋」跟「撈取」是兩件事"><a href="#先講清楚，「搜尋」跟「撈取」是兩件事" class="headerlink" title="先講清楚，「搜尋」跟「撈取」是兩件事"></a>先講清楚，「搜尋」跟「撈取」是兩件事</h2><p>在我們開始往下之前，我認為必須要先把一件事情講清楚，也就是 AI 在做查詢時，<strong>「搜尋」</strong> 跟 <strong>「撈取網頁內文」</strong> 不是同一件事，它是兩件事情，而且是分層了兩階段來處理。</p><ul><li>第一階段 <strong>（WebSearch）</strong>：AI 拿著你的問題去呼叫 WebSearch 工具（去查搜尋引擎），這時候回來的會是一份搜尋結果清單，每一筆資料都有網址、標題之類的資訊，可是不包含網頁的整篇內文。</li><li>第二階段 <strong>（WebFetch）</strong>：AI 看完搜尋結果清單之後，開始決定哪幾筆是它值得深入去看，接著再對那幾個網址各使用 WebFetch 工具發一次「把這個頁面的內文給我」的請求，這時候才會真的把內文撈回來。</li></ul><p>嗯？你是不是好奇為什麼它不是直接在搜尋階段就把內文一起撈回來？</p><p>其實最大問題還是在於 Context （上下文）的限制上，你可以思考一下，如果它每次搜尋就把 10 筆的搜尋結果＋內文都塞進來，那想當然你可以使用的 Context 就很容易爆掉，然後接著你就會開始抱怨 AI 很笨、不記得剛剛的提問（畢竟 Context 爆掉了）。所以它才會把這兩件事分成兩階段，先搜尋拿到一個「清單」，然後再決定要從清單裡面挑哪幾筆去撈內文。</p><p>這邊我也順手畫一張流程圖，把兩階段的時序串起來看一眼會更直觀：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/ai-websearch-webfetch/diagram%20(1).png" data-fancybox="gallery" data-caption="AI 搜尋流程圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/ai-websearch-webfetch/diagram%20(1).png" alt="AI 搜尋流程圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AI 搜尋流程圖</figcaption></figure><p>了解兩階段的差異之後，接下來就分別來看每個階段實際撈到了什麼。</p><h2 id="WebSearch-撈到了什麼？"><a href="#WebSearch-撈到了什麼？" class="headerlink" title="WebSearch 撈到了什麼？"></a>WebSearch 撈到了什麼？</h2><p>直接看 Anthropic 官方 web_search tool 文件，每筆搜尋結果裡面只有四個欄位：</p><ul><li><code>url</code>：網頁的網址</li><li><code>title</code>：網頁的標題</li><li><code>page_age</code>：網頁上次更新時間</li><li><code>encrypted_content</code>：加密內容，這個不是給 AI 讀文章用的，是多輪對話時拿回來做 citation 用的</li></ul><p>是的，就這樣，就這四個。</p><p>AI 在搜尋階段（WebSearch）看到的就是「網址、標題、上次更新時間」這三筆人類可讀的資訊，沒有任何內文。</p><p>那如果 AI 引用你的內容呢？也有上限</p><blockquote><p>每個 <code>web_search_result_location</code> 的 <code>cited_text</code> 最多是 <strong>up to 150 characters of the cited content</strong></p></blockquote><p>簡單來講就是：最多 150 個字元的引用文字。而這些還只是 API 層級的規格而已。</p><p>Claude Code 內建的 WebSearch 又再砍一刀，根據 Mikhail Shilkov 拆解 Claude Code 內部行為的<a href="https://mikhail.io/2025/10/claude-code-web-tools/">這篇文章</a> 裡面有提到…Claude Code 連 <code>page_age</code> 跟 <code>encrypted_content</code> 都丟掉了，只保留 <code>title</code> 跟 <code>url</code>。</p><p>所以簡單來講…在搜尋這個階段 AI 看到你網站的東西就是只有 <strong>一個標題加一個網址</strong> 沒了。</p><h2 id="WebFetch-撈到了什麼？"><a href="#WebFetch-撈到了什麼？" class="headerlink" title="WebFetch 撈到了什麼？"></a>WebFetch 撈到了什麼？</h2><p>那接下來就要來聊一下，內文何時會被撈出來了，也就是前面有提到的，第二階段 WebFetch。</p><p>AI 看完搜尋結果之後，如果決定點開某幾筆，會對每個網址發一次 WebFetch 請求，這時候才會把整篇內文撈回來。那撈取範圍多大？</p><p>這邊需要分成兩層來說明，因為 API 跟 Claude Code 的設計不太一樣。</p><blockquote><p><strong>Note</strong><br>這邊講的 API 是指 Anthropic API 的 web_fetch tool，Claude Code 是指 Anthropic 自己的產品線裡面內建的 WebFetch 功能，兩者的規格跟流程不太一樣。</p></blockquote><h3 id="API-層級的-web-fetch"><a href="#API-層級的-web-fetch" class="headerlink" title="API 層級的 web_fetch"></a>API 層級的 web_fetch</h3><p>Anthropic API 的 web_fetch tool 有一個叫 <code>max_content_tokens</code> 的參數，可以由開發者自己設定，雖然官方文件範例用的是 10 萬 tokens。</p><p>但官方文件上也有給參考換算：</p><div class="table-wrapper"><table><thead><tr><th>內容大小</th><th>估算 Token 數</th></tr></thead><tbody><tr><td>平均網頁 10 KB</td><td>約 2,500 tokens</td></tr><tr><td>大型文件頁 100 KB</td><td>約 25,000 tokens</td></tr><tr><td>研究論文 PDF 500 KB</td><td>約 125,000 tokens</td></tr></tbody></table></div><p>由此可知，一篇中長度的中文部落格文章純文字通常 1~2000 tokens 就非常足夠了，遠遠低於 10 萬的上限。所以不太會發生「內文被截斷」的問題，除非你寫了一篇五萬字的超長文章（紅樓夢之類？）。</p><p>但這邊要特別注意，web_fetch tool 的 citation 跟 web_search 的設計不太一樣，它是使用 <code>start_char_index</code> &#x2F; <code>end_char_index</code> 選出文章中的特定位置（雖然文件沒寫死字元上限就是了）。</p><h3 id="Claude-Code-內建的-WebFetch"><a href="#Claude-Code-內建的-WebFetch" class="headerlink" title="Claude Code 內建的 WebFetch"></a>Claude Code 內建的 WebFetch</h3><p>Claude Code 內建的 WebFetch 走的是另一個方式。</p><p>依照 Mikhail Shilkov 的拆解， WebFetch 它的流程會是這樣的：</p><ol><li>把 HTML 用 Turndown 套件轉成 Markdown</li><li>擷取內文的 100 KB 純文字</li><li>把這 100 KB 丟給 Haiku 3.5 模型</li><li>Haiku 根據你給它的 prompt 摘要出答案，再把摘要回傳給主模型</li></ol><p>這邊其實真正的關鍵還是在第三步驟，你會發現我們主模型（也就是我們當前使用的 Claude 模型）並不會直接看到網頁的原文，它都是看到 Haiku 摘要過的版本。也就是說，<strong>你寫的東西最後變成什麼樣回到主模型，是 Haiku 怎麼讀的決定，不是你寫了多少的決定</strong>。</p><p>而且引用部分也有限制，Mikhail 從 Claude Code 內部 prompt 抓到的規則是 <strong>Enforce a strict 125-character maximum for quotes from any source document</strong>，也就是說引用的部分最多只能有 125 個字元。</p><p>這時候應該又有一個疑問了，所以 100 KB 的純文字大概是多少呢？<br>假設以中文來說，每個字算 3 bytes 的話，100 KB 大概可以裝 3 萬多字；如果是英文，每個字算 1 byte 的話，可以裝十幾萬字。對於一個普通部落格文章來講，再長也撐不到這個量了。所以真正該關注的點應該是「怎麼讓 Haiku 摘到我想被引用的內容」，而不是「我寫了多少內容給 Haiku 看」。</p><h2 id="拿我的部落格實測一輪"><a href="#拿我的部落格實測一輪" class="headerlink" title="拿我的部落格實測一輪"></a>拿我的部落格實測一輪</h2><p>講了那麼多，這邊我們就來實際測試一下吧！</p><p>我這邊是直接請 Claude 呼叫 WebFetch 工具，然後把我部落格首頁（<code>israynotarray.com</code>）的 URL 給它，讓它去抓內文。</p><p>那麼該怎麼實際測試呢？我們可以善加利用 <a href="https://israynotarray.com/ai/20260531/2738461950/">Hook</a> 的特性，將每一個階段的輸入輸出都記錄下來，這樣就能清楚看到 AI 在每個階段實際撈到了什麼內容。</p><p>底下是我的 Hook 設定，這樣每次 WebSearch 跟 WebFetch 的輸入輸出都會被記錄下來：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PostToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;WebSearch|WebFetch&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;/claude-tools/hooks/log-web-tool.sh&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;SessionEnd&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;/claude-tools/hooks/dump-transcript.sh&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這邊我提示詞（Prompt）是以下：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">請你幫我用 WebFetch 工具抓一下 israynotarray.com 首頁的內容，然後告訴我你從這個頁面摘了什麼內容回來。<br></code></pre></td></tr></table></figure><p>實際 Input 內容如下：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;url&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://israynotarray.com&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;prompt&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;請告訴我這個首頁的主要內容：站名、站長介紹、最新文章列表（標題與簡介）、分類、以及任何導覽列或側邊欄資訊。&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>接著跑完之後，你可以看到 WebFetch 的結果裡面，AI 摘要回來的內容大概是這樣的：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;bytes&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">1068</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;code&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">200</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;codeText&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;OK&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;result&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;# 網頁內容分析\n\n基於提供的內容片段，我能提供以下資訊：\n\n## 站點結構\n- **主要分類**：AI 相關內容，包括 Gemini 和 Gemini Omni 產品\n\n## 最新文章\n**標題**：「Gemini Omni 影片生成怎麼用？30 個 Reels、產品 Demo、Logo Reveal 提示詞模板」\n\n**簡介摘要**：\n本文介紹了 Google 在 2026 年 5 月 I/O 發布的 Gemini Omni 影片生成功能，說明其如何取代先前的 Veo 3.1。文章涵蓋「一次 10 秒短片、原生同步音訊、中文文字渲染」等新功能，並提供 30 個實用提示詞模板，適用於 Instagram Reels、產品演示、Logo 動畫、B-roll、轉場效果、引言卡片、生活風格與美食旅遊等多種場景。\n\n**發布日期**：2026-06-06  \n**閱讀時間**：約 23 分鐘\n\n## 限制\n提供的內容片段不含站長介紹或完整導覽列資訊。&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;durationMs&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">4603</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;url&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://israynotarray.com&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>發現了嗎？你真正被 AI 撈走的內容其實只有這一小段 1000 字以內的摘要而已，一個網站裡面根本不可能只有這麼一小段內容，剩下的部分根本沒有被 AI 看到。也就是說中介層的 Haiku 模型看完之後，只挑了它認為跟我 prompt 相關的部分摘給我。同樣的頁面如果我用瀏覽器打開，看到的會是一整列文章卡片加 sidebar，可是 Haiku 不會把整列丟回來。</p><p>我順便試了一篇舊文，故意把 URL 路徑的某段寫錯，它就只會回傳這樣：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;bytes&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">0</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;code&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">404</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;codeText&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Not Found&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;result&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;The server returned HTTP 404 Not Found.\n\nThe response body was not retrieved. If this URL requires authentication, use an authenticated tool (e.g. `gh` for GitHub, or an MCP-provided fetch tool) instead of WebFetch.&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;durationMs&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">588</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;url&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;https://israynotarray.com/dqwdqwdqwd&quot;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>你會發現連你寫在 404 頁面的內容都撈不到，因為 WebFetch 直接回報了 404 Not Found 的錯誤，AI 根本沒辦法看到你 404 頁面上寫了什麼內容。也就是說，如果你的網站有任何路徑問題、重構過 URL、或者只有前端路由沒有實體頁面，AI 就撈不到內容了。</p><p>順帶一提，這部分跟 Claude 官方文件裡的注意事項也對得起來：</p><blockquote><p>The web fetch tool currently does not support websites dynamically rendered with JavaScript.</p></blockquote><p>如果你的部落格是用前端 SPA 撐起來、內容完全靠 JavaScript runtime 渲染的話，那 AI 撈到的可能只是空殼 HTML，看不到任何文章。因此使用靜態產生器（像 Hexo、Astro、Next.js 的 SSG 模式）相對安全，因為 build 出來的就是完整 HTML，AI 一抓就有東西看（歡迎加入 Hexo 行列）。</p><h2 id="robots-txt-那層也別忘了"><a href="#robots-txt-那層也別忘了" class="headerlink" title="robots.txt 那層也別忘了"></a>robots.txt 那層也別忘了</h2><p>接下來還有一個重點，AI 撈不撈得到你的網站，有一個大前提，也就是 robots.txt。</p><p>AI 爬蟲基本上分兩種：搜尋型（會引用 + 連回你網站）跟訓練型（吃內容餵模型，不一定有回引）。常見的對應大概長這樣：</p><div class="table-wrapper"><table><thead><tr><th>爬蟲</th><th>類型</th><th>行為</th></tr></thead><tbody><tr><td>Claude-SearchBot &#x2F; Claude-User</td><td>搜尋</td><td>Claude 回答時即時抓取，會引用</td></tr><tr><td>ClaudeBot</td><td>訓練</td><td>抓內容餵 Claude 訓練</td></tr><tr><td>OAI-SearchBot &#x2F; ChatGPT-User</td><td>搜尋</td><td>ChatGPT 回答時即時抓取，會引用</td></tr><tr><td>GPTBot</td><td>訓練</td><td>抓內容餵 GPT 訓練</td></tr><tr><td>PerplexityBot</td><td>搜尋</td><td>Perplexity 引擎用，會引用</td></tr><tr><td>Google-Extended</td><td>訓練</td><td>Gemini 訓練用</td></tr><tr><td>CCBot</td><td>訓練</td><td>Common Crawl 公開資料集</td></tr></tbody></table></div><p>如果你希望被 AI 引用、可是不想被拿去訓練，最常見的策略就是「搜尋型放行、訓練型擋掉」。</p><p>這邊我也給你一個可以直接複製貼上使用的 robots.txt 範例：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br></pre></td><td class="code"><pre><code class="hljs txt"># 搜尋型 AI 爬蟲：放行（會回引）<br>User-agent: OAI-SearchBot<br>Allow: /<br><br>User-agent: ChatGPT-User<br>Allow: /<br><br>User-agent: Claude-SearchBot<br>Allow: /<br><br>User-agent: Claude-User<br>Allow: /<br><br>User-agent: PerplexityBot<br>Allow: /<br><br># 訓練型 AI 爬蟲：擋下（吃資料不回引）<br>User-agent: GPTBot<br>Disallow: /<br><br>User-agent: ClaudeBot<br>Disallow: /<br><br>User-agent: Google-Extended<br>Disallow: /<br><br>User-agent: CCBot<br>Disallow: /<br><br>User-agent: Bytespider<br>Disallow: /<br><br># 內容信號：可被搜尋索引、不可訓練、不直接餵 AI 當輸入<br>Content-Signal: ai-train=no, search=yes, ai-input=no<br></code></pre></td></tr></table></figure><p>robots.txt 的詳細寫法可以看我之前寫的 <a href="https://israynotarray.com/other/20210627/3588736352/">快速掌握 robots.txt 用途</a>，整套 Agent Readiness 的滿分設定可以看 <a href="https://israynotarray.com/ai/2847193056/">從 3 分到滿分！讓你的網站通過 isitagentready AI Agent Readiness 完整攻略</a>。</p><h2 id="這對寫文章有什麼具體影響？"><a href="#這對寫文章有什麼具體影響？" class="headerlink" title="這對寫文章有什麼具體影響？"></a>這對寫文章有什麼具體影響？</h2><p>那講完了這些東西，所以這件事情對於寫文章有什麼具體影響呢？答案是有的，你只要理解上面的限制跟細節之後，寫文章就可以針對這四件事情多下功夫。</p><h3 id="標題要能獨立成立"><a href="#標題要能獨立成立" class="headerlink" title="標題要能獨立成立"></a>標題要能獨立成立</h3><p>搜尋階段 AI 看到你的文章，就只有 title 跟 URL 兩個欄位。</p><p>如果你的標題需要副標 or 上下文才看得懂，AI 把它跟其他十幾筆結果排在一起時就會被略過。</p><p>舉個對照：</p><ul><li>比較弱的標題：「實作筆記」</li><li>比較強的標題：「Cloudflare Worker 把 HTML 轉 Markdown 的內容協商完整實作筆記」</li></ul><p>後者把「主題、工具、做什麼事、文章類型」都塞進去了，AI 不用點開頁面就知道值不值得撈。</p><h3 id="首段就直接給答案"><a href="#首段就直接給答案" class="headerlink" title="首段就直接給答案"></a>首段就直接給答案</h3><p>WebFetch 階段，中介的 Haiku 模型是從上往下讀的，前 300 到 500 字決定它摘什麼回去。如果你的開頭是「在我們聊 X 之前，先來回顧一下歷史…」，Haiku 讀到一半發現都是背景沒答案，就直接摘背景了。</p><p>正確的寫法是每個 H2 段落開頭第一句話就直接給結論，後面再補脈絡。這個原則我在 <a href="https://israynotarray.com/ai/3734112946/">SEO 已經不夠了嗎？認識 AEO 答案引擎優化</a> 那篇也有提過，可以一起看。</p><h3 id="設計可獨立引用的單句"><a href="#設計可獨立引用的單句" class="headerlink" title="設計可獨立引用的單句"></a>設計可獨立引用的單句</h3><p>cited_text 在 web_search 是 150 字元、Claude Code 內建 WebFetch 是 125 字元。這代表 AI 引用你的時候，能塞的就是一段「沒有上下文也能成立」的短句。</p><p>寫作的時候有意識地去設計這種句子，例如：</p><ul><li>比較弱：「這個跟前面提到的那個方法不太一樣，主要差別在…」（沒有上下文看不懂）</li><li>比較強：「llms.txt 是 Answer.AI 共同創辦人 Jeremy Howard 在 2024 年提出的，目的是主動告訴 AI 網站有什麼重要內容。」（不需要前後文就成立）</li></ul><p>寫完一段之後可以自己挑一句出來看，如果單獨拿出來給沒看過全文的人都看得懂，那這句就有機會被引用。</p><h3 id="結構化內容當-Haiku-的航標"><a href="#結構化內容當-Haiku-的航標" class="headerlink" title="結構化內容當 Haiku 的航標"></a>結構化內容當 Haiku 的航標</h3><p>H2、H3、<code>-</code> 列表、表格、<code>```js</code> 程式碼區塊，這些 Markdown 結構對中介摘要層特別有用。Haiku 讀 HTML 轉出來的 Markdown 時，會把標題當「這段在講什麼」的索引，把列表當「主要要點」的訊號，把表格當「資料佐證」的單位。</p><p>如果你整篇文章都是純散文段落，Haiku 沒有航標只能靠語意硬讀，摘出來的東西就會偏散；如果你有清楚的結構，Haiku 摘的時候可以直接照航標走，得到的摘要會跟你想被引用的重點對齊。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>最後總結一下吧。</p><p>所以 AI 搜尋時，實際撈走你部落格多少呢？</p><p>我認為答案可以分成三層：</p><ul><li><strong>搜尋階段：</strong> 只有 title 加 URL</li><li><strong>撈內文階段：</strong> API 預設裝得下你整篇，可是 Claude Code 走 Haiku 中介摘要 + 100 KB 截斷</li><li><strong>引用階段：</strong> web_search 是 150 字元，Claude Code WebFetch 是 125 字元</li></ul><p>寫文章針對的就是這三道閘門，不是讓 AI 把你整篇背起來。</p><p>希望這篇有讓你對 AI 搜尋實際撈走多少內容這件事有更具體的概念哩～</p><p>如果你的部落格還沒做 AI bot 分流，可以參考前面那段 robots.txt 範例先把基礎建設起來，剩下的就交給內容慢慢累積了。</p>]]></content>
    
    
    <summary type="html">這篇來聊一下 AI 在做網路搜尋時實際撈走你部落格多少內容，把 Anthropic 官方的 web_search 跟 web_fetch tool 規格翻出來看，拆解搜尋階段只回傳 title 加 URL、cited_text 150 字元上限、Claude Code 內建 WebFetch 用 Haiku 3.5 摘要 + 100 KB 截斷的設計，再拿自己的部落格實測一輪，最後整理對 AEO 寫作的四個可操作建議：標題自帶語境、首段先給答案、設計可獨立引用的單句、結構化內容當 chunk 航標。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="AEO" scheme="https://israynotarray.com/tags/AEO/"/>
    
    <category term="Claude" scheme="https://israynotarray.com/tags/Claude/"/>
    
    <category term="WebSearch" scheme="https://israynotarray.com/tags/WebSearch/"/>
    
    <category term="WebFetch" scheme="https://israynotarray.com/tags/WebFetch/"/>
    
    <category term="AI Search" scheme="https://israynotarray.com/tags/AI-Search/"/>
    
    <category term="robots.txt" scheme="https://israynotarray.com/tags/robots-txt/"/>
    
  </entry>
  
  <entry>
    <title>Gemini Omni 影片生成怎麼用？30 個 Reels、產品 Demo、Logo Reveal 提示詞模板</title>
    <link href="https://israynotarray.com/ai/20260606/3560206475/"/>
    <id>https://israynotarray.com/ai/20260606/3560206475/</id>
    <published>2026-06-06T10:30:00.000Z</published>
    <updated>2026-06-06T10:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gems-video-gen/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%887%E6%97%A5%20%E4%B8%8B%E5%8D%8812_07_16.png" data-fancybox="gallery" data-caption="Gemini Omni 影片生成怎麼用？30 個 Reels、產品 Demo、Logo Reveal 提示詞模板"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gems-video-gen/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%887%E6%97%A5%20%E4%B8%8B%E5%8D%8812_07_16.png" alt="Gemini Omni 影片生成怎麼用？30 個 Reels、產品 Demo、Logo Reveal 提示詞模板"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Gemini Omni 影片生成怎麼用？30 個 Reels、產品 Demo、Logo Reveal 提示詞模板</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>其實我對於 AI 生成一直抱持著問號的心態，畢竟手指、文字、聲音這些細節一直都是 AI 的罩門，生出來的東西常常看了會覺得「嗯…這是 AI 生的」，不太像真的拍出來的影片。</p><p>可是這陣子我自己實際用了一下，只能說…改善非常多，整體畫面滿厲害的，雖然有些小細節還沒到完美，可是已經到了「這是 AI 生的」跟「這是拍出來的」之間的模糊地帶了，尤其是我前陣子生成的</p><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/SzHbOmeFRUw"    title="Code Review 我的青春"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><p>滿多朋友看了都嚇到，以為我真的親自拍了一個「Code Review 我的青春」的宣傳影片出來（ <del>不可能</del>），所以我覺得 Gemini Omni 在「拍出來的」這件事上是有一定性的實質上的進步了。</p><p>如果你還不太熟 Prompt 結構、跟 LLM 怎麼互動，可以先去翻我之前寫的 <a href="https://israynotarray.com/ai/20250425/3215958971/">你聽過 LLM、RAG、Prompt 嗎？一文帶你看懂生成式 AI 常見技術詞彙</a>，這篇我就直接從 Gemini Omni 開始講。</p><p>那這一篇會分四大段：先釐清一個常見的混淆（Gemini App 裡的 Omni 跟開發者端的 Veo 3.1 不是同一個東西）、接著看 Omni 比 Veo 強在哪、再講提示詞怎麼寫才會穩，最後給你 30 個分場景的提示詞模板，IG Reels 鈎子、產品 Demo、Logo Reveal、Cinematic B-roll、Before-After 轉場、知識懶人包動畫、引言金句動態卡、Lifestyle 跟旅遊食物 cut 都有，可以直接複製去改。</p><h2 id="先釐清：Gemini-App-裡的影片是-Omni-還是-Veo？"><a href="#先釐清：Gemini-App-裡的影片是-Omni-還是-Veo？" class="headerlink" title="先釐清：Gemini App 裡的影片是 Omni 還是 Veo？"></a>先釐清：Gemini App 裡的影片是 Omni 還是 Veo？</h2><p>很多人會問「那 Veo 跑去哪了？」，這個誤會其實滿正常的，因為 2026 年初的時候 Gemini App 裡面影片生成確實是 Veo 3.1 在跑，可是 5 月 I&#x2F;O 之後整個換代了。</p><p>簡單來講就是：</p><ul><li><strong>Gemini Omni</strong> 是 Google 在 2026 年 5 月 I&#x2F;O 發布的全新統一多模態模型，現在是 Gemini App 裡影片生成的主力。一段 10 秒、9:16 或 16:9、原生帶同步音訊，可以混合文字 + 圖 + 影片 + 音訊一起當輸入。</li><li><strong>Veo 3.1 &#x2F; Veo 3.1 Lite</strong> 並沒有消失，只是換崗位 — 它們被推到開發者端去了，現在主要透過 Gemini API、Vertex AI、Google AI Studio 提供，給工程師接 API 做產品或做大量批次生成用。</li></ul><p>如果用比較生活面的例子來講，Gemini Omni 像是一台全自動相機，你跟它講想拍什麼它自己會調光圈快門；Veo 3.1 比較像是手動相機，控制力強但要自己會調。一般人在 Gemini App 上隨手生 Reels、生 IG 短片、做 Logo Reveal，Omni 就夠用了。</p><h2 id="Gemini-Omni-強在哪？"><a href="#Gemini-Omni-強在哪？" class="headerlink" title="Gemini Omni 強在哪？"></a>Gemini Omni 強在哪？</h2><p>以我自己用下來，Omni 真正讓人有感的是這四件事：原生同步音訊、多模態混合輸入、中文文字渲染終於穩、對話式編輯。</p><h3 id="一、原生同步音訊"><a href="#一、原生同步音訊" class="headerlink" title="一、原生同步音訊"></a>一、原生同步音訊</h3><p>過去 AI 影片最痛的不是畫面，是聲音。</p><p>畫面生出來還行，可是要配音、配 SFX、配環境音都得另外去 Pixabay 抓素材或進剪輯軟體加。</p><p>Omni 直接把這塊吞掉了，你只要在提示詞裡寫「對白：女主角說『我們得快點走了』」、「SFX：遠處雷聲」、「Ambient：午夜咖啡店的低語聲」，它會把這三層音訊跟畫面同步生出來，嘴型對得上、SFX 出現的時機也對（當然還是有些時候會稍微跑掉，可是已經很難辨別）。</p><h3 id="二、多模態混合輸入"><a href="#二、多模態混合輸入" class="headerlink" title="二、多模態混合輸入"></a>二、多模態混合輸入</h3><p>不只是文字描述。</p><p>你可以丟一張照片給它說「把這張照片動起來，咖啡冒煙、窗外有人走過」，它會以這張照片當第一幀往後延伸；也可以丟一段既有影片進去說「同樣的場景，改成黃昏光線」，它會做 style transfer。圖、音訊、影片、文字都能混著當輸入。</p><p>以我的範本來講，我就是將我書籍的封面給 Omni，然後他就將影片中書籍的封面直接替換成我給的圖，這樣就不用每次都在提示詞裡描述一次封面長什麼樣了，而且更準確。</p><h3 id="三、中文文字渲染跟著補上來"><a href="#三、中文文字渲染跟著補上來" class="headerlink" title="三、中文文字渲染跟著補上來"></a>三、中文文字渲染跟著補上來</h3><p>這點跟前陣子 Images 2.0 把靜態圖的中文渲染解掉是同一條路線（如果你還沒看過 Images 2.0 在靜態圖上能做到什麼程度，可以順便看一下 <a href="https://israynotarray.com/ai/20260502/2581904736/">GPT-5.5 跟 ChatGPT Images 2.0 怎麼用？30 個 IG 圖卡、知識卡、產品 Mockup 提示詞模板</a> 那篇）。</p><p>Omni 把這套搬到動態畫面上，影片裡的中文字幕、開場標題、Logo 文字基本都寫得對，偶爾還是會跑掉一兩個字，可是已經到能用的程度。</p><h3 id="四、對話式編輯"><a href="#四、對話式編輯" class="headerlink" title="四、對話式編輯"></a>四、對話式編輯</h3><p>最有感的其實是這個。生完一段不滿意，你不用重寫整個 prompt，可以直接接著對話說「把光線改成黃昏的暖色就好，其他保留」，它會只動那塊。這對做系列內容（同主角、同風格、不同動作）很方便，不用每次都把所有細節重描一遍。</p><p>以我自己來講，這次的差別不在「畫得更藝術」，是在「畫得像真的拍出來的」。</p><p>剪輯後製（節奏、字卡、品牌色）還是要做，可是「拍攝」那段大概被 AI 吃掉八成（吧？）。</p><h2 id="寫好影片提示詞的萬用結構"><a href="#寫好影片提示詞的萬用結構" class="headerlink" title="寫好影片提示詞的萬用結構"></a>寫好影片提示詞的萬用結構</h2><p>在你開始複製貼上模板之前，我們先聊一下影片 prompt 的整體結構，不然只是換主題複製貼上，產出穩定度會浮動很大。</p><p>以我自己參考了一輪官方指南再加上實際用下來，prompt 想穩，這六個面向都要描述到：</p><div class="table-wrapper"><table><thead><tr><th>維度</th><th>在指什麼</th><th>範例</th></tr></thead><tbody><tr><td>構圖 &#x2F; 運鏡</td><td>鏡頭怎麼拍、怎麼動</td><td>wide shot、close-up、dolly forward、orbit、POV、handheld</td></tr><tr><td>風格</td><td>整體美感的方向</td><td>cinematic、IG vlog、anime、Pixar、紀錄片、攝影棚</td></tr><tr><td>光線</td><td>光源、色溫、質感</td><td>golden hour、霓虹、自然窗光、攝影棚柔光、逆光</td></tr><tr><td>場景</td><td>地點跟背景物件</td><td>木桌、咖啡店、台北街道、極簡白色背景</td></tr><tr><td>動作</td><td>主體在做什麼、怎麼動</td><td>走過、轉身、伸手拿杯子、敲鍵盤</td></tr><tr><td>文字渲染</td><td>畫面上的字</td><td>字幕內容、位置、動畫進場方式</td></tr></tbody></table></div><p>除了這六項，影片還有兩個靜態圖沒有的範疇，也就是「時間軸」跟「聲音」，這你必須要單獨講：</p><div class="table-wrapper"><table><thead><tr><th>項目</th><th>說明</th></tr></thead><tbody><tr><td>時間軸 &#x2F; Timestamp</td><td>想拍多鏡頭的話，用 <code>[00:00-00:03]</code> 標出每段在哪個時間發生</td></tr><tr><td>音訊</td><td>對白、SFX、Ambient 三層分開標</td></tr></tbody></table></div><blockquote><p><strong>Note</strong><br>兩件事不在 prompt 控制範圍：<strong>比例</strong>（16:9 &#x2F; 9:16）是在 Gemini App 介面上選的、<strong>長度</strong>目前 Google 鎖死 10 秒（<a href="https://blog.google/innovation-and-ai/models-and-research/gemini-models/gemini-omni/">官方</a> 說法是刻意設限不是模型限制）。<br>所以下面 30 個模板不會看到「請生一段幾秒的影片」這種句子，比例你依場景挑就好（人對鏡頭、Reels 風的就 9:16，cinematic、產品 Demo 就 16:9）。</p></blockquote><p>簡單來講就是別讓模型自己猜，你猜得越具體它生得越精準。</p><p>如果你是要編輯既有的影片（局部修改而不是整段重生），那提示詞結構就要再加上「保留」跟「修改」的對比：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">請保留：原片的人物、服裝、場景、構圖<br>請修改：把光線從正午改成黃昏的暖橘色、再加一段女聲說「就決定是這裡了」<br></code></pre></td></tr></table></figure><p>這個結構的邏輯是「這段影片裡哪些元素是我要保留不動的、哪些元素是我要修改的」，這樣模型就不會把整段影片重生一遍，而是只動你說要修改的那部分，其他的保持原來的樣子。</p><h2 id="30-個推薦提示詞模板"><a href="#30-個推薦提示詞模板" class="headerlink" title="30 個推薦提示詞模板"></a>30 個推薦提示詞模板</h2><p>30 個提示詞依場景分成八類，全部中文、可以直接複製去改。</p><p>每一組我先講適用情境，再給 prompt 本體，後面接上我自己跑出來的成品，邊看邊對照，把 <code>[ ]</code> 裡面的內容換成你的主題就行。</p><blockquote><p><strong>Note</strong><br>影片提示詞建議控制在 20 到 50 詞之間（或等長的中文），太短模型沒方向、太長重點會被稀釋。<br>而下面每個模板我都盡量壓在這個範圍。</p></blockquote><h3 id="一、IG-Reels-TikTok-鈎子開場"><a href="#一、IG-Reels-TikTok-鈎子開場" class="headerlink" title="一、IG Reels &#x2F; TikTok 鈎子開場"></a>一、IG Reels &#x2F; TikTok 鈎子開場</h3><p>這類短片就是你滑 Reels 的時候會被前 2 秒勾住停下來看的那種，重點是開頭立刻拋出衝突、提問或反差，不要花時間鋪陳。</p><h4 id="1-提問式鈎子（人對鏡頭講話）"><a href="#1-提問式鈎子（人對鏡頭講話）" class="headerlink" title="1. 提問式鈎子（人對鏡頭講話）"></a>1. 提問式鈎子（人對鏡頭講話）</h4><p>最直接的開場，主角看著鏡頭問一句讓人想往下看的問題。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：醫療診間風格的特寫，從正面看一位 25 歲的台灣女生坐在白色桌前、雙手抱胸、表情困惑。<br>風格：寫實、IG Reels 風、自然光感。<br>光線：柔和的窗光從左前方打進來。<br>場景：簡約白色背景、桌上一台筆電。<br>動作：她直視鏡頭、皺眉、嘆一口氣，然後說：『為什麼你的程式碼總是越改越亂？』<br>文字渲染：句子最後 1 秒在畫面底部用粗體中文字幕跳出『往下滑看答案 ↓』。<br>音訊：對白『為什麼你的程式碼總是越改越亂？』（清晰中文女聲）、Ambient 安靜辦公室微弱白噪音。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/vC4T7KoqB30"    title="提問式鈎子"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="2-第三人稱旁白鈎子"><a href="#2-第三人稱旁白鈎子" class="headerlink" title="2. 第三人稱旁白鈎子"></a>2. 第三人稱旁白鈎子</h4><p>不出現真人臉，用手繪或符號特寫帶旁白，適合不想露臉的創作者。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：俯視角度、特寫一支手在白紙上飛快寫程式碼。<br>風格：手繪動畫、停格動態。<br>光線：紙上方一盞暖黃檯燈。<br>場景：木桌、白紙、右上角一杯黑咖啡。<br>動作：手快速寫下『if、else、for、while』幾個關鍵字，最後畫一個大大的問號。<br>文字渲染：4 秒後在畫面上方浮現粗體標題『程式碼為什麼會亂？』。<br>音訊：鉛筆寫紙的沙沙聲、SFX 翻頁聲（最後一秒）、暖色 lo-fi 鋼琴背景音樂。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/z9ZhvFhv1j8"    title="第三人稱旁白鈎子"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="3-Before-After-兩秒切換開場"><a href="#3-Before-After-兩秒切換開場" class="headerlink" title="3. Before &#x2F; After 兩秒切換開場"></a>3. Before &#x2F; After 兩秒切換開場</h4><p>把對比直接濃縮在開頭兩三秒裡，視覺衝擊強。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">[00:00-00:04] 構圖：特寫一個 90 年代的醜陋登入頁面（彩虹底色、Comic Sans 字體、跑馬燈標題），畫面有 CRT 螢幕掃描線質感。<br>[00:04-00:08] 構圖：同位置切換成現代極簡登入頁面（白底、襯線標題、清楚層級、淡藍 CTA 按鈕），畫面銳利乾淨。<br>風格：UI 設計教學、對比強烈。<br>動作：4 秒整時用 glitch transition 切換。<br>文字渲染：[00:00-00:04] 左上『2005』、[00:04-00:08] 左上『2026』。<br>音訊：[00:00-00:04] 老舊撥接 modem 撥號聲、[00:04-00:08] 現代清脆 UI tap 音效、SFX glitch 切換音。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/2GbWvTTSwJA"    title="Before After 兩秒切換"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="4-數字倒數開場"><a href="#4-數字倒數開場" class="headerlink" title="4. 數字倒數開場"></a>4. 數字倒數開場</h4><p>倒數天生就有「等等要發生什麼？」的拉力，適合榜單型內容。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：純色背景中央放一個超大粗體數字，從 5 倒數到 1。<br>風格：極簡、海報感、現代感。<br>光線：扁平、單一光源。<br>場景：純黑背景。<br>動作：每秒切一個數字，倒數到 1 之後最後一秒切換成大字標題『2026 最該學的 5 個技能』。<br>文字渲染：數字使用霓虹紅、最後標題使用霓虹白；數字每次出現都有微微的 zoom in 動畫。<br>音訊：每秒一聲低沉鼓點、最後一秒換成清亮的鐘聲、SFX subtle whoosh。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/Bo5CSbZKu_s"    title="數字倒數開場"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="5-POV-情境鈎子"><a href="#5-POV-情境鈎子" class="headerlink" title="5. POV 情境鈎子"></a>5. POV 情境鈎子</h4><p>第一人稱視角把觀眾帶進場景，沉浸感最強。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：第一人稱 POV、看著自己的雙手在筆電鍵盤上打字。<br>風格：寫實 vlog 風、淺景深。<br>光線：晚間暖黃桌燈、後方有微弱藍色螢幕反光。<br>場景：木桌、一杯熱拿鐵、旁邊一本翻開的筆記本。<br>動作：手快速打字、然後停下來伸手拿馬克杯喝一口、放回去繼續打字。<br>文字渲染：3 秒時畫面上方浮現中文字幕『工程師的深夜 debug 日常』，停留 3 秒淡出。<br>音訊：機械鍵盤打字聲（青軸）、馬克杯放回桌面的悶響、Ambient 安靜的午夜窗外雨聲。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/vwlgCx710AQ"    title="POV 情境鈎子"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="二、產品-服務-Demo"><a href="#二、產品-服務-Demo" class="headerlink" title="二、產品 &#x2F; 服務 Demo"></a>二、產品 &#x2F; 服務 Demo</h3><p>做行銷素材、做 Side Project landing page Hero、做產品介紹影片的時候，這類模板最常用。重點是介面要清楚、操作節奏要乾淨。</p><h4 id="6-App-介面操作-Demo"><a href="#6-App-介面操作-Demo" class="headerlink" title="6. App 介面操作 Demo"></a>6. App 介面操作 Demo</h4><p>把使用者最關鍵的一個操作流程濃縮在 8 秒內，適合做 IG 廣告或 App Store 預覽。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面中央一支 iPhone 微微傾斜 15 度，螢幕上是一款記帳 App 介面。<br>風格：科技產品 Demo、Apple Keynote 質感。<br>光線：柔和漸層背景光、手機螢幕有明顯反光高光。<br>場景：淺灰漸層背景。<br>動作：[00:00-00:03] 手指從下方滑入點擊『新增交易』按鈕、[00:03-00:06] 介面跳轉到記帳表單填寫畫面、[00:06-00:08] 顯示『已儲存』綠色成功提示。<br>文字渲染：App 名稱『MoneyTrack』固定在畫面右上角；介面內金額顯示『NT$ 320』。<br>音訊：UI tap 音效（清脆短促）、成功提示時的『叮』一聲、整段配輕快薄荷綠色調的 ambient electronica。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/vbFfyDArl1M"    title="App 介面操作 Demo"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="7-SaaS-Landing-Hero-動態背景"><a href="#7-SaaS-Landing-Hero-動態背景" class="headerlink" title="7. SaaS Landing Hero 動態背景"></a>7. SaaS Landing Hero 動態背景</h4><p>放在官網 Hero 區的動態背景，營造「這個產品很潮」的氣場。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：寬廣 Hero banner 構圖，左側 60% 是大標題文字、右側 40% 是一台 MacBook 漂浮在空中，螢幕上滾動著儀表板資料。<br>風格：現代 SaaS Hero、未來感、漸層霓虹。<br>光線：紫藍漸層背景、MacBook 螢幕散發白光。<br>場景：純漸層背景，無多餘物件。<br>動作：MacBook 從畫面右側緩慢飄入、儀表板的資料數字持續向上跳動。<br>文字渲染：左側固定大標題『Ship faster with Insight』（白色粗體無襯線）、副標『Built for product teams』。<br>音訊：低沉電子合成器墊底、subtle UI 跳動聲、最後一秒淡入一聲 swoosh。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/uLQmEdyBtno"    title="SaaS Landing Hero 動態背景"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="8-商品-Mockup-Loop（360-度旋轉）"><a href="#8-商品-Mockup-Loop（360-度旋轉）" class="headerlink" title="8. 商品 Mockup Loop（360 度旋轉）"></a>8. 商品 Mockup Loop（360 度旋轉）</h4><p>電商商品頁、IG 商品 Reels 必備款，這種要做成無縫 loop。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面中央一個牛皮紙袋裝的精品咖啡豆包裝，置中、平視角度。<br>風格：精品攝影、極簡、攝影棚質感。<br>光線：頂光主光加側面柔光、背景無陰影。<br>場景：純白攝影棚背景。<br>動作：咖啡豆包裝沿垂直中軸 360 度緩慢旋轉一圈，剛好 6 秒回到原位置（適合無縫 loop）。<br>文字渲染：包裝正面印著『URBAN BEAN / Ethiopia Yirgacheffe』，旋轉到背面時可看到成份標籤。<br>音訊：純環境白噪音（攝影棚靜謐感），不加 BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/5a20roIbt1I"    title="商品 Mockup Loop"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="9-服務流程動畫（解說型）"><a href="#9-服務流程動畫（解說型）" class="headerlink" title="9. 服務流程動畫（解說型）"></a>9. 服務流程動畫（解說型）</h4><p>把抽象的服務流程視覺化，適合放在官網「How it works」區塊。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：扁平動畫 explainer 風格，畫面分成三個橫向欄位，每欄一個步驟。<br>風格：扁平向量動畫、企業 explainer。<br>光線：扁平無光影。<br>場景：淺灰背景。<br>動作：[00:00-00:03] 第一欄畫出『使用者點擊』、[00:03-00:05] 第二欄畫出『系統處理』、[00:05-00:08] 第三欄畫出『回傳結果』，每欄之間有箭頭從左往右流動。<br>文字渲染：每欄底下分別寫『01 點擊』、『02 處理』、『03 回傳』。<br>音訊：每個步驟出現時配輕脆的『叮』、最後一聲較長的『叮〜』、背景輕快的 marimba BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/N9FMPCU5ddA"    title="服務流程動畫"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="三、Logo-Reveal-品牌動態"><a href="#三、Logo-Reveal-品牌動態" class="headerlink" title="三、Logo Reveal &#x2F; 品牌動態"></a>三、Logo Reveal &#x2F; 品牌動態</h3><p>放在影片開頭或片尾的品牌動態，3 到 6 秒最剛好。重點是節奏要乾脆，不要拖。</p><h4 id="10-經典-Logo-浮現"><a href="#10-經典-Logo-浮現" class="headerlink" title="10. 經典 Logo 浮現"></a>10. 經典 Logo 浮現</h4><p>最安全的版本，任何場合都能用。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：純色背景中央，Logo 從一片光霧中淡入。<br>風格：極簡品牌動態、Apple 質感。<br>光線：背景由暗到亮、Logo 出現時有微微光暈。<br>場景：純黑背景漸變為深藍。<br>動作：Logo 從中央放大 1.1 倍再回到原大小、停留 1 秒。<br>文字渲染：Logo 用『RAY HSU』粗體無襯線白字、下方副標『Frontend Engineer』細字。<br>音訊：低頻電子音逐漸升起、最後一聲清脆的 chime。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/Gh3LoUDr7fo"    title="經典 Logo 浮現"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="11-液態金屬-Logo-Reveal"><a href="#11-液態金屬-Logo-Reveal" class="headerlink" title="11. 液態金屬 Logo Reveal"></a>11. 液態金屬 Logo Reveal</h4><p>質感比較重的版本，適合精品品牌或科技公司。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面中央一團流動的液態金屬球體，慢慢形變成 Logo 形狀。<br>風格：3D CG、奢華、高反射質感。<br>光線：環境光多反射、Logo 表面像鏡子般映出周圍光點。<br>場景：暗灰漸層工作室背景。<br>動作：[00:00-00:03] 金屬球體緩慢蠕動形變、[00:03-00:06] 形變成 Logo 字樣並穩定停留。<br>文字渲染：Logo 為『LUMEN』粗襯線字、最後一秒下方淡入細字 slogan『Crafted in light』。<br>音訊：低頻嗡鳴持續、最後 chime 加 sub bass drop。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/Ujoi8ei9Vak"    title="液態金屬 Logo Reveal"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="12-手繪墨水-Logo"><a href="#12-手繪墨水-Logo" class="headerlink" title="12. 手繪墨水 Logo"></a>12. 手繪墨水 Logo</h4><p>文青系、生活品牌或個人工作室適合的版本。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：白紙特寫俯視角度、一支毛筆從右上方滑入。<br>風格：手繪、書法質感、Wabi-sabi 美學。<br>光線：自然側光、紙面有細微紋理。<br>場景：日式米白紙背景。<br>動作：毛筆從右上方移動到中央、寫下兩個漢字 Logo、寫完後毛筆離開畫面、字停留 2 秒。<br>文字渲染：毛筆寫下『靜流』兩個漢字，行書風格。<br>音訊：毛筆沾水聲、毛筆筆觸沙沙聲、最後一秒淡入古箏單音。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/igq5NOF1b5c"    title="手繪墨水 Logo"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="四、Cinematic-B-roll"><a href="#四、Cinematic-B-roll" class="headerlink" title="四、Cinematic B-roll"></a>四、Cinematic B-roll</h3><p>B-roll 就是用來鋪在主畫面底下、墊節奏的素材。Omni 在這塊很強，因為它認得攝影語言，你寫 anamorphic、dolly forward、shallow depth of field 它都聽得懂。</p><h4 id="13-城市夜景-Drone-shot"><a href="#13-城市夜景-Drone-shot" class="headerlink" title="13. 城市夜景 Drone shot"></a>13. 城市夜景 Drone shot</h4><p>放在 vlog 開頭或封面預覽很有故事感。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：空拍機高空俯視鏡頭、緩慢往前推。<br>風格：cinematic、Blade Runner 質感、anamorphic lens（畫面有 lens flare）。<br>光線：霓虹紫藍光、雨後街道有水反光。<br>場景：未來感的東京涉谷夜景、高樓林立、街上人潮流動。<br>動作：空拍機保持高度緩慢 dolly forward、車燈在街上形成光軌。<br>文字渲染：無。<br>音訊：cinematic ambient（synth pad）、遠處微弱的城市車流聲。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/d0-jHi6a81A"    title="城市夜景 Drone shot"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="14-慢動作雨滴特寫"><a href="#14-慢動作雨滴特寫" class="headerlink" title="14. 慢動作雨滴特寫"></a>14. 慢動作雨滴特寫</h4><p>療癒系內容、ASMR、保養品廣告很常見的鏡頭。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：超特寫鏡頭對著一片綠葉、水滴從上方落下打在葉面。<br>風格：自然紀錄片、macro lens。<br>光線：清晨柔和側光、水珠有 specular highlight。<br>場景：森林綠葉、背景虛化的綠色 bokeh。<br>動作：水滴以慢動作（240fps 質感）落下、撞擊葉面後噴濺。<br>文字渲染：無。<br>音訊：水滴撞擊的低沉 boom（慢動作音效）、森林 ambient 鳥鳴。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/8aIJaUdCqx8"    title="慢動作雨滴特寫"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="15-咖啡沖煮過程"><a href="#15-咖啡沖煮過程" class="headerlink" title="15. 咖啡沖煮過程"></a>15. 咖啡沖煮過程</h4><p>生活感 B-roll 的萬用款，咖啡店、品牌廣告、文青 vlog 都能用。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：手沖咖啡特寫、從上方 45 度俯視看著熱水從鵝頸壺倒進濾杯。<br>風格：質感攝影、咖啡品牌廣告風。<br>光線：早晨側窗光、蒸氣有逆光透明感。<br>場景：木桌、白色濾杯、底下黑色玻璃壺。<br>動作：水流穩定地畫圓注入濾杯、咖啡粉膨脹、蒸氣裊裊上升。<br>文字渲染：8 秒最後 1 秒畫面右下淡入小字『Slow brew. Slower mornings.』。<br>音訊：水流注入聲、咖啡萃取的細微氣泡聲、Ambient 安靜的早晨。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/0yS760P77Ug"    title="咖啡沖煮過程"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="16-Time-lapse-城市黃昏"><a href="#16-Time-lapse-城市黃昏" class="headerlink" title="16. Time-lapse 城市黃昏"></a>16. Time-lapse 城市黃昏</h4><p>縮時很適合用 Omni 跑，因為它能直接生「光線變化」這種有時間軸的素材。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：固定鏡頭、台北 101 為主視覺、前景有大樓剪影。<br>風格：time-lapse、cinematic、暖色調。<br>光線：從金黃 magic hour 過渡到深藍 blue hour 再到夜景燈火。<br>場景：台北信義區夜景。<br>動作：天空雲層加速流動、太陽落下、城市燈光逐一亮起。<br>文字渲染：無。<br>音訊：cinematic 漸進弦樂 swell、隨夜色加深音樂層次也加厚。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/MYNIKY1AZkU"    title="Time-lapse 城市黃昏"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="五、知識懶人包動畫版"><a href="#五、知識懶人包動畫版" class="headerlink" title="五、知識懶人包動畫版"></a>五、知識懶人包動畫版</h3><p>之前在 IG 上做圖卡的人應該都很熟「知識懶人包」這個格式，把它動畫化之後互動率通常會再上一階。</p><h4 id="17-概念卡片翻轉"><a href="#17-概念卡片翻轉" class="headerlink" title="17. 概念卡片翻轉"></a>17. 概念卡片翻轉</h4><p>把名詞解釋濃縮成「正面題目、背面答案」的翻牌動畫。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面中央一張卡片，正反兩面分別寫『API』跟『程式之間的對話橋樑』。<br>風格：扁平向量動畫、IG 知識卡風。<br>光線：扁平柔和光影。<br>場景：米白底色、簡約。<br>動作：[00:00-00:02] 卡片正面『API』停留、[00:02-00:04] 卡片以 Y 軸翻轉、[00:04-00:08] 卡片背面『程式之間的對話橋樑』停留、底下浮現三個圖示（前端、API、後端）連成一條線。<br>文字渲染：卡片正面『API』、背面『程式之間的對話橋樑』。<br>音訊：卡片翻轉時的『刷』聲、最後三個圖示出現時各配一聲『叮』。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/kUGNT1u5p6A"    title="概念卡片翻轉"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="18-三步驟流程動畫"><a href="#18-三步驟流程動畫" class="headerlink" title="18. 三步驟流程動畫"></a>18. 三步驟流程動畫</h4><p>把「該怎麼做」步驟化的動態版本。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面中央由上到下三個圓形節點，依序由箭頭連接。<br>風格：扁平動畫、教學風。<br>光線：扁平。<br>場景：純白背景。<br>動作：[00:00-00:03] 第一個節點放大、文字『01 切到分支』淡入、[00:03-00:06] 第二個節點放大、文字『02 git rebase main』淡入、[00:06-00:08] 第三個節點放大、文字『03 解衝突後 continue』淡入。<br>文字渲染：頂部固定標題『Git Rebase 三步驟』、三個步驟文字。<br>音訊：每個節點出現時配輕脆『噠』聲、背景輕快 marimba BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/d1tf2DnqQ2E"    title="三步驟流程動畫"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="19-對比表動態揭曉"><a href="#19-對比表動態揭曉" class="headerlink" title="19. 對比表動態揭曉"></a>19. 對比表動態揭曉</h4><p>兩個東西並排比，逐項揭曉的版本，比靜態圖更能撐住觀眾的注意力。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：畫面左右分欄、左 Vue 右 React，各列三個比較項目。<br>風格：扁平動畫、設計感。<br>光線：扁平。<br>場景：左欄淺綠底、右欄淺藍底。<br>動作：[00:00-00:02] 兩欄頂部 Logo 區塊淡入、[00:02-00:04] 三個比較項目由上而下逐項浮現、[00:04-00:06] 最後一項出現時兩欄底部都浮現一個對勾，意思是『各有強項』。<br>文字渲染：左欄頂部『Vue』、右欄頂部『React』、三個比較項『學習曲線』、『社群規模』、『生態系』。<br>音訊：每項出現時配輕脆音效、最後對勾出現時一聲較柔的『叮〜』。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/yYhPWAb9oXA"    title="對比表動態揭曉"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="六、Before-After-對比轉場"><a href="#六、Before-After-對比轉場" class="headerlink" title="六、Before &#x2F; After 對比轉場"></a>六、Before &#x2F; After 對比轉場</h3><p>對比型內容在 Reels 上的觸及通常都很好，人類對「變化」這件事很有興趣。</p><h4 id="20-房間改造-Before-After"><a href="#20-房間改造-Before-After" class="headerlink" title="20. 房間改造 Before &#x2F; After"></a>20. 房間改造 Before &#x2F; After</h4><p>居家、收納、爆改類內容的招牌格式。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：固定機位、拍攝同一個房間角落。<br>風格：寫實、IG 居家風。<br>光線：自然窗光。<br>場景：一間房間的書桌角落。<br>動作：[00:00-00:03] Before 狀態：桌面凌亂、電線雜亂、雜物堆積、整體灰塵感、[00:03-00:06] 用 wipe transition 從左往右過渡到 After 狀態：桌面整齊、線材收納、植物擺飾、明亮乾淨。<br>文字渲染：[00:00-00:03] 左上『Before』紅色標籤、[00:03-00:06] 左上『After』綠色標籤。<br>音訊：[00:00-00:03] 沉悶低頻、[00:03-00:06] 清脆鈴聲後接輕快 BGM、SFX wipe transition 音效。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/5gqJ6DmBn6c"    title="房間改造 Before After"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="21-Code-重構前後"><a href="#21-Code-重構前後" class="headerlink" title="21. Code 重構前後"></a>21. Code 重構前後</h4><p>工程師受眾很吃這套，把抽象的重構成果視覺化。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：左右分割畫面、各顯示一段程式碼。<br>風格：終端機質感、深色主題、語法高亮。<br>光線：螢幕自發光。<br>場景：純黑背景。<br>動作：[00:00-00:03] 左側顯示 30 行雜亂程式碼（深紅色波浪線）、[00:03-00:06] 右側陸續打字出簡潔的 10 行版本（綠色 check mark）、最後左右兩側用 split screen 並列比較。<br>文字渲染：左上『Before：if-else 地獄』、右上『After：early return』。<br>音訊：機械鍵盤打字聲（密集）、最後一聲『check』音效。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/fpEmACpugU4"    title="Code 重構前後"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="22-文案改寫前後"><a href="#22-文案改寫前後" class="headerlink" title="22. 文案改寫前後"></a>22. 文案改寫前後</h4><p>寫文案、做 copywriting 教學的人很愛用。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：純色背景、中央一段文字。<br>風格：極簡、編輯設計感。<br>光線：扁平。<br>場景：奶油黃背景。<br>動作：[00:00-00:03] 顯示一段冗長文案（約 30 字）、底下小字『Before』紅色標籤、[00:03-00:06] 文字逐字消失重新打出精簡 8 字標語、底下小字『After』綠色標籤。<br>文字渲染：Before 段『我們提供業界最高品質的客製化網頁設計服務，從零到上線一條龍』、After 段『讓你的網站，被看見』。<br>音訊：文字消失時的『沙沙』聲、新文字打字機聲、最後一聲『叮』。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/le6xwNPmq5I"    title="文案改寫前後"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="七、引言金句動態卡"><a href="#七、引言金句動態卡" class="headerlink" title="七、引言金句動態卡"></a>七、引言金句動態卡</h3><p>把金句卡動態化，比靜態圖在動態牆上更容易停留。</p><h4 id="23-名人金句動態"><a href="#23-名人金句動態" class="headerlink" title="23. 名人金句動態"></a>23. 名人金句動態</h4><p>設計、產品、領導力類的引言常常會被分享。</p><blockquote><p><strong>Note</strong><br>Gemini Omni 對真實知名公眾人物的肖像跟名字會直接擋（政治人物、科技創辦人、藝人、運動員都一樣，Veo &#x2F; Sora &#x2F; Midjourney 那些主流工具也是這樣，這是平台對真人肖像濫用的內容政策）。要做引言或致敬類的內容就用虛構名字、匿名、或自己當作者，下面範例我就直接用自己當署名。</p></blockquote><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：左側 1/3 是低飽和度的人物剪影、右側 2/3 是引言文字。<br>風格：編輯設計感、雜誌風。<br>光線：暖灰背景、單色剪影。<br>場景：奶油色背景。<br>動作：[00:00-00:02] 剪影淡入、[00:02-00:05] 引言逐字打出、[00:05-00:06] 署名行淡入。<br>文字渲染：主引言『Design is how it works.』、署名『— Ray』。<br>音訊：背景輕柔的 piano ambient、文字出現時的『沙沙』筆觸音效。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/Gkerur368rk"    title="名人金句動態"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="24-中文短句金句"><a href="#24-中文短句金句" class="headerlink" title="24. 中文短句金句"></a>24. 中文短句金句</h4><p>日系極簡風、IG 收藏型內容。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：純色背景中央一句中文短文。<br>風格：日系極簡、文青風。<br>光線：扁平。<br>場景：奶油黃底色。<br>動作：[00:00-00:02] 文字由模糊到清晰浮現、[00:02-00:04] 停留 2 秒。<br>文字渲染：主句『慢慢來，比較快。』（手寫感中文字體）、右下角小字『2026.06 / @israynotarray』。<br>音訊：純環境的微弱 ambient，不加 BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/0rw_jGle2jA"    title="中文短句金句"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="25-Story-翻頁式金句"><a href="#25-Story-翻頁式金句" class="headerlink" title="25. Story 翻頁式金句"></a>25. Story 翻頁式金句</h4><p>連續放兩三句，模擬翻書節奏，適合 IG Story 廣告或品牌敘事。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：模仿翻書效果、連續顯示三句格言。<br>風格：手繪本書質感、復古紙張紋理。<br>光線：暖光、紙張有微微陰影。<br>場景：木桌背景、紙張置中。<br>動作：[00:00-00:03] 第一句『起步前，先想清楚為什麼。』、[00:03-00:05] 翻頁、[00:05-00:08] 第二句『動手後，別停下來修正方向。』。<br>文字渲染：兩句中文用襯線字體。<br>音訊：翻頁的紙張聲、背景暖色木吉他和弦。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 9/16; max-width: 320px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/rC4sSPRIblg"    title="Story 翻頁式金句"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h3 id="八、Lifestyle-旅遊-食物-cut"><a href="#八、Lifestyle-旅遊-食物-cut" class="headerlink" title="八、Lifestyle &#x2F; 旅遊 &#x2F; 食物 cut"></a>八、Lifestyle &#x2F; 旅遊 &#x2F; 食物 cut</h3><p>這類是 IG Reels、TikTok 觸及最穩的內容類型，重點是節奏感跟氛圍。Omni 的 ambient 音訊在這邊很加分。</p><h4 id="26-早晨儀式-vlog-cut"><a href="#26-早晨儀式-vlog-cut" class="headerlink" title="26. 早晨儀式 vlog cut"></a>26. 早晨儀式 vlog cut</h4><p>居家、生活感 vlog 的開場款。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：手持攝影機抖動感、第一人稱視角。<br>風格：vlog、IG 系生活感、自然色調。<br>光線：清晨自然窗光。<br>場景：木質地板、白色床、窗邊綠植。<br>動作：[00:00-00:03] 鏡頭從床上的羽絨被慢慢移動到窗邊、[00:03-00:06] 拉開窗簾、晨光灑進室內、[00:06-00:08] 鏡頭轉到桌上的一杯熱拿鐵特寫。<br>文字渲染：8 秒最後 1 秒右下淡入小字『Slow morning, Saturday.』。<br>音訊：鳥鳴聲、窗外微弱車流聲、咖啡杯放下時的悶響、暖色 lo-fi 鋼琴 BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/nJ6CubXFgRc"    title="早晨儀式 vlog cut"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="27-食物擺盤特寫"><a href="#27-食物擺盤特寫" class="headerlink" title="27. 食物擺盤特寫"></a>27. 食物擺盤特寫</h4><p>食譜、餐廳廣告、美食 IG 帳號必備。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：俯視角度、一盤剛上桌的義大利麵特寫。<br>風格：食譜雜誌、暖色調。<br>光線：餐廳柔光、有微微背光透出熱氣。<br>場景：木桌、白色陶瓷盤、旁邊一杯紅酒。<br>動作：[00:00-00:02] 義大利麵剛上桌、熱氣裊裊上升、[00:02-00:04] 一隻手從右邊伸入撒上起司粉、[00:04-00:06] 鏡頭微微推近特寫蒸氣。<br>文字渲染：無。<br>音訊：起司粉撒落聲、餐廳遠處輕微背景人聲、暖色義式手風琴 BGM。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/l95pxso7hs0"    title="食物擺盤特寫"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="28-旅遊一日-cut"><a href="#28-旅遊一日-cut" class="headerlink" title="28. 旅遊一日 cut"></a>28. 旅遊一日 cut</h4><p>多個快速 cut 組成的一日剪輯，旅遊帳號的常駐格式。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：每個 cut 約 1.5 秒、總共 5 到 6 個地點。<br>風格：旅遊 vlog、IG Reels 剪輯感。<br>光線：根據時間變化（早午晚）。<br>場景：台北中山站一日散步行程。<br>動作：[00:00-00:02] 捷運站口 / [00:02-00:03] 文青咖啡店外觀 / [00:03-00:05] 街角文具店 / [00:05-00:06] 公園長椅 / [00:06-00:08] 夜晚霓虹巷子。<br>文字渲染：每個 cut 左下角浮現地點小字（『10:00 中山站』、『11:30 鳴草咖啡』等）。<br>音訊：節奏感強的 indie pop BGM、cut 之間有 swoosh transition。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/H0VWHyzyvZ4"    title="旅遊一日 cut"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="29-寵物日常特寫"><a href="#29-寵物日常特寫" class="headerlink" title="29. 寵物日常特寫"></a>29. 寵物日常特寫</h4><p>療癒系內容，貓狗帳號的萬用素材。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：低角度、貼近寵物視線高度。<br>風格：寵物 vlog、療癒系。<br>光線：午後柔光。<br>場景：木質地板、一隻橘貓躺在陽光斑點裡。<br>動作：[00:00-00:03] 橘貓懶懶地翻身、伸懶腰、[00:03-00:06] 鏡頭推近到貓臉特寫、貓打了個哈欠。<br>文字渲染：6 秒最後 1 秒右下角小字『Today&#x27;s mood.』。<br>音訊：貓打哈欠的微弱聲音、Ambient 安靜午後、極輕的鋼琴單音。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/t3UouglYCQY"    title="寵物日常特寫"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h4 id="30-城市散步-walk-and-talk"><a href="#30-城市散步-walk-and-talk" class="headerlink" title="30. 城市散步 walk-and-talk"></a>30. 城市散步 walk-and-talk</h4><p>訪談、紀錄片、podcast 預告片很常用的鏡頭語言。</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs txt">構圖：跟拍鏡頭（gimbal）、從後方拍主角邊走邊講話。<br>風格：紀錄片風、自然光、新聞訪談感。<br>光線：黃昏側光、長影子。<br>場景：台北舊街區、紅磚老房、騎樓。<br>動作：主角從畫面右側慢步往前走、邊走邊回頭對鏡頭講話、相機保持穩定跟拍。<br>文字渲染：開頭 2 秒右下角浮現姓名字幕『阿明 / 文字工作者』。<br>音訊：主角對白『我覺得這條街有種台北 90 年代的味道』（清晰中文男聲、台灣口音）、Ambient 街角機車聲、遠處狗叫。<br></code></pre></td></tr></table></figure><div style="aspect-ratio: 16/9; max-width: 720px; margin: 0 auto;">  <iframe    src="https://www.youtube.com/embed/hQ7Q8ddstwg"    title="城市散步 walk-and-talk"    style="width: 100%; height: 100%; border: 0;"    loading="lazy"    allow="accelerometer; autoplay; clipboard-write; encrypted-media; gyroscope; picture-in-picture; web-share"    allowfullscreen  ></iframe></div><h2 id="實際使用的小技巧"><a href="#實際使用的小技巧" class="headerlink" title="實際使用的小技巧"></a>實際使用的小技巧</h2><p>光知道結構跟模板還不夠，這邊補幾個我自己用下來覺得有用的小技巧。</p><h3 id="一、中文出包怎麼救（字幕、口音都算）"><a href="#一、中文出包怎麼救（字幕、口音都算）" class="headerlink" title="一、中文出包怎麼救（字幕、口音都算）"></a>一、中文出包怎麼救（字幕、口音都算）</h3><p>字幕跑掉一兩個字的話，不要整段重生，直接在 Gemini App 的對話框接著說…</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">請只把 02 秒的字幕『XXX』改成『YYY』，其他保留。<br></code></pre></td></tr></table></figure><p>那它就只動那塊。</p><p>如果整段字幕都跑掉，那大概率就是提示詞沒有用引號明確標出文字，回去把字用 <code>「」</code>、<code>『』</code> 包起來再生一次就會穩定很多。</p><p>至於…口音的部分，中文對白偶爾會跑成非預期的腔調、或是英文味很重的中文，這時候只要在提示詞裡加「清晰的台灣口音中文女聲」或「自然的台灣男聲」這種明確標註，準確率會明顯提高。</p><h3 id="二、想做無縫-loop-影片的話"><a href="#二、想做無縫-loop-影片的話" class="headerlink" title="二、想做無縫 loop 影片的話"></a>二、想做無縫 loop 影片的話</h3><p>商品 Mockup、Logo 背景、SaaS Hero 這種要不停循環的素材，提示詞裡要明確寫「可循環播放」、「第 0 秒跟第 N 秒的畫面要對齊」，然後動作要設計成週期性的（旋轉一圈、循環移動等），不要寫「咖啡杯被打翻」這種一次性的動作。</p><h3 id="三、構圖比例對應平台"><a href="#三、構圖比例對應平台" class="headerlink" title="三、構圖比例對應平台"></a>三、構圖比例對應平台</h3><p>不同平台的影片比例不一樣，記得在 Gemini App 介面上生成前先把比例切好（目前只有 16:9 跟 9:16 兩種，其他比例要事後裁切），不然產出可能不能直接用。常見比例整理在下面：</p><div class="table-wrapper"><table><thead><tr><th>用途</th><th>比例</th></tr></thead><tbody><tr><td>IG Reels &#x2F; TikTok &#x2F; YouTube Shorts</td><td>9:16（直式）</td></tr><tr><td>IG 貼文影片</td><td>1:1（方形）或 4:5（直式）</td></tr><tr><td>YouTube 主影片 &#x2F; 廣告</td><td>16:9（橫式）</td></tr><tr><td>部落格嵌入 &#x2F; OG 動圖預覽</td><td>16:9</td></tr><tr><td>Facebook 動態</td><td>1:1 或 16:9</td></tr></tbody></table></div><p>從這張表可以看出來，現在的影片內容大宗已經是 9:16 直式，要做 Reels 就在 App 上先切 9:16，要做 YouTube 主影片再切 16:9，不要兩邊都用 16:9 然後直式裁切，那樣畫面比例會跑掉。</p><h3 id="四、不要一鏡塞太多動作"><a href="#四、不要一鏡塞太多動作" class="headerlink" title="四、不要一鏡塞太多動作"></a>四、不要一鏡塞太多動作</h3><p>我只能說 10 秒能講的東西其實很有限。</p><p>如果你的提示詞同時要求「主角走進門 + 坐下 + 打開筆電 + 開始打字 + 喝一口咖啡」，那模型絕對塞不完，最後通常會犧牲掉動作流暢度跟物件一致性。</p><p>建議一段影片最多 1 到 2 個主要動作，要多就用 <code>timestamp</code> 拆成多鏡頭，或者乾脆拆成兩段分別生，再剪在一起。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>整理到這邊，這篇文章把 Gemini Omni 跟 Veo 3.1 在 Gemini App 端的分工、Omni 強在哪、提示詞結構、30 個分場景的提示詞模板都收錄進來了。</p><p>說真的，AI 影片在 Omni 之前我都覺得它是玩具，做做個概念 demo、做生成式藝術很適合，可是要拿來剪 Reels、做產品 Demo、做 Logo Reveal，總是差那一口氣，不是動作會卡、就是文字會糊、不然就是要再去找配樂跟音效。</p><p>Omni 是第一次讓我覺得「這真的可以直接拿來剪」（應該吧）。</p><p>當然 AI 影片還是有它的極限，需要精準品牌規範或長秒數敘事的素材還是要回到真人攝影 + 剪輯，可是日常 80% 的短片素材，你完全可以靠這 30 個模板自己生哩～</p><p>如果你想把 Gemini 變成自己的影片生成小幫手，可以順便看一下 <a href="https://israynotarray.com/ai/20260303/3167917304/">Gemini Gems 教學：輕鬆打造你的專屬 AI 助手</a>，把這套提示詞結構打包成一個專屬的 Gems，下次就不用每次都複製貼上整段了。</p><p>當然這邊如果懶的話，你也可以直接參考一下我之前做的 <a href="https://gems.israynotarray.dev/">Awesome Gemini Gems</a>，裡面有許多我準備好的 Gems，未來我可能也會增加一些影片生成相關的 Gems，歡迎隨時來逛逛～</p><p>希望這 30 個模板對你的內容創作有實際幫助哩～</p><p><del>反正跟其他文章一樣，只要你直接複製貼上就會發現產出不太一樣，畢竟 AI 生成每次都有隨機性（笑）</del></p>]]></content>
    
    
    <summary type="html">Google 在 2026 年 5 月 I/O 發布 Gemini Omni 之後，Gemini App 裡的影片生成正式換代，取代了原本的 Veo 3.1， 一次 10 秒短片、原生同步音訊、中文文字渲染都跟著補上來。這篇先釐清 Gemini App 裡的 Omni 跟開發者端 Veo 3.1 怎麼分工， 再帶你看 Omni 強在哪、提示詞要怎麼寫，最後附上 30 個分場景的提示詞模板，包含 IG Reels 鈎子開場、產品 Demo、Logo Reveal、 Cinematic B-roll、Before-After 轉場、引言金句動態卡、Lifestyle 跟旅遊食物 cut 都有。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Gemini" scheme="https://israynotarray.com/tags/Gemini/"/>
    
    <category term="提示詞" scheme="https://israynotarray.com/tags/%E6%8F%90%E7%A4%BA%E8%A9%9E/"/>
    
    <category term="Gemini Omni" scheme="https://israynotarray.com/tags/Gemini-Omni/"/>
    
    <category term="Veo" scheme="https://israynotarray.com/tags/Veo/"/>
    
    <category term="Veo 3.1" scheme="https://israynotarray.com/tags/Veo-3-1/"/>
    
    <category term="AI 影片生成" scheme="https://israynotarray.com/tags/AI-%E5%BD%B1%E7%89%87%E7%94%9F%E6%88%90/"/>
    
    <category term="IG Reels" scheme="https://israynotarray.com/tags/IG-Reels/"/>
    
  </entry>
  
  <entry>
    <title>Claude Code Auto Memory 一直佔用記憶體？清理與檢查完整教學</title>
    <link href="https://israynotarray.com/ai/20260602/4582937160/"/>
    <id>https://israynotarray.com/ai/20260602/4582937160/</id>
    <published>2026-06-02T06:00:00.000Z</published>
    <updated>2026-06-02T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/auto-memory-clear/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8803_05_05.png" data-fancybox="gallery" data-caption="Claude Code Auto Memory 一直佔用記憶體？清理與檢查完整教學"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/auto-memory-clear/ChatGPT%20Image%202026%E5%B9%B46%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8803_05_05.png" alt="Claude Code Auto Memory 一直佔用記憶體？清理與檢查完整教學"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code Auto Memory 一直佔用記憶體？清理與檢查完整教學</figcaption></figure><blockquote><p><strong>Note</strong><br>Auto Memory 會將你的 feedback 儲存到 <code>~/.claude/projects/&lt;專案&gt;/memory/</code> 底下，雖然不會佔用 RAM，但它卻會佔用你的 Context Window 跟磁碟空間。好消息是記憶檔通常只有 KB 等級，真正讓 <code>~/.claude/projects/</code> 膨脹到幾百 MB 甚至 GB 的兇手其實是同一個資料夾底下的 conversation jsonl。Claude Code 預設會用 <code>cleanupPeriodDays</code>（30 天）自動清掉舊的 session 檔，但如果你最近剛好跑了一個超大的 session，等 30 天可能有點久，可以用 <code>find</code> 把大檔案揪出來處理。</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>前陣子朋友突然私訊我：「ㄟ，Ray，我電腦突然硬碟少了整整 300~500 MB 的空間耶？而且每次啟動 Claude 馬上就少了很多 Context，你有頭緒嗎？」</p><p>其實我大概知道原因，因為朋友這陣子正好瘋狂在使用 Claude Code 開發，第一直覺我就幫他看了一下 <code>~/.claude/projects/</code>，果不其然，這個資料夾已經佔用了 300 多 MB 的空間，而這個東西其實就是 Claude Code 的 Auto Memory 機制導致，所以這一篇就要來聊一下這一塊。</p><p>另外，之前我有寫過一篇 <a href="https://israynotarray.com/ai/20260330/2896397295/">Claude Code Auto Memory 是什麼？自動記憶功能完整教學</a> 介紹 Auto Memory 的基本概念跟運作方式，這一篇就接續那一篇，來聊一下 Auto Memory 到底「佔」了什麼、什麼東西才是真的在吃你的磁碟跟 Context Window，以及怎麼處理。</p><h2 id="Auto-Memory-到底佔用了什麼？"><a href="#Auto-Memory-到底佔用了什麼？" class="headerlink" title="Auto Memory 到底佔用了什麼？"></a>Auto Memory 到底佔用了什麼？</h2><p>我們先來聊一下 Auto Memory 到底佔了什麼哪些資源，簡單來說就是兩個：</p><div class="table-wrapper"><table><thead><tr><th>資源</th><th>佔多少</th><th>為什麼會佔</th></tr></thead><tbody><tr><td>Context Window</td><td><code>MEMORY.md</code> 前 200 行或 25KB</td><td>每次對話啟動時自動載入</td></tr><tr><td>磁碟空間</td><td>通常只有 KB 等級</td><td>記憶內容寫成 Markdown 檔存在 Home 目錄</td></tr></tbody></table></div><blockquote><p><strong>Note</strong><br>這邊要特別提醒一下，我這邊寫的「記憶體」並不是 RAM，而是 Context Window 跟磁碟空間。</p></blockquote><p>那怎麼找到這個 <code>MEMORY.md</code> 呢？它其實就是你專案資料夾底下 <code>memory/</code> 的索引檔，路徑是 <code>~/.claude/projects/&lt;專案&gt;/memory/MEMORY.md</code>，裡面會列出你所有的記憶檔（通常是一些 Markdown 檔），每一條是一個索引，Claude 會根據這個索引去讀對應的記憶內容。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602143940.png" data-fancybox="gallery" data-caption="Claude Code Auto Memory 的記憶檔結構示意圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602143940.png" alt="Claude Code Auto Memory 的記憶檔結構示意圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code Auto Memory 的記憶檔結構示意圖</figcaption></figure><h2 id="Context-Window：MEMORY-md-怎麼吃-token？"><a href="#Context-Window：MEMORY-md-怎麼吃-token？" class="headerlink" title="Context Window：MEMORY.md 怎麼吃 token？"></a>Context Window：MEMORY.md 怎麼吃 token？</h2><p>首先，Auto Memory 的記憶內容是以 Markdown 檔的形式存在磁碟上的，Claude Code 每次啟動對話的時候會自動把 <code>MEMORY.md</code> 裡面的內容載入到 Context Window 裡面，讓 Claude 在對話過程中可以參考這些記憶。</p><p>可是 <code>MEMORY.md</code> 並不是整份載入，而是有一個限制：</p><blockquote><p><strong>Auto Memory 只會載入 <code>MEMORY.md</code> 的前 200 行或前 25KB（取小的那個）到 Context Window 裡面。</strong></p></blockquote><p>也就是說，如果你的 <code>MEMORY.md</code> 超過 200 行或 25KB 的話，超過的部分根本不會被載入到 Context Window 裡面，這也是為什麼有些人使用 Claude 之後，發現自己明明曾經講過，Claude 它也說過它會記憶下來，但它實際上根本不記得了，因為它根本沒看到那些記憶。</p><p>這邊也來讓我們看一下實際的 <code>MEMORY.md</code> 內容會是長什麼樣子：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-bullet">-</span> [<span class="hljs-string">feedback_writing_style.md</span>](<span class="hljs-link">./feedback_writing_style.md</span>) - 不要在文案中大量使用「」和 emoji<br><span class="hljs-bullet">-</span> [<span class="hljs-string">feedback_pnpm.md</span>](<span class="hljs-link">./feedback_pnpm.md</span>) - 此專案使用 pnpm，不要用 npm<br><span class="hljs-bullet">-</span> [<span class="hljs-string">feedback_uiux_skill_for_ui_work.md</span>](<span class="hljs-link">feedback_uiux_skill_for_ui_work.md</span>) — 牽涉 UI／UX 的修改一律呼叫 <span class="hljs-code">`ui-ux-pro-max:ui-ux-pro-max`</span><br></code></pre></td></tr></table></figure><p>其實每一條都是很單純的一句話索引，加上連結指向實際的記憶內容檔案，這樣的管理方式對於 Context Window 的佔用是非常友善的，而實際的細節只會在對話中需要的時候才會被 Claude 讀取，這樣就不會讓 Context Window 被大量細節佔滿，因此以前面講的 <code>feedback_uiux_skill_for_ui_work.md</code> 它就會像這樣：</p><figure class="highlight md"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs md">---<br>name: UI 相關修改必須透過 ui-ux-pro-max skill<br>description: 任何牽涉 UI／UX 的調整、修改、新增，都要用 Skill tool 呼叫 ui-ux-pro-max:ui-ux-pro-max<br>type: feedback<br><span class="hljs-section">originSessionId: bd73d7ce-893d-423f-2344dd123dw</span><br><span class="hljs-section">---</span><br>只要這次修改牽涉 <span class="hljs-strong">**UI／UX**</span>（畫面排版、互動行為、視覺呈現、元件設計、Tailwind class、元件結構等），<span class="hljs-strong">**必須透過 Skill tool 呼叫 `ui-ux-pro-max:ui-ux-pro-max`**</span> 進行，不要自己直接改。<br><br><span class="hljs-strong">**Why:**</span> Ray 明確指示「只要跟 UI 扯到你都要呼叫 UIUX SKILL Pro 去調整跟修改或者新增」。交給專門的 skill 處理可確保一致性與品質。<br><br><span class="hljs-strong">**How to apply:**</span><br><span class="hljs-bullet">-</span> 評估這次修改是否牽涉 UI（任何 .vue 檔的 template / style 區塊、Tailwind class 變更、新元件、互動流程調整都算）<br><span class="hljs-bullet">-</span> 牽涉 UI → 呼叫 <span class="hljs-code">`Skill(&#123; skill: &#x27;ui-ux-pro-max:ui-ux-pro-max&#x27;, args: &#x27;&lt;簡述要改什麼&#x27; &#125;)`</span><br><span class="hljs-bullet">-</span> 不牽涉 UI（僅 .ts service／types／composable 邏輯）→ 可自行處理<br><span class="hljs-bullet">-</span> 邊界案例（例如 composable 影響 UI 行為但本身不是 UI）：以「最終會不會改到 .vue」為判斷基準<br><span class="hljs-bullet">-</span> 呼叫前先準備清楚的需求描述（要改哪個畫面、目前的問題、期望的方向），這個 skill 才能產出有用的建議<br></code></pre></td></tr></table></figure><p>這邊你會看 <code>name</code>、<code>description</code>、<code>type</code>、<code>originSessionId</code> 這些 metadata，其實你會發現跟 SKILL 的格式很像，你可以把 Auto Memory 看成一個另一種特殊的 Skill，Claude 會根據這些 metadata 來判斷這條記憶是什麼類型、來自哪個 session，以及它的內容是什麼，因此 <code>description</code> 的描述是非常重要的。</p><p>如果你發現你的 Claude Code 總是沒辦法正確讀取你要求的記憶內容，那很可能就是因為 <code>description</code> 寫得不夠清楚，Claude 看不懂它是什麼東西，自然就不會去讀取它了。</p><h3 id="怎麼讓-MEMORY-md-瘦下來？"><a href="#怎麼讓-MEMORY-md-瘦下來？" class="headerlink" title="怎麼讓 MEMORY.md 瘦下來？"></a>怎麼讓 MEMORY.md 瘦下來？</h3><p>如果你發現 <code>MEMORY.md</code> 已經膨脹到接近 200 行，有幾個方式可以瘦身：</p><ol><li><strong>把細節拆到 Topic 檔案</strong>：<code>MEMORY.md</code> 只留索引，每條一行帶連結就好，實際內容寫在獨立的 <code>.md</code>，Claude 需要時會自己去讀</li><li><strong>刪掉過時的記憶</strong>：直接在對話中跟 Claude 講「忘記之前關於 X 的規則」，or 自己手動到 <code>~/.claude/projects/&lt;專案&gt;/memory/</code> 底下刪掉對應檔案</li><li><strong>手動編輯 <code>MEMORY.md</code></strong>：它就是普通的 Markdown，自己用編輯器打開整理就好</li></ol><blockquote><p><strong>Note</strong><br>通常大多都是請 AI 整理，而不是自己動手，畢竟 Auto Memory 的重點就是讓 AI 來幫你管理這些記憶內容，自己動手就失去意義了？但理解一下這一塊的結構跟運作方式，對於後續的使用跟排除障礙還是很有幫助。</p></blockquote><h2 id="磁碟空間：真正膨脹的兇手不是-Auto-Memory"><a href="#磁碟空間：真正膨脹的兇手不是-Auto-Memory" class="headerlink" title="磁碟空間：真正膨脹的兇手不是 Auto Memory"></a>磁碟空間：真正膨脹的兇手不是 Auto Memory</h2><p>接下來是磁碟這一塊，這也是大部分人會誤解 Auto Memory 的地方。</p><p>我們先看 Auto Memory 本身佔多少。以我自己的專案為例，記憶資料夾長這樣：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">du</span> -sh ~/.claude/projects/-Users-rayxu/memory/<br><span class="hljs-comment"># 52K</span><br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145423.png" data-fancybox="gallery" data-caption="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F;&lt;專案&gt;&#x2F;memory&#x2F; 的磁碟用量"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145423.png" alt="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F;&lt;專案&gt;&#x2F;memory&#x2F; 的磁碟用量"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F;&lt;專案&gt;&#x2F;memory&#x2F; 的磁碟用量</figcaption></figure><p>整個 <code>memory/</code> 加起來才 52KB，這個大小基本上你一輩子都用不完。</p><p>那…一整個 projects 資料夾呢？</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">du</span> -sh ~/.claude/projects/<br><span class="hljs-comment"># 339M</span><br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145423.png" data-fancybox="gallery" data-caption="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 的磁碟用量"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145423.png" alt="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 的磁碟用量"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 的磁碟用量</figcaption></figure><p>意外吧？竟然高達 <code>339 MB</code>！所以真正佔用的人到底是誰哩？</p><p>答案就是 <strong>conversation jsonl</strong> — Claude Code 把你的每一次對話都存成一個 <code>.jsonl</code> 檔，每一行是一個 JSON 物件，包含你的 prompt、Claude 的回應、工具呼叫、工具結果，全部寫下來。長對話跟有大量檔案讀寫的 session，單一檔案就可能 MB 等級。</p><p>實際看一下：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">ls</span> -lh ~/.claude/projects/*/*.jsonl | <span class="hljs-built_in">sort</span> -k5 -h | <span class="hljs-built_in">tail</span> -5<br><span class="hljs-comment"># 5.7M  -....jsonl</span><br><span class="hljs-comment"># 5.9M  -....jsonl</span><br><span class="hljs-comment"># 6.2M  -....jsonl</span><br><span class="hljs-comment"># 6.2M  -....jsonl</span><br><span class="hljs-comment"># 8.1M  -....jsonl</span><br></code></pre></td></tr></table></figure><p>可以看到最大的單一對話檔已經到 8 MB 了。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145706.png" data-fancybox="gallery" data-caption="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 底下各專案的磁碟用量"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/auto-memory-clear/20260602145706.png" alt="用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 底下各專案的磁碟用量"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">用 du 看 ~&#x2F;.claude&#x2F;projects&#x2F; 底下各專案的磁碟用量</figcaption></figure><p>所以下次你看到 <code>~/.claude/projects/</code> 一直膨脹，先別怪 Auto Memory，去 <code>du</code> 一下就會發現真正的兇手是這些 <code>.jsonl</code>。</p><h2 id="所以該怎麼自動清理？認識-cleanupPeriodDays"><a href="#所以該怎麼自動清理？認識-cleanupPeriodDays" class="headerlink" title="所以該怎麼自動清理？認識 cleanupPeriodDays"></a>所以該怎麼自動清理？認識 cleanupPeriodDays</h2><p>ok，其實有一個方式可以解決這件事情，本身 Claude Code 就有內建一個自動清理機制，預設就會把超過 30 天的 session 檔在啟動時自動刪掉，這個設定叫做 <code>cleanupPeriodDays</code>。</p><blockquote><p><strong>Note</strong><br><code>cleanupPeriodDays</code> 只會清 conversation 紀錄，不會動到 Auto Memory 的記憶檔。你之前寫的記憶都不會被它刪掉，可以放心。</p></blockquote><p>如果你想改保留天數，在 <code>~/.claude/settings.json</code> 加上這條：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;cleanupPeriodDays&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-number">7</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這樣 Claude Code 每次啟動時就會把超過 7 天的 jsonl 都清掉（最少可以設 1 天，設 0 會出現錯誤哦）。</p><p>那如果你完全不想保留任何對話紀錄（隱私考量 or 純粹想省空間），可以直接停用 transcript 寫入：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">export</span> CLAUDE_CODE_SKIP_PROMPT_HISTORY=1<br></code></pre></td></tr></table></figure><p>把這行加到你的 <code>~/.zshrc</code> 或 <code>~/.bashrc</code>，從此 Claude Code 就不會再寫 conversation jsonl 了。代價是你也不能再用 <code>claude --resume</code> 回到之前的對話，魚與熊掌就看你怎麼選擇了。</p><h2 id="手動清理大檔案"><a href="#手動清理大檔案" class="headerlink" title="手動清理大檔案"></a>手動清理大檔案</h2><p>雖然 <code>cleanupPeriodDays</code> 會自動清理，可是如果你最近兩週剛好跑了一個超大的 session（例如連續開 subagent 解 bug），等 30 天有點久。</p><p>這時候可以用 <code>find</code> 把大檔案揪出來：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">find ~/.claude/projects -name <span class="hljs-string">&quot;*.jsonl&quot;</span> -size +50M -<span class="hljs-built_in">exec</span> <span class="hljs-built_in">ls</span> -lh &#123;&#125; \;<br></code></pre></td></tr></table></figure><p>這條指令會找出 <code>~/.claude/projects/</code> 底下所有超過 50 MB 的 jsonl 檔。確認過內容你不需要保留之後，再用 <code>-delete</code> 把它們刪掉：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">find ~/.claude/projects -name <span class="hljs-string">&quot;*.jsonl&quot;</span> -size +50M -delete<br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>刪掉某個 session 的 jsonl 之後，那個 session 就無法用 <code>claude --resume</code> 救回來了。如果你還想保留近期的對話紀錄，刪之前最好先 <code>ls -lh</code> 確認一下檔案的修改時間，挑你真的不會再回頭看的那幾個就好。</p></blockquote><h3 id="順便看哪些專案資料夾最肥"><a href="#順便看哪些專案資料夾最肥" class="headerlink" title="順便看哪些專案資料夾最肥"></a>順便看哪些專案資料夾最肥</h3><p><code>~/.claude/projects/</code> 底下每個資料夾對應一個你曾經用 Claude Code 開啟的目錄，可是專案如果你早就不維護了，那它對應的資料夾也沒必要留。可以這樣看哪些資料夾最肥：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">du</span> -sh ~/.claude/projects/*/ | <span class="hljs-built_in">sort</span> -h | <span class="hljs-built_in">tail</span> -10<br></code></pre></td></tr></table></figure><p>確認哪幾個專案是你已經不會再 resume 的，整個資料夾直接刪掉就好。</p><h2 id="那資料夾底下還有別的東西嗎？"><a href="#那資料夾底下還有別的東西嗎？" class="headerlink" title="那資料夾底下還有別的東西嗎？"></a>那資料夾底下還有別的東西嗎？</h2><p>除了 conversation jsonl 跟 <code>memory/</code> 之外，每個專案資料夾底下還可能有一些以 UUID 命名的子資料夾（沒有 <code>.jsonl</code> 副檔名的純資料夾），那些是 subagent 跑任務時用的暫存空間。一般來講不需要動它們，<code>cleanupPeriodDays</code> 也會一起清。</p><p>如果你有用 Worktree 跑 subagent，舊的 worktree 也會在啟動時被自動清掉，同樣由 <code>cleanupPeriodDays</code> 控制。</p><p>希望這篇有讓你對 Auto Memory 的「佔用」有更近一步的認識哩～</p><p>下次磁碟爆滿的時候，記得先 <code>du</code> 看一眼是哪邊真的在吃，不要冤枉了 Auto Memory。</p>]]></content>
    
    
    <summary type="html">打開 `~/.claude/projects/` 發現怎麼一直在膨脹，第一反應都是「Auto Memory 是不是把我的記憶撐爆了？」其實 Auto Memory 本身根本不吃 RAM，它佔的是 Context Window（MEMORY.md 前 200 行/25KB 載入）跟磁碟空間，而且真正讓資料夾膨脹的兇手是 conversation jsonl，不是記憶檔。這篇帶你搞清楚 Auto Memory 到底佔什麼、怎麼用 `/context` 看 MEMORY.md 吃掉多少 token、cleanupPeriodDays 自動清理機制、`CLAUDE_CODE_SKIP_PROMPT_HISTORY` 關掉 transcript 寫入，以及手動 find 大檔案的清理流程。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="Auto Memory" scheme="https://israynotarray.com/tags/Auto-Memory/"/>
    
    <category term="Claude Code Memory" scheme="https://israynotarray.com/tags/Claude-Code-Memory/"/>
    
    <category term="Context Window" scheme="https://israynotarray.com/tags/Context-Window/"/>
    
    <category term="cleanupPeriodDays" scheme="https://israynotarray.com/tags/cleanupPeriodDays/"/>
    
    <category term="AI 開發" scheme="https://israynotarray.com/tags/AI-%E9%96%8B%E7%99%BC/"/>
    
  </entry>
  
  <entry>
    <title>Claude Code Hooks 是什麼？自動跑 Lint、擋 rm -rf、SessionStart 注入 Context 完整教學</title>
    <link href="https://israynotarray.com/ai/20260531/2738461950/"/>
    <id>https://israynotarray.com/ai/20260531/2738461950/</id>
    <published>2026-05-31T06:00:00.000Z</published>
    <updated>2026-05-31T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-hooks/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8831%E6%97%A5%20%E4%B8%8B%E5%8D%8803_35_04.png" data-fancybox="gallery" data-caption="Claude Code Hooks 是什麼？自動跑 Lint、擋 rm -rf、SessionStart 注入 Context 完整教學"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-hooks/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8831%E6%97%A5%20%E4%B8%8B%E5%8D%8803_35_04.png" alt="Claude Code Hooks 是什麼？自動跑 Lint、擋 rm -rf、SessionStart 注入 Context 完整教學"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code Hooks 是什麼？自動跑 Lint、擋 rm -rf、SessionStart 注入 Context 完整教學</figcaption></figure><blockquote><p><strong>Note</strong><br>Claude Code Hooks 是 Claude Code 生命週期裡的「自動觸發點」，會在你期望的時候自動觸發你寫的 Shell 指令。而常用的生命週期有 <code>PreToolUse</code>（工具執行前）、<code>PostToolUse</code>（工具執行後）、<code>UserPromptSubmit</code>（每次送 prompt 前）、<code>SessionStart</code>（session 開啟時）。你需要將相關設定寫在 <code>settings.json</code> 的 <code>hooks</code> 區塊就可以了。</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>用 Claude Code 開發一段時間後，你應該有遇過類似的狀況，也就是每次叫它改完檔案還要再請它跑一次 Prettier 跟 ESLint（當然也可以手動自己執行 <code>npm run format</code>），但人總是會偷懶，想要在比較放飛的時候開個 <code>--dangerously-skip-permissions</code>，但開了又會怕它跑了 <code>rm -rf</code> 之類的指令；又或者 Context 被 Compact 之後，它就忘了專案的某些重要約定，又要重新講一次 QQ…</p><p>所以這篇就要來介紹與分享一下 Claude Code 中很重要的技巧，也就是 Hooks，讓你可以在 Claude Code 的生命週期裡「掛鉤」你想要的自動化動作，確保它在對的時間點幫你做對的事。</p><p>另外，之前我有寫過 <a href="https://israynotarray.com/ai/20260331/1748203647/">Claude Code 哪些檔案該 Commit？CLAUDE.md、Settings、Rules、Skills 完整解析</a>，裡面有大致提到 <code>.claude/settings.json</code> 的層級概念，這篇就接續那一篇，把 hooks 這個藏在 settings 裡面的進階功能拉出來獨立詳細介紹一下。</p><h2 id="Hook-跟-Skill、CLAUDE-md、Rules-差在哪？"><a href="#Hook-跟-Skill、CLAUDE-md、Rules-差在哪？" class="headerlink" title="Hook 跟 Skill、CLAUDE.md、Rules 差在哪？"></a>Hook 跟 Skill、CLAUDE.md、Rules 差在哪？</h2><p>在開始講 hook 怎麼設之前，先讓我們快速回顧一ㄒ它跟其他「教 Claude 做事」的機制差別，這樣會比較清楚彼此之間到底在幹嘛。</p><div class="table-wrapper"><table><thead><tr><th>機制</th><th>由誰執行</th><th>何時觸發</th><th>適合的場景</th></tr></thead><tbody><tr><td>CLAUDE.md</td><td>Claude</td><td>每次對話載入</td><td>專案規範、命名慣例、工作流程</td></tr><tr><td>Rules</td><td>Claude</td><td>啟動時 or 處理對應路徑檔案時載入</td><td>模組化的規則，可依路徑生效</td></tr><tr><td>Skill</td><td>Claude</td><td>Claude 自己判斷該用時觸發</td><td>把一整段流程包成可重用的能力</td></tr><tr><td>Hook</td><td>你的 Shell</td><td>Claude Code 生命週期事件</td><td>強制執行的自動化動作</td></tr></tbody></table></div><p>如果上面表格看完還是很難懂的話，那你可以這樣理解</p><ol><li>CLAUDE.md 跟 Rules 是「規則」</li><li>Skill 是「能力」，這三個都是 Claude 自己看完之後再決定要不要照做。</li><li>Hook 不一樣，hook 是 Claude Code 本體幫你執行的，<strong>Claude 沒有選擇權</strong>，到了那個時機就一定會執行的事情。</li></ol><p>這邊也簡單舉一個例子，假設你在 CLAUDE.md 裡面寫「每次改完檔案請跑 Prettier」，Claude 大部分時候會聽，可是偶爾會忘記（畢竟 Context 可能爆掉了？！），如果你改用 PostToolUse hook 設定「每次 <code>Edit</code> 或 <code>Write</code> 之後自動跑 Prettier」，那不管 Claude 想不想跑，反正你的 Shell 指令一定會被執行。</p><p>所以 hook 適合的的場景是…</p><blockquote><p>我絕對不能容忍 Claude 漏掉的事</p></blockquote><p>像是 lint、format、安全防護這些，而比較模糊、需要 Claude 判斷的事還是寫在 CLAUDE.md 比較合適。</p><h2 id="Hook-的設定檔長什麼樣子？"><a href="#Hook-的設定檔長什麼樣子？" class="headerlink" title="Hook 的設定檔長什麼樣子？"></a>Hook 的設定檔長什麼樣子？</h2><p>Hook 的設定都是寫在 <code>settings.json</code> 的 <code>hooks</code> 區塊裡面，而它的結構是長這樣：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;事件名稱&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;篩選條件&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;你要跑的 Shell 指令&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>外層的 <code>key</code> 是「事件名稱」（例如 <code>PreToolUse</code>、<code>PostToolUse</code>），裡面包一個陣列，陣列每一項是一組設定，包含 <code>matcher</code>（用來過濾這組要在什麼狀況下啟動）跟 <code>hooks</code>（實際要跑的指令）。</p><p>如果你之前看過 <a href="https://israynotarray.com/ai/20260326/1431610072/">Claude Code Status Line 設定教學</a>，會覺得這個結構滿熟悉的，因為它跟 Status Line 一樣都是「Claude Code 餵資料給你的 Shell，你決定怎麼回應」。</p><h3 id="設定檔放在哪？"><a href="#設定檔放在哪？" class="headerlink" title="設定檔放在哪？"></a>設定檔放在哪？</h3><p>跟其他 Claude Code 設定一樣，hooks 也有層級分別：</p><div class="table-wrapper"><table><thead><tr><th>位置</th><th>範圍</th><th>該不該 commit</th></tr></thead><tbody><tr><td><code>~/.claude/settings.json</code></td><td>你所有的專案</td><td>不會出現在專案 repo</td></tr><tr><td><code>.claude/settings.json</code></td><td>整個團隊</td><td>該 commit</td></tr><tr><td><code>.claude/settings.local.json</code></td><td>只有你個人</td><td>不該 commit（已在 <code>.gitignore</code>）</td></tr></tbody></table></div><p>簡單來講，全團隊都應該裝的 hook（例如 lint）寫在 <code>.claude/settings.json</code>、個人偏好（例如桌面通知）寫在 <code>~/.claude/settings.json</code>、跟敏感路徑相關的本機規則寫在 <code>.claude/settings.local.json</code>。</p><h2 id="Hook-事件有哪些？"><a href="#Hook-事件有哪些？" class="headerlink" title="Hook 事件有哪些？"></a>Hook 事件有哪些？</h2><p>聊完設定檔的結構，接下來就是這篇的重頭戲，也就是「到底有哪些事件可以使用？」</p><p>老實講，Claude Code 提供的事件超過 20 個，全部寫出來會把人嚇跑，所以這邊我以我自己「實務上會用到的頻率」分類列給你看。</p><p>下面這張圖是 Claude Code 一輪典型 session 的生命週期，每個箭頭上面標的就是會觸發的事件，可以對照看 hook 大概在哪個位子會被呼叫：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-hooks/20260531154519.png" data-fancybox="gallery" data-caption="Claude Code Hooks 事件觸發時機示意圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-hooks/20260531154519.png" alt="Claude Code Hooks 事件觸發時機示意圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code Hooks 事件觸發時機示意圖</figcaption></figure><h3 id="最常用的幾個"><a href="#最常用的幾個" class="headerlink" title="最常用的幾個"></a>最常用的幾個</h3><div class="table-wrapper"><table><thead><tr><th>事件</th><th>觸發時機</th><th>經典用途</th></tr></thead><tbody><tr><td><code>PreToolUse</code></td><td>某個工具要執行之前</td><td>擋掉危險指令、改寫參數</td></tr><tr><td><code>PostToolUse</code></td><td>某個工具成功執行之後</td><td>自動 format、跑 lint</td></tr><tr><td><code>UserPromptSubmit</code></td><td>你按下 Enter 送出 prompt 時</td><td>注入額外脈絡、過濾敏感資訊</td></tr><tr><td><code>SessionStart</code></td><td>開啟 or 恢復 session 時</td><td>載入專案脈絡、輸出 git 狀態</td></tr><tr><td><code>Stop</code></td><td>Claude 回完話準備停手時</td><td>驗證任務有沒有真的完成</td></tr><tr><td><code>Notification</code></td><td>Claude 需要你注意時</td><td>桌面通知、語音提示</td></tr></tbody></table></div><p>這六個算是屬於最常被用到的事件，許多實戰範例都會繞著它們轉。</p><h3 id="其他比較少用但值得知道的"><a href="#其他比較少用但值得知道的" class="headerlink" title="其他比較少用但值得知道的"></a>其他比較少用但值得知道的</h3><div class="table-wrapper"><table><thead><tr><th>事件</th><th>觸發時機</th><th>經典用途</th></tr></thead><tbody><tr><td><code>PermissionRequest</code></td><td>權限提示要彈出時</td><td>自動同意特定動作</td></tr><tr><td><code>PermissionDenied</code></td><td>自動模式擋掉某個工具呼叫時</td><td>紀錄被擋的指令</td></tr><tr><td><code>PostToolUseFailure</code></td><td>某個工具執行失敗時</td><td>失敗時通知 or 自動 retry</td></tr><tr><td><code>SubagentStop</code></td><td>Subagent 跑完任務時</td><td>驗證 subagent 結果</td></tr><tr><td><code>PreCompact</code> &#x2F; <code>PostCompact</code></td><td>Context 壓縮前 &#x2F; 後</td><td>備份對話、壓縮後重新注入規則</td></tr><tr><td><code>SessionEnd</code></td><td>session 關閉時</td><td>清理暫存檔、寫日誌</td></tr><tr><td><code>ConfigChange</code></td><td>設定檔在 session 中被改</td><td>稽核設定變動</td></tr><tr><td><code>CwdChanged</code> &#x2F; <code>FileChanged</code></td><td>工作目錄 &#x2F; 監看的檔案有變動</td><td>重新載入環境變數（搭配 direnv）</td></tr></tbody></table></div><p>看到現在為止，應該可以知道 hook 幾乎涵蓋了 Claude Code 跑起來會經過的所有節點，幾乎你想得到的「我希望某件事發生時自動跑某個指令」都做得到，概念就跟寫成是的「生命週期事件」一樣。</p><h2 id="matcher-怎麼寫？"><a href="#matcher-怎麼寫？" class="headerlink" title="matcher 怎麼寫？"></a>matcher 怎麼寫？</h2><p>接下來我們總要來介紹一下該怎麼撰寫 hook 的條件了。</p><p>前面有看到 hook 範例是長這樣：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;事件名稱&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;篩選條件&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;你要跑的 Shell 指令&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>matcher</code> 決定「這組 hook 在什麼狀況下要啟動」，而它要比對什麼東西會隨事件而不同。最常用的 <code>PreToolUse</code> &#x2F; <code>PostToolUse</code> 比對的是「工具名稱」，所以最直覺的寫法就是把工具名直接丟進 <code>matcher</code> 欄位。</p><p>舉個實際的例子，下面這個 hook 是「Claude 每次要跑 Bash 之前，在終端機上顯示一段提醒」：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -n &#x27;&#123; systemMessage: \&quot;Bash 要執行了\&quot; &#125;&#x27;&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>接下來畫面就可以看到 <code>Bash 要執行了</code> 的提醒了。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-hooks/20260531131433.png" data-fancybox="gallery" data-caption="Bash 要執行了 的提醒示意"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-hooks/20260531131433.png" alt="Bash 要執行了 的提醒示意"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Bash 要執行了 的提醒示意</figcaption></figure><p><code>matcher</code> 的值就是工具名 <code>Bash</code>，意思是「只在 Claude 用 Bash 工具時這個 hook 才會跑」。</p><blockquote><p><strong>Note</strong><br>為什麼這邊要用 <code>jq</code> 不用 <code>echo</code>？因為 Claude Code 跟 hook 之間是用「JSON 格式」對話的，如果你直接寫 <code>echo &quot;Bash 要執行了&quot;</code>，Claude Code 收到的就只是一段沒結構的純文字，它看不懂你要它幹嘛，就只會把這段話當成 hook 的執行紀錄收掉（忽略），Claude 永遠看不到。而 <code>jq -n &#39;{ systemMessage: &quot;Bash 要執行了&quot; }&#39;</code> 是在產出一段 JSON，等於是跟 Claude Code 講「這個欄位叫 systemMessage，內容請你塞給 Claude 看」，Claude Code 看到這種它認得的格式，才會真的把訊息餵進去。</p></blockquote><p>如果你想一次盯多個工具，中間用 <code>|</code> 隔開就好。例如下面這個 hook 是「Claude 每次用 <code>Edit</code> 或 <code>Write</code> 改完檔案，自動跑 Prettier」：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PostToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Edit|Write&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -r &#x27;.tool_input.file_path&#x27; | xargs npx prettier --write&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>看到這邊我知道 <code>command</code> 有點複雜，所以我來拆解解釋一下：</p><ol><li>Claude 用 <code>Edit</code> &#x2F; <code>Write</code> 改完檔案的當下，Claude Code 會把一段 JSON 丟給這個 hook，裡面大概長這樣 — <code>{ &quot;tool_input&quot;: {&quot;file_path&quot;: &quot;/path/to/file.ts&quot; }, ... }</code>，告訴你它剛剛改了哪個檔。</li><li>接著 <code>jq -r &#39;.tool_input.file_path&#39;</code> 就是把這段 JSON 拆開，從裡面挖出 <code>tool_input.file_path</code> 這個欄位的值（也就是檔案路徑），<code>-r</code> 的意思是「給我純文字就好，不要有雙引號」。</li><li><code>|</code>（pipe）你就把它當作大隊接力的接力棒，意思是「把上一個指令的結果，當成下一個指令的輸入餵過去」。</li><li><code>xargs</code> 負責把接過來的東西「接在下一個指令的後面當參數」，所以實際跑出來會變成 <code>npx prettier --write /path/to/file.ts</code>。</li><li><code>npx prettier --write</code> 就是呼叫 Prettier 把這個檔案 format 一遍。</li></ol><blockquote><p><strong>Note</strong><br><code>prettier</code> 是一款程式碼格式化工具，<code>--write</code> 是它的參數，意思是「直接改檔案」，如果你不想改檔只想看差異，可以換成 <code>--check</code>。如果你的專案不是用 Prettier 而是 ESLint，把 <code>npx prettier --write</code> 換成 <code>npx eslint --fix</code> 就好。</p></blockquote><p>基本上，後面範例你只要看到 <code>jq -r &#39;.xxx&#39;</code> 就是再做這些事情，也就是：</p><blockquote><p><code>jq -n</code> 將 Claude Code 丟過來的 JSON 解析出來，抓你想要的欄位值，然後丟給下一個指令用。反之前面那個 <code>jq -n</code> 才是反過來「從零產生」一段 JSON 餵回去給 Claude Code。</p></blockquote><p>最後，如果你想要「不管什麼工具都觸發」，那就把 <code>matcher</code> 留空字串：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -r &#x27;.tool_name&#x27; &gt;&gt; ~/.claude/all-tools.log&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這個 hook 會在 Claude 用任何工具之前把工具名追加到 log 檔，等於是把整個 session 的工具使用紀錄起來。</p><p>到這邊 95% 的場景已經夠用了，要看每個工具叫什麼名字，可以參考之前我寫的 <a href="https://israynotarray.com/ai/20260429/8473628195/">Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具</a>，裡面把每個工具的名字都列出來了。</p><h3 id="進階：要篩-MCP-工具就要用-regex"><a href="#進階：要篩-MCP-工具就要用-regex" class="headerlink" title="進階：要篩 MCP 工具就要用 regex"></a>進階：要篩 MCP 工具就要用 regex</h3><p>剩下 5% 的情境就是當你要比對 MCP server 提供的工具時。MCP 工具的命名格式長這樣：<code>mcp__&lt;server&gt;__&lt;tool&gt;</code>，舉例來說 GitHub MCP server 提供的搜尋工具叫做 <code>mcp__github__search_repositories</code>。</p><p>如果你想一次盯某個 MCP server 提供的所有工具，可以用 regex 的 <code>.*</code> 配對任何字元：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;mcp__github__.*&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;echo &#x27;GitHub MCP tool called&#x27; &gt;&amp;2&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>.*</code> 是 regex 的「任何字元」，所以 <code>mcp__github__.*</code> 的意思就是「<code>mcp__github__</code> 開頭，後面接什麼都符合」。Claude Code 判斷要不要當 regex 的規則很單純：matcher 只要不只由「字母、數字、<code>_</code>、<code>|</code>」這四種組成，就會走 JavaScript regex 模式；反之就當完整字串比對。所以前面 <code>Bash</code>、<code>Edit|Write</code> 那種寫法不會被誤判成 regex。</p><h3 id="不是所有事件都比工具名"><a href="#不是所有事件都比工具名" class="headerlink" title="不是所有事件都比工具名"></a>不是所有事件都比工具名</h3><p>前面講的都是跟工具有關的事件，可是有些事件 matcher 比對的不是工具名，而是「事件本身的某個分類」。</p><p>最常見的例子是 <code>SessionStart</code>，它比對的是「session 是怎麼開的」。下面這個 hook 是「Context 被 Compact 後 session 重建時，重新提醒 Claude 專案規則」：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;SessionStart&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;compact&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;echo &#x27;提醒：套件管理用 pnpm 不要用 npm&#x27;&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>看到 <code>echo &#39;提醒：套件管理用 pnpm 不要用 npm&#39;</code>你應該會充滿疑問想著…</p><p>「前面你不是說 <code>PreToolUse</code> 的 <code>echo</code> 沒用嗎？怎麼這邊又這樣用？」</p><p>這是因為 <code>SessionStart</code> 跟 <code>UserPromptSubmit</code> 是兩個比較特殊的事件，hook 輸出到畫面的內容會被 Claude Code 自動當成「要塞給 Claude 的額外脈絡」收進去，所以直接 <code>echo</code> 就會被 Claude 看到。其他事件（像前面的 <code>PreToolUse</code>）才要走 <code>jq -n &#39;{systemMessage: ...}&#39;</code> 這種 JSON 格式跟 Claude Code 對話。</p><p><code>matcher: &quot;compact&quot;</code> 限定「只在 Compact 後重建 session 時跑」，第一次開 Claude Code 或 resume 舊 session 都不會觸發。<code>SessionStart</code> 的可選值有 <code>startup</code>（第一次開）、<code>resume</code>（恢復舊 session）、<code>clear</code>（清空 context）、<code>compact</code>（壓縮後）。</p><p><code>Notification</code> 也是類似邏輯，它比對的是「通知類型」，常見的有 <code>permission_prompt</code>（Claude 在等你授權）、<code>idle_prompt</code>（Claude 沒事在等下一句），另外還有 <code>auth_success</code>、<code>elicitation_dialog</code>、<code>elicitation_complete</code>、<code>elicitation_response</code> 等比較少用的。每個事件 matcher 對應什麼，可以查 <a href="https://code.claude.com/docs/en/hooks">官方 hooks reference</a>，這邊就不一一列了。</p><h3 id="進階：用-if-連工具參數也篩"><a href="#進階：用-if-連工具參數也篩" class="headerlink" title="進階：用 if 連工具參數也篩"></a>進階：用 <code>if</code> 連工具參數也篩</h3><p>光是 matcher 還不夠精確，因為它只能比對工具名。如果你想說「只在 Claude 跑 <code>git push</code> 的時候才觸發 hook，其他 Bash 指令不要管」，那你需要更細的 <code>if</code> 欄位：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;if&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash(git push *)&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;echo &#x27;Blocked: 請走 PR 不要直接 push 到 main&#x27; &gt;&amp;2; exit 2&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這個範例做的事情是：當 Claude 想跑任何 <code>git push</code> 開頭的 Bash 指令時，hook 就會輸出一段擋下的訊息然後 <code>exit 2</code> 把這個動作擋掉。可是如果 Claude 想跑的是 <code>git status</code> 或 <code>npm test</code>，hook 根本不會被叫起來。</p><p>兩個篩選欄位的差別是：</p><ul><li><code>matcher: &quot;Bash&quot;</code> 是第一層粗篩，「只看 Bash 工具」。</li><li><code>if: &quot;Bash(git push *)&quot;</code> 是第二層細篩，連 Bash 裡面實際要跑的指令也比對，<code>*</code> 是「後面接什麼都行」的萬用字元，跟 permissions 設定用的是同一套語法。</li></ul><p>它最大的好處是「只有真的命中條件時才會把 hook 跑起來」，比起每次都把 hook 叫起來再用 <code>jq</code> 解析參數判斷，效能差很多。</p><blockquote><p><strong>Note</strong><br><code>if</code> 欄位是後來才加的，比較舊版本的 Claude Code 可能不認得這個欄位，如果你寫了 <code>if</code> 但 hook 行為跟沒寫一樣，先用 <code>claude --version</code> 確認版本然後更新看看。</p></blockquote><h2 id="Hook-怎麼跟-Claude-Code-溝通？"><a href="#Hook-怎麼跟-Claude-Code-溝通？" class="headerlink" title="Hook 怎麼跟 Claude Code 溝通？"></a>Hook 怎麼跟 Claude Code 溝通？</h2><p>理解 matcher 之後，再來看 hook 跟 Claude Code 是「怎麼互傳訊息」的。先看一個最小範例：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br><br><span class="hljs-comment"># 1. 將 Claude Code 丟過來的 JSON 儲存到 INPUT 變數裡</span><br>INPUT=$(<span class="hljs-built_in">cat</span>)<br><br><span class="hljs-comment"># 2. 輸出一段訊息給 Claude Code 看，因此不會直接在終端機上看到，而是被 Claude Code 當成 hook 的回應收掉</span><br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Claude 你好&quot;</span><br><br><span class="hljs-comment"># 3. 結束 script，回傳 0 代表「沒事，繼續」，如果你寫 `exit 2` 就代表「有事，擋下這個動作」</span><br><span class="hljs-built_in">exit</span> 0<br></code></pre></td></tr></table></figure><p>舉個 <code>PreToolUse</code> 的例子，當 Claude 要跑 <code>npm test</code> 時你的 hook 從 stdin 會收到這樣的 JSON：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;session_id&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;abc123&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;cwd&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;/Users/ray/myproject&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;hook_event_name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;tool_name&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>  <span class="hljs-attr">&quot;tool_input&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;npm test&quot;</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>很多人寫 hook 第一次踩雷就在這邊：以為 <code>exit 1</code> 會擋，<strong>但只有 exit 2 才會擋</strong>，其他 non-zero 都不會。</p></blockquote><h3 id="用-JSON-回應做更細的控制"><a href="#用-JSON-回應做更細的控制" class="headerlink" title="用 JSON 回應做更細的控制"></a>用 JSON 回應做更細的控制</h3><p>只能用 <code>exit 0</code> 跟 <code>exit 2</code> 嗎？不對，還可以做更細的控制（例如自動同意 or 直接改寫 Claude 想跑的指令），方法是 <code>exit 0</code> 然後輸出一段 JSON 到 stdout：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hookSpecificOutput&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;hookEventName&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;permissionDecision&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;deny&quot;</span><span class="hljs-punctuation">,</span><br>    <span class="hljs-attr">&quot;permissionDecisionReason&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;請改用 pnpm 不要用 npm&quot;</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>permissionDecision</code> 在 <code>PreToolUse</code> 有四個值：</p><ul><li><code>allow</code>：跳過權限提示直接放行</li><li><code>deny</code>：擋下來並把理由餵給 Claude</li><li><code>ask</code>：照原本的權限流程跳出提示問你</li><li><code>defer</code>：hook 沒有意見，回到原本的權限流程（效果等同沒寫這個欄位）</li></ul><blockquote><p><strong>Note</strong><br>Exit 2 跟 JSON 不要混用，Claude Code 看到 exit 2 就會直接讀 stderr 當理由，stdout 的 JSON 會被忽略。要嘛 exit 2 配 stderr，要嘛 exit 0 配 JSON，選一邊就好。</p></blockquote><h2 id="五種-Hook-Type"><a href="#五種-Hook-Type" class="headerlink" title="五種 Hook Type"></a>五種 Hook Type</h2><p>前面範例都是用 <code>type: &quot;command&quot;</code>，這是最常見的形式，可是 Claude Code 還有另外四種：</p><div class="table-wrapper"><table><thead><tr><th>Type</th><th>怎麼跑</th><th>適合的場景</th></tr></thead><tbody><tr><td><code>command</code></td><td>跑 Shell 指令</td><td>大多數情境，靈活又簡單</td></tr><tr><td><code>http</code></td><td>POST event 資料到指定 URL</td><td>接遠端服務做共用稽核、組織內驗證</td></tr><tr><td><code>mcp_tool</code></td><td>呼叫已連線的 MCP server 的某個工具</td><td>把現成 MCP 工具當成 hook 邏輯</td></tr><tr><td><code>prompt</code></td><td>把資料丟給 Claude 模型自己判斷</td><td>需要語意判斷而不是寫規則的情境</td></tr><tr><td><code>agent</code></td><td>派一個 subagent 帶工具去驗證</td><td>要實際讀檔 or 跑指令才能判斷的情境</td></tr></tbody></table></div><p>90% 的人寫 hook 都會停在 <code>command</code>，可是後面三種（特別是 <code>prompt</code>）滿值得認識的，因為它讓 hook 能做的事情從「死規則」變成「有判斷力的守門員」。</p><p>舉個 <code>prompt</code> hook 的例子，假設你想在 Claude 講完話之後，讓另一顆模型檢查它有沒有真的把任務做完：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;Stop&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;prompt&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;prompt&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;檢查剛剛 Claude 是否真的完成了所有要求的任務。如果沒有，回覆 &#123;\&quot;ok\&quot;: false, \&quot;reason\&quot;: \&quot;還缺什麼\&quot;&#125;。&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這個 hook 觸發時 Claude Code 會自動呼叫一顆模型做單輪 yes&#x2F;no 判斷，如果回覆判斷沒做完，主 Claude 就會繼續做下去，相當於免費幫你裝了一個品管站。</p><h2 id="實戰範例"><a href="#實戰範例" class="headerlink" title="實戰範例"></a>實戰範例</h2><p>聊完概念接下來看實際範例。下面這些是我認為日常最常用到、CP 值最高的幾個。</p><h3 id="自動-format-改過的檔"><a href="#自動-format-改過的檔" class="headerlink" title="自動 format 改過的檔"></a>自動 format 改過的檔</h3><p>每次 Claude 用 <code>Edit</code> 或 <code>Write</code> 改完檔之後，自動跑 Prettier 把格式修好：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PostToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Edit|Write&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -r &#x27;.tool_input.file_path&#x27; | xargs npx prettier --write&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>這邊用 <a href="https://jqlang.github.io/jq/"><code>jq</code></a> 把 stdin 餵進來的 JSON 解析出 <code>file_path</code> 再丟給 Prettier，整個一行解決。如果你的系統沒有 jq，Mac 可以 <code>brew install jq</code>，Ubuntu 是 <code>apt install jq</code>。</p><p>如果你的專案不是用 Prettier 而是 ESLint，把 <code>npx prettier --write</code> 換成 <code>npx eslint --fix</code> 就好。</p><p>這個 hook 跟專案的 lint &#x2F; format 規則綁在一起，建議寫在 <code>.claude/settings.json</code> 跟著 commit 給全團隊吃。</p><h3 id="擋掉-rm-rf-之類的危險指令"><a href="#擋掉-rm-rf-之類的危險指令" class="headerlink" title="擋掉 rm -rf 之類的危險指令"></a>擋掉 rm -rf 之類的危險指令</h3><p>把 hook 寫成一個獨立的 Shell script 會比較好維護，請你先建立 <code>.claude/hooks/block-rm.sh</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br>INPUT=$(<span class="hljs-built_in">cat</span>)<br>COMMAND=$(<span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;<span class="hljs-variable">$INPUT</span>&quot;</span> | jq -r <span class="hljs-string">&#x27;.tool_input.command&#x27;</span>)<br><br><span class="hljs-comment"># 只擋真正的 rm -rf / 或 rm -r /，後面接其他路徑（例如 /tmp/foo）放行</span><br><span class="hljs-keyword">if</span> <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;<span class="hljs-variable">$COMMAND</span>&quot;</span> | grep -qE <span class="hljs-string">&#x27;rm[[:space:]]+-rf?[[:space:]]+/([[:space:]]|$)&#x27;</span>; <span class="hljs-keyword">then</span><br>  <span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;Blocked: 不允許從根目錄 rm -rf&quot;</span> &gt;&amp;2<br>  <span class="hljs-built_in">exit</span> 2<br><span class="hljs-keyword">fi</span><br><br><span class="hljs-built_in">exit</span> 0<br></code></pre></td></tr></table></figure><p>這邊用 POSIX 字元類別 <code>[[:space:]]</code> 不用 <code>\s</code>，是因為 <code>\s</code> 是 GNU grep 的擴充，macOS 預設的 BSD grep 不一定支援，會讓比對整個失效。<code>[[:space:]]</code> 是 POSIX 標準寫法，Mac、Linux 各家 grep 都認得。</p><p>regex 那段 <code>(/([[:space:]]|$))</code> 強制 <code>/</code> 後面要接空白或行尾，所以：</p><ul><li><code>rm -rf /</code>：會擋掉</li><li><code>rm -r /</code>：會擋掉</li><li><code>rm -rf /tmp/foo</code>：不會擋掉，因為後面接了路徑不是空白或行尾</li></ul><p>這個 pattern 故意寫得很保守，只擋字面上的 <code>rm -rf /</code> 跟 <code>rm -r /</code>。如果你想擋更兇一點（例如 <code>rm -rf /*</code>、<code>rm -rf ~</code>、<code>rm -rf $HOME</code> 這些變形），照同樣模式加幾條 <code>grep -qE</code> 串起來，每多一個 case 就 <code>exit 2</code> 即可。</p><p>然後打開在 <code>.claude/settings.json</code> 註冊：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;\&quot;$CLAUDE_PROJECT_DIR\&quot;/.claude/hooks/block-rm.sh&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>$CLAUDE_PROJECT_DIR</code> 是 Claude Code 內建的環境變數，會自己變成專案根目錄（也就是你在哪邊呼叫專案，就會變成哪邊）。</p><blockquote><p><strong>Note</strong><br>這個 hook 即使在 <code>--dangerously-skip-permissions</code> 模式下也會生效。<code>PreToolUse</code> 是在權限模式判斷之前就跑，所以 hook 是少數可以真正擋住「繞過權限」模式的機制。</p></blockquote><p>這種安全防護建議寫在 <code>.claude/settings.json</code> commit 進 repo，全團隊都有保護。如果你只想自己保險，放 <code>~/.claude/settings.json</code> 也行。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-hooks/20260531150218.png" data-fancybox="gallery" data-caption="rm -rf 被擋掉的示意"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-hooks/20260531150218.png" alt="rm -rf 被擋掉的示意"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">rm -rf 被擋掉的示意</figcaption></figure><h3 id="Compact-之後重新注入專案脈絡"><a href="#Compact-之後重新注入專案脈絡" class="headerlink" title="Compact 之後重新注入專案脈絡"></a>Compact 之後重新注入專案脈絡</h3><p>Context 一旦被 Compact，Claude 常常會忘記某些重要約定。可以用 <code>SessionStart</code> 加 <code>compact</code> matcher，在每次壓縮完之後重新塞一段提醒：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;SessionStart&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;compact&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;echo &#x27;提醒：套件管理用 pnpm 不要用 npm；commit 前一定要跑 pnpm test。&#x27;&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>SessionStart</code> 跟 <code>UserPromptSubmit</code> hook 輸出到 stdout 的內容會被當成額外脈絡加進 Claude 的 context，這是它們跟其他事件不太一樣的地方。</p><p>如果你想塞動態內容（例如目前的 git 分支、未 commit 的檔案），把 <code>echo</code> 換成你的 script 就好：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-meta">#!/bin/bash</span><br>BRANCH=$(git rev-parse --abbrev-ref HEAD 2&gt;/dev/null)<br>DIRTY=$(git status --porcelain | <span class="hljs-built_in">wc</span> -l)<br><span class="hljs-built_in">echo</span> <span class="hljs-string">&quot;目前分支：<span class="hljs-variable">$BRANCH</span>，未 commit 檔案數：<span class="hljs-variable">$DIRTY</span>&quot;</span><br></code></pre></td></tr></table></figure><p>這個 hook 寫在 <code>.claude/settings.json</code> 比較適合，因為「用 pnpm」「commit 前要跑 pnpm test」這種是專案內部約定，commit 進去其他人 clone 下來也直接吃得到，不會每個人都要自己設一次。</p><h3 id="Mac-桌面通知"><a href="#Mac-桌面通知" class="headerlink" title="Mac 桌面通知"></a>Mac 桌面通知</h3><p>Claude 在等你授權 or 等下一個 prompt 的時候通知一下，這樣你可以放心去做別的事：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;Notification&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;osascript -e &#x27;display notification \&quot;Claude Code 在等你\&quot; with title \&quot;Claude Code\&quot;&#x27;&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>Linux 把 <code>osascript</code> 換成 <code>notify-send</code>，Windows PowerShell 用 <code>MessageBox.Show</code>，邏輯都一樣。</p><blockquote><p><strong>Note</strong><br>Mac 第一次跑可能沒反應，因為 <code>osascript</code> 是透過內建的 Script Editor app 發通知，要先到「系統設定 → 通知」找到 Script Editor 把通知權限打開才會出現。如果連 Script Editor 都沒看到，先在 Terminal 跑一次 <code>osascript -e &#39;display notification &quot;test&quot;&#39;</code>，它就會出現在通知設定的清單裡。</p></blockquote><p>桌面通知純粹是個人偏好，寫在 <code>~/.claude/settings.json</code> 就好，不用 commit 進 repo。</p><h3 id="把-Claude-的每個動作通知到-Discord"><a href="#把-Claude-的每個動作通知到-Discord" class="headerlink" title="把 Claude 的每個動作通知到 Discord"></a>把 Claude 的每個動作通知到 Discord</h3><p>如果你不想開著終端機盯 Claude，或想把 Claude 在跑什麼丟到 Discord channel 跟團隊共享，可以用 PreToolUse hook 串 Discord webhook。</p><p>先到 Discord channel 設定產一個 webhook URL，然後在 shell profile 裡 export 出來（不要寫死在 script 裡，commit 進 repo 等於外洩）：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-built_in">export</span> DISCORD_WEBHOOK_URL=<span class="hljs-string">&quot;https://discord.com/api/webhooks/你的-webhook-id/你的-token&quot;</span><br></code></pre></td></tr></table></figure><p>接著建立 <code>~/.claude/hooks/discord-notify.js</code>：</p><figure class="highlight javascript"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br></pre></td><td class="code"><pre><code class="hljs javascript"><span class="hljs-meta">#!/usr/bin/env node</span><br><span class="hljs-keyword">const</span> <span class="hljs-variable constant_">WEBHOOK_URL</span> = process.<span class="hljs-property">env</span>.<span class="hljs-property">DISCORD_WEBHOOK_URL</span>;<br><br><span class="hljs-keyword">async</span> <span class="hljs-keyword">function</span> <span class="hljs-title function_">main</span>(<span class="hljs-params"></span>) &#123;<br>  <span class="hljs-comment">// 讀進 Claude Code 從 stdin 餵進來的 JSON</span><br>  <span class="hljs-keyword">const</span> chunks = [];<br>  <span class="hljs-keyword">for</span> <span class="hljs-title function_">await</span> (<span class="hljs-keyword">const</span> chunk <span class="hljs-keyword">of</span> process.<span class="hljs-property">stdin</span>) chunks.<span class="hljs-title function_">push</span>(chunk);<br>  <span class="hljs-keyword">const</span> input = <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">parse</span>(<span class="hljs-title class_">Buffer</span>.<span class="hljs-title function_">concat</span>(chunks).<span class="hljs-title function_">toString</span>());<br><br>  <span class="hljs-keyword">const</span> toolName = input.<span class="hljs-property">tool_name</span> || <span class="hljs-string">&quot;未知工具&quot;</span>;<br>  <span class="hljs-keyword">const</span> toolInput = input.<span class="hljs-property">tool_input</span> || &#123;&#125;;<br><br>  <span class="hljs-comment">// 組一個 Discord embed，讓訊息漂亮一點</span><br>  <span class="hljs-keyword">const</span> embed = &#123;<br>    <span class="hljs-attr">color</span>: <span class="hljs-number">0x7289da</span>,<br>    <span class="hljs-attr">author</span>: &#123; <span class="hljs-attr">name</span>: <span class="hljs-string">`🔧 <span class="hljs-subst">$&#123;toolName&#125;</span>`</span> &#125;,<br>    <span class="hljs-attr">description</span>: toolInput.<span class="hljs-property">file_path</span><br>      ? <span class="hljs-string">`\`<span class="hljs-subst">$&#123;toolInput.file_path&#125;</span>\``</span><br>      : toolInput.<span class="hljs-property">command</span><br>        ? <span class="hljs-string">`\`\`\`bash\n<span class="hljs-subst">$&#123;toolInput.command&#125;</span>\n\`\`\``</span><br>        : <span class="hljs-string">&quot;&quot;</span>,<br>    <span class="hljs-attr">timestamp</span>: <span class="hljs-keyword">new</span> <span class="hljs-title class_">Date</span>().<span class="hljs-title function_">toISOString</span>(),<br>  &#125;;<br><br>  <span class="hljs-keyword">await</span> <span class="hljs-title function_">fetch</span>(<span class="hljs-variable constant_">WEBHOOK_URL</span>, &#123;<br>    <span class="hljs-attr">method</span>: <span class="hljs-string">&quot;POST&quot;</span>,<br>    <span class="hljs-attr">headers</span>: &#123; <span class="hljs-string">&quot;Content-Type&quot;</span>: <span class="hljs-string">&quot;application/json&quot;</span> &#125;,<br>    <span class="hljs-attr">body</span>: <span class="hljs-title class_">JSON</span>.<span class="hljs-title function_">stringify</span>(&#123; <span class="hljs-attr">embeds</span>: [embed] &#125;),<br>  &#125;);<br>&#125;<br><br><span class="hljs-title function_">main</span>().<span class="hljs-title function_">catch</span>(<span class="hljs-variable language_">console</span>.<span class="hljs-property">error</span>);<br></code></pre></td></tr></table></figure><p>最後在 <code>~/.claude/settings.json</code> 註冊（用個人 settings 讓所有專案都生效）：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;node ~/.claude/hooks/discord-notify.js&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p><code>matcher: &quot;&quot;</code> 是「不管什麼工具都通知」，這樣 Claude 不管要 <code>Read</code>、<code>Edit</code>、<code>Bash</code> 你都會在 Discord 看到一則訊息。如果你想根據工具種類顯示不同 emoji、顏色、欄位，照這個結構往下擴就好。</p><p>這整個 hook 是個人通知用、webhook URL 也是你私人的，寫在 <code>~/.claude/settings.json</code> 個人層就好，不用 commit。</p><blockquote><p><strong>Note</strong><br>Webhook URL 一定要透過環境變數讀，不要寫死在 script 裡。萬一你真的想跟團隊共用一個 webhook，hook 本身可以放 <code>.claude/settings.json</code>，但 webhook URL 還是只放在各自的 shell profile 或 <code>.claude/settings.local.json</code>（後者不會被 commit）。</p></blockquote><h3 id="Log-所有-Bash-指令"><a href="#Log-所有-Bash-指令" class="headerlink" title="Log 所有 Bash 指令"></a>Log 所有 Bash 指令</h3><p>如果你想稽核 Claude 到底跑了哪些 Bash 指令，可以在 <code>PostToolUse</code> 加個一行：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PostToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -r &#x27;.tool_input.command&#x27; &gt;&gt; ~/.claude/bash.log&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>每跑一次 Bash 指令就會被追加到 <code>~/.claude/bash.log</code>，事後翻很方便。這個是個人稽核用的，寫在 <code>~/.claude/settings.json</code> 就好。</p><h2 id="多個-hook-怎麼合併？"><a href="#多個-hook-怎麼合併？" class="headerlink" title="多個 hook 怎麼合併？"></a>多個 hook 怎麼合併？</h2><p>當你同一個事件設了好幾個 hook，它們會<strong>全部平行執行</strong>，不會誰擋誰，最後 Claude Code 把所有結果合併。<code>PreToolUse</code> 的權限決定採「最嚴格者勝」：deny 蓋過 ask、ask 蓋過 allow。</p><p>舉個實際的例子，你可以同時設「Log 所有 Bash 指令」跟「擋掉 rm -rf」兩個 hook：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">&#123;</span><br>    <span class="hljs-attr">&quot;PreToolUse&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>      <span class="hljs-punctuation">&#123;</span><br>        <span class="hljs-attr">&quot;matcher&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;Bash&quot;</span><span class="hljs-punctuation">,</span><br>        <span class="hljs-attr">&quot;hooks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-punctuation">[</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;jq -r .tool_input.command &gt;&gt; ~/.claude/bash.log&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><span class="hljs-punctuation">,</span><br>          <span class="hljs-punctuation">&#123;</span><br>            <span class="hljs-attr">&quot;type&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;command&quot;</span><span class="hljs-punctuation">,</span><br>            <span class="hljs-attr">&quot;command&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-string">&quot;\&quot;$CLAUDE_PROJECT_DIR\&quot;/.claude/hooks/block-rm.sh&quot;</span><br>          <span class="hljs-punctuation">&#125;</span><br>        <span class="hljs-punctuation">]</span><br>      <span class="hljs-punctuation">&#125;</span><br>    <span class="hljs-punctuation">]</span><br>  <span class="hljs-punctuation">&#125;</span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><p>當 Claude 試圖跑 <code>rm -rf /tmp/build</code> 時，兩個 hook 會同時跑，log 會被寫進去，rm 會被擋掉。Log 不會因為 rm 被擋就漏記，因為兩個是各自獨立執行的。</p><h2 id="怎麼確認-hook-有跑？"><a href="#怎麼確認-hook-有跑？" class="headerlink" title="怎麼確認 hook 有跑？"></a>怎麼確認 hook 有跑？</h2><p>設好 hook 卻不確定到底有沒有觸發？Claude Code 提供兩個方式可以查。</p><p>第一個是 <code>/hooks</code> 指令，在 Claude Code 裡面直接打 <code>/hooks</code> 會跳出目前已設定的 hook 清單，按事件分組，每個都可以點進去看 matcher 跟 command。<code>/hooks</code> 是唯讀的，要改 hook 還是要編輯 <code>settings.json</code>。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-hooks/20260531150538.png" data-fancybox="gallery" data-caption="&#x2F;hooks 指令的介面示意"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-hooks/20260531150538.png" alt="&#x2F;hooks 指令的介面示意"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">&#x2F;hooks 指令的介面示意</figcaption></figure><p>第二個是 debug log，啟動時加 <code>--debug-file /tmp/claude.log</code>：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">claude --debug-file /tmp/claude.log<br></code></pre></td></tr></table></figure><p>然後另一個視窗開 <code>tail -f /tmp/claude.log</code>，每次 hook 觸發都會在 log 看到完整的 stdout、stderr、exit code。如果你只想看 hooks 相關的訊息不想被其他 debug 噪音淹沒，可以用 <code>claude --debug hooks</code> 加 filter。</p><h2 id="延伸閱讀"><a href="#延伸閱讀" class="headerlink" title="延伸閱讀"></a>延伸閱讀</h2><ul><li><a href="https://israynotarray.com/ai/20260331/1748203647/">Claude Code 哪些檔案該 Commit？CLAUDE.md、Settings、Rules、Skills 完整解析</a>：搞懂 <code>.claude/settings.json</code> 的層級之後再來看 hooks 會更清楚。</li><li><a href="https://israynotarray.com/ai/20260429/8473628195/">Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具</a>：要寫 hook 必須先知道 matcher 對應的工具名稱。</li><li><a href="https://israynotarray.com/ai/20260120/4189755271/">比 MCP 更省 Token 的 Agent Skills</a>：Skill 跟 Hook 是互補關係，Skill 給能力、Hook 給保證。</li></ul>]]></content>
    
    
    <summary type="html">用 Claude Code 開發時，是不是每次改完檔都要再叫它跑 Prettier、或擔心它哪天手滑跑了 rm -rf？這篇帶你了解 Claude Code Hooks 的運作方式：PreToolUse、PostToolUse、UserPromptSubmit、SessionStart、Notification、Stop 這些事件分別在什麼時候觸發、怎麼在 settings.json 設 matcher 跟 command、怎麼用 exit code 2 擋掉動作，並實戰幾個常用 hook：自動 format、擋掉 rm -rf、Compact 後重新注入專案脈絡、Mac 桌面通知，最後補充 prompt / agent hook、常見踩雷跟 /hooks 除錯方式。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="AI Agent" scheme="https://israynotarray.com/tags/AI-Agent/"/>
    
    <category term="AI 開發" scheme="https://israynotarray.com/tags/AI-%E9%96%8B%E7%99%BC/"/>
    
    <category term="Claude Code Hooks" scheme="https://israynotarray.com/tags/Claude-Code-Hooks/"/>
    
    <category term="Hooks" scheme="https://israynotarray.com/tags/Hooks/"/>
    
    <category term="AI 自動化" scheme="https://israynotarray.com/tags/AI-%E8%87%AA%E5%8B%95%E5%8C%96/"/>
    
  </entry>
  
  <entry>
    <title>寫部落格能月入幾萬美金嗎？聊聊 Google AdSense 真實收益與計算方式</title>
    <link href="https://israynotarray.com/other/20260526/2846301957/"/>
    <id>https://israynotarray.com/other/20260526/2846301957/</id>
    <published>2026-05-26T13:00:00.000Z</published>
    <updated>2026-05-26T13:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8826%E6%97%A5%20%E4%B8%8A%E5%8D%8809_23_15.png" data-fancybox="gallery" data-caption="寫部落格能月入幾萬美金嗎？聊聊 Google AdSense 真實收益與計算方式"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8826%E6%97%A5%20%E4%B8%8A%E5%8D%8809_23_15.png" alt="寫部落格能月入幾萬美金嗎？聊聊 Google AdSense 真實收益與計算方式"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">寫部落格能月入幾萬美金嗎？聊聊 Google AdSense 真實收益與計算方式</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇來聊一下寫部落格到底賺不賺錢這件事。先講我自己部落格的三大收益來源（Google AdSense、Buy Me A Coffee、Portaly）比例大約 <code>8:1:1</code>，然後著重聊 Google AdSense。內容包含 CPM 與 CPC 的差異、AdBlock 怎麼影響曝光收益、Google Analytics 月活躍流量、AdSense 成效報表（瀏覽量、曝光次數、點擊、單次點擊出價、網頁千次曝光收益），最後實際把月收益算給你看，並且聊一下我自己的廣告版位設計。</p><h2 id="為什麼想寫這篇"><a href="#為什麼想寫這篇" class="headerlink" title="為什麼想寫這篇"></a>為什麼想寫這篇</h2><p>最近有些朋友看到我有經營部落格，所以就很好奇經營部落格跟寫文章的收益如何，這一篇就是要來破解一下他們以為的「寫部落格收益很高」這個迷思 xD</p><p>其實這個誤會滿常見的，可是真正的收益跟想像差很多，所以乾脆寫一篇來破除迷思。標題寫「一個月幾萬美金都可能！」也是有點故意，這就是大家口中的標題殺，因為大多數人對寫部落格這件事，第一印象就是這個畫面（吧？）。</p><blockquote><p><strong>Note</strong><br>這篇只聊 Google AdSense 這條主線，至於 Buy Me A Coffee 跟 Portaly 為什麼比例很小，講白了就是「不是每次都有人會抖內」，所以就不額外展開了。</p></blockquote><p>如果你還沒看過我之前那篇 <a href="/other/20211003/84953257/">為什麼寫文章可以有收益？以 Google AdSense 與 Liker 為例</a>，那篇是更早期關於部落格收益怎麼運作的入門介紹，這篇可以當成五年後的「真實業績驗收版」。</p><h2 id="我的部落格收益拆解"><a href="#我的部落格收益拆解" class="headerlink" title="我的部落格收益拆解"></a>我的部落格收益拆解</h2><p>先簡單列一下，目前我的部落格主要收益來源有這三種：</p><ul><li>Google AdSense</li><li>Buy Me A Coffee</li><li>Portaly</li></ul><p>這三者的比例大概是 <code>8:1:1</code>，也就是說 AdSense 是真正的大宗，後兩者比較像「偶爾收到讀者鼓勵」的性質。所以接下來篇幅都會放在 AdSense 上，後兩者就點到為止。</p><h2 id="AdSense-是怎麼算錢的？"><a href="#AdSense-是怎麼算錢的？" class="headerlink" title="AdSense 是怎麼算錢的？"></a>AdSense 是怎麼算錢的？</h2><p>AdSense 的收入概念其實不複雜，主要就是看廣告有沒有被人看到，以及看到的人有沒有點下去。當然細節會受很多因素影響，例如廣告類型、版位、主題分類等等，可是核心邏輯就是這兩件事。</p><p>而這兩件事又跟一個東西高度相關，就是流量。流量越多，曝光自然越多，能被點的機會也跟著越多。</p><h3 id="我這個月的流量大概多少？"><a href="#我這個月的流量大概多少？" class="headerlink" title="我這個月的流量大概多少？"></a>我這個月的流量大概多少？</h3><p>我用 Google Analytics 看了一下最近 30 天，活躍使用者大約是 1.8 萬人左右。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526091321.png" data-fancybox="gallery" data-caption="Google Analytics 30 天流量統計"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526091321.png" alt="Google Analytics 30 天流量統計"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Google Analytics 30 天流量統計</figcaption></figure><p>這個數字其實並不高，跟那些一個月幾十萬、甚至百萬流量的網站比起來差的可多了。</p><h3 id="CPM-跟-CPC-是什麼？"><a href="#CPM-跟-CPC-是什麼？" class="headerlink" title="CPM 跟 CPC 是什麼？"></a>CPM 跟 CPC 是什麼？</h3><p>接下來要來介紹 Google AdSense 的兩個重要概念：CPM 跟 CPC。</p><p>AdSense 的計價方式主要有兩種：</p><div class="table-wrapper"><table><thead><tr><th>名詞</th><th>全名</th><th>怎麼算</th></tr></thead><tbody><tr><td>CPM</td><td>Cost Per Mille</td><td>每 1000 次曝光的收益</td></tr><tr><td>CPC</td><td>Cost Per Click</td><td>每次點擊的收益</td></tr></tbody></table></div><p>簡單來講，CPM 是「我這個廣告版位有人看到就有錢」，CPC 是「有人真的點進去才有錢」。</p><p>通常 CPC 的單價會比 CPM 高很多，原因也很合理：使用者真的點下去，代表他對廣告有興趣，可能會轉換成購買，所以廣告主願意付更多錢。CPM 的收益則是看版位、廣告類型，還有最重要的一點，使用者有沒有開 AdBlock。</p><p>只要使用者有開 AdBlock，那個版位的廣告請求根本不會跑出來，曝光、點擊都歸零，所以 CPM 跟 CPC 都沒得收。這也是為什麼有些網站會跳訊息請讀者把站台加進 AdBlock 的白名單。</p><p>但我個人滿不喜歡這種感受的，畢竟有一種「強迫讀者」的感覺，還是希望能在不干擾閱讀的前提下，讓讀者願意支持就好，所以以我來講，我就只是放這種提示為主。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093307.png" data-fancybox="gallery" data-caption="AdSense AdBlock 提示"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093307.png" alt="AdSense AdBlock 提示"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AdSense AdBlock 提示</figcaption></figure><p>畢竟這樣比較能夠維持讀者體驗，長期來講對部落格的成長也比較有幫助。</p><h2 id="真實數字：我這個月到底賺多少？"><a href="#真實數字：我這個月到底賺多少？" class="headerlink" title="真實數字：我這個月到底賺多少？"></a>真實數字：我這個月到底賺多少？</h2><p>ok 講那麼多概念，我們直接看 AdSense 上的「成效」報表。我這邊拉出「最近 28 天」跟「再往前 28 天」做比較，比較看得出趨勢。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526091311.png" data-fancybox="gallery" data-caption="AdSense 28 天成效報表"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526091311.png" alt="AdSense 28 天成效報表"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">AdSense 28 天成效報表</figcaption></figure><p>報表上會有以下幾個欄位：</p><ul><li>瀏覽量（Page Views）</li><li>網頁千次曝光收益（Page RPM）</li><li>曝光次數（Impressions）</li><li>點擊（Clicks）</li><li>單次點擊出價（CPC）</li><li>網頁點擊率（CTR）</li></ul><p>我自己最近 28 天的數字大概是這樣：</p><div class="table-wrapper"><table><thead><tr><th>指標</th><th>數字</th></tr></thead><tbody><tr><td>瀏覽量</td><td>3.01 萬</td></tr><tr><td>網頁千次曝光收益</td><td>0.73 美金</td></tr><tr><td>曝光次數</td><td>6.14 萬</td></tr><tr><td>點擊</td><td>106</td></tr><tr><td>單次點擊出價</td><td>0.21 美金</td></tr><tr><td>網頁點擊率</td><td>0.35%</td></tr></tbody></table></div><p>老實講這個 CPC 很低，平均一次點擊只有 0.21 美金，相較之下有些主題（金融、保險、軟體訂閱）的 CPC 可以飆到 1 美金以上。網頁千次曝光收益 0.73 美金也不算高。</p><p>那這數字大概怎麼換算成總收入呢？最直觀的方式是直接用「曝光次數 × 千次曝光收益 &#x2F; 1000」，因為 AdSense 報表的「千次曝光收益（RPM）」本身就是把整個月 CPC + CPM 所有廣告收益加總後，再正規化成「每 1000 次曝光的均值」，所以反推回來就能還原回月收入：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">收入 = 曝光次數 × 千次曝光收益 / 1000<br></code></pre></td></tr></table></figure><p>代到我自己的數字裡：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">44.82 ≈ 61400 × 0.73 / 1000<br></code></pre></td></tr></table></figure><p>一個月大概是 44.82 美金（AdSense 報表上實際顯示 44.84，差 0.02 是因為 0.73 這個 RPM 本身在報表上就被四捨五入過，反推回去自然會掉一點點精度）。</p><p>聽起來好像還不錯？可是這只是 Google 給的數字，最後實際入帳還要扣稅金、金流跨境手續費，實拿會更少一點。而且這個曝光跟點擊量也不是每個月穩穩的，只要哪一個月沒持續更新文章 or 主題沒踩中搜尋熱度，流量馬上就會掉，收入也跟著縮水，底下是我沒有認真經營部落格的 Google Analytics 流量趨勢：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093620.png" data-fancybox="gallery" data-caption="Google Analytics 流量趨勢"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093620.png" alt="Google Analytics 流量趨勢"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Google Analytics 流量趨勢</figcaption></figure><p>當時的一日廣告費用大概 0.01 美金不到。</p><p>這邊也放上寫這篇文章時的 Google AdSense 收益：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526101636.png" data-fancybox="gallery" data-caption="Google AdSense 收益"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526101636.png" alt="Google AdSense 收益"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Google AdSense 收益</figcaption></figure><h2 id="我的廣告版位設計"><a href="#我的廣告版位設計" class="headerlink" title="我的廣告版位設計"></a>我的廣告版位設計</h2><p>廣告版位這件事是 AdSense 主很愛糾結的點。我自己的部落格廣告版位設計，大方向都是「盡量不要影響閱讀」，所以主要放在這幾個位置：</p><ul><li>文章開頭</li><li>文章結尾</li><li>文章列表（每五篇穿插一個廣告）</li><li>側邊欄左右兩側</li><li>定位錨點（類似浮動廣告，可是只會固定在最上方 or 最下方）</li></ul><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093734.png" data-fancybox="gallery" data-caption="廣告收益"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-adsense-blog/20260526093734.png" alt="廣告收益"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">廣告收益</figcaption></figure><blockquote><p><strong>Note</strong><br>我個人在逛自己的部落格時，大多都會打開 AdBlock，因為 Google AdSense 有嚴格規範，不允許自己點擊自己的廣告，之前有過一次不小心點到幾次，結果被 Ban 兩個月 QQ…</p></blockquote><p>如果以「壓榨收益」的角度來看，這個密度其實算少。要真的把收益拉到極致，還可以加上：</p><ul><li>文章內文中間插入廣告（讀到一半被打斷）</li><li>覆蓋式廣告（看一半畫面被廣告蓋住）</li><li>強制看完倒數才能關掉的廣告</li><li>自動播放有聲音的影片廣告</li></ul><p>這些版型確實會大幅拉高收益，可是讀者體驗就會直接被拖到地板。我自己當讀者也很討厭這類體驗，看到一半被廣告打斷我會直接關掉分頁，連回訪都不會了，更不用說訂閱 RSS or 推薦給朋友。</p><p>關於廣告版位的選擇與優化，我以前寫過一篇 <a href="/other/20200402/69728820/">如何讓 Google 廣告（AdSense）增加收益？</a>，裡面有更細的版位分析，有興趣的話可以延伸閱讀。</p><h2 id="那到底能不能月入幾萬美金？"><a href="#那到底能不能月入幾萬美金？" class="headerlink" title="那到底能不能月入幾萬美金？"></a>那到底能不能月入幾萬美金？</h2><p>回到一開始的標題殺問題：寫部落格可以月入幾萬美金嗎？答案是可以的，但門檻很高。</p><p>你可能要同時滿足以下條件：</p><ul><li>流量等級要在百萬層級（不是 1.8 萬）</li><li>主題切在高 CPC 的領域（金融、保險、SaaS、購物）</li><li>廣告版位策略夠激進，可是又不能讓讀者直接列為 AdBlock 名單</li><li>多語系經營（特別是英文流量）拉高整體 CPM 跟 CPC 的天花板，不局限於特定國家</li><li>文章持續產出、SEO 持續維護</li></ul><p>少了任何一項，數字都會直接砍半甚至更多。</p><p>所以對絕大多數獨立部落客來講，把 AdSense 當主要收入是不切實際的，比較合理的定位是「副業 + 長期累積資產」。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>寫這篇不是要勸退你寫部落格，反過來說，我反而覺得寫部落格的好處遠遠不只在「賺錢」這件事上，像是建立個人品牌、整理知識、訓練表達能力，這些長期收益比廣告分潤值錢太多了。我之前寫過 <a href="/learnexp/20241231/998165636/">AI 當道，為什麼我還是推薦寫部落格</a> 也有聊到這塊，有興趣可以延伸看。</p><p>可是如果你今天就是衝著「靠寫部落格賺錢」這個動機進來，那我希望這篇能幫你校正期待，寫部落格賺錢是真的，可是不會像你以為的那麼香，比較像是把它當成一個會慢慢長大的存錢筒，而不是一個提款機。</p><p>那希望這一篇有讓你對寫部落格的真實收益狀況多一點了解哩～</p>]]></content>
    
    
    <summary type="html">這篇來聊一下寫部落格到底賺不賺錢這件事。先講我自己部落格的三大收益來源（Google AdSense、Buy Me A Coffee、Portaly）比例大約 8:1:1，然後著重聊 Google AdSense。內容包含 CPM 與 CPC 的差異、AdBlock 怎麼影響曝光收益、Google Analytics 月活躍流量、AdSense 成效報表（瀏覽量、曝光次數、點擊、單次點擊出價、網頁千次曝光收益），實際月收益計算式，最後也聊一下我自己的廣告版位設計，以及為什麼要在收益跟讀者體驗中間取平衡。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="部落格" scheme="https://israynotarray.com/tags/%E9%83%A8%E8%90%BD%E6%A0%BC/"/>
    
    <category term="被動收入" scheme="https://israynotarray.com/tags/%E8%A2%AB%E5%8B%95%E6%94%B6%E5%85%A5/"/>
    
    <category term="Google AdSense" scheme="https://israynotarray.com/tags/Google-AdSense/"/>
    
    <category term="廣告收益" scheme="https://israynotarray.com/tags/%E5%BB%A3%E5%91%8A%E6%94%B6%E7%9B%8A/"/>
    
    <category term="CPM" scheme="https://israynotarray.com/tags/CPM/"/>
    
    <category term="CPC" scheme="https://israynotarray.com/tags/CPC/"/>
    
    <category term="Buy Me A Coffee" scheme="https://israynotarray.com/tags/Buy-Me-A-Coffee/"/>
    
    <category term="Portaly" scheme="https://israynotarray.com/tags/Portaly/"/>
    
    <category term="Google Analytics" scheme="https://israynotarray.com/tags/Google-Analytics/"/>
    
  </entry>
  
  <entry>
    <title>Claude Code Routines 排程怎麼設？/schedule 完整教學與 main 分支推錯解法</title>
    <link href="https://israynotarray.com/ai/20260525/1843702956/"/>
    <id>https://israynotarray.com/ai/20260525/1843702956/</id>
    <published>2026-05-25T06:00:00.000Z</published>
    <updated>2026-05-25T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-code-routines/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8825%E6%97%A5%20%E4%B8%8A%E5%8D%8810_00_13.png" data-fancybox="gallery" data-caption="Claude Code Routines 排程怎麼設？&#x2F;schedule 完整教學與 main 分支推錯解法"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-code-routines/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8825%E6%97%A5%20%E4%B8%8A%E5%8D%8810_00_13.png" alt="Claude Code Routines 排程怎麼設？&#x2F;schedule 完整教學與 main 分支推錯解法"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code Routines 排程怎麼設？&#x2F;schedule 完整教學與 main 分支推錯解法</figcaption></figure><blockquote><p><strong>Note</strong><br>Claude Code 有三種排程：Routines（雲端，關電腦也會跑）、Desktop scheduled tasks（本機，要 App 開著）、<code>/loop</code>（session 內排程，要終端機開著）。<code>/schedule</code> CLI 是建立 Routines 的入口，最短 1 小時間隔，<strong>預設只能推 <code>claude/</code> 前綴分支</strong>，要推 main 要去網頁打開 Allow unrestricted branch pushes。</p></blockquote><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>其實我一直都有在使用 Claude Code 的 <code>/schedule</code> 指令，而這個指令就是在建立一個排程任務，每天都會在我指定的時間執行，跑完之後我也會請它 <code>push</code> 到我的 GitHub 上。但，我滿常遇到一個狀況，也就是隔天打開 GitHub 一看，GitHub 分支反而變成了 <code>claude/main-xxxx</code> 這種，而不是我期望的 <code>main</code> 分支 QQ…</p><p>所以這篇就來把 Claude Code 的排程功能寫下來，順便把這個讓我很困擾的 <code>claude/main-xxxx</code> 記錄下來並搞清楚。</p><p>哦對了，如果你之前看過 <a href="https://israynotarray.com/ai/20260319/738324368/">Claude Cowork Dispatch 與 Remote Control</a> 那篇，那這篇可以當作另一條延伸路線：Dispatch 跟 Remote Control 是讓你現在用手機遠端控制，而排程是讓 Claude 自動在你不在的時候幫你做事哩。</p><h2 id="Claude-Code-有三種排程"><a href="#Claude-Code-有三種排程" class="headerlink" title="Claude Code 有三種排程"></a>Claude Code 有三種排程</h2><p>在進到 <code>/schedule</code> 之前，Claude Code 其實有三種不同的排程方式，這邊也準備一張表格比較一下差異：</p><div class="table-wrapper"><table><thead><tr><th></th><th>Routines（Cloud）</th><th>Desktop scheduled tasks</th><th><code>/loop</code></th></tr></thead><tbody><tr><td>跑在哪</td><td>Anthropic 雲端</td><td>你的電腦</td><td>你的電腦</td></tr><tr><td>要電腦開著嗎</td><td>不用</td><td>要</td><td>要</td></tr><tr><td>要 session 開著嗎</td><td>不用</td><td>不用</td><td>要</td></tr><tr><td>看得到本機檔案</td><td>看不到（每次 clone）</td><td>看得到</td><td>看得到</td></tr><tr><td>MCP &#x2F; Connectors</td><td>每個 routine 個別設定</td><td>沿用本機設定</td><td>沿用 session 設定</td></tr><tr><td>最短間隔</td><td>1 小時</td><td>1 分鐘</td><td>1 分鐘</td></tr></tbody></table></div><p>了解這三種排程差異之後，接下來我就會著重於 Routines（Cloud） 的部分來介紹。</p><h2 id="Routines-是什麼？"><a href="#Routines-是什麼？" class="headerlink" title="Routines 是什麼？"></a>Routines 是什麼？</h2><p>Routines 簡單來講，就是你把 prompt 跟它要用的 Repo 等等，放到 Anthropic 雲端，然後它會自己在你設定的時間 or 條件下幫你跑你要的任務。</p><p>跟一般的 Claude Code session 最大的差別有兩個：</p><ol><li><strong>它跑在 Anthropic 的雲端環境</strong>：不是你的電腦，所以你關電腦它也照跑、出門也照跑、睡覺也照跑。</li><li><strong>它是自主執行的</strong>：不會跳出權限提示（也就是 Yes、No），會完全依照你給的 prompt 自己跑完，所以 prompt 也必須非常明確，不然它可能會做出你沒預期到的行為。</li></ol><blockquote><p><strong>Note</strong><br>如果想要讓自己的每個月訂閱額度不被浪費的話，其實我個人滿推薦使用 Routines 來做一些定期性的任務，例如每天幫我檢查有沒有新的 PR 需要 review、每週幫我整理一下這週的 commit log、每月幫我開一個 PR 更新一下 README 的 stats 圖表之類的。</p></blockquote><p>而 Routines 的觸發類型目前有三種：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525120400.png" data-fancybox="gallery" data-caption="Routines"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525120400.png" alt="Routines"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Routines</figcaption></figure><p>但這一篇我們就著重於介紹 <code>/schedule</code> 哩～</p><h2 id="建立排程：用-schedule"><a href="#建立排程：用-schedule" class="headerlink" title="建立排程：用 /schedule"></a>建立排程：用 <code>/schedule</code></h2><p>建立 Routines 有兩種方式：</p><ol><li>網頁的 <code>claude.ai/code/routines</code></li><li>CLI（終端機） <code>/schedule</code></li></ol><p>不論是哪一種都可以，反正兩邊改的是同一筆資料，所以你在哪邊建立、編輯都會即時同步到另一邊。</p><p>基本用法就是這樣：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">/schedule<br></code></pre></td></tr></table></figure><p>直接打 <code>/schedule</code> 不帶任何東西，Claude 會用對話的方式引導你建立，問你 prompt 寫什麼、要選哪個 Repo、跑多久一次等等。</p><p>如果你已經很清楚要建什麼，也可以直接把描述寫在後面：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">/schedule daily PR review at 9am<br></code></pre></td></tr></table></figure><p>或是想要一次性的提醒：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">/schedule <span class="hljs-keyword">in</span> 2 weeks, open a cleanup PR that removes the feature flag<br></code></pre></td></tr></table></figure><p>Claude 會把你寫的時間轉成具體的 timestamp，跟你確認後存起來。</p><blockquote><p><strong>Note</strong><br><code>/schedule</code> CLI 只能建立 Schedule 觸發類型，如果要加 API or GitHub event 觸發，要到 <code>claude.ai/code/routines</code> 網頁端編輯。</p></blockquote><p>除了建立之外，CLI 也支援管理已經存在的 routines：</p><div class="table-wrapper"><table><thead><tr><th>指令</th><th>用途</th></tr></thead><tbody><tr><td><code>/schedule list</code></td><td>列出你目前所有的 routines</td></tr><tr><td><code>/schedule update</code></td><td>編輯某個 routine（包含設 cron）</td></tr><tr><td><code>/schedule run</code></td><td>立刻觸發一次，不等下一個排程點</td></tr></tbody></table></div><blockquote><p><strong>Note</strong><br>真的懶的話，其實你叫 AI 用 <code>/schedule</code> 幫你建排程也可以…</p></blockquote><h2 id="為什麼我請它推到-main-卻變成-claude-xxx？"><a href="#為什麼我請它推到-main-卻變成-claude-xxx？" class="headerlink" title="為什麼我請它推到 main 卻變成 claude/xxx？"></a>為什麼我請它推到 main 卻變成 <code>claude/xxx</code>？</h2><p>了解了基本的建立流程之後，接下來要來講一下我前面所講的 「明明請它推到 main 卻變成 <code>claude/xxx</code>」的問題。</p><p>當你在 Routines 裡跑一個會 push 程式碼的 prompt 時，<strong>Claude 預設只能推到 <code>claude/</code> 前綴的分支</strong>。所以即使你的 prompt 寫「處理完直接 push 到 main」，Claude 也只能開一個像 <code>claude/main</code> or <code>claude/update-main-xxxx</code> 之類的分支推上去，不會真的動到你的 main。</p><p>這不是 bug 是 feature（真的），這樣的設計原因是「避免 routines 不小心改到重要的分支」。畢竟排程任務是自主運作的、沒有任何權限提示也沒人在旁邊盯著，如果它能直接寫 main，一個寫壞的 prompt 就可能讓一個正式的產品雞飛狗跳。</p><p>那怎麼解？兩種選擇：</p><ol><li>接受預設行為：讓它推 <code>claude/xxx</code> 分支，然後你開 PR review，最建議的工作流，畢竟排程任務沒有人在旁邊看，先進到一個獨立分支，等你有空再 review 跟合併，相對安全很多。</li><li>打開 Allow unrestricted branch pushes：如果你真的想讓它直接推到 main，可以在 Edit routine 的 Permissions 區塊把對應 Repo 的「Allow unrestricted branch pushes」打開，這樣就沒有分支前綴的限制了，不過要小心你的 prompt 寫好寫滿，確保它不會做出你不想要的改動。</li></ol><p>如果你真的就是要讓它直接推到指定分支，可以這樣做：</p><ol><li>到 <code>claude.ai/code/routines</code> 點進你的 routine</li><li>按鉛筆圖示進入 Edit routine</li><li>找到下方的 Permissions 區塊</li><li>把對應 Repo 的「<strong>Allow unrestricted branch pushes</strong>」勾起來</li><li>存檔</li></ol><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525115018.png" data-fancybox="gallery" data-caption="Allow unrestricted branch pushes 設定位置"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525115018.png" alt="Allow unrestricted branch pushes 設定位置"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Allow unrestricted branch pushes 設定位置</figcaption></figure><blockquote><p><strong>Note</strong><br>如果你的 main 是受保護的、設了 branch protection rule 的話，就算打開 Allow unrestricted branch pushes，GitHub 那邊還是會擋下來。這時候要嘛在 protection rule 加例外，要嘛還是讓它走 PR 流程比較實際。</p></blockquote><h2 id="排程跑在哪、會用到哪些東西？"><a href="#排程跑在哪、會用到哪些東西？" class="headerlink" title="排程跑在哪、會用到哪些東西？"></a>排程跑在哪、會用到哪些東西？</h2><p>除了分支推錯之外，還有幾個 Routines 相關的細節，第一次設排程很容易忽略，這邊一起講一下。</p><h3 id="每次都是從-default-branch-重新-clone"><a href="#每次都是從-default-branch-重新-clone" class="headerlink" title="每次都是從 default branch 重新 clone"></a>每次都是從 default branch 重新 clone</h3><p>Routines 每跑一次，都是<strong>從你選的 Repo 的 default branch 重新 clone 一份</strong>來跑，不會延續上次的 working tree。所以你不能寫一個排程是「接續昨天那次跑到一半的事」，每次都要當作從零開始。</p><p>如果你的任務需要參考上次的狀態，可以把狀態寫到 Repo 的某個檔案 or 連接上第三方服務。</p><h3 id="一次性排程不吃-daily-cap"><a href="#一次性排程不吃-daily-cap" class="headerlink" title="一次性排程不吃 daily cap"></a>一次性排程不吃 daily cap</h3><p>Routines 有 daily run cap（一天最多能跑幾次的限制），但 <strong>one-off 排程不會吃這個 cap</strong>，它只會吃你訂閱方案的一般用量。所以如果你想用排程當「兩週後幫我做某件事」這種一次性提醒，可以放心開很多個，不會把 daily cap 用爆。</p><h2 id="管理-routines"><a href="#管理-routines" class="headerlink" title="管理 routines"></a>管理 routines</h2><p>設好的 routine 要管的話有兩條路。</p><p>CLI 端用前面提到的 <code>/schedule list</code>、<code>/schedule update</code>、<code>/schedule run</code>，做基本的列表、編輯、立刻觸發。</p><p>網頁端則是到 <code>claude.ai/code/routines</code>，點進每個 routine 的詳細頁，這邊可以做更多事：</p><ul><li><strong>Run now</strong>：立刻觸發一次，不等下一個排程</li><li><strong>Repeats 開關</strong>：暫停 or 恢復排程，暫停不會刪掉 routine 本身</li><li><strong>編輯</strong>：改名稱、prompt、Repo、環境、Connectors、觸發類型</li><li><strong>刪除</strong>：移除這個 routine，過去跑過的 session 還會留在 session 列表</li></ul><p>另外每次 routine 跑完都會在你的 session 列表開一個新 session，你可以點進去看 Claude 做了什麼、review 變更、開 PR、甚至接著對話繼續做下去。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525120246.png" data-fancybox="gallery" data-caption="管理 routines"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-code-routines/20260525120246.png" alt="管理 routines"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">管理 routines</figcaption></figure><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>Claude Code 的排程功能講複雜也不複雜，主要就是搞清楚三種排程的差別、<code>/schedule</code> 怎麼用、然後記得<strong>預設只能推 <code>claude/</code> 前綴分支</strong>這件事，剩下的就是依照你的需求調整觸發類型跟環境設定。</p><p>我自己後來的習慣是：日常的「每天跑一次某個小事」用 Routines 設 daily、需要存取本機檔案的 batch 任務用 Desktop scheduled task、想在當下 session 裡反覆 poll 某個狀態就用 <code>/loop</code>，三個各有各的場景。</p><p>至於那個一直推到 <code>claude/xxx</code> 分支的問題，知道是設計使然之後就釋懷了，反正讓它推 <code>claude/</code> 分支然後 PR review 也比較安心。如果你跟我一樣有遇到這個困惑，希望這篇有幫你省下翻文件的時間哩～</p><h2 id="延伸閱讀"><a href="#延伸閱讀" class="headerlink" title="延伸閱讀"></a>延伸閱讀</h2><p>如果你想把 Claude Code 玩得更全面，也可以接著看：</p><ul><li><a href="https://israynotarray.com/ai/20260319/738324368/">Claude Cowork Dispatch 與 Remote Control - 用手機遠端控制你的 AI Agent</a>：另一條讓你不用守在電腦前的路線，但要的是「現在就用手機操控」而不是「自動排程」。</li><li><a href="https://israynotarray.com/ai/20260128/374686091/">想用 Claude Code 開發？這篇帶你從入門到進階</a>：Claude Code 完整指南，從基本操作到進階開發技巧。</li><li><a href="https://israynotarray.com/ai/20260330/2896397295/">Claude Code Auto Memory 是什麼？自動記憶功能完整教學</a>：搭配排程一起用，讓 Claude 在每次排程跑起來時都記得你的偏好。</li></ul>]]></content>
    
    
    <summary type="html">用 Claude Code 的 /schedule 排了一個任務，跑完打開 Repo 一看，明明請它推到 main，怎麼多出一堆 claude/xxx 分支？這篇從 Claude Code 的三種排程方式（Routines、Desktop scheduled tasks、/loop）切入，介紹 /schedule CLI 怎麼用、Routines 的三種觸發類型（Schedule、API、GitHub event）、cron 自訂規則、最短 1 小時間隔等限制，最後重點講為什麼會推到 claude/ 前綴分支，以及怎麼用 Allow unrestricted branch pushes 解掉這個讓人很困惑的行為。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="AI Agent" scheme="https://israynotarray.com/tags/AI-Agent/"/>
    
    <category term="Claude Code Routines" scheme="https://israynotarray.com/tags/Claude-Code-Routines/"/>
    
    <category term="schedule" scheme="https://israynotarray.com/tags/schedule/"/>
    
    <category term="排程" scheme="https://israynotarray.com/tags/%E6%8E%92%E7%A8%8B/"/>
    
    <category term="Automation" scheme="https://israynotarray.com/tags/Automation/"/>
    
  </entry>
  
  <entry>
    <title>為什麼 CSS overflow: scroll 看不到捲軸？macOS 跟 Windows 的 Chrome 差異</title>
    <link href="https://israynotarray.com/css/20260518/2738592104/"/>
    <id>https://israynotarray.com/css/20260518/2738592104/</id>
    <published>2026-05-18T09:00:00.000Z</published>
    <updated>2026-05-18T09:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/css/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8821%E6%97%A5%20%E4%B8%8A%E5%8D%8811_49_22.png" data-fancybox="gallery" data-caption="為什麼 CSS overflow: scroll 看不到捲軸？macOS 跟 Windows 的 Chrome 差異"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/css/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8821%E6%97%A5%20%E4%B8%8A%E5%8D%8811_49_22.png" alt="為什麼 CSS overflow: scroll 看不到捲軸？macOS 跟 Windows 的 Chrome 差異"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">為什麼 CSS overflow: scroll 看不到捲軸？macOS 跟 Windows 的 Chrome 差異</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>相信很多寫 CSS 的人都有遇過這個情境，明明在自己的元素上加了 <code>overflow: scroll</code>，但打開 Chrome 卻看不到捲軸，傳給同事看，他那邊卻又跑出一條灰灰的捲軸，明明用的都是 Chrome，怎麼會有這種差異？這一篇就從 <code>overflow</code> 屬性是什麼開始講。</p><h2 id="先聊聊-overflow-是什麼"><a href="#先聊聊-overflow-是什麼" class="headerlink" title="先聊聊 overflow 是什麼"></a>先聊聊 overflow 是什麼</h2><p>在分析問題之前，先稍微回顧一下 <code>overflow</code> 這個屬性是什麼。</p><p>簡單來講，<code>overflow</code> 是用來決定<strong>當一個元素裡面的內容超出它本身的寬高時，要怎麼處理那一段超出的部分</strong>，這個屬性算是 CSS 裡面非常基本但又非常常用的東西。</p><p>舉個例子，假設我有一個寬 200px、高 100px 的 <code>div</code>，但裡面塞了一段超長的文字 or 一張比 200px 還寬的圖片，這時候畫面就會發生兩件事：</p><ul><li>內容直接跑出（溢出）容器到外面去</li><li>內容被切掉，看不到後面那一段</li><li>出現捲軸，讓使用者自己滾動</li></ul><p>那這要怎麼解決呢？那就要來講講 <code>overflow</code> 了。</p><h3 id="overflow-的四個常見屬性"><a href="#overflow-的四個常見屬性" class="headerlink" title="overflow 的四個常見屬性"></a>overflow 的四個常見屬性</h3><p><code>overflow</code> 屬性有四個比較常用的屬性，這邊用一張表格快速看一下：</p><div class="table-wrapper"><table><thead><tr><th>屬性</th><th>用途</th></tr></thead><tbody><tr><td><code>visible</code></td><td>預設屬性，當內容超出容器範圍就會讓它跑出去，不做任何處理。</td></tr><tr><td><code>hidden</code></td><td>內容超出的部分直接被切掉，看不到也沒辦法捲動。</td></tr><tr><td><code>scroll</code></td><td>不管內容有沒有超出，都保留捲軸的位置一直顯示捲軸。</td></tr><tr><td><code>auto</code></td><td>只有當內容真的超出時，捲軸才會出現。</td></tr></tbody></table></div><p>這邊要特別拉 <code>scroll</code> 跟 <code>auto</code> 出來講，因為這兩個屬性最容易搞混，但它們兩者差異只有一個小地方，也就是 <code>scroll</code> 是「永遠都會出現捲軸」，但 <code>auto</code> 則是「內容超出時，需要才會出現捲軸」。</p><h2 id="那為什麼-overflow-scroll-在我電腦上看不到捲軸？"><a href="#那為什麼-overflow-scroll-在我電腦上看不到捲軸？" class="headerlink" title="那為什麼 overflow: scroll 在我電腦上看不到捲軸？"></a>那為什麼 overflow: scroll 在我電腦上看不到捲軸？</h2><p>這是因為 macOS 預設使用的是 <strong>「隱形捲軸（Overlay Scrollbar）」</strong>，這種捲軸只有在你正在捲動時才會浮現出來，平常是隱藏的，而且不會占據版面的空間。但你同事如果是用 Windows，那邊的捲軸是 <strong>「古典捲軸（Classic Scrollbar）」</strong>，會固定保留位置而且永遠看得到。</p><p>所以兩個人開同一個網頁、同一個版本的 Chrome，但看到的視覺結果就會不同，根本原因不是 Chrome 不一樣，而是底下的作業系統不一樣。</p><h3 id="macOS-的「隱形捲軸」"><a href="#macOS-的「隱形捲軸」" class="headerlink" title="macOS 的「隱形捲軸」"></a>macOS 的「隱形捲軸」</h3><p>在 macOS 上，捲軸的顯示行為是由系統設定決定的，查看方式是「設定 → 外觀 → 顯示捲軸」，這邊有三個選項可以選：</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/css/20260521112123.png" data-fancybox="gallery" data-caption="macOS 系統設定中的顯示捲軸選項"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/css/20260521112123.png" alt="macOS 系統設定中的顯示捲軸選項"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">macOS 系統設定中的顯示捲軸選項</figcaption></figure><p>通常預設屬性是「自動依揵滑鼠或觸控式軌跡板」，也就是當你用的是觸控板 or Mac 內建的軌跡板時，它就會走「捲動時」的行為，有點饒舌對吧？簡單來講，也就是平常完全看不到捲軸，只有你在捲的當下會浮出一條半透明的細條，停下來幾秒它又會自動淡出消失。</p><p>而 Chrome、Safari 跟其他大多數的 Mac 瀏覽器，預設都會跟著這個系統設定走，所以即便你在 CSS 寫了 <code>overflow: scroll</code>，瀏覽器還是會用「隱形捲軸」的方式渲染，不會把捲軸固定地顯示在那邊。</p><p>那要怎麼確認到底是不是這個原因呢？很簡單，你只要走兩個步驟驗證：</p><ol><li>把 macOS 的**「顯示捲軸」<strong>切到</strong>「總是」**</li><li>重新整理一下你的瀏覽器（或者重新開啟）</li></ol><p>這時候捲軸就會直接固定地出現了，這代表 CSS 沒寫錯，是作業系統的設定在控制視覺呈現。</p><h3 id="為什麼-Windows-沒這個問題？"><a href="#為什麼-Windows-沒這個問題？" class="headerlink" title="為什麼 Windows 沒這個問題？"></a>為什麼 Windows 沒這個問題？</h3><p>因為 Windows 從以前到現在都是用「古典捲軸」，捲軸會固定占住版面右側（大約 15 ~ 17px 寬），不管你有沒有在捲都看得到，也不會自動淡出消失。</p><p>所以你同事打開同一個頁面時，那條灰灰的細長捲軸就直接掛在右邊，他寫的 <code>overflow: scroll</code> 看起來就是 CSS 預期的樣子，沒有「捲軸到底跑去哪了」的疑問。</p><p>這邊順便提一個容易忽略的點，如果你都在 Mac 上開發、可是實際使用者大多用 Windows，那你看到的內容寬度其實會比使用者那邊還多一段（被捲軸吃掉的那段）。做 RWD 切版時，建議借同事的 Windows 開一下看看版面有沒有跑掉，免得交付後才發現右邊有點擠。</p><h2 id="那怎麼讓兩邊看起來一樣？"><a href="#那怎麼讓兩邊看起來一樣？" class="headerlink" title="那怎麼讓兩邊看起來一樣？"></a>那怎麼讓兩邊看起來一樣？</h2><p>如果你希望不管使用者用什麼系統都看得到捲軸（例如怕 Mac 使用者根本不知道內容還可以往下捲），這邊有幾種做法可以選：</p><h3 id="方法一：用-webkit-scrollbar-強制顯示"><a href="#方法一：用-webkit-scrollbar-強制顯示" class="headerlink" title="方法一：用 ::-webkit-scrollbar 強制顯示"></a>方法一：用 <code>::-webkit-scrollbar</code> 強制顯示</h3><p>最直接的做法是用 <code>::-webkit-scrollbar</code> 這個 CSS 偽元素去自訂捲軸樣式，只要你給它一個明確的 <code>width</code> 或 <code>height</code>，Chrome 就會自動把這個元素的捲軸從隱形捲軸切換成古典捲軸，永遠固定顯示。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.scroll-box</span> &#123;<br>  <span class="hljs-attribute">width</span>: <span class="hljs-number">300px</span>;<br>  <span class="hljs-attribute">height</span>: <span class="hljs-number">200px</span>;<br>  <span class="hljs-attribute">overflow</span>: scroll;<br>&#125;<br><br><span class="hljs-selector-class">.scroll-box</span>::-webkit-scrollbar &#123;<br>  <span class="hljs-attribute">width</span>: <span class="hljs-number">8px</span>;<br>  <span class="hljs-attribute">height</span>: <span class="hljs-number">8px</span>;<br>&#125;<br><br><span class="hljs-selector-class">.scroll-box</span>::-webkit-scrollbar-thumb &#123;<br>  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#888</span>;<br>  <span class="hljs-attribute">border-radius</span>: <span class="hljs-number">4px</span>;<br>&#125;<br><br><span class="hljs-selector-class">.scroll-box</span>::-webkit-scrollbar-track &#123;<br>  <span class="hljs-attribute">background-color</span>: <span class="hljs-number">#f0f0f0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><p>寫完之後你會發現 Mac 上的 Chrome 終於把捲軸固定顯示出來了，而且因為它已經變成古典捲軸，會吃掉一段內容寬度，這時候版面就會跟 Windows 上看到的比較接近。</p><p>可是這個做法有兩個小坑要記得：</p><ul><li><code>::-webkit-scrollbar</code> 是 Webkit &#x2F; Blink 系列才支援的非標準屬性，Chrome、Edge、Safari、Opera 都吃，可是 Firefox 不吃</li><li>iOS Safari 對 <code>::-webkit-scrollbar</code> 的支援也很有限，大部分情況下還是會走系統的隱形捲軸</li></ul><h3 id="方法二：用標準化的-scrollbar-width-跟-scrollbar-color"><a href="#方法二：用標準化的-scrollbar-width-跟-scrollbar-color" class="headerlink" title="方法二：用標準化的 scrollbar-width 跟 scrollbar-color"></a>方法二：用標準化的 scrollbar-width 跟 scrollbar-color</h3><p>如果你不想用非標準屬性，CSS 後來也有把捲軸樣式標準化，新增了 <code>scrollbar-width</code> 跟 <code>scrollbar-color</code> 兩個 W3C 標準屬性（出自 CSS Scrollbars Module Level 1），跟 <code>::-webkit-scrollbar</code> 比起來最大的差異就是它有規格背書。</p><figure class="highlight css"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs css"><span class="hljs-selector-class">.scroll-box</span> &#123;<br>  <span class="hljs-attribute">width</span>: <span class="hljs-number">300px</span>;<br>  <span class="hljs-attribute">height</span>: <span class="hljs-number">200px</span>;<br>  <span class="hljs-attribute">overflow</span>: scroll;<br>  <span class="hljs-attribute">scrollbar-width</span>: thin;<br>  <span class="hljs-attribute">scrollbar-color</span>: <span class="hljs-number">#888</span> <span class="hljs-number">#f0f0f0</span>;<br>&#125;<br></code></pre></td></tr></table></figure><ul><li><code>scrollbar-width</code> 可以設 <code>auto</code>、<code>thin</code> 或 <code>none</code>，分別對應「預設粗細」、「細捲軸」、「隱藏捲軸」</li><li><code>scrollbar-color</code> 接收兩個顏色值，前者是捲軸 thumb 的顏色，後者是 track 的底色</li></ul><p>可是支援度要先注意一下，Firefox 從 64 就支援這兩個屬性了，Chromium 系列（Chrome、Edge）要到 Chrome 121 之後才支援，Safari 則是要 Safari 26.2 之後才有完整支援。如果你的網站還要兼容比較舊的瀏覽器，建議把 <code>::-webkit-scrollbar</code> 一起寫上去當 fallback。</p><blockquote><p><strong>Note</strong><br>MDN 文件有提到，當 <code>scrollbar-width</code> 或 <code>scrollbar-color</code> 設成非 <code>auto</code> 的值時，會直接蓋掉 <code>::-webkit-scrollbar</code> 的樣式。所以兩個一起寫的時候，要注意誰會贏。</p></blockquote><h3 id="為什麼不用-scrollbar-gutter？"><a href="#為什麼不用-scrollbar-gutter？" class="headerlink" title="為什麼不用 scrollbar-gutter？"></a>為什麼不用 scrollbar-gutter？</h3><p>看到這邊你可能會想說，CSS 不是還有 <code>scrollbar-gutter</code> 可以拿來保留捲軸位置嗎？那直接用它來解這個問題不是更省事？</p><p>可惜不行，這個屬性對 macOS 的隱形捲軸沒效果。</p><p><code>scrollbar-gutter: stable</code> 原本的設計目的是保留捲軸需要的版面空間，避免內容因為捲軸有無而抖動。可是 MDN 文件上有明確寫一行：當瀏覽器使用隱形捲軸時，<code>scrollbar-gutter</code> 不會有任何效果。</p><p>簡單來講，它要解決的是古典捲軸那種會占空間的場景下，內容因為捲軸出現&#x2F;消失而跳動的問題。可是 macOS 預設用的是隱形捲軸，本來就不占空間，自然也沒東西好保留。</p><p>所以如果你的目標是讓 Mac 上的 Chrome 永遠看得到捲軸，<code>scrollbar-gutter</code> 解決不了這個問題，還是要回去用 <code>::-webkit-scrollbar</code> 或 <code>scrollbar-width</code>。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>回到開頭那個「同事看得到、你看不到」的情境，這個問題的根源其實不是 CSS 寫錯，也不是 Chrome 哪裡壞掉，而是作業系統層級的捲軸機制本來就不一樣 — macOS 預設走隱形捲軸，Windows 預設走古典捲軸，兩邊對 <code>overflow: scroll</code> 的視覺呈現自然就不同。</p><p>知道這件事之後，下次再遇到一樣的狀況就不用懷疑自己 CSS 寫錯，可以直接想到是系統設定差異，再依需求選 <code>::-webkit-scrollbar</code> or 新版的 <code>scrollbar-width</code> 補上一致的視覺。</p><p>如果你對瀏覽器底層怎麼把 CSS 渲染成你看到的畫面有興趣，可以再看看 <a href="https://israynotarray.com/other/20250424/3992645625/">前端工程師應該要懂的瀏覽器渲染機制</a> 這一篇，裡面把 Render Process 從 DOM 樹一路到 Composite 的流程整理得滿完整的，跟這篇可以串成更完整的「為什麼瀏覽器會這樣顯示」的脈絡。</p>]]></content>
    
    
    <summary type="html">CSS 設了 overflow: scroll 之後，Mac 的 Chrome 看不到捲軸，但同事的 Windows 卻看得到？這一篇從 overflow 屬性四個值開始講起，搭配 macOS 的『隱形捲軸（Overlay Scrollbar）』機制，解釋為什麼 macOS 上 Chrome 即使設了 overflow: scroll 也不會永遠顯示捲軸，並提供三種解法：::-webkit-scrollbar 強制顯示、新版 scrollbar-width 跟 scrollbar-color 標準屬性，以及把 macOS 系統的『顯示捲軸』切到『總是』。</summary>
    
    
    
    <category term="CSS" scheme="https://israynotarray.com/categories/css/"/>
    
    
    <category term="CSS" scheme="https://israynotarray.com/tags/CSS/"/>
    
    <category term="overflow" scheme="https://israynotarray.com/tags/overflow/"/>
    
    <category term="捲軸" scheme="https://israynotarray.com/tags/%E6%8D%B2%E8%BB%B8/"/>
    
    <category term="scrollbar" scheme="https://israynotarray.com/tags/scrollbar/"/>
    
    <category term="macOS" scheme="https://israynotarray.com/tags/macOS/"/>
    
    <category term="Chrome" scheme="https://israynotarray.com/tags/Chrome/"/>
    
    <category term="前端開發" scheme="https://israynotarray.com/tags/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/"/>
    
  </entry>
  
  <entry>
    <title>VSCode 連結都自動開在內建瀏覽器？教你關掉整合瀏覽器自動接管</title>
    <link href="https://israynotarray.com/other/20260518/3674582910/"/>
    <id>https://israynotarray.com/other/20260518/3674582910/</id>
    <published>2026-05-18T06:30:00.000Z</published>
    <updated>2026-05-18T06:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8818%E6%97%A5%20%E4%B8%8A%E5%8D%8810_06_00.png" data-fancybox="gallery" data-caption="VSCode 連結都自動開在內建瀏覽器？教你關掉整合瀏覽器自動接管"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8818%E6%97%A5%20%E4%B8%8A%E5%8D%8810_06_00.png" alt="VSCode 連結都自動開在內建瀏覽器？教你關掉整合瀏覽器自動接管"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">VSCode 連結都自動開在內建瀏覽器？教你關掉整合瀏覽器自動接管</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>VSCode 1.109 推出新的整合瀏覽器（Integrated Browser）後，只要 <code>workbench.browser.openLocalhostLinks</code> 這個設定被打開，點終端機或聊天視窗裡面的 localhost 連結就會被自動拉進編輯器內顯示，畫面又小又難用。所以這一篇會記錄一下 <code>workbench.browser.openLocalhostLinks</code> 跟 <code>simpleBrowser.useIntegratedBrowser</code> 這兩個關鍵設定怎麼關，以及還是想偶爾用一下整合瀏覽器時可以怎麼從命令面板手動叫出。</p><h2 id="為什麼會變成這樣？"><a href="#為什麼會變成這樣？" class="headerlink" title="為什麼會變成這樣？"></a>為什麼會變成這樣？</h2><p>這個行為其實不是 VSCode 一直都有的，而是從 1.109 版（差不多是 2026 年 1 月）開始新增了一個叫做「整合瀏覽器（Integrated Browser）」的功能，當這個功能開著時，只要 VSCode 偵測到你點的是 localhost 連結，就會優先把這個連結拉進編輯器內顯示，這樣你就不用切換到其他應用程式去看畫面。</p><p>立意上來講是不錯，但對許多開發者來講其實是滿困擾的，因為編輯器本來就已經被切成檔案總管、編輯區、輸出視窗等好幾塊，再多塞一個瀏覽器進來，能用的空間就會變得很小，你想在裡面測 RWD、開 DevTools 都不太方便 QQ…</p><h2 id="整合瀏覽器跟-Simple-Browser-有什麼關係？"><a href="#整合瀏覽器跟-Simple-Browser-有什麼關係？" class="headerlink" title="整合瀏覽器跟 Simple Browser 有什麼關係？"></a>整合瀏覽器跟 Simple Browser 有什麼關係？</h2><p>如果你有用 VSCode 一段時間了，那你應該會聽過 Simple Browser，這是 VSCode 早期就有的一個內建瀏覽器套件，但它的限制比較多，例如：</p><ul><li>只能顯示靜態頁面</li><li>不能登入需要驗證的網站</li><li>不會保留 cookie</li></ul><p>所以以前大家頂多拿來瞄一下 localhost 起來有沒有跑而已。</p><p>那麼新版的整合瀏覽器跟 Simple Browser 之間又是什麼關係呢？</p><p>簡單來講，整合瀏覽器是 Simple Browser 的進階版本，但兩者目前是並存的，並不是新的把舊的取代掉，你可以透過設定來決定命令面板叫出來的是哪一個。</p><p>整合瀏覽器強化了 Simple Browser 所缺乏的能力，所以它可以做到：</p><ul><li>登入需要驗證的網站（cookie 跟登入狀態都會被保留）</li><li>瀏覽任何頁面，不限 localhost</li><li>內建 DevTools，可以直接檢查元素</li><li>把選到的元素丟給 Copilot 當作上下文</li></ul><p>簡單來講，就是 VSCode 把一個比較接近 Chrome 的小瀏覽器塞進了編輯器裡，讓你不需要離開 VSCode 也能完整看一個網頁，而不是像以前 Simple Browser 那樣只能看個半套。</p><h2 id="那為什麼點連結會被自動拉進去？"><a href="#那為什麼點連結會被自動拉進去？" class="headerlink" title="那為什麼點連結會被自動拉進去？"></a>那為什麼點連結會被自動拉進去？</h2><p>整合瀏覽器本身只是「多了一個可以用的瀏覽器」，會發生「點連結就被搶走」這件事，是因為 VSCode 提供了一個叫做 <code>workbench.browser.openLocalhostLinks</code> 的設定，只要這個設定是打開的，VSCode 任何地方（終端機、聊天視窗、Markdown 預覽、輸出面板等等）點到的 localhost 連結都會被導去整合瀏覽器，而不是丟給你系統預設的瀏覽器。</p><blockquote><p><strong>Note</strong><br>1.109 釋出說明裡面寫的是「可以打開這個設定」，所以理論上預設是關閉的，但有些情境下會在你沒注意的時候被打開（例如點過某個提示、or 裝了某個套件偷偷幫你開），這時候你就會發現怎麼點 localhost 連結都跑去 VSCode 內。</p></blockquote><h2 id="解決方法"><a href="#解決方法" class="headerlink" title="解決方法"></a>解決方法</h2><p>知道是哪個設定在搞鬼之後，把它關掉就解決了，這邊提供兩種方式，看你比較順手哪一種。</p><h3 id="方法一：透過設定面板關掉"><a href="#方法一：透過設定面板關掉" class="headerlink" title="方法一：透過設定面板關掉"></a>方法一：透過設定面板關掉</h3><p>首先打開 VSCode 的設定面板：</p><ul><li>macOS：<code>Cmd + ,</code></li><li>Windows &#x2F; Linux：<code>Ctrl + ,</code></li></ul><p>接著在最上方的搜尋框輸入：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">workbench.browser.openLocalhostLinks<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100131.png" data-fancybox="gallery" data-caption="在 VSCode 設定面板搜尋 workbench.browser.openLocalhostLinks"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100131.png" alt="在 VSCode 設定面板搜尋 workbench.browser.openLocalhostLinks"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">在 VSCode 設定面板搜尋 workbench.browser.openLocalhostLinks</figcaption></figure><p>接著你應該會看到這個設定，把它的勾勾取消掉就好，這邊不需要重開 VSCode，會立刻生效。</p><h3 id="方法二：直接改-settings-json"><a href="#方法二：直接改-settings-json" class="headerlink" title="方法二：直接改 settings.json"></a>方法二：直接改 settings.json</h3><p>如果你比較喜歡改 JSON 檔案（or 有同步多台機器設定的需求），那也可以走這條路。</p><p>先按 <code>Cmd + Shift + P</code>（Windows 是 <code>Ctrl + Shift + P</code>）打開命令面板，然後輸入：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">Preferences: Open User Settings (JSON)<br></code></pre></td></tr></table></figure><p>選擇之後它會幫你開啟 <code>settings.json</code>，接著我們只要在裡面加入這個設定：</p><figure class="highlight json"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs json"><span class="hljs-punctuation">&#123;</span><br>  <span class="hljs-attr">&quot;workbench.browser.openLocalhostLinks&quot;</span><span class="hljs-punctuation">:</span> <span class="hljs-literal"><span class="hljs-keyword">false</span></span><br><span class="hljs-punctuation">&#125;</span><br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100239.png" data-fancybox="gallery" data-caption="在 VSCode settings.json 加入 workbench.browser.openLocalhostLinks 設定"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100239.png" alt="在 VSCode settings.json 加入 workbench.browser.openLocalhostLinks 設定"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">在 VSCode settings.json 加入 workbench.browser.openLocalhostLinks 設定</figcaption></figure><p>存檔後就會立刻生效，再去終端機點一次 localhost 連結，畫面就會回到你系統的預設瀏覽器了哩～</p><h2 id="順便處理：Simple-Browser-套件自己的設定"><a href="#順便處理：Simple-Browser-套件自己的設定" class="headerlink" title="順便處理：Simple Browser 套件自己的設定"></a>順便處理：Simple Browser 套件自己的設定</h2><p>關掉 <code>workbench.browser.openLocalhostLinks</code> 之後，理論上 localhost 連結就會回到正常，但這邊要特別提一下，VSCode 還有一個跟整合瀏覽器有關的設定叫做 <code>simpleBrowser.useIntegratedBrowser</code>，這個其實是「Simple Browser 套件自己的設定」，並不是 VSCode 核心提供的，從命名上其實就可以看出來。</p><div class="table-wrapper"><table><thead><tr><th>設定</th><th>屬於</th><th>它在管什麼</th></tr></thead><tbody><tr><td><code>workbench.browser.openLocalhostLinks</code></td><td>VSCode 核心</td><td>點 localhost 連結時要不要自動走整合瀏覽器</td></tr><tr><td><code>simpleBrowser.useIntegratedBrowser</code></td><td>Simple Browser（內建套件）</td><td>從命令面板叫 Simple Browser 時要不要改走新版整合瀏覽器</td></tr></tbody></table></div><p>從這張表你應該可以發現，前綴名稱其實就反映了它的出處，<code>workbench.*</code> 是 VSCode 自己的，而 <code>simpleBrowser.*</code> 則是屬於 Simple Browser 這個內建套件的，所以如果你關了核心設定之後還是發現從命令面板叫出來的瀏覽器跑去新版整合瀏覽器，那就是這個套件設定還開著，搜尋一下名稱關掉就好。</p><h2 id="還是想偶爾用一下整合瀏覽器？"><a href="#還是想偶爾用一下整合瀏覽器？" class="headerlink" title="還是想偶爾用一下整合瀏覽器？"></a>還是想偶爾用一下整合瀏覽器？</h2><p>把自動接管關掉之後，整合瀏覽器並不是就完全不能用了，如果哪天你想在編輯器內快速確認一下某個畫面，還是可以手動把它叫出來。</p><p>一樣按 <code>Cmd + Shift + P</code>（Windows 是 <code>Ctrl + Shift + P</code>）打開命令面板，接著輸入：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">Simple Browser<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100823.png" data-fancybox="gallery" data-caption="Simple Browser"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/vscode/vscode-integrated-browser/20260518100823.png" alt="Simple Browser"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Simple Browser</figcaption></figure><p>按 enter 後輸入你想看的網址，整合瀏覽器就會在編輯器內開出一個分頁，這樣需要的時候自己叫出來，不需要的時候連結也不會被搶走。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>整合瀏覽器這個功能本身其實是不錯的，但預設行為對於習慣用外部瀏覽器當主力的人來講就比較困擾，那個小視窗實在難以取代日常開發在用的 Chrome 或 Edge，因此這時候把它關掉是比較好的選擇。</p><p>希望這一篇有讓你對 VSCode 整合瀏覽器的自動接管行為與關閉設定有更近一步的認識哩～如果你還想再順手把 Mac 端的開發環境整理一下，可以再看看 <a href="https://israynotarray.com/other/20260421/3921847205/">Mac 上裝軟體到底該用哪種方式？Homebrew、官網下載、curl 完整比較</a> 這一篇，跟這一篇可以串成一條更完整的環境整理流程，我們下篇文章見囉～</p>]]></content>
    
    
    <summary type="html">VSCode 1.109 推出新的整合瀏覽器（Integrated Browser）功能後，只要 workbench.browser.openLocalhostLinks 這個設定被打開，點終端機或聊天視窗裡面的 localhost 連結就會被自動拉進編輯器內顯示，畫面又小又難用。所以這一篇會記錄一下 workbench.browser.openLocalhostLinks 跟 simpleBrowser.useIntegratedBrowser 這兩個關鍵設定怎麼關，以及還是想偶爾用一下整合瀏覽器時可以怎麼從命令面板手動叫出。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="VSCode" scheme="https://israynotarray.com/tags/VSCode/"/>
    
    <category term="前端開發" scheme="https://israynotarray.com/tags/%E5%89%8D%E7%AB%AF%E9%96%8B%E7%99%BC/"/>
    
    <category term="Visual Studio Code" scheme="https://israynotarray.com/tags/Visual-Studio-Code/"/>
    
    <category term="整合瀏覽器" scheme="https://israynotarray.com/tags/%E6%95%B4%E5%90%88%E7%80%8F%E8%A6%BD%E5%99%A8/"/>
    
    <category term="Integrated Browser" scheme="https://israynotarray.com/tags/Integrated-Browser/"/>
    
    <category term="Simple Browser" scheme="https://israynotarray.com/tags/Simple-Browser/"/>
    
    <category term="編輯器設定" scheme="https://israynotarray.com/tags/%E7%B7%A8%E8%BC%AF%E5%99%A8%E8%A8%AD%E5%AE%9A/"/>
    
  </entry>
  
  <entry>
    <title>Vibe Coding 升級教學：2026 Agentic Engineering 六階段實戰 SOP（含範本與案例）</title>
    <link href="https://israynotarray.com/ai/20260517/3851629074/"/>
    <id>https://israynotarray.com/ai/20260517/3851629074/</id>
    <published>2026-05-17T06:30:00.000Z</published>
    <updated>2026-05-17T06:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/vide-agentic-engineering/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8817%E6%97%A5%20%E4%B8%8B%E5%8D%8802_43_12.png" data-fancybox="gallery" data-caption="Vibe Coding 升級教學：2026 Agentic Engineering 六階段實戰 SOP（含範本與案例）"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/vide-agentic-engineering/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8817%E6%97%A5%20%E4%B8%8B%E5%8D%8802_43_12.png" alt="Vibe Coding 升級教學：2026 Agentic Engineering 六階段實戰 SOP（含範本與案例）"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Vibe Coding 升級教學：2026 Agentic Engineering 六階段實戰 SOP（含範本與案例）</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如果你有用過 Claude Code、Cursor、Codex 這類 AI Coding 工具，應該都遇過類似的瓶頸：剛上手覺得超神奇，可是真的拿來開發一個有規模的專案，各種雷就跑出來 — context 爆掉之後 AI 忘光、改 A 壞 B 改了又改、AI 改著改著把好的 code 也蓋掉、好幾個檔案一起被改沒辦法回滾等等。</p><p>這些都不是工具的問題，是跟 AI 協作的流程沒建立起來。</p><p>說到這邊有件事滿值得提一下，<a href="https://en.wikipedia.org/wiki/Andrej_Karpathy">Andrej Karpathy</a> 是 2025 年 2 月（<a href="https://x.com/karpathy/status/1886192184808149383">原始 tweet</a>）提出 Vibe Coding 這個詞的人，到了 2026 年 4 月他自己在 <a href="https://karpathy.bearblog.dev/sequoia-ascent-2026/">Sequoia Ascent 2026</a> 的演講（標題「Software 3.0, Agentic Engineering, and Jagged Intelligence」）上直接升級了這個概念，提出 <strong>Agentic Engineering（代理工程）</strong>。演講裡關鍵的一句話是：</p><blockquote><p>「Vibe coding raises the floor. Agentic engineering is about extrapolating the ceiling.」</p></blockquote><p>翻成白話就是：Vibe coding 是抬升底線，讓沒寫過 code 的人也能做出東西；Agentic engineering 是抬升天花板，是專業工程師該有的工作方式。</p><p>那 agentic engineer 跟以前的 vibe coder 差在哪？根據演講的核心訊息，重點不再是描述需求讓 AI 寫然後直接 ship，而是 AI 扮演實作者、你扮演監督者，要做的幾件事大致包含：設計規格、監督計畫、檢查 diff、寫測試、建立評估迴圈、管理權限、隔離 worktree、維持品質。換句話說，agentic engineer 不會盲目接受 AI 生成的 code，而是用工程紀律包住 AI 速度。</p><p>之前我寫過 <a href="https://israynotarray.com/ai/20251202/797714546/">淺談 AI Vibe Coding 當今主流的 BDD 與 SDD</a> 跟 <a href="https://israynotarray.com/ai/20260427/1657384902/">想開始 Vibe Coding？新手推薦先搞懂的這些基礎知識</a> 這兩篇，前者偏方法論、後者偏新手該補的基礎技能，可是都還停在 vibe coding 框架。這一篇就直接升級，用 agentic engineering 的紀律把整套實戰流程攤開來講，每個環節都附可以直接抄走的範本，最後用一個個人電商 MVP（Nuxt 3 + Supabase + Stripe）的案例走一遍完整流程。</p><blockquote><p><strong>Note</strong><br>為什麼一定要有紀律？根據 <a href="https://www.coderabbit.ai/blog/state-of-ai-vs-human-code-generation-report">CodeRabbit 2025 年 12 月發布的「State of AI vs Human Code Generation」報告</a>（分析了 470 個 GitHub open-source PR，320 個 AI co-authored + 150 個 human-only），AI 寫的 code 比人寫的多 <strong>1.7 倍</strong> major bug、整體資安問題多 <strong>1.57 倍</strong>，其中 <strong>XSS 漏洞高達 2.74 倍</strong>、不安全的密碼處理多 1.88 倍。AI 生成的速度很快可是品質沒保證，靠的是你流程設計接住它的失誤。</p></blockquote><h2 id="Vibe-Coding-vs-Agentic-Engineering：2026-你該選哪個？"><a href="#Vibe-Coding-vs-Agentic-Engineering：2026-你該選哪個？" class="headerlink" title="Vibe Coding vs Agentic Engineering：2026 你該選哪個？"></a>Vibe Coding vs Agentic Engineering：2026 你該選哪個？</h2><p>在進實戰流程之前，先用一張表把兩者差別講清楚：</p><div class="table-wrapper"><table><thead><tr><th>面向</th><th>Vibe Coding</th><th>Agentic Engineering</th></tr></thead><tbody><tr><td>提出時間</td><td>2025 年 2 月 Karpathy 提出</td><td>2026 年 4 月 Karpathy 升級</td></tr><tr><td>核心精神</td><td>直接憑直覺描述、AI 寫、不太 review</td><td>設計 spec、監督 plan、檢查 diff、寫測試、做評估</td></tr><tr><td>適用情境</td><td>demo、PoC、拋棄式專案</td><td>生產級服務、需要長期維護的專案</td></tr><tr><td>流程紀律</td><td>鬆散、即興</td><td>結構化、有 SOP</td></tr><tr><td>人類角色</td><td>描述想法的 user</td><td>監督 AI 員工的工程師</td></tr><tr><td>失敗模式</td><td>跑得起來但隨時會爆</td><td>慢一點但較可控</td></tr></tbody></table></div><p>從這張表可以看出來，兩者不是替代關係，而是適用情境不一樣。週末做個小工具玩玩，vibe coding 完全沒問題；可是要做給別人用的東西、要長期維護的專案，就得切換到 agentic engineering 模式。</p><p>接下來這篇的 SOP，全部都是站在 agentic engineering 的紀律寫的。</p><h2 id="完整流程一張圖：一個功能從需求到-ship"><a href="#完整流程一張圖：一個功能從需求到-ship" class="headerlink" title="完整流程一張圖：一個功能從需求到 ship"></a>完整流程一張圖：一個功能從需求到 ship</h2><p>在進細節之前，先用一張流程圖把整套 SOP 壓縮成一個功能的完整協作流程。這張圖有三個角色 — 你（使用者）、Leader（主 Claude）、員工（Sub-agent + Skill），整體 7 個 step：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br></pre></td><td class="code"><pre><code class="hljs text">[使用者]                    [Leader / 主 Claude]            [員工 / Sub-agent + Skill]<br>   │                              │                                  │<br>   │ ① 提需求                     │                                  │<br>   ├─────────────────────────────▶│                                  │<br>   │                              │ ② plan mode 拆解需求             │<br>   │                              │   （必要時呼叫 sub-agent 探索）   │<br>   │                              ├─────────────────────────────────▶│<br>   │ ⚠ 需求有疑問時，Leader       │   產出 docs/tasks.md             │<br>   │   主動 AskUserQuestion       │◀─────────────────────────────────│<br>   │◀─────────⇄───────────────────│                                  │<br>   │                              │ ③ 開發（UI 切給 ui-engineer）    │<br>   │                              ├─────────────────────────────────▶│<br>   │                              │   同一輪補單元測試                │<br>   │                              │◀─────────────────────────────────│<br>   │                              │ ④ Code Review                    │<br>   │                              │   （另開 session or sub-agent）   │<br>   │                              ├─────────────────────────────────▶│<br>   │                              │   fresh context 抓 bias           │<br>   │                              │◀─────────────────────────────────│<br>   │                              │ ⑤ 驗收 + 三段驗證                 │<br>   │                              │   typecheck + test + build        │<br>   │                              ├─────────────────────────────────▶│<br>   │                              │   基於 spec（CLAUDE.md / AGENTS）│<br>   │                              │◀─────────────────────────────────│<br>   │ ⑥ 回報 + 更新 ROADMAP/tasks  │                                  │<br>   │◀─────────────────────────────│                                  │<br>   │ ⑦ 你 review diff + commit / push                                │<br>   ├──────────⇄───────────────────│                                  │<br></code></pre></td></tr></table></figure><p>幾個關鍵看點：</p><ul><li><strong>你只在 ①、⑥、⑦ 三個 step 親自參與</strong>（提需求、看回報、review diff + commit），中間 ② → ⑤ 都是 Leader 在 orchestrate</li><li><strong>理想狀態下 Leader 偏向 orchestrator 角色</strong> — 拆解、分派、收回報，把實作切給員工。實務上主 agent 還是常常會自己接手寫 code，除非你明確設計 delegation 流程（例如 prompt 內直接寫請呼叫 X sub-agent 處理），不要假設它會自動 route 出去</li><li><strong>⚠ step 是 Anthropic 官方推薦的 <a href="https://code.claude.com/docs/en/best-practices#let-claude-interview-you">interview pattern</a></strong> — 需求模糊時 Leader 主動用 <code>AskUserQuestion</code> 反問你，避免下游做錯方向</li><li><strong>④ Code Review 一定要 fresh context</strong> — 由另一個 session 的 Claude or sub-agent 來做，避免 self-confirmation bias</li><li><strong>⑤ 驗收測試的依據是 spec</strong>（CLAUDE.md &#x2F; AGENTS.md），不是 Leader 自己腦補的標準</li></ul><p>每個 step 對應到後面章節的細節：① ② 在 <strong>一、規劃階段</strong>、③ 在 <strong>二、員工分工</strong> + <strong>三、開發紀律</strong>、④ ⑤ 在 <strong>四、收尾驗證</strong>、⑦ 在 <strong>三、開發紀律</strong> 的 commit 切點。</p><p>如果中間 Leader 走偏了，回到 <strong>五、失誤救援</strong> 的回滾決策表；雷被抓到之後寫進 <strong>六、Memory 系統</strong> 變長期 rule。</p><h2 id="一、規劃階段：Spec-→-Plan-→-Tasks-三層落檔"><a href="#一、規劃階段：Spec-→-Plan-→-Tasks-三層落檔" class="headerlink" title="一、規劃階段：Spec → Plan → Tasks 三層落檔"></a>一、規劃階段：Spec → Plan → Tasks 三層落檔</h2><p>Agentic engineering 的第一個原則就是 <strong>規劃要文件化</strong>。業界主流框架例如 GitHub 在 2025 年釋出的 <a href="https://github.com/github/spec-kit">Spec Kit</a>（Specify → Plan → Tasks → Implement 四階段，每階段都會產出 <code>spec.md</code> &#x2F; <code>plan.md</code> &#x2F; <code>tasks.md</code> 這類檔案）、Anthropic 官方的 <a href="https://code.claude.com/docs/en/best-practices">Claude Code best practices</a>（Explore → Plan → Implement → Commit 四階段，plan mode 可以按 <code>Ctrl+G</code> 把 plan 打開到編輯器直接編輯落檔），核心精神都一致 — plan 跟 spec 應該寫成可讀可改的檔案，而不是只活在對話裡。</p><p>具體要分三層：</p><ol><li><strong>Spec（規格）</strong>：這個專案在做什麼、給誰用、邊界在哪</li><li><strong>Plan（計畫）</strong>：要做哪些 feature、什麼時間做完</li><li><strong>Tasks（任務）</strong>：當前任務拆成哪些步驟、做到哪了</li></ol><p>三層各自落檔，每層解決一個 context 問題。</p><h3 id="1-1-Spec：寫一份-CLAUDE-md（搭配-AGENTS-md）當專案憲法"><a href="#1-1-Spec：寫一份-CLAUDE-md（搭配-AGENTS-md）當專案憲法" class="headerlink" title="1.1 Spec：寫一份 CLAUDE.md（搭配 AGENTS.md）當專案憲法"></a>1.1 Spec：寫一份 CLAUDE.md（搭配 AGENTS.md）當專案憲法</h3><p>開工前要先搞清楚一件事：<strong>不同 AI Coding 工具讀的設定檔不一樣</strong>。</p><ul><li><strong>Claude Code 只讀 <code>CLAUDE.md</code>，不讀 <code>AGENTS.md</code></strong>（<a href="https://code.claude.com/docs/en/memory">Anthropic 官方文件</a>明確說「Claude Code reads <code>CLAUDE.md</code>, not <code>AGENTS.md</code>」）</li><li><strong>其他工具（Cursor、Codex、Windsurf、Aider 等）讀 <code>AGENTS.md</code></strong></li></ul><h4 id="AGENTS-md-是什麼？"><a href="#AGENTS-md-是什麼？" class="headerlink" title="AGENTS.md 是什麼？"></a>AGENTS.md 是什麼？</h4><p><a href="https://agents.md/">AGENTS.md</a> 是 OpenAI 在 2025 年 8 月主導推出的開放標準，跟 Amp、Google Jules、Cursor、Factory 等多家 AI 工具廠商合作發展，2025 年 12 月由 OpenAI 捐給 <a href="https://www.linuxfoundation.org/press/linux-foundation-announces-the-formation-of-the-agentic-ai-foundation">Linux Foundation 旗下的 Agentic AI Foundation（AAIF）</a>，跟 Anthropic 捐的 MCP（Model Context Protocol）、Block 捐的 Goose 並列為 AAIF 三大創始項目。截至 2026 年 5 月，全球已經有超過 6 萬個開源專案在用它，支援 Amp、Codex、Cursor、Devin、Factory、Gemini CLI、GitHub Copilot、Jules、VS Code 等 20+ 個 AI Coding 工具。</p><h4 id="該怎麼配置？"><a href="#該怎麼配置？" class="headerlink" title="該怎麼配置？"></a>該怎麼配置？</h4><p>最實用的做法是 <strong>兩個檔案都寫，但內容不重複</strong>：</p><ul><li><code>AGENTS.md</code> 放專案規範本體（給跨工具團隊共用）</li><li><code>CLAUDE.md</code> 第一行 <code>@AGENTS.md</code> 把 AGENTS 內容 import 進來，下面再加 Claude Code 專屬規則 + <code>@</code> import ROADMAP &#x2F; tasks</li></ul><p>或者更省事的做法是 <code>ln -s AGENTS.md CLAUDE.md</code> 建立 symlink，內容一份兩個檔名共用（Windows 需要 Administrator 權限 or Developer Mode，沒開的話用 <code>@AGENTS.md</code> import 比較保險）。</p><p>如果你只用 Claude Code 不在意跨工具，直接寫 <code>CLAUDE.md</code> 即可，AGENTS.md 不用寫。</p><h4 id="CLAUDE-md-範本"><a href="#CLAUDE-md-範本" class="headerlink" title="CLAUDE.md 範本"></a>CLAUDE.md 範本</h4><p>直接 copy 這個結構到你的專案根目錄 <code>CLAUDE.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 專案名 - Claude Code Instructions</span><br><br>@AGENTS.md<br><br><span class="hljs-section">## 開工前必讀</span><br>@docs/ROADMAP.md<br>@docs/tasks.md<br><br><span class="hljs-section">## Project Overview</span><br>（一兩句話：這個專案是什麼、給誰用）<br><br><span class="hljs-section">## Build &amp; Test</span><br><span class="hljs-bullet">-</span> 安裝：pnpm install<br><span class="hljs-bullet">-</span> 開發：pnpm dev<br><span class="hljs-bullet">-</span> 測試：pnpm test（全綠才能 commit）<br><span class="hljs-bullet">-</span> 打包：pnpm build<br><span class="hljs-bullet">-</span> 型別檢查：pnpm typecheck<br><br><span class="hljs-section">## Code Style</span><br><span class="hljs-bullet">-</span> 使用 ES modules（import/export），不用 CommonJS<br><span class="hljs-bullet">-</span> 命名：composable 用 useXxx、service 用 xxxService、純函式用 camelCase<br><span class="hljs-bullet">-</span> 縮排：2 空格<br><span class="hljs-bullet">-</span> 引號：單引號<br><br><span class="hljs-section">## Testing Rules</span><br><span class="hljs-bullet">-</span> 每次改 code 同一輪就把測試補上，不可拖到下一個 task<br><span class="hljs-bullet">-</span> 測試檔命名：xxx.test.ts，跟實作放同一個資料夾<br><span class="hljs-bullet">-</span> 純函式優先抽出來測（比 mock 容易）<br><br><span class="hljs-section">## Git Workflow</span><br><span class="hljs-bullet">-</span> commit message 用 Conventional Commits（feat / fix / test / refactor / docs / chore）<br><span class="hljs-bullet">-</span> 一個邏輯改動一個 commit，不要堆<br><span class="hljs-bullet">-</span> 主分支：main；功能分支：feature/xxx<br><br><span class="hljs-section">## Security</span><br><span class="hljs-bullet">-</span> 不要把 API key、token、密碼寫進 code<br><span class="hljs-bullet">-</span> .env 檔案不可 commit<br><span class="hljs-bullet">-</span> 敏感檔案：.env<span class="hljs-emphasis">*、*</span>.key、credentials.json<br><br><span class="hljs-section">## 明確排除的事</span><br><span class="hljs-bullet">-</span> 不引入 [某類功能]，原因：[原因]<br><span class="hljs-bullet">-</span> 不使用 [某個技術]，原因：[原因]<br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>上面範本第 3 行 <code>@AGENTS.md</code> 是雙檔架構用的。<strong>如果你只寫 CLAUDE.md 不寫 AGENTS.md，把這行刪掉即可</strong>（否則 Claude Code 會抱怨找不到 AGENTS.md）。<br>反過來如果走雙檔架構：AGENTS.md 的內容跟上面 CLAUDE.md 範本一樣，<strong>只是不需要 <code>@AGENTS.md</code> 那行</strong>（因為它本身就是被 import 的目標）；而且 <code>## 開工前必讀</code> 區塊建議只放在 CLAUDE.md 裡，因為 <code>@import</code> 是 Anthropic 對 CLAUDE.md 的擴充、其他工具實作 AGENTS.md 不一定支援。</p></blockquote><h4 id="import-機制（這是-Claude-Code-的關鍵）"><a href="#import-機制（這是-Claude-Code-的關鍵）" class="headerlink" title="@import 機制（這是 Claude Code 的關鍵）"></a>@import 機制（這是 Claude Code 的關鍵）</h4><p>注意上面範本第一個區塊 <code>## 開工前必讀</code> 裡的 <code>@&lt;path&gt;</code> 語法 — 這是 <strong>Anthropic 對 CLAUDE.md 的擴充語法</strong>（<a href="https://code.claude.com/docs/en/memory">官方文件確認</a>），AI 啟動時讀到 <code>@&lt;path&gt;</code> 會自動把那個檔案的內容也載進 context，遞迴 <strong>最多 5 hops 深</strong>，relative path 跟 absolute path（例如 <code>@~/.claude/personal-rules.md</code>）都支援。</p><p>這個機制超關鍵，因為 Claude Code 啟動時 <strong>預設只會自動載入 CLAUDE.md 本身</strong>（以及 ancestor 路徑的 CLAUDE.md），<code>docs/</code> 底下的 ROADMAP.md、tasks.md 不會被自動讀到。如果你只寫 ROADMAP 沒在 CLAUDE.md 裡 <code>@</code> 引用，AI 開新 session 進來根本看不到你的計畫，等於白寫。</p><blockquote><p><strong>Note</strong><br><code>@&lt;path&gt;</code> 是 Anthropic 對 CLAUDE.md 的擴充，<strong>AGENTS.md 規格本身沒有規定 import 語法</strong>，其他工具實作 AGENTS.md 不一定支援 <code>@import</code>。所以跨工具情境，重要規範建議直接 inline 寫進 AGENTS.md 比較保險，不要假設其他工具會跟著解析 <code>@import</code>。Claude Code 這邊照常透過 <code>@AGENTS.md</code> 把整份內容拉進來、再透過 <code>@docs/ROADMAP.md</code> 把計畫接上。</p></blockquote><h3 id="1-2-Plan：寫一份-ROADMAP-md-列功能跟階段"><a href="#1-2-Plan：寫一份-ROADMAP-md-列功能跟階段" class="headerlink" title="1.2 Plan：寫一份 ROADMAP.md 列功能跟階段"></a>1.2 Plan：寫一份 ROADMAP.md 列功能跟階段</h3><p>CLAUDE.md（搭配 AGENTS.md）是專案憲法、不太會變；ROADMAP.md 是這個 sprint or 這個月要做什麼 feature 的計畫，會隨開發推進更新。</p><h4 id="ROADMAP-md-範本"><a href="#ROADMAP-md-範本" class="headerlink" title="ROADMAP.md 範本"></a>ROADMAP.md 範本</h4><p>放在 <code>docs/ROADMAP.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 專案名 ROADMAP</span><br><br><span class="hljs-section">## 願景</span><br>（一兩句話：這個專案要解決什麼問題、給誰用）<br><br><span class="hljs-section">## Week 1：MVP</span><br><span class="hljs-bullet">-</span> [ ] 功能 A：[簡述行為跟驗收條件]<br><span class="hljs-bullet">-</span> [ ] 功能 B：[同上]<br><br><span class="hljs-section">## Week 2：補強</span><br><span class="hljs-bullet">-</span> [ ] 功能 C：[同上]<br><span class="hljs-bullet">-</span> [ ] 功能 D：[同上]<br><br><span class="hljs-section">## 待評估</span><br><span class="hljs-bullet">-</span> 功能 X：可能做、可能不做，看 Week 1 完成度<br><span class="hljs-bullet">-</span> 功能 Y：同上<br><br><span class="hljs-section">## 明確排除的事</span><br><span class="hljs-bullet">-</span> 功能 Z：因為 [原因]<br></code></pre></td></tr></table></figure><p><code>## 明確排除的事</code> 這一塊很重要，AI 容易自己腦補功能、越界做事，把不做的事明文列出來可以幫你卡住範圍。</p><h3 id="1-3-Tasks：寫一份-tasks-md-同步當前進度"><a href="#1-3-Tasks：寫一份-tasks-md-同步當前進度" class="headerlink" title="1.3 Tasks：寫一份 tasks.md 同步當前進度"></a>1.3 Tasks：寫一份 tasks.md 同步當前進度</h3><p>第三層是當前任務的進度追蹤。AI 內建有待辦清單工具（例如 Claude Code 的 todo 系統），可是這個本質上活在 context 裡，跑 <code>/compact</code>、關掉 session 重開、context 一爆掉，這份清單就跟著消失了。所以更穩的做法是同時請 AI 把進度寫到實體檔案。</p><h4 id="tasks-md-範本"><a href="#tasks-md-範本" class="headerlink" title="tasks.md 範本"></a>tasks.md 範本</h4><p>放在 <code>docs/tasks.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># 本次任務：[ 任務簡述 ]</span><br><br><span class="hljs-section">## 待辦清單</span><br><span class="hljs-bullet">-</span> [x] 1. 讀懂需求、plan 已落到 docs/ROADMAP.md<br><span class="hljs-bullet">-</span> [x] 2. 拆解子任務<br><span class="hljs-bullet">-</span> [ ] 3. 實作 A<br><span class="hljs-bullet">  -</span> [ ] 3-1. 寫 A.composable<br><span class="hljs-bullet">  -</span> [ ] 3-2. 同步寫 A.composable.test<br><span class="hljs-bullet">-</span> [ ] 4. 實作 B<br><span class="hljs-bullet">  -</span> [ ] 4-1. 寫 B.service<br><span class="hljs-bullet">  -</span> [ ] 4-2. 同步寫 B.service.test<br><span class="hljs-bullet">-</span> [ ] 5. 跑 typecheck + test + build 三段驗證<br><span class="hljs-bullet">-</span> [ ] 6. commit + push<br><br><span class="hljs-section">## 進行中</span><br>3-1：寫 A.composable，目前卡在 [描述]<br><br><span class="hljs-section">## 完成紀錄</span><br><span class="hljs-bullet">-</span> 2026-XX-XX：plan 確認、tasks 拆完<br></code></pre></td></tr></table></figure><p>開工前直接跟 AI 說：先列待辦清單、同步寫到 <code>docs/tasks.md</code>，每完成一項就更新檔案。這樣即使隔天重開新 session，AI 一進專案讀檔案就接得回去。</p><h2 id="二、員工分工：你的三種-AI-員工配置"><a href="#二、員工分工：你的三種-AI-員工配置" class="headerlink" title="二、員工分工：你的三種 AI 員工配置"></a>二、員工分工：你的三種 AI 員工配置</h2><p>Karpathy 在演講裡有強調 agentic engineering 的 agentic 二字 — 在 2026 年的現在，你不再是親手寫 code 的工程師，而是 <strong>管理一群 AI 員工的監督者</strong>。Claude Code 在 2025-2026 演進出三種員工配置，每一種解決不同的問題。</p><h3 id="2-1-Skill：注入主-agent-的標準流程包"><a href="#2-1-Skill：注入主-agent-的標準流程包" class="headerlink" title="2.1 Skill：注入主 agent 的標準流程包"></a>2.1 Skill：注入主 agent 的標準流程包</h3><p>Skill 是放在 <code>.claude/skills/&lt;name&gt;/SKILL.md</code> 的 markdown 指令包，當 Claude 偵測到任務匹配 skill 的描述就會自動 load 進主 context。Skill 跟主 agent <strong>共用 context、共用模型</strong>，適合重複會做的標準動作。</p><h4 id="Skill-範本（API-慣例）"><a href="#Skill-範本（API-慣例）" class="headerlink" title="Skill 範本（API 慣例）"></a>Skill 範本（API 慣例）</h4><p><code>.claude/skills/api-conventions/SKILL.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: api-conventions<br><span class="hljs-section">description: REST API design conventions for this project</span><br><span class="hljs-section">---</span><br><br><span class="hljs-section"># API Conventions</span><br><br><span class="hljs-bullet">-</span> URL 路徑用 kebab-case：/api/user-profiles<br><span class="hljs-bullet">-</span> JSON property 用 camelCase：userId, createdAt<br><span class="hljs-bullet">-</span> list endpoint 一律支援分頁：?page=1&amp;pageSize=20<br><span class="hljs-bullet">-</span> API 版本放在 URL 路徑：/v1/、/v2/<br><span class="hljs-bullet">-</span> 錯誤回應格式：&#123; code, message, details? &#125;<br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>想了解 Skill 完整機制，可以看我之前寫的 <a href="https://israynotarray.com/ai/20260120/4189755271/">比 MCP 更省 Token 的 Agent Skills</a>。</p></blockquote><h3 id="2-2-Sub-agent：獨立-context、獨立權限的專職員工"><a href="#2-2-Sub-agent：獨立-context、獨立權限的專職員工" class="headerlink" title="2.2 Sub-agent：獨立 context、獨立權限的專職員工"></a>2.2 Sub-agent：獨立 context、獨立權限的專職員工</h3><p>Sub-agent 跟 Skill 最大的差別是：sub-agent 有 <strong>獨立的 context window、獨立的 system prompt、獨立的 tool 權限，甚至可以跑不同的模型</strong>。Claude 主 agent 把任務派給 sub-agent 之後，sub-agent 在自己的 context 裡做事，做完只回傳摘要給主 agent，主 agent 的 context 完全不會被汙染。</p><p>放在 <code>.claude/agents/&lt;name&gt;.md</code>：</p><h4 id="Sub-agent-範本（security-reviewer）"><a href="#Sub-agent-範本（security-reviewer）" class="headerlink" title="Sub-agent 範本（security-reviewer）"></a>Sub-agent 範本（security-reviewer）</h4><p><code>.claude/agents/security-reviewer.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: security-reviewer<br>description: 替新寫的 code 做資安 review<br>tools: Read, Grep, Glob, Bash<br><span class="hljs-section">model: opus</span><br><span class="hljs-section">---</span><br><br>你是資深資安工程師。Review code 時關注：<br><span class="hljs-bullet">-</span> Injection 漏洞（SQL、XSS、command injection）<br><span class="hljs-bullet">-</span> 認證跟授權瑕疵<br><span class="hljs-bullet">-</span> code 裡的 secrets / credentials<br><span class="hljs-bullet">-</span> 敏感資料處理<br><br>請給出具體的行號跟修法建議。<br></code></pre></td></tr></table></figure><h4 id="Sub-agent-範本（test-writer）"><a href="#Sub-agent-範本（test-writer）" class="headerlink" title="Sub-agent 範本（test-writer）"></a>Sub-agent 範本（test-writer）</h4><p><code>.claude/agents/test-writer.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: test-writer<br>description: 替剛寫好的 composable / service 補對應的單元測試<br>tools: Read, Edit, Bash<br><span class="hljs-section">model: sonnet</span><br><span class="hljs-section">---</span><br><br>你是專門寫單元測試的 AI engineer。<br><br>收到任務時：<br><span class="hljs-bullet">1.</span> 先讀目標檔案、理解函式 / composable 的行為<br><span class="hljs-bullet">2.</span> 列出 happy path + edge case + error case 三類測試<br><span class="hljs-bullet">3.</span> 用 Vitest 寫成 .test.ts 檔案<br><span class="hljs-bullet">4.</span> 跑 pnpm test 確認全綠<br><span class="hljs-bullet">5.</span> 若有 case 沒過，回報失敗 case 給主 agent 決定怎麼處理<br></code></pre></td></tr></table></figure><h4 id="Sub-agent-範本（ui-engineer）"><a href="#Sub-agent-範本（ui-engineer）" class="headerlink" title="Sub-agent 範本（ui-engineer）"></a>Sub-agent 範本（ui-engineer）</h4><p><code>.claude/agents/ui-engineer.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: ui-engineer<br>description: 處理 Tailwind class、元件樣式、design system 相關修改<br>tools: Read, Edit, Glob<br><span class="hljs-section">model: sonnet</span><br><span class="hljs-section">---</span><br><br>你是專門處理 UI/UX 的 frontend engineer。修改畫面樣式時遵守：<br><br><span class="hljs-bullet">-</span> filled input 不要再加 border<br><span class="hljs-bullet">-</span> focus ring 走 design system token，不要 hardcode 顏色<br><span class="hljs-bullet">-</span> touch target 至少 44px（包含按鈕、icon button、可點區塊）<br><span class="hljs-bullet">-</span> 間距用 design system 級距（mt-2 / mt-4 / mt-6 / mt-8），不要亂塞 mt-5 mb-3 沒邏輯的數字<br><span class="hljs-bullet">-</span> 顏色用 design system color token，不要 hardcode hex 色碼<br><span class="hljs-bullet">-</span> 不要用 !important 覆蓋既有樣式 — 找到根本原因改正確的層級<br><span class="hljs-bullet">-</span> RWD 優先寫 mobile，再用 md:、lg: 加大版斷點<br><br>收到任務時：<br><span class="hljs-bullet">1.</span> 先讀目標元件、確認既有 pattern 跟 token 用法<br><span class="hljs-bullet">2.</span> 修改 Tailwind class（不寫自訂 CSS 除非真的必要）<br><span class="hljs-bullet">3.</span> 改完後對照範例畫面 / Figma（如果有的話）確認視覺一致<br></code></pre></td></tr></table></figure><h4 id="什麼時候該選-Skill、什麼時候該選-Sub-agent？"><a href="#什麼時候該選-Skill、什麼時候該選-Sub-agent？" class="headerlink" title="什麼時候該選 Skill、什麼時候該選 Sub-agent？"></a>什麼時候該選 Skill、什麼時候該選 Sub-agent？</h4><div class="table-wrapper"><table><thead><tr><th>情境</th><th>選 Skill</th><th>選 Sub-agent</th></tr></thead><tbody><tr><td>重複會做的標準動作（API 慣例、commit 規範）</td><td>✓</td><td></td></tr><tr><td>需要獨立 context 不汙染主對話（深度搜尋、code review）</td><td></td><td>✓</td></tr><tr><td>要跑不同模型省錢（主 Sonnet + sub Haiku）</td><td></td><td>✓</td></tr><tr><td>要限制工具權限（reviewer 只能讀不能寫）</td><td></td><td>✓</td></tr><tr><td>想要 AI 自動偵測就用、不要手動觸發</td><td>✓</td><td></td></tr><tr><td>多步驟、有決策點的 workflow</td><td></td><td>✓</td></tr></tbody></table></div><h3 id="2-3-Agent-Teams：跨-session-平行的進階模式"><a href="#2-3-Agent-Teams：跨-session-平行的進階模式" class="headerlink" title="2.3 Agent Teams：跨 session 平行的進階模式"></a>2.3 Agent Teams：跨 session 平行的進階模式</h3><p>Agent Teams 是 2026 年新出的進階模式 — 多個 Claude 跑在獨立 worktree、透過 git 協調工作，每個 agent 認領自己的任務、自己 merge、自己解衝突。實務經驗大致是 3-5 個 parallel agents 算是 sweet spot，超過 10 個之後成本提高但效益不一定增加。</p><p>這個模式對新手過於複雜，可以先掌握 Skill + Sub-agent 兩種就好，等到專案大到一個人 + 一個主 Claude 跑不動的時候再考慮。</p><h2 id="三、開發紀律：三條每天都要堅持的鐵則"><a href="#三、開發紀律：三條每天都要堅持的鐵則" class="headerlink" title="三、開發紀律：三條每天都要堅持的鐵則"></a>三、開發紀律：三條每天都要堅持的鐵則</h2><p>進入實作階段之後，三條紀律是每天都要堅持的，每一條都是直接接住 AI 失誤的網。</p><h3 id="3-1-改-code-同一輪對話就把測試補上"><a href="#3-1-改-code-同一輪對話就把測試補上" class="headerlink" title="3.1 改 code 同一輪對話就把測試補上"></a>3.1 改 code 同一輪對話就把測試補上</h3><p>這條規則最常拿出來提醒 AI：每次改 code 都要同一輪對話就把測試補上，不要拖到下次再說「再開一個 task 統一補」。</p><p>為什麼這麼堅持？因為 AI 開發有個很容易踩的雷，就是改 A 壞 B。你今天叫它調整某個 composable 的邏輯，它跑出來的 code 表面上看起來都對，可是其實已經 side effect 改到別的地方了，你眼睛看不出來。而測試就是接住這種 side effect 的網。</p><p>那為什麼要強調同一輪呢？因為事後補的測試很容易變成為了讓 code pass 的測試，而不是真的在驗證行為的測試。同一輪補的時候，AI 寫實作跟寫測試的上下文是連在一起的，需求、邏輯、case 三件事都還活著；如果你拖到下一個 task 才補，那時候上下文已經漂移，AI 補出來的 case 通常只能蓋到表面，蓋不到真正的 edge case。</p><h4 id="補測試提示範本"><a href="#補測試提示範本" class="headerlink" title="補測試提示範本"></a>補測試提示範本</h4><p>開工前 &#x2F; 每次新功能前直接跟 AI 說：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs text">這個任務每改一個檔案，同一輪就把對應的測試補上來。<br>補測試要列為跟實作並列的子任務（不是放在實作下面的 sub-task），<br>這樣才不會做完實作就漏掉。<br>最後跑 pnpm test 確認全綠才算這個 task 結束。<br></code></pre></td></tr></table></figure><h3 id="3-2-動-UI-一律切給專門的-Skill-或-Sub-agent"><a href="#3-2-動-UI-一律切給專門的-Skill-或-Sub-agent" class="headerlink" title="3.2 動 UI 一律切給專門的 Skill 或 Sub-agent"></a>3.2 動 UI 一律切給專門的 Skill 或 Sub-agent</h3><p>只要這次修改牽涉到 UI&#x2F;UX（畫面排版、元件樣式、Tailwind class 調整等等），都直接呼叫專門處理 UI 的員工去做，而不是讓主 agent 自己亂改。</p><p>為什麼這樣分？</p><p>簡單來講就是術業有專攻。主 agent 在做架構決策、寫邏輯的時候很強，可是一旦動到 UI&#x2F;UX，它常常會犯一些通病：例如用一堆 <code>!important</code> 覆蓋既有樣式、配色完全沒有 design system 概念、間距亂塞 <code>mt-5 mb-3</code> 沒邏輯。</p><p>UI 相關的員工通常會內建 design system 知識、配色規範、可用性原則，它知道 filled input 不要再加 border、focus ring 要走 token、touch target 至少 44px 這些常識。</p><h4 id="UI-切員工提示範本"><a href="#UI-切員工提示範本" class="headerlink" title="UI 切員工提示範本"></a>UI 切員工提示範本</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs text">這次要動 [元件名 / 畫面區塊]，目標是 [描述要達成的效果]。<br>請呼叫 UI 專門的員工處理（前面 2.2 的 ui-engineer sub-agent，<br>or 如果有裝 plugin 例如 ui-ux-pro-max 也可以呼叫那個）。<br>主 agent 只負責決定動哪個元件、想要什麼方向，<br>實際改 Tailwind class 那一刻交給專門員工接手。<br></code></pre></td></tr></table></figure><h3 id="3-3-一個邏輯改動就一個-commit，不要堆成大塊"><a href="#3-3-一個邏輯改動就一個-commit，不要堆成大塊" class="headerlink" title="3.3 一個邏輯改動就一個 commit，不要堆成大塊"></a>3.3 一個邏輯改動就一個 commit，不要堆成大塊</h3><p>第三條是 commit 紀律。AI 如果沒有明確規則，常常會一次改十幾個檔案塞進一個 commit，等到後面某一段需要 revert 的時候，發現整包綁在一起，根本沒辦法乾淨地拆出來。</p><p>所以規則一定要明文化：<strong>一個邏輯改動 &#x3D; 一個 commit</strong>。例如：</p><ul><li>補 A 元件的測試 → 一個 commit</li><li>修 B 路由的 bug → 另一個 commit</li><li>重構 C 共用邏輯 → 另一個 commit</li></ul><p>這樣未來要 revert 任何一段都不會牽動其他工作，搭配第五段會講的回滾策略，補救成本可以壓到最低。</p><h4 id="commit-message-範本（Conventional-Commits）"><a href="#commit-message-範本（Conventional-Commits）" class="headerlink" title="commit message 範本（Conventional Commits）"></a>commit message 範本（Conventional Commits）</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">feat: 新增 A 功能<br>fix: 修 B 元件的 router bug<br>test: 補 C composable 的測試<br>refactor: 抽出 D 共用邏輯<br>docs: 更新 ROADMAP.md<br>chore: 升級套件版本<br></code></pre></td></tr></table></figure><h4 id="AI-commit-切點提示範本"><a href="#AI-commit-切點提示範本" class="headerlink" title="AI commit 切點提示範本"></a>AI commit 切點提示範本</h4><p>開工前跟 AI 說：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">commit 切點規則：一個邏輯改動就一個 commit。<br>- 不要把實作 + 測試 + 修 bug 堆在同一個 commit<br>- 補測試跟實作要拆開 commit（test: 跟 feat: 各一個）<br>- 重構獨立 commit、不混進其他改動<br>commit message 用 Conventional Commits 格式<br>（feat / fix / test / refactor / docs / chore）。<br></code></pre></td></tr></table></figure><h2 id="四、收尾驗證：三層安全網接住-AI-失誤"><a href="#四、收尾驗證：三層安全網接住-AI-失誤" class="headerlink" title="四、收尾驗證：三層安全網接住 AI 失誤"></a>四、收尾驗證：三層安全網接住 AI 失誤</h2><p>寫完 code 不等於做完，收尾階段有三層安全網要過，每一層接的失誤類型都不一樣。</p><h3 id="4-1-第一層：typecheck-test-build-三段驗證"><a href="#4-1-第一層：typecheck-test-build-三段驗證" class="headerlink" title="4.1 第一層：typecheck + test + build 三段驗證"></a>4.1 第一層：typecheck + test + build 三段驗證</h3><p>每次任務告一段落要交付的時候，固定請 AI 跑三段驗證：</p><ol><li><code>pnpm typecheck</code>（or <code>tsc --noEmit</code>）— 確認型別都過得了</li><li><code>pnpm test</code> — 確認既有測試全綠</li><li><code>pnpm build</code> — 確認可以打包出乾淨的產物</li></ol><p>這三段抓的是不同層的問題：</p><ul><li><strong>typecheck</strong> 抓型別錯誤，例如 AI 把 <code>User</code> 物件傳到一個吃 <code>string</code> 的 function</li><li><strong>test</strong> 抓行為錯誤，例如 AI 改了一個 function 結果該回傳 true 的 case 變成 false</li><li><strong>build</strong> 抓打包之後才會爆的錯誤，例如某個 import 路徑 dev mode 跑得起來、production build 卻爆掉</li></ul><p>只跑其中一段是不夠的，這三段加在一起才是相對完整的安全網。</p><h4 id="交付前-checklist-範本"><a href="#交付前-checklist-範本" class="headerlink" title="交付前 checklist 範本"></a>交付前 checklist 範本</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs text">- [ ] pnpm typecheck（or tsc --noEmit）通過<br>- [ ] pnpm test 全綠<br>- [ ] pnpm build 成功<br>- [ ] 自己跑一次實際功能、用瀏覽器戳一下畫面<br>- [ ] git status 沒有意外的檔案改動<br>- [ ] commit message 符合 Conventional Commits 格式<br></code></pre></td></tr></table></figure><h3 id="4-2-第二層：Writer-Reviewer-雙-Claude-模式"><a href="#4-2-第二層：Writer-Reviewer-雙-Claude-模式" class="headerlink" title="4.2 第二層：Writer &#x2F; Reviewer 雙 Claude 模式"></a>4.2 第二層：Writer &#x2F; Reviewer 雙 Claude 模式</h3><p>這是 Anthropic 官方在 best practices 推薦的進階工法。簡單講就是 <strong>開兩個 Claude session，一個負責寫、一個負責 review</strong>，因為 Claude review 自己剛寫的 code 會有 confirmation bias、不容易抓到問題；換一個 fresh context 的 Claude 來 review，能抓到更多 edge case。</p><p>實作方式：</p><div class="table-wrapper"><table><thead><tr><th>Session A（Writer）</th><th>Session B（Reviewer）</th></tr></thead><tbody><tr><td><code>實作 A 元件的 rate limiter middleware</code></td><td></td></tr><tr><td></td><td><code>Review @src/middleware/rateLimiter.ts。找出 edge case、race condition、跟既有 middleware pattern 的不一致處</code></td></tr><tr><td><code>根據以上 review 意見修正：[Session B 輸出]</code></td><td></td></tr></tbody></table></div><p>更進階的做法是把 Reviewer 設成一個 sub-agent（像前面 2.2 的 security-reviewer），這樣每次寫完都可以一鍵呼叫 review，不用手動開新 session。</p><h3 id="4-3-第三層：AI-引用的版本號、套件名、API-自己去官方對一次"><a href="#4-3-第三層：AI-引用的版本號、套件名、API-自己去官方對一次" class="headerlink" title="4.3 第三層：AI 引用的版本號、套件名、API 自己去官方對一次"></a>4.3 第三層：AI 引用的版本號、套件名、API 自己去官方對一次</h3><p>第三層安全網是抗 AI 過期記憶用的：每次 AI 在寫 code 時引用了套件版本、API 用法、雲端服務後台這類更新很快的東西，都自己打開官方文件對照一遍，不要直接讓 AI 的記憶 ship 進專案。</p><p>為什麼這麼龜毛？因為 AI 的 training data 是有截止日期的，特別是套件版本、API 規格、雲端後台 UI 這些東西更新非常快，AI 用記憶答出來的常常已經是好幾個月前的版本了。例如它可能寫某個套件的 API，可是其實在新版裡早就改名了；或者它告訴你某個雲端服務在後台點 X 設定，可是後台介面早改版了。</p><h4 id="AI-引用必查清單"><a href="#AI-引用必查清單" class="headerlink" title="AI 引用必查清單"></a>AI 引用必查清單</h4><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs text">看到 AI 寫的 code 出現以下引用，自己打開官方文件對一次：<br><br>[必查]<br>- 套件 API 用法（特別是 major 版本可能改過）<br>- 雲端服務後台 UI / 設定步驟（AWS、GCP、Cloudflare、Firebase console 等）<br>- 環境變數 / 設定檔欄位名<br>- 框架 / SDK 版本對應的語法（Vue 3 vs Vue 2、Nuxt 3 vs 2 等）<br>- 第三方服務 API 規格<br><br>[不用查]<br>- 純語法（JS 內建陣列方法、字串處理等）<br>- 純邏輯（演算法、純函式）<br>- 你自己專案內的 module 跟 type<br></code></pre></td></tr></table></figure><h2 id="五、失誤救援：回滾決策表"><a href="#五、失誤救援：回滾決策表" class="headerlink" title="五、失誤救援：回滾決策表"></a>五、失誤救援：回滾決策表</h2><p>實戰中一定會遇到 AI 改壞 code 的時候，這時候最浪費時間的反應是在錯的方向上繼續加東西想 fix — 越救越大坑。所以一定要有明確的回滾判斷尺。</p><p>Claude Code 在 2026 年加了一個叫 <code>/rewind</code> 的功能（or <code>Esc + Esc</code>），可以回到任一個 checkpoint，連 Claude 改過的 code state 都會還原。這個是第一層救援工具。</p><blockquote><p><strong>Note</strong><br>一個重要 caveat — Anthropic 官方明確說 checkpoint <strong>只追蹤 Claude 改過的檔案，不追蹤 Bash command 造成的檔案變更</strong>（例如 <code>npm install</code> 改的 node_modules、<code>rm</code> 刪掉的東西、build 產出的檔案）。這部分要靠 git 才能 recover，所以 git commit 紀律不能省。</p></blockquote><p>判斷尺一句話：<strong>壞掉的是執行細節？還是規劃方向？</strong></p><h3 id="回滾決策表"><a href="#回滾決策表" class="headerlink" title="回滾決策表"></a>回滾決策表</h3><div class="table-wrapper"><table><thead><tr><th>情境</th><th>動作</th></tr></thead><tbody><tr><td>AI 剛剛改的東西 review 完不對</td><td><code>/rewind</code>（or <code>Esc + Esc</code>）回上個 checkpoint</td></tr><tr><td>單一檔案實作細節錯，整體方向 OK</td><td><code>git restore &lt;檔案&gt;</code> 重做</td></tr><tr><td>多個相關檔案壞掉，方向 OK</td><td><code>git restore</code> 多個檔案、重做</td></tr><tr><td>AI 套錯架構 &#x2F; 誤解需求</td><td>砍 plan 重寫，從規劃階段重來</td></tr><tr><td>補的測試蓋不到實作意圖</td><td>砍 test、重看 plan、重補測試</td></tr><tr><td>AI 一次改太多看不出哪邊是好的</td><td>先 <code>git status</code> 確認 + <code>git stash</code> 保險，再 <code>git restore .</code> 全部回滾（這就是為什麼前面強調小 commit）</td></tr><tr><td>跑 typecheck &#x2F; test 大規模爆掉</td><td>高機率是方向錯，先停手檢查 plan</td></tr></tbody></table></div><p>從這張表可以看出來，回滾不是失敗、是流程的一部分，把它當成抗 AI 失誤的工具來用，整個開發節奏會穩很多。</p><h2 id="六、Memory-系統：兩層架構-正向-pattern-範本"><a href="#六、Memory-系統：兩層架構-正向-pattern-範本" class="headerlink" title="六、Memory 系統：兩層架構 + 正向 pattern 範本"></a>六、Memory 系統：兩層架構 + 正向 pattern 範本</h2><p>最後一個環節是 memory 系統的設計，這個環節做好之後，前面所有的紀律都會變成 AI 主動會做的事，不用每次都手動提醒。</p><p>memory 要分兩層設計：</p><h3 id="6-1-全域層：用-claude-CLAUDE-md-或-claude-rules-放跨專案通用規則"><a href="#6-1-全域層：用-claude-CLAUDE-md-或-claude-rules-放跨專案通用規則" class="headerlink" title="6.1 全域層：用 ~/.claude/CLAUDE.md 或 ~/.claude/rules/ 放跨專案通用規則"></a>6.1 全域層：用 <code>~/.claude/CLAUDE.md</code> 或 <code>~/.claude/rules/</code> 放跨專案通用規則</h3><p>跨專案會通用、你明確想讓 Claude 每次都遵守的規則（套件管理慣例、補測試紀律、不過度設計、不擅自擴大範圍等），按 <a href="https://code.claude.com/docs/en/memory">Anthropic 官方文件</a>有兩個位置可以放：</p><ul><li><strong><code>~/.claude/CLAUDE.md</code></strong> — 整份單一檔案、每次 session 自動載入，適合放 5-10 條核心原則</li><li><strong><code>~/.claude/rules/&lt;topic&gt;.md</code></strong> — 按主題拆檔案（一條 rule 一個檔），也是每次 session 自動載入，好處是可以用 YAML frontmatter <code>paths:</code> 欄位做 path-scoped rules（例如某條 rule 只在處理 <code>src/api/**</code> 時才載入）</li></ul><p>新專案直接共用不用重教。</p><blockquote><p><strong>Note</strong><br>順帶釐清一個常見誤會 — Claude Code 還有一個叫 <strong>Auto memory</strong> 的功能，是 Claude 自己累積的（不是你手寫的），存在 <code>~/.claude/projects/&lt;project&gt;/memory/</code>，<strong>按專案分而不是全域</strong>。本章談的「全域層規則」指的是你手動寫給 AI 看的規範，跟 Auto memory 是不同的東西。Auto memory 完整機制可以看我之前寫的 <a href="https://israynotarray.com/ai/20260330/2896397295/">Claude Code Auto Memory 是什麼？</a>。</p></blockquote><h4 id="全域規則範本（pnpm-慣例）"><a href="#全域規則範本（pnpm-慣例）" class="headerlink" title="全域規則範本（pnpm 慣例）"></a>全域規則範本（pnpm 慣例）</h4><p><code>~/.claude/rules/pnpm.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: 套件管理用 pnpm<br>description: JS/TS 專案一律用 pnpm 不要用 npm<br><span class="hljs-section">type: feedback</span><br><span class="hljs-section">---</span><br><br>JS/TS 專案預設用 pnpm，不要用 npm。<br><br><span class="hljs-strong">**Why:**</span> lock file 慣例統一、硬碟空間節省、monorepo 友善。<br><br><span class="hljs-strong">**How to apply:**</span> 看到 package.json 預設下 pnpm install / pnpm run xxx。<br>如果專案有 package-lock.json 但沒有 pnpm-lock.yaml，<br>先問使用者要不要遷移到 pnpm。<br></code></pre></td></tr></table></figure><h4 id="全域規則範本（補測試紀律）"><a href="#全域規則範本（補測試紀律）" class="headerlink" title="全域規則範本（補測試紀律）"></a>全域規則範本（補測試紀律）</h4><p><code>~/.claude/rules/testing.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: 改 code 同一輪就補測試<br>description: 每次程式異動都要在同一輪對話補上對應的測試<br><span class="hljs-section">type: feedback</span><br><span class="hljs-section">---</span><br><br>每一次新增或修改程式碼，都必須在同一次工作中補上對應的測試。<br><br><span class="hljs-strong">**Why:**</span> 事後補的測試容易變成「為了 pass 而 pass」，蓋不到 edge case。<br><br><span class="hljs-strong">**How to apply:**</span><br><span class="hljs-bullet">-</span> 寫新 composable / service / 純函式時，同一次任務內加 <span class="hljs-emphasis">*.test.ts</span><br><span class="hljs-emphasis">- 改既有檔案造成行為變化時，擴充既有測試 or 新增測試</span><br><span class="hljs-emphasis">- 純函式優先抽出來測（比 mock 容易得多）</span><br><span class="hljs-emphasis">- 跑 pnpm test 全綠才算完成</span><br><span class="hljs-emphasis">- 補測試列為跟實作並列的子任務</span><br></code></pre></td></tr></table></figure><h4 id="全域規則範本（嚴守交付範圍）"><a href="#全域規則範本（嚴守交付範圍）" class="headerlink" title="全域規則範本（嚴守交付範圍）"></a>全域規則範本（嚴守交付範圍）</h4><p><code>~/.claude/rules/minimal-scope.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: 嚴守指定交付範圍<br>description: 使用者說只要 X 時不要順手附帶其他檔案<br><span class="hljs-section">type: feedback</span><br><span class="hljs-section">---</span><br><br>使用者交付任務時明確說只改 X 就只改 X，不要順手改其他檔案。<br><br><span class="hljs-strong">**Why:**</span> 擴大範圍會引入未授權的變更，使用者花更多時間 review，<br>甚至引入 regression。<br><br><span class="hljs-strong">**How to apply:**</span><br><span class="hljs-bullet">-</span> 每次任務開頭明確列出範圍<br><span class="hljs-bullet">-</span> 超出範圍的發現先記下來再問，不要先動<br></code></pre></td></tr></table></figure><h3 id="6-2-專案-AGENTS-md-CLAUDE-md-放專案特定-rule"><a href="#6-2-專案-AGENTS-md-CLAUDE-md-放專案特定-rule" class="headerlink" title="6.2 專案 AGENTS.md &#x2F; CLAUDE.md 放專案特定 rule"></a>6.2 專案 AGENTS.md &#x2F; CLAUDE.md 放專案特定 rule</h3><p>專案層放這個專案才用得到的設計決策、產品邊界、踩過的雷。判斷尺：<strong>這條 rule 換到別的專案還會用嗎？</strong> 會用就放全域、只在這專案用就放專案層。</p><p>如果你想了解 Claude Code memory 完整機制，可以參考我之前寫的 <a href="https://israynotarray.com/ai/20260330/2896397295/">Claude Code Auto Memory 是什麼？自動記憶功能完整教學</a>。</p><h3 id="6-3-寫-rule-的時候，正向-pattern-比禁令式更有用"><a href="#6-3-寫-rule-的時候，正向-pattern-比禁令式更有用" class="headerlink" title="6.3 寫 rule 的時候，正向 pattern 比禁令式更有用"></a>6.3 寫 rule 的時候，正向 pattern 比禁令式更有用</h3><p>最後一個關鍵：寫 rule 的時候要刻意混搭禁令式跟正向 pattern，光寫禁令是不夠的。</p><div class="table-wrapper"><table><thead><tr><th>類型</th><th>例子</th><th>效果</th></tr></thead><tbody><tr><td>禁令式</td><td>不要列大綱開場、不要用 emoji、不要過度設計</td><td>AI 會避開，可是不知道對的長怎樣</td></tr><tr><td>正向 pattern</td><td>成功的 plan 文件應該包含目標、任務拆解、不做的事、驗收條件四塊</td><td>AI 知道要主動產出什麼</td></tr></tbody></table></div><p>從這張表可以看出來，禁令式只能擋住雷、無法主動引導正確的產出，所以要搭配正向 pattern 一起用。</p><h4 id="正向-pattern-範本"><a href="#正向-pattern-範本" class="headerlink" title="正向 pattern 範本"></a>正向 pattern 範本</h4><p><code>~/.claude/rules/plan-template.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br></pre></td><td class="code"><pre><code class="hljs markdown">---<br>name: 成功 plan 文件的範本<br>description: AI 寫 plan 文件時應該參考的正向結構<br><span class="hljs-section">type: feedback</span><br><span class="hljs-section">---</span><br><br>寫 plan 文件時，應該包含以下四塊：<br><br><span class="hljs-bullet">1.</span> <span class="hljs-strong">**目標**</span>：一兩句話寫清楚要解決什麼問題、給誰用<br><span class="hljs-bullet">2.</span> <span class="hljs-strong">**任務拆解**</span>：每一個子任務獨立列出來，含驗收條件<br><span class="hljs-bullet">3.</span> <span class="hljs-strong">**不做的事**</span>：明確排除（很重要，AI 容易越界）<br><span class="hljs-bullet">4.</span> <span class="hljs-strong">**開發紀律備忘**</span>：套件管理、測試、commit 切點等<br><br>範例參考專案的 docs/ROADMAP.md。<br><br><span class="hljs-strong">**Why:**</span> 禁令式 rule 只能擋住雷，正向 pattern 才能引導出對的東西。<br><br><span class="hljs-strong">**How to apply:**</span> 收到寫 plan、規劃功能、整理 ROADMAP 這類任務時，<br>先讀過這份結構再動筆。<br></code></pre></td></tr></table></figure><p>兩種一起用最有效：禁令防雷、正向引導。</p><h2 id="實戰案例：個人電商-MVP-從-Spec-到-Ship"><a href="#實戰案例：個人電商-MVP-從-Spec-到-Ship" class="headerlink" title="實戰案例：個人電商 MVP 從 Spec 到 Ship"></a>實戰案例：個人電商 MVP 從 Spec 到 Ship</h2><p>講完六大環節，最後用一個比較貼近真實工作的案例走一遍完整流程：<strong>做一個個人小型電商 MVP</strong>，賣自製商品、要能收得到錢。</p><p>技術棧用 Nuxt 3 + Vue 3 + Tailwind + Pinia + Supabase（DB + Auth）+ Stripe（付款）+ Vercel（hosting）。這個 stack 是 2026 年小型電商 MVP 滿主流的組合 — 後端不用自己架、付款交給 Stripe、hosting 一鍵部署。</p><h3 id="Step-1-—-寫-CLAUDE-md（搭配-AGENTS-md）當專案憲法"><a href="#Step-1-—-寫-CLAUDE-md（搭配-AGENTS-md）當專案憲法" class="headerlink" title="Step 1 — 寫 CLAUDE.md（搭配 AGENTS.md）當專案憲法"></a>Step 1 — 寫 CLAUDE.md（搭配 AGENTS.md）當專案憲法</h3><p>開新專案第一件事是寫 CLAUDE.md（如果跨工具團隊也同步寫一份 AGENTS.md，CLAUDE.md 第一行 <code>@AGENTS.md</code> import 進來）。電商比一般工具多了 <strong>金錢 &#x2F; 安全 &#x2F; RLS</strong> 這幾個重點區塊，所以憲法裡要明文寫清楚：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br><span class="line">26</span><br><span class="line">27</span><br><span class="line">28</span><br><span class="line">29</span><br><span class="line">30</span><br><span class="line">31</span><br><span class="line">32</span><br><span class="line">33</span><br><span class="line">34</span><br><span class="line">35</span><br><span class="line">36</span><br><span class="line">37</span><br><span class="line">38</span><br><span class="line">39</span><br><span class="line">40</span><br><span class="line">41</span><br><span class="line">42</span><br><span class="line">43</span><br><span class="line">44</span><br><span class="line">45</span><br><span class="line">46</span><br><span class="line">47</span><br><span class="line">48</span><br><span class="line">49</span><br><span class="line">50</span><br><span class="line">51</span><br><span class="line">52</span><br><span class="line">53</span><br><span class="line">54</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># RaySelf Shop - Claude Code Instructions</span><br><br>@AGENTS.md<br><br><span class="hljs-section">## 開工前必讀</span><br>@docs/ROADMAP.md<br>@docs/tasks.md<br><br><span class="hljs-section">## Project Overview</span><br>個人賣自製商品的小型電商 MVP，目標 1 個月內上線收得到錢。<br><br><span class="hljs-section">## Tech Stack</span><br><span class="hljs-bullet">-</span> Frontend: Nuxt 3, Vue 3, Tailwind, Pinia<br><span class="hljs-bullet">-</span> Backend: Supabase（PostgreSQL + Auth + Storage）<br><span class="hljs-bullet">-</span> Payment: Stripe Checkout（Redirect 模式）<br><span class="hljs-bullet">-</span> Hosting: Vercel<br><span class="hljs-bullet">-</span> Package Manager: pnpm<br><br><span class="hljs-section">## Build &amp; Test</span><br><span class="hljs-bullet">-</span> 安裝：pnpm install<br><span class="hljs-bullet">-</span> 開發：pnpm dev<br><span class="hljs-bullet">-</span> 測試：pnpm test<br><span class="hljs-bullet">-</span> 型別檢查：pnpm typecheck<br><span class="hljs-bullet">-</span> 打包：pnpm build<br><br><span class="hljs-section">## Code Style</span><br><span class="hljs-bullet">-</span> Vue 3 Composition API + script setup<br><span class="hljs-bullet">-</span> composable 用 useXxx 命名（useCart、useProducts、useCheckout）<br><span class="hljs-bullet">-</span> API 呼叫抽到 composables/api/<span class="hljs-emphasis">*.ts</span><br><span class="hljs-emphasis">- 純函式（價格計算、運費計算）抽到 utils/</span><br><span class="hljs-emphasis">- server route 放 server/api/</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## Testing Rules</span><br><span class="hljs-emphasis">- 純函式（價格計算、運費、稅金、折扣）100% 覆蓋</span><br><span class="hljs-emphasis">- API composable 至少測 happy path + 1 個 error case</span><br><span class="hljs-emphasis">- 同一輪補測試，不可延後</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## Git Workflow</span><br><span class="hljs-emphasis">- commit message 用 Conventional Commits</span><br><span class="hljs-emphasis">- 一個邏輯改動一個 commit</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## Security（電商核心、不可妥協）</span><br><span class="hljs-emphasis">- <span class="hljs-strong">**金額一律以 server-side Supabase 查為準，絕對不信任前端傳來的價格**</span></span><br><span class="hljs-emphasis">- Stripe secret key 只在 server route 用，前端不可碰</span><br><span class="hljs-emphasis">- Supabase RLS 一律啟用，所有 table 設定權限規則</span><br><span class="hljs-emphasis">- Webhook signature 一定要驗證（防止偽造訂單）</span><br><span class="hljs-emphasis">- .env 不可 commit；.env.example 只放空白範本</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## 明確排除的事</span><br><span class="hljs-emphasis">- 不做使用者註冊登入（Week 1-3 範圍外，用 Stripe Checkout 的 email 收單）</span><br><span class="hljs-emphasis">- 不做商品上傳後台（先用 Supabase Studio 手動上架）</span><br><span class="hljs-emphasis">- 不做多商品變體（size / color）</span><br><span class="hljs-emphasis">- 不做退款流程</span><br><span class="hljs-emphasis">- 不做多語言</span><br></code></pre></td></tr></table></figure><h3 id="Step-2-—-寫-ROADMAP-md-切三個-Week"><a href="#Step-2-—-寫-ROADMAP-md-切三個-Week" class="headerlink" title="Step 2 — 寫 ROADMAP.md 切三個 Week"></a>Step 2 — 寫 ROADMAP.md 切三個 Week</h3><p><code>docs/ROADMAP.md</code>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br><span class="line">19</span><br><span class="line">20</span><br><span class="line">21</span><br><span class="line">22</span><br><span class="line">23</span><br><span class="line">24</span><br><span class="line">25</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># RaySelf Shop ROADMAP</span><br><br><span class="hljs-section">## 願景</span><br>讓個人創作者能用最低成本上線一個收得到錢的小型電商。<br><br><span class="hljs-section">## Week 1：商品展示（公開頁面）</span><br><span class="hljs-bullet">-</span> [ ] Supabase schema：products / orders / order<span class="hljs-emphasis">_items 三張表 + RLS policy</span><br><span class="hljs-emphasis">- [ ] 商品列表頁 `/`：抓 products、顯示卡片</span><br><span class="hljs-emphasis">- [ ] 商品詳情頁 `/products/[slug]`：完整圖文 + 加入購物車</span><br><span class="hljs-emphasis">- [ ] 購物車 store（Pinia + localStorage 同步）</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## Week 2：結帳 + 訂單寫入</span><br><span class="hljs-emphasis">- [ ] server route `/api/checkout` 建立 Stripe Checkout Session</span><br><span class="hljs-emphasis">- [ ] 結帳流程串接：購物車 → Stripe Redirect</span><br><span class="hljs-emphasis">- [ ] server route `/api/stripe-webhook` 收 webhook、寫單到 Supabase</span><br><span class="hljs-emphasis">- [ ] 訂單成功頁 `/success`、取消頁 `/cancel`</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## Week 3：簡易後台</span><br><span class="hljs-emphasis">- [ ] 後台訂單列表頁（用環境變數鎖訪問 or basic auth）</span><br><span class="hljs-emphasis">- [ ] 訂單狀態手動更新（pending / shipped / completed）</span><br><span class="hljs-emphasis"></span><br><span class="hljs-emphasis">## 明確排除的事</span><br><span class="hljs-emphasis">- 使用者註冊登入：之後再做</span><br><span class="hljs-emphasis">- 商品上傳後台：Week 1-3 用 Supabase Studio 手動上架</span><br><span class="hljs-emphasis">- 退款流程：之後再做</span><br></code></pre></td></tr></table></figure><h3 id="Step-3-—-開工前進-plan-mode-跟-AI-討論"><a href="#Step-3-—-開工前進-plan-mode-跟-AI-討論" class="headerlink" title="Step 3 — 開工前進 plan mode 跟 AI 討論"></a>Step 3 — 開工前進 plan mode 跟 AI 討論</h3><p>進 Claude Code，先進 plan mode：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">讀 CLAUDE.md（裡面已經 @import AGENTS.md 跟 docs/ROADMAP.md），了解專案脈絡。<br>我要開始做 Week 1 商品展示，請列出檔案結構、主要 composable / 純函式、<br>Supabase schema 跟 RLS policy 草稿。<br></code></pre></td></tr></table></figure><p>AI 大概會回類似這樣的計畫：</p><ul><li>Supabase schema：<ul><li><code>products</code>：id, slug, name, description, price_cents, image_url, stock</li><li><code>orders</code>：id, stripe_session_id, customer_email, total_cents, status, created_at</li><li><code>order_items</code>：id, order_id, product_id, quantity, price_cents_at_purchase</li></ul></li><li>程式碼結構：<ul><li><code>composables/api/useProducts.ts</code> — products fetch</li><li><code>composables/useCart.ts</code> — Pinia store + localStorage 同步</li><li><code>utils/cart.ts</code> — 純函式（小計、運費、總額計算）</li><li><code>pages/index.vue</code>、<code>pages/products/[slug].vue</code></li><li>對應 test 檔</li></ul></li><li>RLS policy：products 公開 read、orders 只有 server-side service_role 能 write</li></ul><p>確認 plan 之後請 AI 寫進 <code>docs/tasks.md</code>，然後離開 plan mode 開始實作。</p><h3 id="Step-4-—-實作-同一輪補測試"><a href="#Step-4-—-實作-同一輪補測試" class="headerlink" title="Step 4 — 實作 + 同一輪補測試"></a>Step 4 — 實作 + 同一輪補測試</h3><p>開工前先 prompt：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs text">每改一個檔案，同一輪就把對應的測試補上。<br>補測試列為跟實作並列的子任務。<br>完成一項就同步更新 docs/tasks.md。<br></code></pre></td></tr></table></figure><p>AI 會：</p><ol><li>寫 <code>utils/cart.ts</code>（純函式：計算小計、總額）→ 同 turn 寫 <code>utils/cart.test.ts</code> → 跑 pnpm test</li><li>寫 <code>composables/api/useProducts.ts</code> → 同 turn 寫對應 test（mock Supabase client）</li><li>寫 <code>composables/useCart.ts</code> → 同 turn 寫 test</li><li>寫 <code>pages/index.vue</code> + <code>pages/products/[slug].vue</code> UI → 呼叫 ui-engineer sub-agent（前面 2.2 的範本）處理 Tailwind class</li></ol><h3 id="Step-5-—-三段驗證-Writer-Reviewer-抓安全議題"><a href="#Step-5-—-三段驗證-Writer-Reviewer-抓安全議題" class="headerlink" title="Step 5 — 三段驗證 + Writer&#x2F;Reviewer 抓安全議題"></a>Step 5 — 三段驗證 + Writer&#x2F;Reviewer 抓安全議題</h3><p>實作完跑：</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs bash">pnpm typecheck  <span class="hljs-comment"># 型別過</span><br>pnpm <span class="hljs-built_in">test</span>       <span class="hljs-comment"># 全綠</span><br>pnpm build      <span class="hljs-comment"># 打包成功</span><br></code></pre></td></tr></table></figure><p>然後 <strong>這裡是電商案例最關鍵的一步</strong> — 開另一個 Claude session 當 Reviewer，<strong>特別 review 安全層</strong>：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs text">你是 sub-agent security-reviewer。<br>請 review 這個 Week 2 的結帳流程：<br>- @server/api/checkout.post.ts<br>- @composables/useCheckout.ts<br>- @server/api/stripe-webhook.post.ts<br><br>重點關注：<br>1. 金額是否從 server-side Supabase 查（不可信任前端 price）<br>2. Stripe webhook signature 是否有驗證<br>3. Supabase RLS 是否擋住未授權的 orders write<br>4. 客戶 email 等敏感資料是否有正確處理<br></code></pre></td></tr></table></figure><h3 id="Step-6-—-切-commit"><a href="#Step-6-—-切-commit" class="headerlink" title="Step 6 — 切 commit"></a>Step 6 — 切 commit</h3><p>照 Conventional Commits 一個邏輯一個 commit：</p><figure class="highlight text"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br></pre></td><td class="code"><pre><code class="hljs text">feat: Supabase schema + RLS policy（products / orders / order_items）<br>feat: 商品列表 API composable + 純函式 cart utils<br>test: 補 cart utils 跟 useProducts 的單元測試<br>feat: 商品列表頁 + 商品詳情頁 UI<br>feat: 購物車 Pinia store + localStorage 同步<br>test: 補 useCart 的單元測試<br>docs: 更新 ROADMAP，Week 1 商品展示完成<br></code></pre></td></tr></table></figure><p>未來如果 Week 1 的某段需要 revert，每個 commit 都是獨立邏輯改動，可以乾淨地拆出來。</p><h3 id="Step-7-—-踩到雷：前端竄改價格的安全雷"><a href="#Step-7-—-踩到雷：前端竄改價格的安全雷" class="headerlink" title="Step 7 — 踩到雷：前端竄改價格的安全雷"></a>Step 7 — 踩到雷：前端竄改價格的安全雷</h3><p>Week 2 串完 Stripe Checkout 之後跑 Writer&#x2F;Reviewer，Reviewer Claude 抓出一個關鍵問題：</p><blockquote><p><code>server/api/checkout.post.ts</code> 直接把前端購物車裡的 <code>price_cents</code> 拿來建立 Stripe Session，這個是經典的 client-side trust 漏洞 — 使用者可以打開 DevTools 改 localStorage，把 10000 元商品改成 1 元再付款。</p></blockquote><p>照回滾決策表判斷，這不是 fix 一行 code 就能解決的事：</p><div class="table-wrapper"><table><thead><tr><th>情境</th><th>動作</th></tr></thead><tbody><tr><td>不是個別欄位寫錯</td><td>✗ 不適用 git restore 單檔</td></tr><tr><td>是整個架構的信任邊界劃錯</td><td>✓ 砍 plan 重寫 Stripe 部分</td></tr></tbody></table></div><p>所以正確動作是 <strong>砍 plan 重寫 Stripe 結帳流程</strong>，新版的 plan：</p><ol><li><code>/api/checkout</code> 只收 <code>product_id</code> 跟 <code>quantity</code>，<strong>不收 price</strong></li><li>server-side 從 Supabase 查每個 product 的 <code>price_cents</code></li><li>用 server-side 算出來的金額建立 Stripe Checkout Session</li></ol><p>修完之後，<strong>把這條雷寫進 AGENTS.md 變成長期 rule</strong>：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section">## Security</span><br><span class="hljs-bullet">-</span> 金額一律以 server-side Supabase 查為準，絕對不信任前端傳來的價格<br><span class="hljs-bullet">-</span> 任何涉及金錢計算的 endpoint，必須以資料庫查出來的 source-of-truth 為基準<br><span class="hljs-bullet">-</span> 開新的 server route 涉及金額時，先檢查這條 rule<br></code></pre></td></tr></table></figure><p>同時把這條昇級到全域層的 <code>~/.claude/rules/</code>，因為這條 rule 換到任何電商 &#x2F; 訂閱 &#x2F; 任何涉及金錢的專案都適用：</p><figure class="highlight markdown"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br><span class="line">12</span><br><span class="line">13</span><br><span class="line">14</span><br><span class="line">15</span><br><span class="line">16</span><br><span class="line">17</span><br><span class="line">18</span><br></pre></td><td class="code"><pre><code class="hljs markdown"><span class="hljs-section"># ~/.claude/rules/money-server-side.md</span><br><br>---<br>name: 金錢一律 server-side<br>description: 任何涉及金錢的計算必須以 server-side 資料為準，不可信任 client<br><span class="hljs-section">type: feedback</span><br><span class="hljs-section">---</span><br><br>涉及金錢的 endpoint（結帳、儲值、訂閱、退款等）：<br>client 只送識別 ID（product<span class="hljs-emphasis">_id / plan_</span>id），server-side 自己查價格、自己算總額。<br><br><span class="hljs-strong">**Why:**</span> client-side 資料可以被 DevTools / localStorage 隨意竄改，<br>任何 client 傳來的金額都是不可信的。<br><br><span class="hljs-strong">**How to apply:**</span><br><span class="hljs-bullet">-</span> 收 client 的 endpoint signature 不可包含 price / amount / total 欄位<br><span class="hljs-bullet">-</span> server-side 一定要從 DB 查 source-of-truth 金額<br><span class="hljs-bullet">-</span> Stripe / 金流 API 的金額參數只能用 server-side 算出的結果<br></code></pre></td></tr></table></figure><p>這樣同樣的雷大幅降低在 Week 3 後台 &#x2F; 之後的退款流程 &#x2F; 之後的訂閱機制再踩第二次的機率。這就是踩坑封 SOP 這個元 pattern 的完整示範：<strong>從 review 抓到 → 砍 plan 重寫 → 寫進 AGENTS.md + 全域層 <code>~/.claude/rules/</code> → 大幅降低再次踩雷的機率</strong>（不是百分百免疫，AI 偶爾還是會漏 rule，可是頻率會大幅降低）。</p><h2 id="這套-SOP-的局限：什麼時候不要用、什麼時候會失靈"><a href="#這套-SOP-的局限：什麼時候不要用、什麼時候會失靈" class="headerlink" title="這套 SOP 的局限：什麼時候不要用、什麼時候會失靈"></a>這套 SOP 的局限：什麼時候不要用、什麼時候會失靈</h2><p>寫到這邊也要誠實講局限 — 這套 SOP 不是萬靈丹，跑過幾輪你會碰到下面這幾個天敵：</p><ol><li><strong>小專案 overkill</strong>：週末做個 demo、寫個一次性腳本、PoC 試水溫，整套 CLAUDE.md + AGENTS.md + ROADMAP + tasks.md + sub-agent 配置維護起來太重，這時候直接 vibe coding 就好，前面那張對照表的 <code>適用情境</code> 欄已經有講</li><li><strong>Token 成本不便宜</strong>：Writer&#x2F;Reviewer 等於每個 review 都跑兩次 Claude、sub-agent 每次叫起來都是 fresh context，跑 Opus + 重度使用很容易月費三位數美金跳起來。比較省的配置是重的任務（plan、實作）用 Opus、輕的任務（Reviewer、test-writer、ui-engineer 這類 sub-agent）切 Sonnet or Haiku</li><li><strong>Context 衰減是這套 SOP 最大的天敵</strong>：對話超過 100K+ token 之後 AI 開始漏 rule、忘記 CLAUDE.md 寫的紀律、tasks.md 也常常忘記同步。要養成定期 <code>/compact</code> + 該開新 session 就開的習慣，不要讓單一對話跑太久</li><li><strong>Sub-agent 不會自動穩定 routing</strong>：Claude 自動偵測這個任務該不該叫 ui-engineer 這個 routing 不是 100% 準，跑久了主 agent 會自己接手亂改 Tailwind。穩一點的做法是 prompt 內明確指定要呼叫的 sub-agent 名稱，不要全靠自動觸發</li><li><strong>memory 不是越多越好</strong>：累積到 20-30 條之後 AI 不一定每條都遵守，重要的 rule 跟次要的 rule 混在一起會稀釋。定期 prune、把過時或低優先的條目刪掉，比一直加新的有用</li></ol><p>這幾個局限不是叫你別用，而是讓你照做之前有合理期待。整套 SOP 提高的是穩定性、不是保證穩定 — 真正穩的還是底層的 Git、測試、typecheck、build、人工 review，AI 只是讓你跑得更快。</p><h2 id="結語：把這套變成你自己的-SOP"><a href="#結語：把這套變成你自己的-SOP" class="headerlink" title="結語：把這套變成你自己的 SOP"></a>結語：把這套變成你自己的 SOP</h2><p>整理一下，這篇給你的是一套從 spec 到 ship 的完整 agentic engineering 實戰流程：</p><ol><li><strong>規劃階段</strong>：CLAUDE.md（搭配 AGENTS.md）的專案憲法 + ROADMAP.md（功能計畫）+ tasks.md（進度同步）三層落檔，靠 <code>@&lt;path&gt;</code> import 串起來</li><li><strong>員工分工</strong>：Skill 注入主 context、Sub-agent 獨立 context、Agent Teams 跨 session 平行</li><li><strong>開發紀律</strong>：同一輪補測試 + UI 切員工 + 一個邏輯一個 commit</li><li><strong>收尾驗證</strong>：typecheck + test + build 三段 + Writer&#x2F;Reviewer 雙 Claude + AI 引用 cross-check</li><li><strong>失誤救援</strong>：&#x2F;rewind → git restore → 砍 plan 重寫 三層救援</li><li><strong>Memory 系統</strong>：全域 + 專案兩層架構 + 正向 pattern 範本</li></ol><p>這六大環節背後其實有一個共同精神：<strong>每一條都是抗 AI 失憶、抗方向漂移、抗 side effect 的紀律</strong>。Karpathy 講 agentic engineering 的精髓就是這個 — 你不再是親手寫 code 的工程師，而是監督一群 AI 員工的工程師，你的工作是設計 spec、監督 plan、檢查 diff、寫測試、做評估、管權限。</p><p>更重要的是，你開始跑這套流程之後，會逐漸踩到自己專案特有的雷。當你踩到的時候不要只是當下解決，<strong>要把它寫進 memory or CLAUDE.md &#x2F; AGENTS.md 變成下次的 rule</strong>。這樣同樣的雷不會踩第二次，每多踩一個就多固化一條流程，慢慢就會累積出一套真正屬於你自己的 agentic engineering SOP。</p><p>希望這篇對你建立自己的 agentic engineering 流程有幫助哩～如果你跑過幾輪之後發現自己的流程跟我不太一樣，那也很正常，畢竟每個團隊習慣本來就不同，可以挑你覺得有用的部分自己試試看，慢慢調出自己的版本就好。</p>]]></content>
    
    
    <summary type="html">之前寫過 Vibe Coding 是什麼跟新手該補的基礎，這一篇是 2026 升級版完整教學。Karpathy 自己在 2026 年 4 月 Sequoia Ascent 演講把 vibe coding 升級成 agentic engineering 的概念，這篇就是用 agentic engineering 紀律重做 AI 開發實戰流程，涵蓋 Spec / Plan / Tasks 三層落檔（CLAUDE.md + AGENTS.md、ROADMAP、tasks.md 範本）、員工分工（Skill vs Sub-agent vs Agent Teams 差別）、開發紀律（同一輪補測試、UI 分工、Conventional Commits）、雙層收尾驗證（typecheck + test + build 三段驗證 + Writer/Reviewer 模式 + AI 引用 cross-check）、失誤回滾決策表、Memory + CLAUDE.md 雙層架構，最後用一個個人電商 MVP（Nuxt 3 + Supabase + Stripe）案例走一遍完整流程，包含 Stripe 結帳前端竄改價格安全雷怎麼救。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="Vibe Coding" scheme="https://israynotarray.com/tags/Vibe-Coding/"/>
    
    <category term="Agentic Engineering" scheme="https://israynotarray.com/tags/Agentic-Engineering/"/>
    
    <category term="AI 開發流程" scheme="https://israynotarray.com/tags/AI-%E9%96%8B%E7%99%BC%E6%B5%81%E7%A8%8B/"/>
    
    <category term="Sub-agent" scheme="https://israynotarray.com/tags/Sub-agent/"/>
    
    <category term="AGENTS.md" scheme="https://israynotarray.com/tags/AGENTS-md/"/>
    
    <category term="Spec-Driven Development" scheme="https://israynotarray.com/tags/Spec-Driven-Development/"/>
    
    <category term="Plan Mode" scheme="https://israynotarray.com/tags/Plan-Mode/"/>
    
  </entry>
  
  <entry>
    <title>Nuxt3 使用 ngrok 對外時出現 Blocked request 的解法</title>
    <link href="https://israynotarray.com/nuxt/20260516/2577897627/"/>
    <id>https://israynotarray.com/nuxt/20260516/2577897627/</id>
    <published>2026-05-16T02:00:00.000Z</published>
    <updated>2026-05-16T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/nuxt/nuxt-ngrok/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8817%E6%97%A5%20%E4%B8%8A%E5%8D%8810_52_16.png" data-fancybox="gallery" data-caption="Nuxt3 使用 ngrok 對外時出現 Blocked request 的解法"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/nuxt/nuxt-ngrok/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8817%E6%97%A5%20%E4%B8%8A%E5%8D%8810_52_16.png" alt="Nuxt3 使用 ngrok 對外時出現 Blocked request 的解法"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Nuxt3 使用 ngrok 對外時出現 Blocked request 的解法</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>如果你有在用 ngrok 把本機 dev server 暴露出去給人看的話，最近升級到 Nuxt 3（或者把 Vite 升到 6.x 以上）之後，應該會撞到一個訊息：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">Blocked request. This host (&quot;xxxx-xx-xx-xx-xx.ngrok-free.app&quot;) is not allowed.<br>To allow this host, add &quot;xxxx-xx-xx-xx-xx.ngrok-free.app&quot; to `server.allowedHosts` in vite.config.js.<br></code></pre></td></tr></table></figure><p>我第一次看到也愣了一下，想說 Nuxt 不是用 Nitro 嗎？怎麼會冒出 <code>vite.config.js</code> 來叫我改設定。後來想想 Nuxt 3 client 端其實是 Vite 在跑，這個 host check 是 Vite 從 6.x 開始補上的安全機制，主要是要避免 <a href="https://vite.dev/config/server-options.html#server-allowedhosts">DNS Rebinding 攻擊</a>。</p><p>可是 Nuxt 3 不是純 Vite 專案，根本沒有 <code>vite.config.js</code> 可以改，所以這篇就來記錄一下要在哪裡設定。順帶一提，之前我有寫過一篇 <a href="https://israynotarray.com/Nuxt/2023/08/08/2129825058/">Nuxt3 使用 Nitro Proxy 解決跨域問題</a>，也是在處理 Nuxt 3 dev server 的設定問題，可以一起服用。</p><h2 id="解決方式"><a href="#解決方式" class="headerlink" title="解決方式"></a>解決方式</h2><p>直接把 <code>server.allowedHosts</code> 加到 <code>nuxt.config.ts</code> 的 <code>vite</code> key 底下就行了：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// nuxt.config.ts</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">defineNuxtConfig</span>(&#123;<br>  <span class="hljs-attr">vite</span>: &#123;<br>    <span class="hljs-attr">server</span>: &#123;<br>      <span class="hljs-attr">allowedHosts</span>: [<span class="hljs-string">&#x27;xxxx-xx-xx-xx-xx.ngrok-free.app&#x27;</span>],<br>    &#125;,<br>  &#125;,<br>&#125;)<br></code></pre></td></tr></table></figure><p>Nuxt 3 的設定統一寫在 <code>nuxt.config.ts</code>，Vite 相關的設定都收在 <code>vite</code> 這個 key 底下，所以錯誤訊息叫你改 <code>vite.config.js</code>，對 Nuxt 專案來講就是改這個位置。存檔後重啟 <code>pnpm dev</code>，再打開 ngrok 的網址就正常了。</p><h2 id="用萬用語法一勞永逸"><a href="#用萬用語法一勞永逸" class="headerlink" title="用萬用語法一勞永逸"></a>用萬用語法一勞永逸</h2><p>不過 ngrok 免費版每次重啟隧道都會給你一個新的 subdomain，總不能每次都回來改設定吧？</p><p>這時候可以用 Vite 的萬用語法，<strong>字串開頭加一個 <code>.</code> 就會涵蓋所有子網域</strong>：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// nuxt.config.ts</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">defineNuxtConfig</span>(&#123;<br>  <span class="hljs-attr">vite</span>: &#123;<br>    <span class="hljs-attr">server</span>: &#123;<br>      <span class="hljs-comment">// 開頭的 `.` 是 Vite 的萬用語法，會涵蓋所有子網域</span><br>      <span class="hljs-attr">allowedHosts</span>: [<span class="hljs-string">&#x27;.ngrok-free.app&#x27;</span>, <span class="hljs-string">&#x27;.ngrok.app&#x27;</span>, <span class="hljs-string">&#x27;.ngrok.io&#x27;</span>],<br>    &#125;,<br>  &#125;,<br>&#125;)<br></code></pre></td></tr></table></figure><p>這樣不管 ngrok 給你什麼新網址都會放行。以我自己來講，會把 <code>.ngrok-free.app</code>、<code>.ngrok.app</code>、<code>.ngrok.io</code> 三個都加進去，免得之後換成付費版的網域又要回來改一次。</p><p>如果你還會用其他穿透工具像 Cloudflare Tunnel、Tailscale Funnel，把對應的網域加進這個陣列即可。</p><h2 id="為什麼不直接全部放行？"><a href="#為什麼不直接全部放行？" class="headerlink" title="為什麼不直接全部放行？"></a>為什麼不直接全部放行？</h2><p>Vite 其實有提供一個更暴力的選項，把 <code>allowedHosts</code> 直接設成 <code>true</code> 就會放行<strong>所有</strong>來源：</p><figure class="highlight ts"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br></pre></td><td class="code"><pre><code class="hljs ts"><span class="hljs-comment">// 不建議</span><br><span class="hljs-keyword">export</span> <span class="hljs-keyword">default</span> <span class="hljs-title function_">defineNuxtConfig</span>(&#123;<br>  <span class="hljs-attr">vite</span>: &#123;<br>    <span class="hljs-attr">server</span>: &#123;<br>      <span class="hljs-attr">allowedHosts</span>: <span class="hljs-literal">true</span>,<br>    &#125;,<br>  &#125;,<br>&#125;)<br></code></pre></td></tr></table></figure><blockquote><p><strong>Note</strong><br>我自己很不喜歡這樣寫，因為這等於把 Vite 加上 host check 的安全意義整個關掉。雖然 dev server 只在本機跑，可是只要你開了 ngrok、Cloudflare Tunnel 之類的穿透服務，這台 dev server 就等於是對外的，多花十秒鐘設個白名單其實不虧。</p></blockquote><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>簡單整理一下這篇講的事情：</p><ul><li>Nuxt 3 沒有 <code>vite.config.js</code>，Vite 的設定要寫在 <code>nuxt.config.ts</code> 的 <code>vite</code> key 底下</li><li><code>allowedHosts</code> 用陣列指定允許的 host，開頭加 <code>.</code> 可以萬用涵蓋子網域</li><li>不要為了方便直接 <code>allowedHosts: true</code>，乖乖列白名單比較香</li><li>這個設定只影響 dev server，production build 完全不受影響</li></ul><p>希望這一篇有幫到剛好被 ngrok + Nuxt 3 擋下來的你哩～如果你也在踩 Nuxt 3 的環境坑，可以順手翻一下 <a href="https://israynotarray.com/Nuxt/2023/08/08/3162324445/">初始化 Nuxt3 專案時出現 Issues with peer dependencies found 警告</a>。</p><h2 id="參考資料"><a href="#參考資料" class="headerlink" title="參考資料"></a>參考資料</h2><ul><li><a href="https://vite.dev/config/server-options.html#server-allowedhosts">Vite｜server.allowedHosts</a></li><li><a href="https://nuxt.com/docs/api/nuxt-config#vite">Nuxt 3｜Configuration - vite</a></li></ul>]]></content>
    
    
    <summary type="html">這篇記錄一下用 ngrok 把 Nuxt 3 dev server 對外開放時出現「Blocked request. This host is not allowed」的解決方式， 包含為什麼 Nuxt 會冒出 vite.config.js 的錯誤訊息、要在 nuxt.config.ts 的 vite key 底下加 server.allowedHosts、 用開頭加 . 的萬用語法涵蓋所有 ngrok 子網域，以及為什麼不建議直接把 allowedHosts 設成 true。</summary>
    
    
    
    <category term="Nuxt" scheme="https://israynotarray.com/categories/nuxt/"/>
    
    
    <category term="Vite" scheme="https://israynotarray.com/tags/Vite/"/>
    
    <category term="Vue" scheme="https://israynotarray.com/tags/Vue/"/>
    
    <category term="Nuxt3" scheme="https://israynotarray.com/tags/Nuxt3/"/>
    
    <category term="Nuxt" scheme="https://israynotarray.com/tags/Nuxt/"/>
    
    <category term="ngrok" scheme="https://israynotarray.com/tags/ngrok/"/>
    
    <category term="allowedHosts" scheme="https://israynotarray.com/tags/allowedHosts/"/>
    
    <category term="dev server" scheme="https://israynotarray.com/tags/dev-server/"/>
    
  </entry>
  
  <entry>
    <title>分享網址到 LINE、Discord 沒預覽圖？用 ?v=1 強制重抓社群 OG cache</title>
    <link href="https://israynotarray.com/other/20260514/7521893064/"/>
    <id>https://israynotarray.com/other/20260514/7521893064/</id>
    <published>2026-05-14T02:00:00.000Z</published>
    <updated>2026-05-14T02:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/og-cache-v/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8813%E6%97%A5%20%E4%B8%8B%E5%8D%8810_23_26.png" data-fancybox="gallery" data-caption="分享網址到 LINE、Discord 沒預覽圖？用 ?v&#x3D;1 強制重抓社群 OG cache"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/og-cache-v/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8813%E6%97%A5%20%E4%B8%8B%E5%8D%8810_23_26.png" alt="分享網址到 LINE、Discord 沒預覽圖？用 ?v&#x3D;1 強制重抓社群 OG cache"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">分享網址到 LINE、Discord 沒預覽圖？用 ?v&#x3D;1 強制重抓社群 OG cache</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇從分享自己部落格文章到社群結果預覽圖一直是舊版的問題切入，講 Open Graph 標籤、社群平台對 og:image &#x2F; og:title &#x2F; og:description 的 cache 機制、為什麼 FB &#x2F; X &#x2F; LINE &#x2F; Discord 在你改完 OG 標籤後還是抓不到新內容，以及用 <code>?v=1</code> <code>?v=2</code> 這種 cache-busting query string 強制讓社群 cache 不認得而重新抓取的技巧。也整理 Facebook Sharing Debugger 用法、各平台 cache 大致過期時間、Twitter &#x2F; X Card Validator 2022 停用後該怎麼測，以及 <code>?v=1</code> 對 SEO 跟 canonical URL 會不會造成影響。</p><p>如果你前一陣子也有在用 Cloudflare Pages 部署部落格 or 試圖鎖預覽網址，可以順便看看 <a href="https://israynotarray.com/other/20260513/8264193057/">Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險</a> 那篇，跟這一篇可以一起服用。</p><h2 id="事情是這樣的"><a href="#事情是這樣的" class="headerlink" title="事情是這樣的"></a>事情是這樣的</h2><p>事情是這樣的，我前幾天寫了一篇 Cloudflare Access 鎖預覽網址的文章，部署完興沖沖把網址分享到 LINE 給朋友看，結果分享出來的卡片<strong>沒有封面圖</strong>，只有一張預設的章魚貓出現在預覽縮圖上。</p><p>當下覺得奇怪，因為文章內第一張就是封面圖，按理講 og:image 應該會抓到那張才對。回頭去看部落格 HTML 確認 og:image 真的有指對封面圖、curl 圖檔 URL 也回 200 OK，可是 LINE 那邊就是不認。</p><p>換到 Discord 貼一次、X 貼一次，全部都是同一張預設圖。連自己看了都想笑，明明文章已經改好了，社群這邊就是不更新。</p><p>後來查了一下才知道，這不是部落格的問題，而是<strong>社群平台自己的 OG cache</strong> 在作祟。</p><h2 id="為什麼社群會-cache-預覽圖？"><a href="#為什麼社群會-cache-預覽圖？" class="headerlink" title="為什麼社群會 cache 預覽圖？"></a>為什麼社群會 cache 預覽圖？</h2><p>先講結論：社群平台<strong>會把每個網址的 OG metadata cache 起來</strong>，第二次貼同樣的網址不會回你的網站重抓，而是直接拿 cache 顯示。</p><p>OG 是 Open Graph 的縮寫，由 Facebook 在 2010 年提出的一個 metadata 規範，目的是讓社群平台分享連結時，可以快速取得這個連結的標題、描述、預覽圖等資訊。網站只要在 <code>&lt;head&gt;</code> 裡放這幾個 meta tag，社群就會抓來顯示</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:title&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;文章標題&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:description&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;文章描述&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:image&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;封面圖網址&quot;</span>&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">meta</span> <span class="hljs-attr">property</span>=<span class="hljs-string">&quot;og:url&quot;</span> <span class="hljs-attr">content</span>=<span class="hljs-string">&quot;頁面網址&quot;</span>&gt;</span><br></code></pre></td></tr></table></figure><p>那社群為什麼要 cache？簡單來講就是<strong>省成本</strong>。</p><p>想想看，你發一則貼文夾一個連結，如果 100 個朋友都看到這則貼文，社群平台不可能讓 100 個人的手機分別去打你的網站抓 OG tag，那是 100 個 request。所以社群會在第一次有人貼這個網址時，自己去抓一次 OG metadata 存起來，之後其他人看到這則貼文 or 後來再有人貼相同網址，都從 cache 直接拿，不會再打你的網站。</p><p>這個 cache 的設計本身是合理的，可是站在內容創作者角度，當你<strong>改了 og:image 想要更新預覽</strong>的時候，cache 就變成擋路的東西。</p><p>各平台 cache 時間大概抓一下</p><div class="table-wrapper"><table><thead><tr><th>平台</th><th>cache 過期時間</th><th>有官方 debugger 嗎？</th></tr></thead><tbody><tr><td>Facebook</td><td>約 7–30 天</td><td>有（Sharing Debugger）</td></tr><tr><td>LinkedIn</td><td>約 7 天</td><td>有（Post Inspector）</td></tr><tr><td>X（前 Twitter）</td><td>約 24–48 小時</td><td>已停用預覽功能</td></tr><tr><td>Discord</td><td>約 30 分鐘</td><td>沒有</td></tr><tr><td>LINE</td><td>官方未公布</td><td>沒有</td></tr><tr><td>Slack &#x2F; Telegram</td><td>官方未公布</td><td>沒有</td></tr></tbody></table></div><p>從這張表可以看出來，Discord 算是 cache 過期最快的，Facebook 跟 LinkedIn 都偏長。LINE 跟 Slack、Telegram 官方都沒公布過數字，遇到的話只能用接下來要講的這招處理。</p><h2 id="用-v-1-強制重抓-cache"><a href="#用-v-1-強制重抓-cache" class="headerlink" title="用 ?v&#x3D;1 強制重抓 cache"></a>用 ?v&#x3D;1 強制重抓 cache</h2><p>最簡單的強制重抓方式就是<strong>在網址後面加一段 query string</strong>，例如</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">https://israynotarray.com/other/20260513/8264193057/?v=1<br></code></pre></td></tr></table></figure><p>對社群的 cache 來說，網址字串不一樣就是不同條目，原本的 <code>https://israynotarray.com/other/20260513/8264193057/</code> 已經有 cache，可是加上 <code>?v=1</code> 之後變成「新網址」，cache 找不到對應紀錄，就會回你網站重抓一次最新的 OG metadata。</p><p><code>v=1</code> 的數字隨便挑，下次要再強制刷新就改 <code>v=2</code>、<code>v=3</code>，每次給一個沒貼過的新值就好。也不一定要叫 <code>v</code>，叫 <code>?refresh=1</code>、<code>?nocache=true</code> 都可以，只要是「以前沒貼過的 query string」就有效。</p><h3 id="你的網站收到-v-1-不會壞掉"><a href="#你的網站收到-v-1-不會壞掉" class="headerlink" title="你的網站收到 ?v&#x3D;1 不會壞掉"></a>你的網站收到 ?v&#x3D;1 不會壞掉</h3><p>可能你會擔心：那 <code>?v=1</code> 跑進我網站會不會有事？</p><p>放心，<strong>多數 framework 跟 web server 預設都會忽略未知的 query string</strong>。Hexo、Next.js、Vite、Cloudflare Pages、Vercel 等等，收到 <code>?v=1</code> 就照原本 path 走，內容跟沒帶 query string 完全一樣，讀者看到的網頁不會差任何一個字。</p><blockquote><p><strong>Note</strong><br>如果你的網站有自己處理 query string（例如分頁 <code>?page=2</code> or 搜尋 <code>?q=xxx</code>），那只要不挑跟你 query string 同名的字串就好。<code>?v</code>、<code>?refresh</code>、<code>?nocache</code> 這些是很安全的選擇，幾乎不可能跟你網站原本的功能撞名。</p></blockquote><h2 id="各平台對應做法"><a href="#各平台對應做法" class="headerlink" title="各平台對應做法"></a>各平台對應做法</h2><h3 id="Facebook：用-Sharing-Debugger"><a href="#Facebook：用-Sharing-Debugger" class="headerlink" title="Facebook：用 Sharing Debugger"></a>Facebook：用 Sharing Debugger</h3><p>Facebook 是唯一還活著的官方 debugger，網址 <a href="https://developers.facebook.com/tools/debug/">https://developers.facebook.com/tools/debug/</a>。把你的網址貼進去，點 <code>Debug</code> 看 FB 抓到什麼，如果是舊資料就按 <code>Scrape Again</code> 強制重抓。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/social-og-cache/fb-debugger.png" data-fancybox="gallery" data-caption="Facebook Sharing Debugger"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/social-og-cache/fb-debugger.png" alt="Facebook Sharing Debugger"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Facebook Sharing Debugger</figcaption></figure><p>Sharing Debugger 也會告訴你如果 og:image 有問題（檔案太大、格式不支援、抓不到等等），對 debug 很有幫助。</p><blockquote><p><strong>Note</strong><br>Facebook 到 2026 為止官方文件仍然只支援 <code>JPG</code>、<code>PNG</code>、<code>GIF</code> 三種格式當 og:image，<strong>WebP 不支援</strong>。如果你用 WebP 圖檔 og:image 在 FB 就會壞掉。建議 og:image 用 PNG or JPG，網站其他圖片再用 WebP 提速即可。</p></blockquote><p>另外 og:image 建議尺寸是 <strong>1200 x 630 像素</strong>，小於 600 x 315 的圖 FB 會顯示成小縮圖 or 直接不顯示。</p><p>實際上有時候第一次 Scrape Again 沒用，連續點個兩三次反而會成功，這個沒有什麼合理解釋，純粹是 FB 自己會抽風。</p><h3 id="X、LinkedIn、LINE、Discord：直接用-v-1"><a href="#X、LinkedIn、LINE、Discord：直接用-v-1" class="headerlink" title="X、LinkedIn、LINE、Discord：直接用 ?v&#x3D;1"></a>X、LinkedIn、LINE、Discord：直接用 ?v&#x3D;1</h3><p>X 的 Card Validator 在 2022 已經拿掉預覽功能，現在剩下的網頁也漸漸難用。LinkedIn 有 Post Inspector 可是限制比較多。LINE、Discord、Slack、Telegram 從以前就沒提供 debugger。</p><p>所以對這幾個平台，最快也最通用的解法就是 <code>?v=1</code> cache busting。下次貼網址到 LINE 群組之前先在後面加個 <code>?v=1</code>，社群就會回你網站重抓一次。</p><blockquote><p><strong>Note</strong><br>已經分享到群組的舊訊息<strong>不會更新</strong>，社群 cache 是綁在當時那則訊息上，舊訊息抓到什麼就永遠是什麼。要救只能刪掉原訊息，再用 <code>?v=1</code> 重貼一次。</p></blockquote><h3 id="替代方案：第三方-OG-預覽工具"><a href="#替代方案：第三方-OG-預覽工具" class="headerlink" title="替代方案：第三方 OG 預覽工具"></a>替代方案：第三方 OG 預覽工具</h3><p>如果你想在分享前先看看會長什麼樣，市面上也有不少第三方工具可以模擬抓 OG，例如</p><ul><li><a href="https://opengraph.dev/">opengraph.dev</a></li><li><a href="https://share-preview.com/">share-preview.com</a></li><li><a href="https://microlink.io/tools/sharing-debugger">microlink.io</a></li></ul><p>這些工具是即時抓你網頁的 OG meta，跟社群本身的 cache 無關，所以可以拿來在發文前確認 og:image 真的有抓到。可是要注意，<strong>第三方工具能抓不代表 FB &#x2F; LINE 抓得到</strong>，最終還是以官方 debugger（FB Sharing Debugger）為準。</p><h2 id="那-SEO-會被影響嗎？"><a href="#那-SEO-會被影響嗎？" class="headerlink" title="那 SEO 會被影響嗎？"></a>那 SEO 會被影響嗎？</h2><p>不會，前提是你的網站有設好 canonical URL。</p><p><code>&lt;link rel=&quot;canonical&quot;&gt;</code> 是放在 <code>&lt;head&gt;</code> 裡的一個標籤，告訴搜尋引擎「<strong>這個頁面的正規網址是哪一個</strong>」。當 Google 抓到 <code>https://your-site.com/post/?v=1</code> 這個變體網址，發現</p><figure class="highlight html"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs html"><span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">rel</span>=<span class="hljs-string">&quot;canonical&quot;</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://your-site.com/post/&quot;</span>&gt;</span><br></code></pre></td></tr></table></figure><p>它就會把 <code>?v=1</code> 當成跟原網址同一頁處理，搜尋權重不會被分散，也不會被視為 duplicate content 而被懲罰。</p><p>而且 <code>?v=1</code> 你只是偶爾為了強制重抓社群 cache 才用一次，不會持續對外推廣這個變體網址，所以實際上 Google 連看到都未必。對 SEO 沒任何實質影響。</p><blockquote><p><strong>Note</strong><br>如果你不確定自己的網站有沒有 canonical URL，可以打開文章在瀏覽器按 F12 → Elements 找 <code>&lt;link rel=&quot;canonical&quot;&gt;</code>，看看那個 <code>href</code> 是不是指向你預期的網址。多數現代部落格架構（Hexo、Astro、Next.js、Vite SSG）都會幫你自動加。</p></blockquote><h2 id="補充：分享前先測過再發"><a href="#補充：分享前先測過再發" class="headerlink" title="補充：分享前先測過再發"></a>補充：分享前先測過再發</h2><p>最後分享一個我自己被坑過之後養成的習慣：<strong>發文之前先用 FB Sharing Debugger 跑一次</strong>。</p><p>流程很簡單</p><ol><li>文章 publish 完先打開 <a href="https://developers.facebook.com/tools/debug/">Facebook Sharing Debugger</a></li><li>貼上文章網址，點 <code>Debug</code></li><li>看下方的 og:image、og:title、og:description 有沒有抓對</li><li>如果抓錯就先回去修，修完再 Scrape Again</li><li>確認都沒問題再正式分享到各社群</li></ol><p>雖然多一個步驟，可是比起發出去之後才發現沒圖、要刪訊息重貼、還要叫朋友重新點一次，這個事前檢查的 1 分鐘很值得。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>社群 cache 這件事其實在大多時候是好的（節省你網站的流量、加快預覽速度），可是當你急著更新 og:image 的時候它就會變成擋路的東西。<code>?v=1</code> 這招說起來很土，可是它真的好用，特別是對 LINE 跟 Discord 這種沒 debugger 的平台來講幾乎是唯一的辦法。</p><p>希望這一篇有讓你對社群 OG cache 跟 cache-busting 的概念有更近一步的認識哩～如果你也是用 Hexo or Cloudflare Pages 在跑部落格，可以再看一下 <a href="https://israynotarray.com/other/20260513/8264193057/">Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險</a> 那篇，把社群分享 + 部署側的小坑一起補起來哩。</p>]]></content>
    
    
    <summary type="html">這篇從分享自己部落格文章到社群結果預覽圖一直是舊版的問題切入，講 Open Graph 標籤、社群平台對 og:image / og:title / og:description 的 cache 機制、為什麼 FB / X / LINE / Discord 在你改完 OG 標籤後還是抓不到新內容，以及用 `?v=1` `?v=2` 這種 cache-busting query string 強制讓社群 cache 不認得而重新抓取的技巧。也整理 Facebook Sharing Debugger 用法、各平台 cache 大致過期時間、Twitter / X Card Validator 2022 停用後該怎麼測，以及 `?v=1` 對 SEO 跟 canonical URL 會不會造成影響。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="Discord" scheme="https://israynotarray.com/tags/Discord/"/>
    
    <category term="Facebook" scheme="https://israynotarray.com/tags/Facebook/"/>
    
    <category term="Open Graph" scheme="https://israynotarray.com/tags/Open-Graph/"/>
    
    <category term="og:image" scheme="https://israynotarray.com/tags/og-image/"/>
    
    <category term="社群分享" scheme="https://israynotarray.com/tags/%E7%A4%BE%E7%BE%A4%E5%88%86%E4%BA%AB/"/>
    
    <category term="LINE" scheme="https://israynotarray.com/tags/LINE/"/>
    
    <category term="cache busting" scheme="https://israynotarray.com/tags/cache-busting/"/>
    
    <category term="query string" scheme="https://israynotarray.com/tags/query-string/"/>
    
  </entry>
  
  <entry>
    <title>Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險</title>
    <link href="https://israynotarray.com/other/20260513/8264193057/"/>
    <id>https://israynotarray.com/other/20260513/8264193057/</id>
    <published>2026-05-13T02:30:00.000Z</published>
    <updated>2026-05-13T02:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8813%E6%97%A5%20%E4%B8%8B%E5%8D%8801_33_24.png" data-fancybox="gallery" data-caption="Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8813%E6%97%A5%20%E4%B8%8B%E5%8D%8801_33_24.png" alt="Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Cloudflare Pages 預覽部署網址會永久保留？用 Cloudflare Access 鎖住外洩風險</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>Cloudflare Pages 每次部署都會產生一組 hash 預覽網址（例如 <code>d682e28c.your-project.pages.dev</code>），這篇從預覽網址永久保留的安全疑慮聊起，整理如何用 Cloudflare Access 免費 50 人額度設定 Self-hosted 應用程式，搭配 Emails ending in 原則把預覽鎖起來，包含建立應用程式步驟、Access 原則的 OR 規則寫法，以及收不到 One-time PIN 信時的三個排查方向。</p><p>如果你還沒有自己的 Cloudflare 網域 or Pages 專案，可以先看一下 <a href="https://israynotarray.com/other/20250521/617540884/">Google Domains 停止後怎麼辦？用 Cloudflare 建立網域、部署網站完整圖文教學</a>，那一篇有完整的 Cloudflare Pages 部署流程，跟這一篇可以一起服用。</p><h2 id="事情是這樣的"><a href="#事情是這樣的" class="headerlink" title="事情是這樣的"></a>事情是這樣的</h2><p>最近在玩 Hexo 部落格的自動部署，把專案接到 Cloudflare Pages，每次 push 上去就會自動觸發新的部署。Cloudflare 很貼心，除了正式版的 <code>your-project.pages.dev</code> 之外，每次部署都還會額外給你一組獨立的預覽網址，網址長這樣</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">https://d682e28c.your-project.pages.dev<br></code></pre></td></tr></table></figure><p>那一串 <code>d682e28c</code> 其實就是 commit hash 的前 8 碼，這個機制超實用，你可以拿去分享給同事 or 老師看「我這一版改完長這樣」，不用先合進主分支也能 demo。</p><p>可是當下我突然想到一件事：這個預覽網址會保留多久？如果這個連結不小心被別人轉貼出去，會不會變成資安問題？因為有些公司會 Preview 測試一些敏感內容（例如內部專案、還沒公開的功能頁面），如果預覽網址會一直存在，那不就等於說這些內容會一直躺在那邊，等人挖出來看嗎？</p><p>於是查了一下…對，<strong>永久</strong>。</p><h2 id="Cloudflare-Pages-預覽網址會保留多久？"><a href="#Cloudflare-Pages-預覽網址會保留多久？" class="headerlink" title="Cloudflare Pages 預覽網址會保留多久？"></a>Cloudflare Pages 預覽網址會保留多久？</h2><p>如同前面所言：<strong>永久</strong>。</p><p>Cloudflare Pages 官方文件 <a href="https://developers.cloudflare.com/pages/configuration/preview-deployments/">Preview deployments</a> 裡面其實寫滿清楚的</p><blockquote><p>When a preview deployment is published, it is given a unique, hash-based address — for example, <code>&lt;hash&gt;.&lt;project&gt;.pages.dev</code>. These are atomic and may always be visited in the future.</p></blockquote><p>翻成白話就是：每個預覽部署都會綁一組 hash 網址，這個網址是 atomic（原子的、不會變動的），未來都可以一直被造訪。</p><p>換句話說，只要你不手動進 Cloudflare 儀表板把那筆部署刪掉，那個網址永遠都打得開，看到的就是當時推上去的那一版內容。</p><blockquote><p><strong>Note</strong><br>Cloudflare 不僅不會幫你自動清除舊預覽，而且根據官方文件，當一個 Pages 專案累積超過 100 筆部署的時候，連刪除整個專案都會失敗，必須先用 Wrangler 一筆一筆把舊部署清掉才行。</p></blockquote><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513133103.png" data-fancybox="gallery" data-caption="Cloudflare Pages 預覽網址永久保留示意"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513133103.png" alt="Cloudflare Pages 預覽網址永久保留示意"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Cloudflare Pages 預覽網址永久保留示意</figcaption></figure><h2 id="那這樣會有什麼風險？"><a href="#那這樣會有什麼風險？" class="headerlink" title="那這樣會有什麼風險？"></a>那這樣會有什麼風險？</h2><p>hash 本身有 8 個字元的隨機性，要靠暴力枚舉去猜中其實不太可能，所以從攻擊面來看，它不是一個容易被掃描器掃到的網址。</p><p>可是這不代表沒風險，我列幾個常見情境…</p><ul><li>你把預覽連結貼到 Slack、Discord 群組</li><li>你把連結傳給設計師、PM、老師看</li><li>你 Tweet 出去 or 截圖到部落格上</li><li>你 demo 給客戶看，會議結束後沒刪除</li></ul><p>只要其中任何一個情境不小心讓不該看到的人看到了，這個網址就回不來了。就算你後來把那個分支砍掉、把專案改名，那筆預覽部署的網址還是會繼續活著，這就是讓我覺得有風險的地方。</p><p>因此這邊就來分享一下我自己用的方式，用 Cloudflare Access 在預覽網址前面加一道身分驗證，只有授權過的人才能進去。</p><h2 id="用-Cloudflare-Access-鎖住預覽"><a href="#用-Cloudflare-Access-鎖住預覽" class="headerlink" title="用 Cloudflare Access 鎖住預覽"></a>用 Cloudflare Access 鎖住預覽</h2><p>Cloudflare Access 是 Cloudflare Zero Trust 底下的一個 ZTNA（Zero Trust Network Access）服務，它的作用就是在你的網站前面架一道身份驗證關卡，任何人要進去都必須先通過驗證。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513131700.png" data-fancybox="gallery" data-caption="Cloudflare Access"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513131700.png" alt="Cloudflare Access"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Cloudflare Access</figcaption></figure><p>那它要錢嗎？根據 <a href="https://www.cloudflare.com/plans/zero-trust-services/">Cloudflare 官方 Zero Trust 方案頁面</a>，Zero Trust 的 Free 方案最多 50 個使用者免費，對個人 blog 來講根本就是吃免費的等級。</p><h3 id="步驟-1：建立-Self-hosted-應用程式"><a href="#步驟-1：建立-Self-hosted-應用程式" class="headerlink" title="步驟 1：建立 Self-hosted 應用程式"></a>步驟 1：建立 Self-hosted 應用程式</h3><p>進到 Cloudflare 儀表板，左側選單找到 <code>Zero Trust</code>（如果是第一次進，會先要你取一個 Team name，那會變成你的登入網址 <code>https://你取的名字.cloudflareaccess.com</code>）。</p><p>接著走以下路徑</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">Zero Trust → Access 控制 → 應用程式 → 新增應用程式 → 自我裝載（Self-hosted）<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513121850.png" data-fancybox="gallery" data-caption="Access 控制"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513121850.png" alt="Access 控制"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Access 控制</figcaption></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513121907.png" data-fancybox="gallery" data-caption="新增應用程式"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513121907.png" alt="新增應用程式"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">新增應用程式</figcaption></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122119.png" data-fancybox="gallery" data-caption="自我裝載"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122119.png" alt="自我裝載"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">自我裝載</figcaption></figure><p>在目的地這個區塊，填入…</p><div class="table-wrapper"><table><thead><tr><th>欄位</th><th>填什麼</th></tr></thead><tbody><tr><td>子網域</td><td><code>*</code></td></tr><tr><td>網域</td><td>你的 Pages 網域，例如 <code>your-project.pages.dev</code></td></tr><tr><td>路徑</td><td>留空</td></tr></tbody></table></div><p>子網域填 <code>*</code> 是關鍵，這樣 <code>&lt;任何 hash&gt;.your-project.pages.dev</code> 全部都會被這個應用程式接管，正式版 <code>your-project.pages.dev</code> 也會被一起保護。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122145.png" data-fancybox="gallery" data-caption="建立新的自我裝載應用程式"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122145.png" alt="建立新的自我裝載應用程式"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">建立新的自我裝載應用程式</figcaption></figure><h3 id="步驟-2：設定-Access-原則"><a href="#步驟-2：設定-Access-原則" class="headerlink" title="步驟 2：設定 Access 原則"></a>步驟 2：設定 Access 原則</h3><p>接著要設定誰可以進來。在下方有一塊 <code>Access 原則</code> 區塊</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122403.png" data-fancybox="gallery" data-caption="Access 原則"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122403.png" alt="Access 原則"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Access 原則</figcaption></figure><p>點 <code>建立新原則</code>，填…</p><div class="table-wrapper"><table><thead><tr><th>欄位</th><th>填什麼</th></tr></thead><tbody><tr><td>原則名稱</td><td>例如 <code>僅限自己人</code></td></tr><tr><td>動作</td><td>Allow</td></tr><tr><td>規則選擇器</td><td><code>結尾為以下的電子郵件</code>（Emails ending in）</td></tr><tr><td>值</td><td><code>@gmail.com</code>（你的公司網域）</td></tr></tbody></table></div><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122538.png" data-fancybox="gallery" data-caption="設定 Access 原則"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513122538.png" alt="設定 Access 原則"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">設定 Access 原則</figcaption></figure><p>如果你還想額外讓自己的 gmail 也能登入，可以在同一個原則裡再加一條規則</p><div class="table-wrapper"><table><thead><tr><th>規則</th><th>選擇器</th><th>值</th></tr></thead><tbody><tr><td>規則 1</td><td>結尾為以下的電子郵件</td><td><code>@gmail.com</code></td></tr><tr><td>規則 2</td><td>電子郵件</td><td><code>your-name@gmail.com</code></td></tr></tbody></table></div><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513123440.png" data-fancybox="gallery" data-caption="多條規則"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513123440.png" alt="多條規則"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">多條規則</figcaption></figure><p>兩條規則之間是 OR 關係，任一條件成立就放行。</p><h3 id="步驟-3：儲存應用程式"><a href="#步驟-3：儲存應用程式" class="headerlink" title="步驟 3：儲存應用程式"></a>步驟 3：儲存應用程式</h3><p>最後填上應用程式名稱、工作階段持續時間（建議 24 hours），按 <code>建立</code> 就完成了。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513123624.png" data-fancybox="gallery" data-caption="儲存應用程式"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513123624.png" alt="儲存應用程式"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">儲存應用程式</figcaption></figure><p>完成後開無痕視窗、貼上預覽網址，理論上你應該會看到 Cloudflare 的登入頁要你輸入 email，輸入正確 email 後會收到一封 PIN 信，輸入 PIN 之後就會看到網站內容。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/20260513131700.png" data-fancybox="gallery" data-caption="Cloudflare Pages 預覽網址永久保留示意"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/20260513131700.png" alt="Cloudflare Pages 預覽網址永久保留示意"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Cloudflare Pages 預覽網址永久保留示意</figcaption></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/cloudflare-access/202605131328.png" data-fancybox="gallery" data-caption="email 收到 PIN 信"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/cloudflare-access/202605131328.png" alt="email 收到 PIN 信"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">email 收到 PIN 信</figcaption></figure><h3 id="設定好了卻收不到-PIN-信？"><a href="#設定好了卻收不到-PIN-信？" class="headerlink" title="設定好了卻收不到 PIN 信？"></a>設定好了卻收不到 PIN 信？</h3><p>如果你跟我一樣，興沖沖設定完之後測試卻一直沒收到 PIN 信，先冷靜，三個地方檢查一下。</p><p>第一，垃圾信件夾。Cloudflare 的 PIN 信寄件人是 <code>noreply@notify.cloudflare.com</code>，Gmail 很常把它丟到垃圾郵件 or 促銷內容分頁。標題類似 <code>Your one-time code is...</code> 這種，先去這兩個資料夾翻翻看。</p><p>第二，你輸入的 email 沒有符合原則。這個最坑。如果你原則設定的是 <code>@hexschool.com</code> 結尾，可是你登入的時候打的是 <code>gmail.com</code> 的 email，Cloudflare 會直接拒絕、根本不會寄信給你，而且畫面也不會告訴你為什麼不寄，就是靜靜的什麼都沒有。</p><blockquote><p><strong>Note</strong><br>這個設計從安全性角度來看是合理的（避免讓攻擊者知道哪些 email 在白名單上），可是對第一次設定的人來講真的很容易摸不著頭緒，請務必確認你輸入的 email 有在原則裡。</p></blockquote><p>第三，One-time PIN 沒啟用。到 <code>Zero Trust → 設定 → 驗證 → 登入方法</code> 看看 <code>One-time PIN</code> 有沒有在啟用狀態，如果沒有就點 <code>新增</code> 把它打開。</p><h2 id="補充：用-Wrangler-批次刪除舊預覽"><a href="#補充：用-Wrangler-批次刪除舊預覽" class="headerlink" title="補充：用 Wrangler 批次刪除舊預覽"></a>補充：用 Wrangler 批次刪除舊預覽</h2><p>如果你想做的不是「擋人看」而是「乾脆把舊預覽通通清掉」，可以用 Cloudflare 官方提供的 <a href="https://developers.cloudflare.com/workers/wrangler/">Wrangler CLI</a> 一鍵把列表抓出來</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs bash">npx wrangler pages deployment list --project-name=your-project<br></code></pre></td></tr></table></figure><p>配合 <code>wrangler pages deployment delete &lt;id&gt;</code> 一筆一筆刪，效率比點儀表板高很多。如同前面 Note 提到的，當部署累積超過 100 筆，連刪除整個專案都會失敗，所以這個指令在後期維護的時候會滿好用的。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>Cloudflare Pages 預覽部署是個雙面刃，該分享的時候很方便、該保密的時候就要記得加把鎖。在這篇文章之前，我其實滿天真地把預覽網址當作「臨時測試環境」，現在回頭看，它根本是個會永久躺在那邊等人挖出來的快照。</p><p>希望這一篇有讓你對 Cloudflare Pages 的預覽機制與 Access 保護有更近一步的認識哩～如果你也想把部落格搬上 Cloudflare，可以再看一下 <a href="https://israynotarray.com/other/20260417/830271645/">用 Cloudflare Workers 快取 Firebase Storage 圖片，完全免費降低流量費用</a> 這一篇，跟這一篇可以串成一條更完整的 Cloudflare 部署流程。我們下篇文章見囉～</p>]]></content>
    
    
    <summary type="html">Cloudflare Pages 每次部署都會產生一組 hash 預覽網址（例如 d682e28c.your-project.pages.dev），這篇從預覽網址永久保留的安全疑慮聊起，整理如何用 Cloudflare Access 免費 50 人額度設定 Self-hosted 應用程式，搭配 Emails ending in 原則把預覽鎖起來，包含建立應用程式步驟、Access 原則的 OR 規則寫法，以及收不到 One-time PIN 信時的三個排查方向。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="Cloudflare" scheme="https://israynotarray.com/tags/Cloudflare/"/>
    
    <category term="資安" scheme="https://israynotarray.com/tags/%E8%B3%87%E5%AE%89/"/>
    
    <category term="Cloudflare Pages" scheme="https://israynotarray.com/tags/Cloudflare-Pages/"/>
    
    <category term="Cloudflare Access" scheme="https://israynotarray.com/tags/Cloudflare-Access/"/>
    
    <category term="Zero Trust" scheme="https://israynotarray.com/tags/Zero-Trust/"/>
    
    <category term="One-time PIN" scheme="https://israynotarray.com/tags/One-time-PIN/"/>
    
    <category term="預覽部署" scheme="https://israynotarray.com/tags/%E9%A0%90%E8%A6%BD%E9%83%A8%E7%BD%B2/"/>
    
  </entry>
  
  <entry>
    <title>RSS 是什麼？為什麼 2026 年了還要用 RSS？順便聊聊我自己寫的 Chrome 擴充套件 Raydar</title>
    <link href="https://israynotarray.com/other/20260511/5847291063/"/>
    <id>https://israynotarray.com/other/20260511/5847291063/</id>
    <published>2026-05-11T06:00:00.000Z</published>
    <updated>2026-05-11T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/rss-ray/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8811%E6%97%A5%20%E4%B8%8A%E5%8D%8809_32_28.png" data-fancybox="gallery" data-caption="RSS 是什麼？為什麼 2026 年了還要用 RSS？順便聊聊我自己寫的 Chrome 擴充套件 Raydar"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/rss-ray/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%8811%E6%97%A5%20%E4%B8%8A%E5%8D%8809_32_28.png" alt="RSS 是什麼？為什麼 2026 年了還要用 RSS？順便聊聊我自己寫的 Chrome 擴充套件 Raydar"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">RSS 是什麼？為什麼 2026 年了還要用 RSS？順便聊聊我自己寫的 Chrome 擴充套件 Raydar</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇從 RSS 是什麼、跟訂閱電子報差在哪裡、為什麼 2026 年了還值得用 RSS、跟演算法推送的差異、Atom 是什麼，一路聊到我自己寫了一個 Chrome RSS 閱讀器擴充套件 Raydar，包含 RSS 自動偵測、一鍵訂閱、三種閱讀模式、本地優先儲存、無帳戶、OPML 匯入匯出、JSON 備份、鍵盤快捷鍵等功能，最後也會說明怎麼裝跟為什麼會想自己刻一個。</p><h2 id="事情是這樣的"><a href="#事情是這樣的" class="headerlink" title="事情是這樣的"></a>事情是這樣的</h2><p>這幾天我把自己寫的 Chrome 擴充套件收尾收得差不多了，是一個 RSS 閱讀器，名字叫 Raydar。</p><p>寫完之後跟朋友隨口聊了一下，結果有個朋友反問我「RSS 是什麼？」，我才意識到一件事，RSS 對工程師圈來講雖然不算冷門，可是對一般人來講，這個詞真的已經很久沒出現在主流的視野了。</p><p>再加上 2013 年 Google Reader 收掉之後，網路上有滿多文章寫說「RSS 已死」，導致很多新手會直接跳過這個東西，覺得它是「上個時代的技術」。</p><p>可是說真的，在現在這個演算法越來越霸道、IG &#x2F; X 各種重新排序、新聞網站 paywall 越開越多的時代，RSS 反而是少數還能讓你「自己決定要看什麼」的方法之一，所以這一篇就想說來介紹一下 RSS 是什麼、為什麼還值得用，然後最後也順便聊聊我寫的這個小工具哩～</p><h2 id="RSS-到底是什麼？"><a href="#RSS-到底是什麼？" class="headerlink" title="RSS 到底是什麼？"></a>RSS 到底是什麼？</h2><p>簡單來講，<strong>RSS 是一份網站對外公開的「最新內容清單」</strong>，格式是 XML，內容包含每篇文章的標題、連結、發佈時間、摘要等等。</p><p>什麼意思呢？舉個實際的例子比較好懂。</p><p>假設你今天打開我的部落格首頁，你看到的是一個「為了人類眼睛而排版」的頁面，有導覽列、有側邊欄、有廣告、有各種互動元素。</p><p>那如果你今天打開的不是首頁，而是 <code>https://israynotarray.com/atom.xml</code>，你看到的是一份這樣的東西</p><figure class="highlight xml"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br><span class="line">7</span><br><span class="line">8</span><br><span class="line">9</span><br><span class="line">10</span><br><span class="line">11</span><br></pre></td><td class="code"><pre><code class="hljs xml"><span class="hljs-meta">&lt;?xml version=<span class="hljs-string">&quot;1.0&quot;</span> encoding=<span class="hljs-string">&quot;utf-8&quot;</span>?&gt;</span><br><span class="hljs-tag">&lt;<span class="hljs-name">feed</span> <span class="hljs-attr">xmlns</span>=<span class="hljs-string">&quot;http://www.w3.org/2005/Atom&quot;</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>是 Ray 不是 Array<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>  <span class="hljs-tag">&lt;<span class="hljs-name">entry</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">title</span>&gt;</span>RSS 是什麼？...<span class="hljs-tag">&lt;/<span class="hljs-name">title</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">link</span> <span class="hljs-attr">href</span>=<span class="hljs-string">&quot;https://israynotarray.com/other/.../&quot;</span> /&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">published</span>&gt;</span>2026-05-11T14:00:00.000Z<span class="hljs-tag">&lt;/<span class="hljs-name">published</span>&gt;</span><br>    <span class="hljs-tag">&lt;<span class="hljs-name">summary</span>&gt;</span>這篇從 RSS 是什麼...<span class="hljs-tag">&lt;/<span class="hljs-name">summary</span>&gt;</span><br>  <span class="hljs-tag">&lt;/<span class="hljs-name">entry</span>&gt;</span><br>  ...<br><span class="hljs-tag">&lt;/<span class="hljs-name">feed</span>&gt;</span><br></code></pre></td></tr></table></figure><p>這就是 RSS（嚴格來講上面是 Atom，可是先不管，後面會講差別）。</p><p>它是一份「給機器讀的版本」，沒有 CSS、沒有 JavaScript、沒有廣告，就是純粹的內容清單。RSS 閱讀器就是負責去定期抓這些 XML 檔案，把所有訂閱來源的最新文章匯集到一個地方給你看。</p><h3 id="用一個生活化的比喻"><a href="#用一個生活化的比喻" class="headerlink" title="用一個生活化的比喻"></a>用一個生活化的比喻</h3><p>如果用比較生活面的例子來講的話，RSS 比較像「報紙的訂閱機制，可是反過來」。</p><p>傳統訂報紙是報社主動把報紙送到你家信箱，RSS 則是你主動去報社門口看「有沒有出新一期」，有的話就拿回家看。差別在於你不需要把地址（email）告訴任何人，純粹是你去拿，對方甚至不知道你是誰。</p><p>這個「你去拿、對方不知道是誰」的特性，是 RSS 在這個資料追蹤越來越嚴重的時代滿值得重提的一個賣點。</p><h2 id="RSS-跟訂閱電子報差在哪？"><a href="#RSS-跟訂閱電子報差在哪？" class="headerlink" title="RSS 跟訂閱電子報差在哪？"></a>RSS 跟訂閱電子報差在哪？</h2><p>這幾年 Newsletter（電子報）很紅，例如 Substack、ConvertKit 等等，所以滿多人會問 RSS 跟 Newsletter 是不是同一個東西。</p><p>答案是不一樣，差別還滿大的，這邊整理一下</p><div class="table-wrapper"><table><thead><tr><th>面向</th><th>RSS</th><th>電子報（Newsletter）</th></tr></thead><tbody><tr><td>推送方式</td><td>你主動去拉資料</td><td>對方主動寄到你信箱</td></tr><tr><td>需要的資訊</td><td>不需要 email</td><td>必須給 email</td></tr><tr><td>隱私</td><td>對方不知道你是誰</td><td>對方有你的 email、可以做開信追蹤</td></tr><tr><td>取消訂閱</td><td>你自己從閱讀器移除即可</td><td>要找到取消連結，有時候會被忽略</td></tr><tr><td>內容呈現</td><td>純內容，不含追蹤元件</td><td>通常會嵌入追蹤像素、UTM 參數</td></tr><tr><td>集中查看</td><td>一個閱讀器看所有來源</td><td>信箱跟個人通訊混在一起</td></tr></tbody></table></div><p>從這張表可以看出來，RSS 跟電子報走的是兩條不同的路線。電子報的優點是被動推送、作者跟讀者距離近，可是缺點是你的信箱會被資訊洗版，而且你的個資（email）會被很多人拿到。</p><p>RSS 則是相反，你自己決定要不要去拉、什麼時候拉、用什麼工具讀，是一種更「資料主權留在自己手上」的方式。</p><h2 id="為什麼-2026-年還要用-RSS？"><a href="#為什麼-2026-年還要用-RSS？" class="headerlink" title="為什麼 2026 年還要用 RSS？"></a>為什麼 2026 年還要用 RSS？</h2><p>接下來這部分可能比較主觀，可是我覺得有幾個原因，是這幾年讓我重新認真用 RSS 的關鍵。</p><h3 id="演算法已經吃掉你大部分的注意力"><a href="#演算法已經吃掉你大部分的注意力" class="headerlink" title="演算法已經吃掉你大部分的注意力"></a>演算法已經吃掉你大部分的注意力</h3><p>你應該有遇過這種狀況，打開 IG 滑了 20 分鐘，發現自己都在看一些根本沒追蹤的人的影片；打開 X 想找某個人的最新動態，結果首頁全部都是被推薦的奇怪內容；打開 YouTube 想看訂閱頻道，結果首頁推薦的東西比訂閱還多。</p><p>這不是錯覺，這是平台的設計。</p><p>平台希望你停留越久越好，所以它會用各種方式塞「它認為你會看得下去」的內容給你，而你「主動訂閱誰」這件事，反而被排在很後面。</p><p>RSS 沒有這件事。你訂了誰，就只會看到誰，按發布時間從新到舊排列，乾乾淨淨。</p><h3 id="你不會錯過你真正在意的內容"><a href="#你不會錯過你真正在意的內容" class="headerlink" title="你不會錯過你真正在意的內容"></a>你不會錯過你真正在意的內容</h3><p>承接上面那點，正因為演算法會幫你「過濾」內容，所以你真正在追蹤的某些小眾來源，常常會被忽略掉。</p><p>以我自己來講，我有訂幾個寫得很好可是更新頻率很低的個人部落格，這種一個月可能才出一篇的作者，在 IG &#x2F; Twitter 完全打不過演算法，可是在 RSS 閱讀器裡，他發了就是會出現在我的時間軸上，不會被埋掉。</p><h3 id="一次處理所有資訊來源"><a href="#一次處理所有資訊來源" class="headerlink" title="一次處理所有資訊來源"></a>一次處理所有資訊來源</h3><p>技術部落格、新聞網站、開源專案的 Release、Hacker News、Reddit 的特定 subreddit、GitHub trending、甚至 Mastodon、Bluesky 都可以走 RSS，所以一旦你習慣了，你會發現不再需要這麼多 APP 各自打開查資訊，一個閱讀器全部搞定。</p><h3 id="AI-時代資料來源反而更重要"><a href="#AI-時代資料來源反而更重要" class="headerlink" title="AI 時代資料來源反而更重要"></a>AI 時代資料來源反而更重要</h3><p>這個比較少人聊。</p><p>AI 模型訓練的時候，其實滿依賴「結構化、語意清楚」的內容，而 RSS 友善的網站通常也是內容語意清楚的網站，所以未來 AI 在替你做摘要、整理資訊的時候，RSS feed 反而會變成一個重要的「乾淨資料源」。</p><p>換句話說，RSS 不只是給人看的，未來你想自己接一些 AI 工具來自動化資訊處理，RSS 也是最容易切入的入口。</p><h2 id="Atom-又是什麼？跟-RSS-差別在哪？"><a href="#Atom-又是什麼？跟-RSS-差別在哪？" class="headerlink" title="Atom 又是什麼？跟 RSS 差別在哪？"></a>Atom 又是什麼？跟 RSS 差別在哪？</h2><p>前面有提到 Atom，這邊稍微說明一下。</p><p>Atom 跟 RSS 簡單來講是兩份不同的規格文件，可是做的事情幾乎一樣，都是把網站的最新內容用 XML 格式公開出來。差別大概是這樣</p><ul><li>RSS（特別是 2.0 版）：比較老，欄位定義比較寬鬆，所以不同網站產出的 RSS 有可能格式不太一樣</li><li>Atom：後來才提出的標準，欄位定義比較嚴謹，例如時間欄位強制要 RFC 3339 格式</li></ul><p>對使用者來講，這兩個其實沒什麼差，因為現在的 RSS 閱讀器通常兩種都吃，網站也常常兩種都提供。你訂的時候閱讀器會自動處理，不用煩惱是哪一種。</p><blockquote><p><strong>Note</strong><br>除了 RSS 跟 Atom 之外，還有一個更老的格式叫 RDF（RSS 1.0），可是現在已經很少網站還在用，遇到的機率不高。</p></blockquote><h2 id="RSS-閱讀器有哪些選擇？"><a href="#RSS-閱讀器有哪些選擇？" class="headerlink" title="RSS 閱讀器有哪些選擇？"></a>RSS 閱讀器有哪些選擇？</h2><p>聊完規格，接下來就是工具。RSS 閱讀器市面上其實還滿多選擇的，這邊列幾個比較常見的</p><ul><li><strong>Feedly</strong>：應該是目前最知名的雲端 RSS 閱讀器，免費版有功能限制</li><li><strong>Inoreader</strong>：另一個雲端 RSS 閱讀器，功能比 Feedly 多一點</li><li><strong>NetNewsWire</strong>：macOS &#x2F; iOS 原生 APP，開源免費</li><li><strong>Reeder</strong>：Apple 平台老牌付費 APP</li><li><strong>Miniflux</strong>：可以自架的開源 RSS 閱讀器，走極簡路線</li></ul><p>這些選擇各有各的好，可是我自己用了一輪之後，發現幾個共同的問題</p><ol><li>雲端服務都要註冊帳號、訂閱資料會跑到對方伺服器</li><li>多數要嘛是要付費才有完整功能，要嘛就是要自己架機器</li><li>桌面端的原生 APP 大多沒有 Chrome 擴充套件這種「順手在瀏覽器發現 RSS 就訂」的體驗</li></ol><p>所以我就想說，乾脆自己寫一個。</p><h2 id="我自己寫的-Chrome-擴充套件：Raydar"><a href="#我自己寫的-Chrome-擴充套件：Raydar" class="headerlink" title="我自己寫的 Chrome 擴充套件：Raydar"></a>我自己寫的 Chrome 擴充套件：Raydar</h2><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/rss-ray/unnamed.png" data-fancybox="gallery" data-caption="Raydar"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/rss-ray/unnamed.png" alt="Raydar"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Raydar</figcaption></figure><p>Raydar 是我這幾個月寫的 Chrome 擴充套件，目標就是做一個<strong>本地優先、不需要帳戶</strong>的極簡 RSS 閱讀器，把雲端 reader 的那些「要先註冊才能用」的步驟通通拿掉。</p><p>擴充套件已經上架 Chrome Web Store：<a href="https://chromewebstore.google.com/detail/raydar/gcmmbglgnmjdljakigbfchodmjnimpgg">Raydar — Chrome Web Store</a>。</p><p>接下來介紹幾個我自己覺得比較有意思的特色。</p><h3 id="邊瀏覽邊偵測-RSS"><a href="#邊瀏覽邊偵測-RSS" class="headerlink" title="邊瀏覽邊偵測 RSS"></a>邊瀏覽邊偵測 RSS</h3><p>這是一開始就想做的功能。你應該有遇過一種狀況，你在某個部落格看到一篇很喜歡的文章，想訂閱可是找不到 RSS 連結在哪。</p><p>Raydar 會在你瀏覽任何網頁的時候自動掃描頁面有沒有 RSS &#x2F; Atom 的 <code>&lt;link rel=&quot;alternate&quot;&gt;</code> 標籤，有的話會跳出一個訂閱提示，按一下就完成訂閱，不用自己翻原始碼或挖 URL。</p><p>如果某個網站沒有對外 expose 這種 link 標籤，你也可以從工具列的 popup 點「訂閱目前的 feed」手動加入。</p><h3 id="三種閱讀模式"><a href="#三種閱讀模式" class="headerlink" title="三種閱讀模式"></a>三種閱讀模式</h3><p>讀文章的時候每個人偏好不太一樣，所以 Raydar 提供三種模式</p><ul><li><strong>閱讀器模式</strong>：把文章內文擷取出來，乾乾淨淨的排版，可以自己調字級、深淺色</li><li><strong>Web 模式</strong>：直接把原網頁嵌進閱讀頁，不用離開分頁</li><li><strong>原始 RSS 模式</strong>：直接看 feed 的 XML，給開發者除錯用</li></ul><p>我自己最常用的是閱讀器模式，因為很多新聞網站的版型實在太花，閱讀器模式可以讓你只看到該看到的東西。</p><h3 id="完全本地優先，不需要註冊"><a href="#完全本地優先，不需要註冊" class="headerlink" title="完全本地優先，不需要註冊"></a>完全本地優先，不需要註冊</h3><p>Raydar 不需要註冊、不需要 email、不需要密碼，裝完就用。</p><p>訂閱清單、文章快取、設定全部存在你瀏覽器本地，跨裝置同步走的是 Chrome 內建的 <code>chrome.storage.sync</code>，由 Google 加密處理，作者完全碰不到任何使用者的訂閱資料。</p><p>也沒有接 Google Analytics、沒有崩潰回報、沒有 AI API、沒有 Newsletter bridge，全部都是純本地處理。</p><h3 id="鍵盤快捷鍵"><a href="#鍵盤快捷鍵" class="headerlink" title="鍵盤快捷鍵"></a>鍵盤快捷鍵</h3><p>對重度閱讀的人來講鍵盤快捷鍵是必備，這個我也補上了</p><div class="table-wrapper"><table><thead><tr><th>快捷鍵</th><th>功能</th></tr></thead><tbody><tr><td><code>J</code> &#x2F; <code>K</code></td><td>切換上下篇文章</td></tr><tr><td><code>S</code></td><td>收藏 &#x2F; 取消收藏</td></tr><tr><td><code>M</code></td><td>切換已讀 &#x2F; 未讀</td></tr><tr><td><code>O</code></td><td>在新分頁開啟原文</td></tr><tr><td><code>Shift + R</code></td><td>重新刷新所有訂閱</td></tr><tr><td><code>?</code></td><td>顯示快捷鍵說明</td></tr></tbody></table></div><p>設計上是參考 Gmail、Google Reader 那一套 J&#x2F;K 慣例，所以用過類似工具的人應該會很順手。</p><h3 id="全文搜尋是純本地的"><a href="#全文搜尋是純本地的" class="headerlink" title="全文搜尋是純本地的"></a>全文搜尋是純本地的</h3><p>訂閱量大了之後，搜尋會變成必備功能。</p><p>Raydar 的全文搜尋用的是 <a href="https://github.com/lucaong/minisearch">MiniSearch</a>，整個索引在瀏覽器記憶體裡建立，搜尋過程不會跟任何伺服器互動。中文跟日文還有特別處理斷詞，所以搜「Vue 元件」這種詞也找得到，不會卡在英文斷詞邏輯上。</p><h3 id="資料可攜、隨時搬家"><a href="#資料可攜、隨時搬家" class="headerlink" title="資料可攜、隨時搬家"></a>資料可攜、隨時搬家</h3><p>我自己很在意一件事，就是「使用者要離開的時候不要有摩擦」。所以 Raydar 從一開始就把資料匯入匯出做好</p><ul><li><strong>OPML 匯入匯出</strong>：跟其他 RSS 閱讀器無縫遷移</li><li><strong>JSON 完整備份</strong>：訂閱清單、文章快取、設定、收藏一鍵打包</li></ul><p>也就是說你今天不喜歡 Raydar 了，匯出 OPML 丟到 Feedly &#x2F; Inoreader &#x2F; NetNewsWire 都可以，不會被鎖住。</p><h3 id="其他"><a href="#其他" class="headerlink" title="其他"></a>其他</h3><p>除了上面這些主要功能，還有一些細節</p><ul><li>繁體中文、English、System 三段語言切換</li><li>Light &#x2F; Dark &#x2F; System 三段主題</li><li>未讀數會顯示在 toolbar 圖示的 badge 上，一眼看到還有多少新文章</li><li>標籤分類、來源置頂、關鍵字 mute 等等管理工具</li></ul><p>整體就是想做成那種「裝起來就忘記它存在、可是每天打開來看資訊很順手」的工具。</p><h2 id="怎麼裝"><a href="#怎麼裝" class="headerlink" title="怎麼裝"></a>怎麼裝</h2><p>Raydar 已經上架 Chrome Web Store，直接點下面這個連結，再按「加入 Chrome」就可以了</p><p><a href="https://chromewebstore.google.com/detail/raydar/gcmmbglgnmjdljakigbfchodmjnimpgg">Raydar — Chrome Web Store</a></p><p>裝完之後在工具列點一下 Raydar 圖示，會跳出 popup，第一次使用裡面會有提示，按照流程走就可以開始訂閱了。</p><p>如果你之前已經有在用其他 RSS 閱讀器，記得把舊閱讀器的 OPML 匯出，丟到 Raydar 的設定頁就可以一次匯入所有訂閱來源囉。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>RSS 真的不是死去的技術，它只是不再是主流話題而已。</p><p>對於想要遠離演算法、想要主動掌握資訊來源、不希望自己被推薦系統綁架的人來講，RSS 是一個用了之後就回不去的東西，希望這一篇可以讓本來不知道 RSS 的人有個比較具體的認識，也希望 Raydar 對於本來就在用 RSS 的人，是一個值得試一下的選項哩～</p><p>如果用了之後覺得不錯，回報 bug 或想要的功能也都歡迎，我們下篇文章見囉～</p>]]></content>
    
    
    <summary type="html">這篇從 RSS 是什麼、跟訂閱電子報差在哪裡、為什麼 2026 年了還值得用 RSS、跟演算法推送的差異、Atom 是什麼，一路聊到我自己寫了一個 Chrome RSS 閱讀器擴充套件 Raydar，包含 RSS 自動偵測、一鍵訂閱、三種閱讀模式、本地優先儲存、無帳戶、OPML 匯入匯出、JSON 備份、鍵盤快捷鍵等功能，最後也會說明怎麼裝跟為什麼會想自己刻一個。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="RSS" scheme="https://israynotarray.com/tags/RSS/"/>
    
    <category term="Atom" scheme="https://israynotarray.com/tags/Atom/"/>
    
    <category term="RSS Reader" scheme="https://israynotarray.com/tags/RSS-Reader/"/>
    
    <category term="Raydar" scheme="https://israynotarray.com/tags/Raydar/"/>
    
    <category term="Chrome 擴充套件" scheme="https://israynotarray.com/tags/Chrome-%E6%93%B4%E5%85%85%E5%A5%97%E4%BB%B6/"/>
    
    <category term="訂閱" scheme="https://israynotarray.com/tags/%E8%A8%82%E9%96%B1/"/>
    
    <category term="資訊管理" scheme="https://israynotarray.com/tags/%E8%B3%87%E8%A8%8A%E7%AE%A1%E7%90%86/"/>
    
    <category term="OPML" scheme="https://israynotarray.com/tags/OPML/"/>
    
  </entry>
  
  <entry>
    <title>localhost 跟 127.0.0.1 是什麼？工程師笑話與「拒絕連線」常見原因解析</title>
    <link href="https://israynotarray.com/other/20260505/7384502619/"/>
    <id>https://israynotarray.com/other/20260505/7384502619/</id>
    <published>2026-05-04T16:00:00.000Z</published>
    <updated>2026-05-04T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/localhost-hostname/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%885%E6%97%A5%20%E4%B8%8A%E5%8D%8810_03_07.png" data-fancybox="gallery" data-caption="localhost 跟 127.0.0.1 是什麼？工程師笑話與「拒絕連線」常見原因解析"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/localhost-hostname/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%885%E6%97%A5%20%E4%B8%8A%E5%8D%8810_03_07.png" alt="localhost 跟 127.0.0.1 是什麼？工程師笑話與「拒絕連線」常見原因解析"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">localhost 跟 127.0.0.1 是什麼？工程師笑話與「拒絕連線」常見原因解析</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這篇把 localhost 跟 127.0.0.1 從 hostname 解析、loopback 的概念、127.0.0.0&#x2F;8 從 RFC 790 到 RFC 1122 的歷史小故事，到 2005 年那個被 Slashdot 稱為「地表最蠢駭客」的德國 IRC bitchchecker 事件 — 攻擊 127.0.0.1 結果把自己電腦打掛 — 一次講清楚。後半段也整理本機開發遇到「localhost 拒絕連線」最常見的原因，包含 dev server 沒啟動、port 被佔用、瀏覽器 HSTS 強制 HTTPS、hosts 檔案、IPv6 ::1 解析衝突、防火牆等情境的排查方式。</p><p>如果你還對 port 這件事有點模糊，建議先讀一下 <a href="https://israynotarray.com/other/20250605/2959757908/">為什麼大家都用 3000？一文搞懂 Port 的用途與選擇</a>，那一篇可以跟這一篇一起看，會比較完整。</p><h2 id="事情是這樣的"><a href="#事情是這樣的" class="headerlink" title="事情是這樣的"></a>事情是這樣的</h2><p>最近又看到網路上有人在轉那篇 2005 年 IRC 上的 bitchchecker 事件 — 一位自稱駭客的德國網友嗆說要駭爆管理員，結果攻擊 127.0.0.1 攻擊到把自己電腦打掛 — 順便也翻了 IETF 的 RFC 文件，看到 127 這個數字背後其實有一段滿有趣的小故事。</p><p>這時候我想到一件事，這幾年看下來，雖然 localhost 跟 127.0.0.1 是入門開發第一週就會遇到的東西，可是真的能講清楚它們是不是同一個東西、為什麼是 127、拒絕連線到底在拒絕什麼的人其實沒有想像中的多。</p><p>所以這一篇就把概念、歷史、笑話、踩坑通通寫進來，希望看完之後你下次再遇到拒絕連線錯誤的時候，可以比較有底氣的去排查問題。</p><h2 id="localhost-跟-127-0-0-1-到底是什麼？"><a href="#localhost-跟-127-0-0-1-到底是什麼？" class="headerlink" title="localhost 跟 127.0.0.1 到底是什麼？"></a>localhost 跟 127.0.0.1 到底是什麼？</h2><p>簡單來講，<strong>127.0.0.1 是 IP 位址，localhost 是 hostname（主機名稱）</strong>，作業系統會幫你把 localhost 翻譯成 127.0.0.1。</p><p>這兩個東西指的是同一個目的地，可是它們的身份不一樣，這個差異在某些踩坑情境下會非常重要。</p><h3 id="localhost-是給人看的名字"><a href="#localhost-是給人看的名字" class="headerlink" title="localhost 是給人看的名字"></a>localhost 是給人看的名字</h3><p>localhost 不是一組 IP 位址，它是一個 hostname，意思就是這台電腦自己。當你在瀏覽器輸入 <code>http://localhost:3000</code>，你的作業系統會去翻一個叫做 hosts 的檔案，把 localhost 對應到一組 IP 位址。</p><p>hosts 檔案的位置如下：</p><div class="table-wrapper"><table><thead><tr><th>系統</th><th>hosts 檔案路徑</th></tr></thead><tbody><tr><td>macOS &#x2F; Linux</td><td><code>/etc/hosts</code></td></tr><tr><td>Windows</td><td><code>C:\Windows\System32\drivers\etc\hosts</code></td></tr></tbody></table></div><p>打開來看，你會看到類似這樣的內容</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">127.0.0.1       localhost<br>::1             localhost<br></code></pre></td></tr></table></figure><p>意思就是 localhost 這個名字會被翻譯成 127.0.0.1（IPv4）or ::1（IPv6）。所以當你瀏覽器送出 <code>http://localhost:3000</code> 的請求，背後其實是被解析成 <code>http://127.0.0.1:3000</code> 才送出去的。</p><h3 id="127-0-0-1-是真正的-IP-位址"><a href="#127-0-0-1-是真正的-IP-位址" class="headerlink" title="127.0.0.1 是真正的 IP 位址"></a>127.0.0.1 是真正的 IP 位址</h3><p>127.0.0.1 是一組真實存在的 IPv4 位址，它有一個專有名稱叫做 <strong>loopback address</strong>（中文叫做迴路位址）。</p><p>什麼是 loopback？簡單來講，當你送一個封包到 127.0.0.1，這個封包不會真的跑到外面的網路 or 路由器，而是直接在你電腦內部繞一圈就回來了。</p><p>如果用比較生活面的例子來講的話，假設你寄了一封信，可是這封信的收件人是你自己，那這封信根本不需要交給郵差，你直接從信箱拿出來自己看就好了。loopback 就是這個概念。</p><p>更冷門的一個事實是，並不是只有 127.0.0.1 是 loopback，整個 <code>127.0.0.0/8</code> 區段（也就是從 127.0.0.0 到 127.255.255.254，超過 1600 萬個位址）都是 loopback 用途，全部都會繞回自己這台電腦。只是因為 127.0.0.1 最方便、最好記，所以大家慣例上都用它。</p><h3 id="那-IPv6-的-1-又是什麼？"><a href="#那-IPv6-的-1-又是什麼？" class="headerlink" title="那 IPv6 的 ::1 又是什麼？"></a>那 IPv6 的 ::1 又是什麼？</h3><p><code>::1</code> 是 IPv6 版本的 loopback 位址。</p><p>IPv4 用 32 bit、IPv6 用 128 bit，IPv6 完整寫起來是 <code>0000:0000:0000:0000:0000:0000:0000:0001</code>，可是這樣寫太煩人，所以縮寫成 <code>::1</code>。</p><blockquote><p><strong>Note</strong><br>IPv6 的 loopback 只有 <code>::1</code> 這一個位址，不像 IPv4 是整個 <code>127.0.0.0/8</code> 區段那麼大方。</p></blockquote><p>這個東西看似不重要，可是在本機開發踩雷的時候它會跳出來嚇你，後面會聊到。</p><h2 id="為什麼是-127？關於-loopback-的小故事"><a href="#為什麼是-127？關於-loopback-的小故事" class="headerlink" title="為什麼是 127？關於 loopback 的小故事"></a>為什麼是 127？關於 loopback 的小故事</h2><p>接下來來聊聊歷史。為什麼是 127 這個數字，而不是 1、2、100 之類的呢？</p><p>老實說，<strong>沒有特別偉大的理由，純粹是慣例</strong>。</p><p>把時間拉回 1981 年，當時 Jon Postel（網際網路歷史上最重要的工程師之一）在寫 <a href="https://datatracker.ietf.org/doc/html/rfc790">RFC 790</a> 這份 <code>Assigned Numbers</code> 文件的時候，他習慣會把 Class A IP 位址範圍裡的第一個跟最後一個網路號保留起來，當時 IPv4 Class A 的網路號從 0 到 127，於是 127 就被他先保留下來了，可是當下他並沒有寫明這個保留是要做什麼用的。</p><p>到了 1983 年，柏克萊大學在 4.2BSD 的程式碼裡面，由 Joy 跟 Leffler 把 127.0.0.1 實作成 loopback 位址（早期實驗版本他們其實還用過 254.0.0.0），這個實作被廣泛採用之後，127 就變成大家都默認的 loopback 數字。</p><p>接著 1986 年，Jon Postel 跟 Joyce Reynolds 在 <a href="https://datatracker.ietf.org/doc/html/rfc990">RFC 990</a> 把 loopback 這件事正式記錄下來，可是仍然只是 assignment 等級的描述而已。</p><p>最後到 1989 年的 <a href="https://datatracker.ietf.org/doc/html/rfc1122">RFC 1122</a>，才把 127.0.0.0&#x2F;8 強制定義為 internal host loopback address，並且寫明這個位址不可以出現在主機外的網路上。</p><p>整個過程簡單講就是：</p><div class="table-wrapper"><table><thead><tr><th>年份</th><th>文件 &#x2F; 事件</th><th>做了什麼</th></tr></thead><tbody><tr><td>1981</td><td>RFC 790</td><td>Jon Postel 把 127 保留，沒寫用途</td></tr><tr><td>1983</td><td>4.2BSD</td><td>程式碼裡實作成 loopback 位址</td></tr><tr><td>1986</td><td>RFC 990</td><td>正式記錄 loopback 這個用法</td></tr><tr><td>1989</td><td>RFC 1122</td><td>強制 127.0.0.0&#x2F;8 整段都是 loopback</td></tr></tbody></table></div><p>從這張表可以看出來，loopback 是先有慣例、後來才寫進標準的典型案例。下次有人問你為什麼是 127，你就可以告訴他這個答案：Jon Postel 1981 年隨手保留了，柏克萊在 1983 年拿來用，後來大家都這樣用，最後才寫進 RFC。</p><h2 id="那個用-127-0-0-1-把自己電腦打掛的德國駭客"><a href="#那個用-127-0-0-1-把自己電腦打掛的德國駭客" class="headerlink" title="那個用 127.0.0.1 把自己電腦打掛的德國駭客"></a>那個用 127.0.0.1 把自己電腦打掛的德國駭客</h2><p>接下來這個才是工程師圈真正流傳最廣的 127.0.0.1 笑話 — 重點是它<strong>真實發生過</strong>。</p><p>2005 年 4 月，德國一個嘻哈音樂網站 StopHipHop 的 IRC 頻道 <code>#stophiphop</code> 上，有一位 ID 叫做 bitchchecker 的使用者（中文圈轉述常常變成「某個德國小男孩」，原始 log 自稱 26 歲）在頻道裡鬧事被踢出去，於是嗆聲說要駭爆管理員 Elch 的電腦。</p><p>接下來他做的事情，後來在 Slashdot 上被冠上一個稱號 — <strong>The Planet’s Most Moronic Hacker（地表最蠢駭客）</strong>。</p><h3 id="事件經過"><a href="#事件經過" class="headerlink" title="事件經過"></a>事件經過</h3><p>bitchchecker 嗆完之後，Elch 想了一下，淡淡丟出一句</p><blockquote><p>好啊，那你來駭我，我的 IP 是 <code>127.0.0.1</code>。</p></blockquote><p>bitchchecker 信了。他打開手上號稱可以遠端刪除別人硬碟的「神兵利器」，把 <code>127.0.0.1</code> 輸進去，按下發射。</p><p>幾秒後，IRC 頻道上跳出第一條訊息</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">bitchchecker has quit (Ping timeout)<br></code></pre></td></tr></table></figure><p>什麼意思呢？簡單來講就是 bitchchecker 自己斷線了。因為他攻擊的 127.0.0.1 就是他自己這台電腦，他親手把自己的網路給打掉了。</p><p>過了幾分鐘他重新連回頻道，氣呼呼</p><blockquote><p>你一定是開了防火牆！把防火牆關掉！</p></blockquote><p>旁邊看戲的人立刻吐槽</p><blockquote><p>真正的駭客是突破防火牆吧，哪有人請對方關防火牆的。</p></blockquote><p>bitchchecker 不死心，再攻擊一次。然後</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br></pre></td><td class="code"><pre><code class="hljs txt">*** bitchchecker has quit (Ping timeout)<br></code></pre></td></tr></table></figure><p>又一次。如此反覆好幾輪。</p><p>到後面他開始為了面子虛報戰績，跟 Elch 嗆</p><blockquote><p>我看到你的 E: 槽在縮小！D: 已經少 45% 了！我正在打 C:！</p></blockquote><p>Elch 跟旁邊的人也很惡趣味，配合假裝慌張說「啊我的硬碟真的怪怪的！」bitchchecker 越打越得意，然後不出所料 — 又一次 Ping timeout，整場戲就這樣結束。</p><h3 id="為什麼這個故事這麼經典？"><a href="#為什麼這個故事這麼經典？" class="headerlink" title="為什麼這個故事這麼經典？"></a>為什麼這個故事這麼經典？</h3><p>它把 127.0.0.1 這個概念用最爆笑的方式講清楚：<strong>送到 127.0.0.1 的封包永遠會回到自己這台電腦，所以「攻擊 127.0.0.1」就是在攻擊自己</strong>，bitchchecker 每按一次發射，斷的都是他自己的網。</p><p>更荒謬的是，他不但不知道為什麼斷線，還反過來怪 Elch 的防火牆太強，最後甚至開始幫自己編戰績。整個過程把一個技術概念跟人類的執著一次演完。</p><p>所以下次有人嗆說要駭你的電腦，你可以淡淡回他一句</p><blockquote><p>好啊，我的 IP 是 <code>127.0.0.1</code>，盡量打。</p></blockquote><p>附帶一提，工程師圈也有一句改編自《綠野仙蹤》(The Wizard of Oz) 的口號 — <code>There&#39;s no place like 127.0.0.1</code>（沒有地方比 127.0.0.1 更像家）— 玩的同樣是「127.0.0.1 永遠回到自己」這個梗，常常被印在 T-shirt 跟貼紙上面。可是論名氣，它在 bitchchecker 事件面前還是矮了一截。</p><h2 id="為什麼會出現「localhost-拒絕連線」？"><a href="#為什麼會出現「localhost-拒絕連線」？" class="headerlink" title="為什麼會出現「localhost 拒絕連線」？"></a>為什麼會出現「localhost 拒絕連線」？</h2><p>聊完輕鬆的，來聊一下實戰的部分。</p><p>「localhost 拒絕連線」（在 Chrome&#x2F;Edge 是 <code>ERR_CONNECTION_REFUSED</code>，Firefox 會說「無法連線」）這個錯誤背後的意思很簡單，就是<strong>你的瀏覽器送了一個封包到 127.0.0.1 的某個 port，可是那個 port 上面沒有東西在聽，或是有東西擋住了它</strong>。</p><p>底下整理幾種最常見的原因，依照踩到的機率由高到低排序。</p><h3 id="dev-server-根本沒啟動（最常見）"><a href="#dev-server-根本沒啟動（最常見）" class="headerlink" title="dev server 根本沒啟動（最常見）"></a>dev server 根本沒啟動（最常見）</h3><p>這個是真的最常發生，可是也最容易被忽略，特別是熬夜寫到一半閃神的時候。</p><p>假設你今天要開瀏覽器看 <code>http://localhost:3000</code>，那這個 port 上面要有一個東西在聽才行。檢查方式很簡單</p><ul><li>看你的終端機有沒有印出類似 <code>Server is running at http://localhost:3000</code> 的訊息</li><li>如果沒有，代表你忘了啟動 dev server，跑一下 <code>npm run dev</code> or <code>pnpm dev</code> or <code>yarn dev</code> 就好</li></ul><p>聽起來很白癡，可是這個是排查 connection refused 的第一步，先確認伺服器真的有在跑，再來查其他的。</p><h3 id="port-寫錯-or-被佔用"><a href="#port-寫錯-or-被佔用" class="headerlink" title="port 寫錯 or 被佔用"></a>port 寫錯 or 被佔用</h3><p>第二常見的是 port 對不起來。譬如你的 dev server 跑在 <code>5173</code>（Vite 預設），可是你瀏覽器卻打成 <code>3000</code>，那當然連不到。</p><p>另一種情況是 port 被別的程式佔用了，這時候你 dev server 啟動的時候會報錯，or 它會自己跳到下一個可用的 port，所以還是要回去看終端機的訊息。</p><p>要檢查一個 port 到底是不是被佔用，可以用以下指令</p><figure class="highlight bash"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs bash"><span class="hljs-comment"># macOS / Linux</span><br>lsof -i :3000<br><br><span class="hljs-comment"># Windows</span><br>netstat -aon | findstr :3000<br></code></pre></td></tr></table></figure><p>如果發現被佔用，可以參考 <a href="https://israynotarray.com/other/20220117/241830554/">Mac 系統 Port 號被佔用的解決方式</a> 把佔用的程序殺掉，or 直接把 dev server 改用別的 port。</p><h3 id="瀏覽器自動把-http-強制變成-https"><a href="#瀏覽器自動把-http-強制變成-https" class="headerlink" title="瀏覽器自動把 http 強制變成 https"></a>瀏覽器自動把 http 強制變成 https</h3><p>這個是踩過的人都知道有多氣的雷。</p><p>事情是這樣的，你的瀏覽器有一個叫做 <strong>HSTS (HTTP Strict Transport Security)</strong> 的快取，當你某個網域曾經有跑 https，瀏覽器就會把它記下來，之後你輸入 <code>http://...</code> 它會自動幫你換成 <code>https://...</code>。</p><p>這個機制本來是為了安全（防止 downgrade 攻擊），可是放在 localhost 開發環境就很煩，因為你的本機 dev server 通常只跑 http，不跑 https，於是瀏覽器強制跳到 https 之後，自然就連不到了。</p><p>判斷方式：你看一下網址列，如果原本你輸入 <code>http://localhost:3000</code>，可是它變成 <code>https://localhost:3000</code>，那就是這個問題。</p><p>解決方法可以參考 <a href="https://israynotarray.com/other/20200723/3866554212/">解決 Chrome 、 Firefox 或 Edge 等瀏覽器強制跳轉 HTTPS 網址問題</a> 把 HSTS 紀錄清掉。</p><h3 id="hosts-檔案被改過-or-IPv4-IPv6-解析衝突"><a href="#hosts-檔案被改過-or-IPv4-IPv6-解析衝突" class="headerlink" title="hosts 檔案被改過 or IPv4 &#x2F; IPv6 解析衝突"></a>hosts 檔案被改過 or IPv4 &#x2F; IPv6 解析衝突</h3><p>前面有提到 localhost 是透過 hosts 檔案解析成 127.0.0.1 or ::1，那如果這個對應壞了，連線當然也會壞。</p><p>最常見的兩種情況：</p><ul><li>hosts 檔案被某些軟體（譬如某些防毒 or 上網管控工具）改寫，把 localhost 對應到別的 IP</li><li>localhost 解析到 IPv6 ::1，可是你的 dev server 只綁在 IPv4 127.0.0.1 上面（or 反過來）</li></ul><p>第二種情況在 Node.js 早期版本特別常見，那時候 Node.js 17 之後 DNS 解析的預設行為改變，導致 <code>localhost</code> 開始優先解析到 ::1，很多人因此發現原本可以跑的程式突然連不上資料庫。</p><p>解法很簡單，<strong>把網址改成 <code>http://127.0.0.1:port</code></strong>，跳過 hostname 解析，直接走 IPv4，通常就會通了。</p><h3 id="防火牆-or-防毒軟體擋掉-loopback"><a href="#防火牆-or-防毒軟體擋掉-loopback" class="headerlink" title="防火牆 or 防毒軟體擋掉 loopback"></a>防火牆 or 防毒軟體擋掉 loopback</h3><p>這個比較少見，可是在公司電腦 or 政府單位電腦上偶爾會發生。</p><p>某些比較嚴格的防火牆 or 防毒軟體會把 loopback 流量也列入管控，這時候你的封包送出去會被擋掉，連線就被拒絕了。如果你前面幾項都試過了還是不通，可以試著暫時關掉防火牆 or 把 dev server 加到白名單看看。</p><h3 id="特殊情境：自然人憑證之類的本機服務"><a href="#特殊情境：自然人憑證之類的本機服務" class="headerlink" title="特殊情境：自然人憑證之類的本機服務"></a>特殊情境：自然人憑證之類的本機服務</h3><p>有一種特殊情境是某些本機服務（譬如台灣的自然人憑證 HiCOS）會在 <code>localhost:61161</code> 這種特定 port 上跑一個小型伺服器，當這個服務的背景程序沒啟動，網頁就會出現「localhost 拒絕連線」。</p><p>這個案例我之前有寫過一篇 <a href="https://israynotarray.com/other/20231224/4103794407/">Windows 自然人憑證網頁總是出現「localhost 拒絕連線」的解決方式</a>，如果你剛好在弄政府網站遇到這個錯誤，可以直接過去看。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>這一篇從 localhost 跟 127.0.0.1 的差異、loopback 的歷史、那個經典的工程師笑話，一路講到本機開發踩雷的常見原因，希望可以讓你之後看到「localhost 拒絕連線」這幾個字的時候，不會再像第一次遇到那樣手足無措。</p><p>整理一下重點：</p><ul><li>localhost 是 hostname、127.0.0.1 是 IP，前者透過 hosts 檔案解析成後者</li><li>127.0.0.0&#x2F;8 整段都是 loopback，源自 Jon Postel 1981 年的隨手保留</li><li>攻擊 127.0.0.1 等於攻擊自己 — 2005 年 bitchchecker 事件就是最爆笑的證明</li><li>連不上的時候優先檢查：dev server 有沒有啟動、port 對不對、HSTS 有沒有把你跳到 https、要不要改用 127.0.0.1 跳過解析</li></ul><p>最後送上一句工程師最忠實的好朋友 — <code>127.0.0.1</code>，永遠不會背叛你（除了它沒在跑的時候），就算你想拿它來嗆人，它也會幫你打你自己（誤）。</p><p>我們下篇文章見囉～</p><p>希望這篇有讓你對 localhost 跟 127.0.0.1 多了一點認識哩～</p>]]></content>
    
    
    <summary type="html">這篇把 localhost 跟 127.0.0.1 從 hostname 解析、loopback 的概念、127.0.0.0/8 從 RFC 790 到 RFC 1122 的歷史小故事，到 2005 年那個被 Slashdot 稱為「地表最蠢駭客」的德國 IRC bitchchecker 事件 — 攻擊 127.0.0.1 結果把自己電腦打掛 — 一次講清楚。後半段也整理本機開發遇到「localhost 拒絕連線」最常見的原因，包含 dev server 沒啟動、port 被佔用、瀏覽器 HSTS 強制 HTTPS、hosts 檔案、IPv6 ::1 解析衝突、防火牆等情境的排查方式。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="localhost" scheme="https://israynotarray.com/tags/localhost/"/>
    
    <category term="127.0.0.1" scheme="https://israynotarray.com/tags/127-0-0-1/"/>
    
    <category term="loopback" scheme="https://israynotarray.com/tags/loopback/"/>
    
    <category term="IP 位址" scheme="https://israynotarray.com/tags/IP-%E4%BD%8D%E5%9D%80/"/>
    
    <category term="網路基礎" scheme="https://israynotarray.com/tags/%E7%B6%B2%E8%B7%AF%E5%9F%BA%E7%A4%8E/"/>
    
    <category term="本機開發" scheme="https://israynotarray.com/tags/%E6%9C%AC%E6%A9%9F%E9%96%8B%E7%99%BC/"/>
    
    <category term="後端" scheme="https://israynotarray.com/tags/%E5%BE%8C%E7%AB%AF/"/>
    
  </entry>
  
  <entry>
    <title>GPT-5.5 跟 ChatGPT Images 2.0 怎麼用？30 個 IG 圖卡、知識卡、產品 Mockup 提示詞模板</title>
    <link href="https://israynotarray.com/ai/20260502/2581904736/"/>
    <id>https://israynotarray.com/ai/20260502/2581904736/</id>
    <published>2026-05-02T10:30:00.000Z</published>
    <updated>2026-05-02T10:30:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_30_13.png" data-fancybox="gallery" data-caption="GPT-5.5 跟 ChatGPT Images 2.0 怎麼用？30 個 IG 圖卡、知識卡、產品 Mockup 提示詞模板"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_30_13.png" alt="GPT-5.5 跟 ChatGPT Images 2.0 怎麼用？30 個 IG 圖卡、知識卡、產品 Mockup 提示詞模板"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">GPT-5.5 跟 ChatGPT Images 2.0 怎麼用？30 個 IG 圖卡、知識卡、產品 Mockup 提示詞模板</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>OpenAI 在 2026 年 4 月底連著丟了兩個更新，一個是 GPT-5.5 這個語言模型，另一個是搭配它的 ChatGPT Images 2.0（內部代號 gpt-image-2）。剛看到新聞的時候其實沒太多感覺，反正每隔幾個月就有新模型發布，多半是換湯不換藥，可是這次實際打開來試了一下，發現圖片生成的部分跟以前完全不是同一個層級，特別是中文字終於不會糊掉這件事，對做內容的人來講影響很大。</p><p>兩個更新裡面，前者大家比較會聽到，可是真正讓內容創作者眼睛一亮的，其實是後者。</p><p>為什麼這次值得特別寫一篇呢？因為 AI 生圖一直以來最被嫌棄的兩件事，文字會糊掉、排版很醜，這次都被解掉了。中文字也能寫對，IG 圖卡、知識懶人包、產品 Mockup、Infographic 都能直接拿來用，不太需要再進 Figma 補刀。</p><p>如果你還不太熟 Prompt 是什麼、跟 LLM 怎麼互動，可以先去翻我之前寫的 <a href="https://israynotarray.com/ai/20250425/3215958971/">你聽過 LLM、RAG、Prompt 嗎？一文帶你看懂生成式 AI 常見技術詞彙</a>，這篇我就直接從 GPT-5.5 開始講。</p><p>這篇會分四大段：先釐清一個常見誤會、接著看 GPT-5.5 跟 GPT-5 的差異、再講 ChatGPT Images 2.0 強在哪、最後給你一套萬用提示詞結構，加上 30 個分場景的提示詞模板，IG 教學圖卡、輪播封面、文章封面圖、Infographic、產品 Mockup、引言金句卡、行程表都有，可以直接複製去改。</p><h2 id="先釐清：GPT-5-5-跟-ChatGPT-Images-2-0-是同一個嗎？"><a href="#先釐清：GPT-5-5-跟-ChatGPT-Images-2-0-是同一個嗎？" class="headerlink" title="先釐清：GPT-5.5 跟 ChatGPT Images 2.0 是同一個嗎？"></a>先釐清：GPT-5.5 跟 ChatGPT Images 2.0 是同一個嗎？</h2><p>不是，是兩個不同的模型，這個誤會其實滿多人有的，連各家媒體標題都常常把它們混在一起講。</p><p>簡單來講就是：</p><ul><li><strong>GPT-5.5</strong> 是語言模型，2026 年 4 月 23 日發布，負責讀懂你的需求、思考、寫程式、處理文字。它支援圖片輸入（你丟圖給它看），可是不會自己畫圖。</li><li><strong>ChatGPT Images 2.0</strong>（內部代號 <code>gpt-image-2</code>）才是這次真正在畫圖的模型，4 月 21 日就先發布了，比 GPT-5.5 還早兩天。</li></ul><p>兩個剛好是 OpenAI 同一個禮拜釋出的，所以很多人會以為是一個東西，可是技術上是分開的。當你在 ChatGPT 裡面說「幫我畫一張 IG 圖卡」，背後其實是兩個模型在合作：GPT-5.5 讀你的句子、想清楚你想要什麼，然後把任務交給 ChatGPT Images 2.0 去畫，最後結果回傳給你。</p><p>如果用比較生活面的例子來講的話，GPT-5.5 比較像是出版社的編輯，讀懂你的想法、整理需求；ChatGPT Images 2.0 則是負責畫的插畫師。你只跟編輯講話，可是真正下筆的是插畫師。</p><p>了解這個分工之後，下面就分別來看這兩個模型各自升級了什麼。</p><h2 id="GPT-5-5-跟-GPT-5-差在哪？"><a href="#GPT-5-5-跟-GPT-5-差在哪？" class="headerlink" title="GPT-5.5 跟 GPT-5 差在哪？"></a>GPT-5.5 跟 GPT-5 差在哪？</h2><p>先講結論，GPT-5.5 是 GPT-5 的延伸升級版，OpenAI 自己稱它是目前最聰明、最直觀的模型，2026 年 4 月 23 日在 ChatGPT 上開放，隔天 4 月 24 日 API 也跟著上線。</p><p>那實際升級了什麼呢？以我自己來講，最有感的有四個地方：</p><div class="table-wrapper"><table><thead><tr><th>面向</th><th>升級內容</th></tr></thead><tbody><tr><td>寫程式 &#x2F; Debug</td><td>在 Terminal-Bench 2.0 拿到 82.7%，多輪修正能力比 GPT-5 強很多</td></tr><tr><td>任務理解</td><td>同樣的任務用更少 Token 完成，反應速度跟 GPT-5.4 差不多可是聰明程度明顯提升</td></tr><tr><td>圖片輸入</td><td>最高保留 1024 萬像素不縮圖（單邊上限 6000 px），讀截圖、文件、設計稿細節更清楚</td></tr><tr><td>Context Window</td><td>1M Token，可以一次塞進長文件、整份程式碼 Repo</td></tr></tbody></table></div><p>簡單來講，GPT-5.5 不是那種重新設計架構的世代跳躍，比較像是 OpenAI 把 GPT-5 系列再優化過一輪的成熟版本。日常使用下來最明顯的差別就是它更會自己接著做事，你不用再一直追著問下一步要怎麼做，它會主動往前推進。</p><h2 id="ChatGPT-Images-2-0-強在哪？"><a href="#ChatGPT-Images-2-0-強在哪？" class="headerlink" title="ChatGPT Images 2.0 強在哪？"></a>ChatGPT Images 2.0 強在哪？</h2><p>ChatGPT Images 2.0 這次升級的幅度相當大，OpenAI 的 Sam Altman 自己形容從 gpt-image-1 升到 gpt-image-2，差距就像 GPT-3 跨到 GPT-5。聽起來有點誇張，可是實際用過你會懂他在說什麼。</p><p>那它到底強在哪？我整理成下面四個重點：</p><h3 id="一、文字渲染終於可靠"><a href="#一、文字渲染終於可靠" class="headerlink" title="一、文字渲染終於可靠"></a>一、文字渲染終於可靠</h3><p>過去 AI 生圖最大的破綻就是文字。雜誌封面想加個標題，畫出來會變成糊糊的、像火星文的奇怪字串。Images 2.0 把這件事解掉了，標題、副標、價格標籤、條碼下面的日期都能寫對，連菜單拿去印出來客人都看不出來是 AI 生的。</p><h3 id="二、中文寫得對了"><a href="#二、中文寫得對了" class="headerlink" title="二、中文寫得對了"></a>二、中文寫得對了</h3><p>對台灣的內容創作者來講這點超重要。以前要做中文 IG 圖卡幾乎沒辦法用 ChatGPT 生圖，因為中文字會缺筆、會錯字、會變成像是火星文。Images 2.0 在中文、日文、韓文、印地文、孟加拉文這幾種非拉丁語系的字符上都有大幅進步。</p><p>當然偶爾還是會跑掉一兩個字，可是你可以用編輯模式針對那塊重生，不用整張重畫，這個我後面會講。</p><h3 id="三、排版跟版面感變好了"><a href="#三、排版跟版面感變好了" class="headerlink" title="三、排版跟版面感變好了"></a>三、排版跟版面感變好了</h3><p>不只是字會寫了，連排版的層級感都對了。標題大、副標小、列點對齊、留白合理，整張圖看起來就是有設計過的樣子。可以直接生品牌組合包，配色、Logo、字體一致的多頁素材一次出。</p><h3 id="四、內建-Reasoning-在動"><a href="#四、內建-Reasoning-在動" class="headerlink" title="四、內建 Reasoning 在動"></a>四、內建 Reasoning 在動</h3><p>Images 2.0 是第一個內建推論能力的圖像模型，在生圖之前會先想過一輪文字要放哪、空間怎麼分配、視覺邏輯通不通，產出來才開畫。所以複雜構圖、密集排版的圖比以前穩很多，不會東一塊西一塊。</p><p>從這四點可以看出來，這次升級的方向不是「畫得更藝術」，而是「畫得更實用」。設計師一開始可能會覺得有點威脅，可是實際用下來，它比較像是把以前要花兩三個小時排版的活壓縮到三分鐘，省掉的是那些重複勞動，不是真正的設計判斷。</p><h2 id="寫好提示詞的萬用結構"><a href="#寫好提示詞的萬用結構" class="headerlink" title="寫好提示詞的萬用結構"></a>寫好提示詞的萬用結構</h2><p>在開始抄提示詞之前，先聊一下提示詞的結構，不然你只是複製貼上換主題，產出品質還是會浮動很大。</p><p>ChatGPT Images 2.0 偏好結構化的提示詞，意思是把一張圖該有的元素拆開來描述，比叫它「畫一張漂亮的 IG 圖」要穩定很多。我自己常用的結構是這六項：</p><div class="table-wrapper"><table><thead><tr><th>項目</th><th>說明</th></tr></thead><tbody><tr><td>主題</td><td>這張圖要傳達的核心訊息或內容</td></tr><tr><td>用途</td><td>IG 貼文、輪播封面、部落格首圖、Infographic 等</td></tr><tr><td>版面</td><td>直式、橫式、方形、比例、版面分區方式</td></tr><tr><td>配色</td><td>主色、輔色、背景色，可以用具體色號或情緒形容詞</td></tr><tr><td>字體</td><td>標題字體風格（黑體、宋體、襯線、手寫）、字級階層</td></tr><tr><td>要出現的文字</td><td>用引號標出來的具體文字內容，包含標題、副標、列點</td></tr></tbody></table></div><p>簡單來講就是別讓模型自己猜，你猜得越具體它畫得越精準。</p><blockquote><p><strong>Note</strong><br>寫中文提示詞的時候，要出現在圖上的文字一定要用引號 『』 框起來，並且分開標明哪個是標題、哪個是副標、哪個是列點。這樣模型才知道哪些字要實際畫進去、哪些字只是描述。</p></blockquote><p>如果你是要編輯既有的圖（局部修改而不是重畫），那提示詞結構就要再加上「保留」跟「修改」的對比：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br></pre></td><td class="code"><pre><code class="hljs txt">請保留：原圖的整體配色、人物姿勢、背景物件<br>請修改：把標題從『今日重點』改成『一週彙整』，副標放大一點<br></code></pre></td></tr></table></figure><p>這個 change + preserve 的寫法可以避免模型把整張圖重畫，只動你想動的地方。</p><h2 id="30-個推薦提示詞模板"><a href="#30-個推薦提示詞模板" class="headerlink" title="30 個推薦提示詞模板"></a>30 個推薦提示詞模板</h2><p>接下來就是重點了。這 30 個提示詞我依照場景分成八類，全部都是中文、可以直接複製去改的版本。每一組我會先講這個場景適合什麼用途，再給提示詞本體，後面也會附上我自己用同一組提示詞實際跑出來的成品圖，你可以邊看邊對照效果，把括號 <code>[ ]</code> 裡面的內容換成你的主題就好。</p><h3 id="一、IG-教學圖卡（單張知識懶人包）"><a href="#一、IG-教學圖卡（單張知識懶人包）" class="headerlink" title="一、IG 教學圖卡（單張知識懶人包）"></a>一、IG 教學圖卡（單張知識懶人包）</h3><p>這類圖卡就是現在 IG 上會被瘋狂存檔的那種，一張圖把一個概念講清楚。重點是版面要乾淨、資訊層級要清楚、要有一個強而有力的主標題。</p><h4 id="1-程式新手概念懶人包"><a href="#1-程式新手概念懶人包" class="headerlink" title="1. 程式新手概念懶人包"></a>1. 程式新手概念懶人包</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『什麼是 [API]？三分鐘看懂』。<br>版面分四區：頂部主標題佔 1/5、中間用三個圓角卡片各放一個重點、底部留白放署名。<br>配色：米白底色、深藍標題、橘色點綴。<br>字體：標題用粗黑體、內文用無襯線體。<br>要出現的文字：標題『什麼是 API？』、副標『三分鐘看懂』、三個重點分別是『程式之間的對話橋樑』、『把資料從 A 點搬到 B 點』、『前後端合作的關鍵』。<br>風格：扁平設計、線稿圖示、留白多。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_50_44.png" data-fancybox="gallery" data-caption="程式新手概念懶人包"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_50_44.png" alt="程式新手概念懶人包"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">程式新手概念懶人包</figcaption></figure><h4 id="2-五分鐘快查表"><a href="#2-五分鐘快查表" class="headerlink" title="2. 五分鐘快查表"></a>2. 五分鐘快查表</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張方形 IG 圖卡（1:1 比例），主題是『[VS Code 必學十大快捷鍵] 一張收藏』。<br>版面：頂部標題佔 1/6、下方用 2x5 表格列出十組快捷鍵，每組左邊是按鍵組合（用鍵盤圖示包起來）、右邊是功能說明。<br>配色：黑底、白字、青綠色按鍵框。<br>字體：等寬字體（Monospace）。<br>要出現的文字：標題『VS Code 必學十大快捷鍵』、底部小字『存起來用一輩子』。<br>風格：科技感、終端機質感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_50_49.png" data-fancybox="gallery" data-caption="五分鐘快查表"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_50_49.png" alt="五分鐘快查表"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">五分鐘快查表</figcaption></figure><h4 id="3-對比表卡"><a href="#3-對比表卡" class="headerlink" title="3. 對比表卡"></a>3. 對比表卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[Vue vs React] 五分鐘搞懂差別』。<br>版面：頂部標題、中間左右分欄對比、每欄頂部各放一個 Logo 區塊、下方列五個比較項目。<br>配色：左欄淺綠（Vue）、右欄淺藍（React）、背景灰白。<br>字體：標題粗體、內文清晰可讀。<br>要出現的文字：標題『Vue vs React 五分鐘搞懂』、左欄頂部『Vue』、右欄頂部『React』、五個比較項分別是『學習曲線』、『社群規模』、『就業機會』、『更新速度』、『官方支援』。<br>風格：簡約資訊圖表、無多餘裝飾。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_52_01.png" data-fancybox="gallery" data-caption="對比表卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_52_01.png" alt="對比表卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">對比表卡</figcaption></figure><h4 id="4-Step-by-step-流程卡"><a href="#4-Step-by-step-流程卡" class="headerlink" title="4. Step-by-step 流程卡"></a>4. Step-by-step 流程卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[Git Rebase 三步驟] 一次學會』。<br>版面：頂部主標題、下方用三個橫向卡片由上到下排列，每張卡片左側是大數字（01、02、03）、右側是步驟說明。<br>配色：白底、深藍數字、橘色強調線。<br>字體：數字用襯線粗體（很大）、步驟說明用無襯線體。<br>要出現的文字：標題『Git Rebase 三步驟』、三步分別是『01 切到要 rebase 的分支』、『02 執行 git rebase main』、『03 解決衝突後 continue』。<br>風格：教學手冊感、層次分明。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_53_36.png" data-fancybox="gallery" data-caption="Step-by-step 流程卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_53_36.png" alt="Step-by-step 流程卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Step-by-step 流程卡</figcaption></figure><h4 id="5-常見錯誤-vs-正確做法"><a href="#5-常見錯誤-vs-正確做法" class="headerlink" title="5. 常見錯誤 vs 正確做法"></a>5. 常見錯誤 vs 正確做法</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[CSS 切版] 新手最常踩的三個雷』。<br>版面：頂部標題、下方用左右兩欄對照，左欄標『常見錯誤』（紅色頂條）、右欄標『正確做法』（綠色頂條），各列三點。<br>配色：紅綠對比、白底、灰色分隔線。<br>字體：標題粗體、內文清晰。<br>要出現的文字：標題『新手切版最常踩的三個雷』、左欄『常見錯誤』、右欄『正確做法』，每點都要有對應的程式碼小範例。<br>風格：教學對照、清楚分界。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_55_19.png" data-fancybox="gallery" data-caption="常見錯誤 vs 正確做法"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_55_19.png" alt="常見錯誤 vs 正確做法"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">常見錯誤 vs 正確做法</figcaption></figure><h4 id="6-資料事實卡（適合做趨勢報導）"><a href="#6-資料事實卡（適合做趨勢報導）" class="headerlink" title="6. 資料事實卡（適合做趨勢報導）"></a>6. 資料事實卡（適合做趨勢報導）</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張方形 IG 圖卡（1:1 比例），主題是『[2026 年最熱門前端框架] 一張資料看懂』。<br>版面：頂部標題、中間放一張清楚的長條圖（標好百分比）、底部放資料來源跟一句總結。<br>配色：黑底白字、橘黃色長條。<br>字體：標題粗黑體、數字用襯線體加大。<br>要出現的文字：標題『2026 最多人用的前端框架』、長條上的數值要清楚標出、底部資料來源寫『StackOverflow 2026 開發者調查』。<br>風格：報導級資訊圖表、權威感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_56_50.png" data-fancybox="gallery" data-caption="資料事實卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_56_50.png" alt="資料事實卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">資料事實卡</figcaption></figure><h3 id="二、IG-多頁輪播（封面-內頁）"><a href="#二、IG-多頁輪播（封面-內頁）" class="headerlink" title="二、IG 多頁輪播（封面 + 內頁）"></a>二、IG 多頁輪播（封面 + 內頁）</h3><p>輪播的關鍵在第一頁，第一頁能不能把人勾住會直接決定整個輪播的觸及。中間頁要保持視覺一致，最後一頁要有 CTA。</p><h4 id="7-第一頁鈎子封面（提問式）"><a href="#7-第一頁鈎子封面（提問式）" class="headerlink" title="7. 第一頁鈎子封面（提問式）"></a>7. 第一頁鈎子封面（提問式）</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 輪播封面（4:5 比例），主題是『[為什麼你的程式碼總是越改越亂？] 揭密三個壞習慣』。<br>版面：頂部留白、中間放一個超大粗體的提問句（佔畫面 2/3）、底部放小字提示『往右滑看答案』還有一個右滑箭頭。<br>配色：強烈對比色（黑底亮黃文字 或 米白底深紅文字）。<br>字體：超粗大標、現代感無襯線。<br>要出現的文字：主問題『為什麼你的程式碼總是越改越亂？』、底部『往右滑 →』。<br>風格：海報感、視覺衝擊強、文字為主視覺。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_57_42.png" data-fancybox="gallery" data-caption="第一頁鈎子封面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_57_42.png" alt="第一頁鈎子封面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">第一頁鈎子封面</figcaption></figure><h4 id="8-中間頁標準版"><a href="#8-中間頁標準版" class="headerlink" title="8. 中間頁標準版"></a>8. 中間頁標準版</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 輪播內頁（4:5 比例），主題是『[壞習慣 1：函式越寫越長]』。<br>版面：頂部小標『壞習慣 1』（靠左小字）、中間放主要解釋（兩到三段短文）、底部放對應的程式碼小範例（用語法高亮樣式呈現）。<br>配色：跟封面一致的米白底深紅色系。<br>字體：跟封面一致。<br>要出現的文字：頂部小標『壞習慣 1 / 3』、主標『函式越寫越長』、內文『一個函式不該超過 20 行，超過就該拆』、底部小字『往右滑看下一個 →』。<br>風格：跟封面同系列，視覺延續。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_59_14.png" data-fancybox="gallery" data-caption="中間頁標準版"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8805_59_14.png" alt="中間頁標準版"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">中間頁標準版</figcaption></figure><h4 id="9-結尾頁-CTA"><a href="#9-結尾頁-CTA" class="headerlink" title="9. 結尾頁 CTA"></a>9. 結尾頁 CTA</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 輪播結尾頁（4:5 比例），主題是『[結尾 CTA：追蹤 + 收藏 + 分享]』。<br>版面：頂部一句總結、中間三個圓形按鈕示意（追蹤、收藏、分享，各配對應的 icon）、底部放署名跟 IG 帳號。<br>配色：跟前面頁面一致。<br>字體：標題粗體、按鈕說明短。<br>要出現的文字：頂部『覺得有幫助嗎？』、三個按鈕『追蹤我看更多』、『收藏起來複習』、『分享給朋友』、底部『@your_handle』。<br>風格：呼籲行動感、視覺收斂。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_00_25.png" data-fancybox="gallery" data-caption="結尾頁 CTA"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_00_25.png" alt="結尾頁 CTA"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">結尾頁 CTA</figcaption></figure><h4 id="10-個人故事輪播封面"><a href="#10-個人故事輪播封面" class="headerlink" title="10. 個人故事輪播封面"></a>10. 個人故事輪播封面</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 輪播封面（4:5 比例），主題是『[從文組轉職前端工程師] 我走過的三個坑』。<br>版面：上半部放手繪風人物剪影、下半部放主標題跟副標。<br>配色：暖色調（米色、橘色、深咖啡），有手繪感。<br>字體：標題用手寫感字體、副標用無襯線。<br>要出現的文字：主標『從文組轉職前端工程師』、副標『我走過的三個坑』、底部小字『一段真實心路歷程 →』。<br>風格：日系手繪、療癒感、文青風。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_02_06.png" data-fancybox="gallery" data-caption="個人故事輪播封面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_02_06.png" alt="個人故事輪播封面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">個人故事輪播封面</figcaption></figure><h4 id="11-工具評測輪播封面"><a href="#11-工具評測輪播封面" class="headerlink" title="11. 工具評測輪播封面"></a>11. 工具評測輪播封面</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 輪播封面（4:5 比例），主題是『[2026 五大 AI 寫程式工具] 實測誰最強』。<br>版面：頂部主標、中間放五個工具的 Logo 排成五邊形、底部放副標跟滑動提示。<br>配色：科技藍底、白字、霓虹色 Logo 框。<br>字體：未來感、科技風粗體。<br>要出現的文字：主標『2026 五大 AI 寫程式工具』、副標『實測誰最強』、底部『往右滑看排行 →』。<br>風格：科技未來感、霓虹光效。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_02_37.png" data-fancybox="gallery" data-caption="工具評測輪播封面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_02_37.png" alt="工具評測輪播封面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">工具評測輪播封面</figcaption></figure><h3 id="三、文章封面-部落格主圖"><a href="#三、文章封面-部落格主圖" class="headerlink" title="三、文章封面 &#x2F; 部落格主圖"></a>三、文章封面 &#x2F; 部落格主圖</h3><p>部落格主圖的目的不一樣，它要在 Google 圖片搜尋、社群分享卡上一秒被看見。所以視覺要有辨識度、主題要一目了然。</p><h4 id="12-工程師桌面情境圖"><a href="#12-工程師桌面情境圖" class="headerlink" title="12. 工程師桌面情境圖"></a>12. 工程師桌面情境圖</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式部落格封面（16:9 比例），主題是『[Side Project 從零到上線] 真實工作流程分享』。<br>版面：上方俯視角度的工作桌面，桌上有筆電（螢幕上有程式碼跟瀏覽器分頁）、咖啡杯、筆記本、植物、機械鍵盤。<br>配色：暖色木桌、自然光、低飽和度。<br>要出現的文字：筆電螢幕上隱約可見『Side Project』字樣、可有可無的便利貼上寫『launch v1.0』。<br>風格：寫實攝影感、Lifestyle Tech、IG 系工程師桌面。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_05_44.png" data-fancybox="gallery" data-caption="工程師桌面情境圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_05_44.png" alt="工程師桌面情境圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">工程師桌面情境圖</figcaption></figure><h4 id="13-抽象-metaphor-概念圖"><a href="#13-抽象-metaphor-概念圖" class="headerlink" title="13. 抽象 metaphor 概念圖"></a>13. 抽象 metaphor 概念圖</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式部落格封面（16:9 比例），主題是『[認識 Promise]，把 JavaScript 非同步看成一張取餐號碼牌』。<br>版面：畫面中央一張立體寫實的取餐號碼牌（牌上印著 #042），背景是模糊的飲料店吧台。<br>配色：暖橘色光線、低飽和度。<br>要出現的文字：號碼牌上『#042』、牌子下方一行小字『Your Promise is pending...』。<br>風格：擬真攝影感、有故事性、適合作為文章視覺隱喻。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_04_13.png" data-fancybox="gallery" data-caption="抽象 metaphor 概念圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_04_13.png" alt="抽象 metaphor 概念圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">抽象 metaphor 概念圖</figcaption></figure><h4 id="14-條列式封面（含主標）"><a href="#14-條列式封面（含主標）" class="headerlink" title="14. 條列式封面（含主標）"></a>14. 條列式封面（含主標）</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式部落格封面（16:9 比例），主題是『[2026 年新手前端工程師] 該學的 10 個技能』。<br>版面：左側放主標跟副標、右側放一個圖層化的數字 10（很大、漸層色）。<br>配色：深紫漸層、霓虹粉強調色。<br>字體：標題用粗無襯線、數字 10 超大佔畫面右半。<br>要出現的文字：主標『2026 新手前端必學』、副標『10 個你不能錯過的技能』、右側大數字『10』。<br>風格：榜單感、視覺衝擊、現代感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_05_00.png" data-fancybox="gallery" data-caption="條列式封面（含主標）"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_05_00.png" alt="條列式封面（含主標）"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">條列式封面（含主標）</figcaption></figure><h4 id="15-雜誌封面風"><a href="#15-雜誌封面風" class="headerlink" title="15. 雜誌封面風"></a>15. 雜誌封面風</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式封面（3:4 比例），模仿『[Wired]』雜誌封面風格，主題是『[AI 改寫工程師工作的下一個十年]』。<br>版面：上方雜誌名稱、中央放主標題（兩行）、底部放期數跟出版日期、左下角條碼。<br>配色：黑白為主，主色點綴紅色或螢光綠。<br>字體：襯線標題、編輯級排版。<br>要出現的文字：頂部刊名『TECH MAG』、主標『AI 改寫工程師的下一個十年』、副標『2026 年技術大趨勢』、底部『May 2026』、條碼下方『Display until May 30』。<br>風格：仿雜誌封面、編輯設計感、印刷品質感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_07_48.png" data-fancybox="gallery" data-caption="雜誌封面風"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_07_48.png" alt="雜誌封面風"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">雜誌封面風</figcaption></figure><h4 id="16-復古-Polaroid-拼貼"><a href="#16-復古-Polaroid-拼貼" class="headerlink" title="16. 復古 Polaroid 拼貼"></a>16. 復古 Polaroid 拼貼</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式部落格封面（16:9 比例），主題是『[我的 2025 年技術回顧] 學了什麼、做了什麼』。<br>版面：木質背景上散落著六張 Polaroid 拍立得照片（隨機角度），每張照片下方有手寫小字標註。<br>配色：暖色木紋、自然光、有膠帶貼紙感。<br>要出現的文字：每張 Polaroid 下方各寫一個關鍵字，例如『Vue 3』、『Side Project』、『開源貢獻』、『轉職』、『讀書會』、『年度總結』。<br>風格：復古拼貼、手作感、生活感濃。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_08_11.png" data-fancybox="gallery" data-caption="復古 Polaroid 拼貼"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_08_11.png" alt="復古 Polaroid 拼貼"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">復古 Polaroid 拼貼</figcaption></figure><h3 id="四、Infographic-資訊圖表"><a href="#四、Infographic-資訊圖表" class="headerlink" title="四、Infographic &#x2F; 資訊圖表"></a>四、Infographic &#x2F; 資訊圖表</h3><p>Infographic 的用途是把數字跟流程視覺化。Images 2.0 在這塊有大躍進，因為它能畫對數字、能對齊欄位、能畫出符合邏輯的圖表。</p><h4 id="17-流程圖"><a href="#17-流程圖" class="headerlink" title="17. 流程圖"></a>17. 流程圖</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式資訊圖表（A4 比例），主題是『[使用者註冊流程] 從點擊註冊到成功登入』。<br>版面：頂部標題、中間用六個步驟由上到下用箭頭串接，每個步驟一個圓圈圖示加說明。<br>配色：藍紫漸層底色、白色卡片、橘色箭頭。<br>字體：標題粗體、步驟說明清晰。<br>要出現的文字：標題『使用者註冊流程』、六步驟分別是『點擊註冊』、『填寫資料』、『驗證 Email』、『建立帳號』、『首次登入』、『進入後台』。<br>風格：扁平資訊圖表、企業級簡報感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_08_20.png" data-fancybox="gallery" data-caption="流程圖"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8806_08_20.png" alt="流程圖"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">流程圖</figcaption></figure><h4 id="18-時間軸（產品演進）"><a href="#18-時間軸（產品演進）" class="headerlink" title="18. 時間軸（產品演進）"></a>18. 時間軸（產品演進）</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式 Infographic（16:9 比例），主題是『[ChatGPT 模型演進史] 2022 到 2026』。<br>版面：橫向時間軸從左到右、五個節點分別代表 GPT-3.5、GPT-4、GPT-4o、GPT-5、GPT-5.5，每個節點上方有日期、下方有特色說明。<br>配色：科技藍漸層、節點用白圓點。<br>字體：日期粗體、說明清晰。<br>要出現的文字：標題『ChatGPT 模型演進史』、五個節點日期『2022.11』、『2023.03』、『2024.05』、『2025.08』、『2026.04』。<br>風格：時間軸、清晰邏輯。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_02_38.png" data-fancybox="gallery" data-caption="時間軸（產品演進）"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_02_38.png" alt="時間軸（產品演進）"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">時間軸（產品演進）</figcaption></figure><h4 id="19-比較矩陣"><a href="#19-比較矩陣" class="headerlink" title="19. 比較矩陣"></a>19. 比較矩陣</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式比較圖（16:9 比例），主題是『[四款 AI 寫程式工具] 功能對照矩陣』。<br>版面：頂部標題、中間 4x6 矩陣，第一欄是功能項、後四欄是工具名稱（GitHub Copilot、Cursor、Claude Code、Codex），格內用打勾叉文字標示有沒有支援。<br>配色：白底、淡灰格線、深藍標題。<br>字體：標題粗體、表格內文清晰可讀。<br>要出現的文字：標題『四款 AI 寫程式工具功能對照』、六個功能項分別是『程式碼補全』、『多檔重構』、『終端機操作』、『MCP 支援』、『離線使用』、『免費額度』。<br>風格：報告級、清楚對齊。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_01_57.png" data-fancybox="gallery" data-caption="比較矩陣"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_01_57.png" alt="比較矩陣"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">比較矩陣</figcaption></figure><h4 id="20-統計資料圖卡"><a href="#20-統計資料圖卡" class="headerlink" title="20. 統計資料圖卡"></a>20. 統計資料圖卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張方形 Infographic（1:1 比例），主題是『[2026 開發者最愛的程式語言] Top 5』。<br>版面：頂部標題、中間放一張水平長條圖（五條，由長到短）、每條長條上有語言名稱跟百分比。<br>配色：每條用不同代表色（JavaScript 黃、Python 藍、TypeScript 深藍、Go 青、Rust 橘）。<br>字體：百分比數字粗體加大。<br>要出現的文字：標題『2026 開發者最愛的程式語言』、五個語言名稱跟百分比都要清楚標示、底部資料來源『StackOverflow Survey 2026』。<br>風格：商業報告級、權威感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_03_00%20(1).png" data-fancybox="gallery" data-caption="統計資料圖卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_03_00%20(1).png" alt="統計資料圖卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">統計資料圖卡</figcaption></figure><h3 id="五、產品-服務-Mockup"><a href="#五、產品-服務-Mockup" class="headerlink" title="五、產品 &#x2F; 服務 Mockup"></a>五、產品 &#x2F; 服務 Mockup</h3><p>這類提示詞特別適合在做行銷素材、提案、Side Project 視覺化的時候用。</p><h4 id="21-App-介面-Mockup"><a href="#21-App-介面-Mockup" class="headerlink" title="21. App 介面 Mockup"></a>21. App 介面 Mockup</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式視覺（16:9 比例），主題是『[一款記帳 App] 的介面 Mockup』。<br>版面：畫面中央一支手機（角度約 15 度傾斜），手機螢幕上是 App 介面截圖，背景是淺灰漸層。<br>配色：手機外框深灰、App 介面用清新的薄荷綠跟白。<br>要出現的文字：App 名稱『MoneyTrack』、頂部本月總支出『NT$ 24,580』、下方三筆交易紀錄（午餐、計程車、咖啡）。<br>風格：產品 Mockup、現代 UI、柔和陰影。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_06_17.png" data-fancybox="gallery" data-caption="App 介面 Mockup"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_06_17.png" alt="App 介面 Mockup"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">App 介面 Mockup</figcaption></figure><h4 id="22-商品包裝設計"><a href="#22-商品包裝設計" class="headerlink" title="22. 商品包裝設計"></a>22. 商品包裝設計</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張正方形包裝視覺（1:1 比例），主題是『[一款手沖咖啡豆] 的包裝設計』。<br>版面：袋裝咖啡豆正面視角，背景是磨豆機跟咖啡豆散落的場景。<br>配色：牛皮紙袋色、深棕色印刷字、金色點綴。<br>要出現的文字：品牌名『URBAN BEAN』、品項『Ethiopia Yirgacheffe』、副標『Single Origin』、烘焙日期『Roast Date: 2026.05.01』。<br>風格：精品咖啡品牌、極簡設計、攝影感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_05_23.png" data-fancybox="gallery" data-caption="商品包裝設計"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_05_23.png" alt="商品包裝設計"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">商品包裝設計</figcaption></figure><h4 id="23-名片設計"><a href="#23-名片設計" class="headerlink" title="23. 名片設計"></a>23. 名片設計</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張名片預覽（橫式 16:9，名片置中），主題是『[一位獨立前端工程師] 的個人名片設計』。<br>版面：名片正反兩面並排，正面放名字跟職稱、背面放聯絡方式。<br>配色：純白卡紙、燙黑字、單一霓虹粉色點綴。<br>字體：襯線粗體標題、無襯線細字內文。<br>要出現的文字：正面『Ray Hsu / Frontend Engineer』、背面『hello@example.com』、『israynotarray.com』、『@israynotarray』。<br>風格：極簡、Material Tactile、印刷品質感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_05_03.png" data-fancybox="gallery" data-caption="名片設計"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_05_03.png" alt="名片設計"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">名片設計</figcaption></figure><h3 id="六、Before-After-對比"><a href="#六、Before-After-對比" class="headerlink" title="六、Before &#x2F; After 對比"></a>六、Before &#x2F; After 對比</h3><p>對比圖的觸及率通常很高，因為人類大腦對比較這件事天生有興趣。</p><h4 id="24-介面改造前後"><a href="#24-介面改造前後" class="headerlink" title="24. 介面改造前後"></a>24. 介面改造前後</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張橫式對比圖（16:9 比例），主題是『[一個登入頁面] 改版前後對比』。<br>版面：左右並排兩張電腦螢幕截圖，中間一條分隔線，上方各標 Before / After。<br>配色：左側 Before 用 90 年代醜陋風（亂七八糟字體、彩虹底色），右側 After 用現代極簡（白底、襯線標題、清楚層級）。<br>要出現的文字：左上『Before』、右上『After』、兩張截圖內都顯示『會員登入』表單。<br>風格：對比強烈、設計教學用。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_09_12.png" data-fancybox="gallery" data-caption="介面改造前後"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_09_12.png" alt="介面改造前後"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">介面改造前後</figcaption></figure><h4 id="25-文案改寫前後"><a href="#25-文案改寫前後" class="headerlink" title="25. 文案改寫前後"></a>25. 文案改寫前後</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式對比圖（4:5 比例），主題是『[產品文案] 改寫前後對比』。<br>版面：上半部 Before 標籤加一段冗長文案、下半部 After 標籤加一段精簡有力的文案，中間一條分隔線。<br>配色：Before 區用淡灰色、After 區用清新薄荷綠。<br>字體：兩段都清晰可讀，After 標題加粗。<br>要出現的文字：上方『Before』後接一段冗長介紹、下方『After』後接一句精準有力的標語。<br>風格：文案教學、清楚對照。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_10_06.png" data-fancybox="gallery" data-caption="文案改寫前後"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_10_06.png" alt="文案改寫前後"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">文案改寫前後</figcaption></figure><h3 id="七、引言金句卡"><a href="#七、引言金句卡" class="headerlink" title="七、引言金句卡"></a>七、引言金句卡</h3><p>金句卡是內容創作者的萬用素材，做名人引言、產品 slogan、自媒體個人觀點都好用。</p><h4 id="26-名人金句卡"><a href="#26-名人金句卡" class="headerlink" title="26. 名人金句卡"></a>26. 名人金句卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張方形金句卡（1:1 比例），主題是『[Steve Jobs] 的設計哲學引言』。<br>版面：左側放一個低飽和度、剪影風格的人物側臉、右側放引言。<br>配色：暖灰背景、白色引言文字、單色剪影。<br>字體：襯線粗體引言、無襯線小字署名。<br>要出現的文字：引言『Design is not just what it looks like and feels like. Design is how it works.』、署名『— Steve Jobs』。<br>風格：人物剪影、編輯設計感、引言為視覺主體。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_04.png" data-fancybox="gallery" data-caption="名人金句卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_04.png" alt="名人金句卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">名人金句卡</figcaption></figure><h4 id="27-日系極簡金句卡"><a href="#27-日系極簡金句卡" class="headerlink" title="27. 日系極簡金句卡"></a>27. 日系極簡金句卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張方形金句卡（1:1 比例），主題是『[一句療癒的中文短句]』。<br>版面：純色背景中央放一句短文、右下角放小小的署名跟日期。<br>配色：奶油黃底色、深咖啡字。<br>字體：手寫體中文、極小字署名。<br>要出現的文字：主句『慢慢來，比較快。』、右下角『2026.05 / @israynotarray』。<br>風格：日系極簡、文青風、IG 收藏型。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_08_33.png" data-fancybox="gallery" data-caption="日系極簡金句卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_08_33.png" alt="日系極簡金句卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">日系極簡金句卡</figcaption></figure><h3 id="八、其他高流量場景"><a href="#八、其他高流量場景" class="headerlink" title="八、其他高流量場景"></a>八、其他高流量場景</h3><p>最後三個是我自己跟朋友聊下來，發現觸及很穩定的內容類型，特別適合補進你的內容曆。</p><h4 id="28-週計畫表-Habit-Tracker"><a href="#28-週計畫表-Habit-Tracker" class="headerlink" title="28. 週計畫表 &#x2F; Habit Tracker"></a>28. 週計畫表 &#x2F; Habit Tracker</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[一週工程師習慣養成表] Habit Tracker』。<br>版面：頂部標題、中間放一個 7 天 x 5 個習慣的格子表，每格用圓形勾選框、底部留一行勵志短句。<br>配色：奶油白底、淡綠色完成狀態、灰色未完成。<br>字體：手寫感標題、清晰內文。<br>要出現的文字：標題『一週工程師習慣表』、五個習慣『Code Review』、『Read 30 mins』、『Push Commit』、『Sleep 7hrs』、『Walk 8000』、底部『Tiny steps, huge wins.』。<br>風格：手帳風、可填寫、收藏型內容。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_10_35.png" data-fancybox="gallery" data-caption="週計畫表 &#x2F; Habit Tracker"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_10_35.png" alt="週計畫表 &#x2F; Habit Tracker"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">週計畫表 &#x2F; Habit Tracker</figcaption></figure><h4 id="29-食譜步驟卡"><a href="#29-食譜步驟卡" class="headerlink" title="29. 食譜步驟卡"></a>29. 食譜步驟卡</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[三分鐘上桌的工程師午餐] 一鍋義大利麵』。<br>版面：頂部食物成品照（俯視角度）、中間列出材料清單、下方用四個編號步驟說明做法。<br>配色：奶油黃底色、深綠色標題、橘紅色強調。<br>字體：手寫感標題、清晰內文。<br>要出現的文字：標題『三分鐘上桌一鍋義大利麵』、材料區列六項、步驟『01 燒水』、『02 下麵』、『03 拌醬』、『04 起鍋』。<br>風格：食譜雜誌、暖色調、生活感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_25.png" data-fancybox="gallery" data-caption="食譜步驟卡"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_25.png" alt="食譜步驟卡"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">食譜步驟卡</figcaption></figure><h4 id="30-一日旅行行程"><a href="#30-一日旅行行程" class="headerlink" title="30. 一日旅行行程"></a>30. 一日旅行行程</h4><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br><span class="line">4</span><br><span class="line">5</span><br><span class="line">6</span><br></pre></td><td class="code"><pre><code class="hljs txt">請畫一張直式 IG 圖卡（4:5 比例），主題是『[台北中山站] 一日散步行程』。<br>版面：頂部地名標題、中間用時間軸（左側是時間、右側是地點跟說明）列出五到六個站點、底部放交通方式提示。<br>配色：莫蘭迪粉、米白、淡綠。<br>字體：襯線標題、清晰內文。<br>要出現的文字：標題『台北中山站一日散步』、時間軸從『10:00』到『18:00』各標一個地點跟一句話介紹、底部『交通：捷運中山站 1 號出口』。<br>風格：旅遊雜誌、文青系、慢生活感。<br></code></pre></td></tr></table></figure><figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_32.png" data-fancybox="gallery" data-caption="一日旅行行程"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/gpt-5-5-image/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8B%E5%8D%8807_11_32.png" alt="一日旅行行程"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">一日旅行行程</figcaption></figure><h2 id="實際使用的小技巧"><a href="#實際使用的小技巧" class="headerlink" title="實際使用的小技巧"></a>實際使用的小技巧</h2><p>光知道結構跟模板還不夠，這邊補幾個我自己用下來覺得很救命的小技巧。</p><h3 id="一、中文字跑掉怎麼救"><a href="#一、中文字跑掉怎麼救" class="headerlink" title="一、中文字跑掉怎麼救"></a>一、中文字跑掉怎麼救</h3><p>前面提到 Images 2.0 中文寫得對了，可是不是 100%。如果整張圖只有一兩個字錯，不要整張重生，直接在 ChatGPT 的編輯介面選那塊文字區域，跟它說「請只把『XXX』改成『YYY』，其他保持不變」，它會只動那一塊，其他地方完全不影響。</p><p>如果整張字都跑掉，那通常是提示詞沒有用引號明確標出文字，回去把要出現的字用 『』 框起來再生一次就會穩很多。</p><h3 id="二、風格一致性"><a href="#二、風格一致性" class="headerlink" title="二、風格一致性"></a>二、風格一致性</h3><p>要做系列內容（例如同一檔 IG 輪播的多頁）的時候，第一張產出滿意之後，後面幾張的提示詞要明確寫「保持跟前一張一樣的配色、字體、版面風格」，並且把前一張的關鍵風格描述（例如「米白底、深紅字、扁平設計」）重複貼進去。模型不會自動記住前一張長什麼樣子，要你主動帶過去。</p><h3 id="三、構圖比例對應"><a href="#三、構圖比例對應" class="headerlink" title="三、構圖比例對應"></a>三、構圖比例對應</h3><p>不同平台的圖片比例不一樣，提示詞裡面要明確指定，不然產出可能不能直接用。常見比例整理在下面：</p><div class="table-wrapper"><table><thead><tr><th>用途</th><th>比例</th></tr></thead><tbody><tr><td>IG 貼文 &#x2F; 圖卡</td><td>1:1（方形）或 4:5（直式）</td></tr><tr><td>IG Story &#x2F; Reels 封面</td><td>9:16（直式）</td></tr><tr><td>部落格封面 &#x2F; OG Image</td><td>16:9 或 1.91:1</td></tr><tr><td>YouTube 縮圖</td><td>16:9</td></tr><tr><td>FB 貼文</td><td>1.91:1 或 1:1</td></tr><tr><td>雜誌風封面</td><td>3:4（直式）</td></tr></tbody></table></div><p>從這張表可以看出來，IG 圖卡用 4:5 是最划算的，因為它在 IG 上會佔最大版面，演算法相對也比較給觸及。</p><h3 id="四、不要一次叫它做太多"><a href="#四、不要一次叫它做太多" class="headerlink" title="四、不要一次叫它做太多"></a>四、不要一次叫它做太多</h3><p>如果你的提示詞同時要求「複雜構圖 + 多種字體 + 五段文字 + 三個圖示」，模型會抓不到重點，最後通常會犧牲掉文字準確度。建議一張圖最多只放一個主標、一個副標、最多五個列點，再多就拆成輪播。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>整理到這邊，這篇文章把 GPT-5.5 的升級重點、ChatGPT Images 2.0 的真正威力、提示詞結構、30 個分場景的提示詞模板都收進來了。</p><p>說真的，AI 生圖到 Images 2.0 這代之前，我都覺得它是玩具，畫個頭像、畫個概念圖很適合，可是要拿來做商用素材、做要上 IG 發的圖卡，總是差那一口氣，不是文字醜就是排版怪，最後還是要進 Figma 補。Images 2.0 是第一次讓我覺得「這真的可以直接用」。</p><p>當然 AI 生圖還是有它的極限，需要精準品牌規範的素材還是要回到設計師手上做最後的把關，可是日常 80% 的內容素材，你完全可以靠這 30 個模板自己生。</p><p>如果你還沒接觸過 AI 助手客製化，可以順便看一下 <a href="https://israynotarray.com/ai/20260303/3167917304/">Gemini Gems 教學：輕鬆打造你的專屬 AI 助手</a>，把你常用的 Prompt 結構直接打包成一個專屬的生圖小幫手，下次就不用每次都複製貼上了。</p><p>希望這 30 個模板對你的內容創作有實際幫助哩～ 我們下篇文章見囉～</p><p><del>當然如果你直接複製貼上發現產出不一樣，那是因為 AI 生圖每次都有隨機性，多生幾次就好（笑）</del></p>]]></content>
    
    
    <summary type="html">OpenAI 在 2026 年 4 月底同期釋出 GPT-5.5 跟 ChatGPT Images 2.0，前者是新的語言模型負責理解跟推論、後者是專門生圖的模型， 兩個合作起來才是這次圖片生成大躍進的真相，文字渲染終於不會糊掉，中文、英文、日文都寫得對。這篇會先釐清兩者的分工， 再帶你看 GPT-5.5 跟 GPT-5 差在哪、ChatGPT Images 2.0（gpt-image-2）強在哪，最後附上 30 個分場景的提示詞模板， 包含 IG 教學圖卡、知識懶人包、輪播封面、文章封面、Infographic、產品 Mockup、引言金句卡、行程表都有。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="ChatGPT" scheme="https://israynotarray.com/tags/ChatGPT/"/>
    
    <category term="GPT-5.5" scheme="https://israynotarray.com/tags/GPT-5-5/"/>
    
    <category term="ChatGPT Images 2.0" scheme="https://israynotarray.com/tags/ChatGPT-Images-2-0/"/>
    
    <category term="gpt-image-2" scheme="https://israynotarray.com/tags/gpt-image-2/"/>
    
    <category term="AI 圖片生成" scheme="https://israynotarray.com/tags/AI-%E5%9C%96%E7%89%87%E7%94%9F%E6%88%90/"/>
    
    <category term="提示詞" scheme="https://israynotarray.com/tags/%E6%8F%90%E7%A4%BA%E8%A9%9E/"/>
    
    <category term="IG 圖卡" scheme="https://israynotarray.com/tags/IG-%E5%9C%96%E5%8D%A1/"/>
    
  </entry>
  
  <entry>
    <title>手機摔壞照片就沒了？拜託先去開 Google 相簿備份（iPhone 完整設定教學）</title>
    <link href="https://israynotarray.com/other/20260502/1592048371/"/>
    <id>https://israynotarray.com/other/20260502/1592048371/</id>
    <published>2026-05-01T16:00:00.000Z</published>
    <updated>2026-05-01T16:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8A%E5%8D%8812_43_47.png" data-fancybox="gallery" data-caption="手機摔壞照片就沒了？拜託先去開 Google 相簿備份"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/ChatGPT%20Image%202026%E5%B9%B45%E6%9C%882%E6%97%A5%20%E4%B8%8A%E5%8D%8812_43_47.png" alt="手機摔壞照片就沒了？拜託先去開 Google 相簿備份"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">手機摔壞照片就沒了？拜託先去開 Google 相簿備份</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>手機摔壞、掉到水裡、突然開不了機，這時候裡面的照片其實是最難救回來的東西。這篇就分享為什麼我會推薦把 Google 相簿備份打開，並且實際示範 iPhone 上的設定流程，包含備份畫質的選擇、15GB 免費空間怎麼用、行動數據備份要不要打開，以及 iOS 上很容易踩到的坑 — 把 APP 從多工列滑掉備份就會中斷。</p><p>電腦端的備份我之前有寫過 <a href="https://israynotarray.com/other/20260222/1723617915/">macOS 時光機備份超久？教你一招加速！</a>，這一篇則是補上手機端的備份做法，兩邊都做好你的資料就比較安心了。</p><h2 id="事情是這樣的"><a href="#事情是這樣的" class="headerlink" title="事情是這樣的"></a>事情是這樣的</h2><p>這篇文章的起因是這幾天有個學生跟我說他的手機不小心摔到，結果整個無法開機，送去維修還要等差不多一個禮拜。</p><p>他跟我說的當下，第一個反應其實不是手機壞掉很心痛，而是「我裡面的照片怎麼辦」，因為手機根本開不了機，所以也沒辦法把資料先匯出來，只能等送修回來看狀況。</p><p>我聽完馬上問他一句「那你有開 Google 相簿備份嗎？」，他想了一下說「好像沒有耶」，這時候我心裡其實已經默默在想「完了」。</p><p>因為手機送修這件事真的很看運氣，運氣好可能只是換個螢幕，照片資料都還在，可是運氣不好，主機板壞掉、儲存晶片損壞，那基本上裡面的東西就回不來了，付錢找救援公司也不一定救得回來。</p><p>所以我就決定來寫這一篇，順便提醒所有看到的人，<strong>真的，拜託，先去開 Google 相簿備份</strong>。</p><h2 id="為什麼一定要備份照片？"><a href="#為什麼一定要備份照片？" class="headerlink" title="為什麼一定要備份照片？"></a>為什麼一定要備份照片？</h2><p>照片這種東西其實滿特別的，它跟手機裡面的 App、聯絡人、訊息不一樣，App 可以重新下載、聯絡人可以重新加，可是照片是「那個當下的瞬間」，錯過就是錯過了。</p><p>舉個實際的例子比較好懂，你跟朋友、家人出去玩拍的照片、小時候的合照、你家貓咪睡覺的可愛樣子、阿嬤煮的最後一頓飯，這些東西的價值真的不是用錢可以衡量的。</p><p>而手機這種隨身的東西，它壞掉的方式真的太多了：</p><ul><li>摔到地上螢幕碎掉、主機板壞掉</li><li>不小心掉到水裡</li><li>充電孔接觸不良導致無法充電</li><li>某一天就突然黑屏開不了機</li><li>電池膨脹</li><li>被偷被搶</li></ul><p>以我自己來講，以前也踩過這個坑，那時候我手機還是 Android，某天就突然開不了機，裡面一些我家寵物的照片就再也找不回來，事後真的後悔到不行 QQ…</p><p>所以那次之後我就乖乖把備份打開，到現在不管換了幾支手機，照片永遠都在 Google 相簿裡面，新手機只要登入帳號，所有照片都會回來，超方便。</p><h2 id="為什麼我推薦-Google-相簿？"><a href="#為什麼我推薦-Google-相簿？" class="headerlink" title="為什麼我推薦 Google 相簿？"></a>為什麼我推薦 Google 相簿？</h2><p>雲端備份其實有滿多選項可以選，例如 iCloud、OneDrive、Dropbox 等等，那為什麼我會特別推薦 Google 相簿呢？簡單來講就是這幾個原因：</p><div class="table-wrapper"><table><thead><tr><th>面向</th><th>說明</th></tr></thead><tbody><tr><td>跨平台</td><td>不論你是 iPhone 還是 Android 都可以用，未來換手機也不用煩惱</td></tr><tr><td>自動分類</td><td>Google 會自動依照人物、地點、事件分類，找照片超快</td></tr><tr><td>搜尋能力</td><td>在搜尋欄打「狗」or「鳥」，它就會把相關的照片撈出來</td></tr><tr><td>免費空間</td><td>申請 Google 帳號就有 15GB 免費空間（與 Gmail、Drive 共用）</td></tr><tr><td>整合度</td><td>Google 帳號基本上每個人都有，不用再額外註冊新帳號</td></tr></tbody></table></div><p>從這張表可以看出來，Google 相簿對於一般人來講真的滿適合的，尤其是「跨平台」這件事很重要，因為你不知道下一支手機會買 iPhone 還是 Android，用 Google 相簿就可以避免被綁死在某個系統。</p><h2 id="Google-相簿備份怎麼設定？"><a href="#Google-相簿備份怎麼設定？" class="headerlink" title="Google 相簿備份怎麼設定？"></a>Google 相簿備份怎麼設定？</h2><p>設定其實非常簡單，不論是 iPhone 還是 Android，流程基本上一樣，主要差別就是介面上的細微差異而已。</p><h3 id="安裝-Google-相簿-App"><a href="#安裝-Google-相簿-App" class="headerlink" title="安裝 Google 相簿 App"></a>安裝 Google 相簿 App</h3><p>底下這邊我會使用 iPhone 的畫面來示範，Android 的話步驟也是差不多的，畫面可能會有些微不同，不過大致上是差不多的。</p><p>如果你是 iPhone，那你需要先到 App Store 下載「Google Photos（Google 相簿）」這個 App。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2516.jpg" data-fancybox="gallery" data-caption="App Store 搜尋 Google Photos"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2516.jpg" alt="App Store 搜尋 Google Photos"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">App Store 搜尋 Google Photos</figcaption></figure><h3 id="登入-Google-帳號"><a href="#登入-Google-帳號" class="headerlink" title="登入 Google 帳號"></a>登入 Google 帳號</h3><p>打開 Google 相簿 App 之後，第一件事情就是登入你的 Google 帳號，如果你是第一次使用，它會引導你輸入帳號密碼。</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2517.jpg" data-fancybox="gallery" data-caption="登入 Google 帳號"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2517.jpg" alt="登入 Google 帳號"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">登入 Google 帳號</figcaption></figure><h3 id="開啟備份功能"><a href="#開啟備份功能" class="headerlink" title="開啟備份功能"></a>開啟備份功能</h3><p>登入完成後，基本上你可以看到下方就會直接幫你打勾「自動備份這部裝置上的相片和影片」選項</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2518.jpg" data-fancybox="gallery" data-caption="開啟備份選項"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2518.jpg" alt="開啟備份選項"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">開啟備份選項</figcaption></figure><p>如果沒看到，或者沒有啟動也沒關係，進入畫面後，我們可以點右上角的個人頭像</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2519.jpg" data-fancybox="gallery" data-caption="個人頭像點開後的選單"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2519.jpg" alt="個人頭像點開後的選單"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">個人頭像點開後的選單</figcaption></figure><p>會跳出一個選單，在這裡你可以看到現在備份到底有沒有開啟，如果是顯示「備份功能已關閉」，那就代表你還沒有開啟備份功能</p><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2521.jpg" data-fancybox="gallery" data-caption="備份功能已關閉畫面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2521.jpg" alt="備份功能已關閉畫面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">備份功能已關閉畫面</figcaption></figure><p>接著只要點一下「開啟備份」的按鈕，你就會跳出相關備份選項，這邊要注意的是，要調整兩處：</p><ol><li>備份品質：建議選「壓縮畫質」，普通帳號大多只有 15GB 的免費空間，如果選「原始畫質」很快就會用完了，除非你真的對照片品質有很高的要求，不然選「壓縮畫質」就很夠用了。</li><li>備份方式：建議打開「沒有 Wi-Fi 時使用行動數據備份」，這樣就算你在外面拍照，也能隨時備份，不用等到回家才上傳，而且也可以隨時預防手機不小心摔壞了，照片還沒備份的窘境。</li></ol><figure class="my-4"><a href="https://images.israynotarray.com/blog/other/google-photo/IMG_2522.PNG" data-fancybox="gallery" data-caption="備份選項畫面"><img class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/other/google-photo/IMG_2522.PNG" alt="備份選項畫面"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">備份選項畫面</figcaption></figure><p>以上這樣你的手機就會開始自動備份照片到 Google 相簿了，之後不管你換手機還是手機壞掉，只要登入同一個 Google 帳號，所有照片都會回來的～</p><h2 id="一些要注意的小細節"><a href="#一些要注意的小細節" class="headerlink" title="一些要注意的小細節"></a>一些要注意的小細節</h2><p>設定雖然很簡單，可是有幾件事情我覺得有必要提醒一下，免得你之後遇到狀況會慌。</p><h3 id="儲存空間"><a href="#儲存空間" class="headerlink" title="儲存空間"></a>儲存空間</h3><p>前面有提到 Google 帳號預設給你 15GB 的免費空間，這個空間是 Gmail、Google Drive、Google 相簿三個服務共用。</p><p>如果你照片真的很多用滿了，可以把不需要的照片刪一刪 or 把過大的 Gmail 附件清掉，這樣空間就會被釋放出來。</p><h3 id="保留-APP-在背景"><a href="#保留-APP-在背景" class="headerlink" title="保留 APP 在背景"></a>保留 APP 在背景</h3><p>這個是 iPhone 用戶特別要注意的地方，因為 iOS 的背景管理比 Android 嚴格滿多的，當你把 Google 相簿從多工列「往上滑掉」的時候，整個 APP 就直接停止運作了，連帶備份也會跟著中斷。</p><p>什麼意思呢？舉個實際的例子，假設你今天出門拍了一整天的照片，回家後打開 Google 相簿想說讓它備份一下，看到照片正在上傳，然後你以為「OK 它在跑了」就把 APP 滑掉去做別的事情，這時候備份其實是直接被你終止的，照片並沒有真的上傳完成。</p><p>所以正確的做法是，當 Google 相簿在備份的時候，請不要把它從多工列滑掉，你可以按 Home 鍵 or 滑回主畫面讓它在背景跑，這樣它才有機會把照片完整上傳上去。</p><p>那如果你真的不小心滑掉了，也不用太緊張，下次再打開 Google 相簿，它會自己接著上次沒備份完的地方繼續上傳，只是過程中如果手機剛好壞掉，那些還沒上傳的照片就回不來了。</p><h2 id="結語"><a href="#結語" class="headerlink" title="結語"></a>結語</h2><p>寫這一篇主要是希望大家不要等到手機壞掉才後悔，照片這種東西真的就是「有備無患」，平常花五分鐘設定一下，可以救你之後可能哭著找不回照片的窘境。</p><p>那位學生後來手機修好拿回來，照片運氣不錯都還在，可是他第一件事情就是把 Google 相簿備份打開，這次學乖了。</p><p>希望這一篇有幫到正在看的你，如果你還沒設定備份，看完這篇之後就趕快去設定一下吧～</p><p>照片是回不來的，可是設定只要五分鐘哩～</p>]]></content>
    
    
    <summary type="html">手機摔壞、掉到水裡、突然開不了機，這時候裡面的照片其實是最難救回來的東西。這篇就分享為什麼我會推薦把 Google 相簿備份打開，並且實際示範 iPhone 上的設定流程，包含備份畫質的選擇、15GB 免費空間怎麼用、行動數據備份要不要打開，以及 iOS 上很容易踩到的坑 — 把 APP 從多工列滑掉備份就會中斷。</summary>
    
    
    
    <category term="雜談" scheme="https://israynotarray.com/categories/other/"/>
    
    
    <category term="備份" scheme="https://israynotarray.com/tags/%E5%82%99%E4%BB%BD/"/>
    
    <category term="Google 相簿" scheme="https://israynotarray.com/tags/Google-%E7%9B%B8%E7%B0%BF/"/>
    
    <category term="雲端" scheme="https://israynotarray.com/tags/%E9%9B%B2%E7%AB%AF/"/>
    
    <category term="手機" scheme="https://israynotarray.com/tags/%E6%89%8B%E6%A9%9F/"/>
    
    <category term="iPhone" scheme="https://israynotarray.com/tags/iPhone/"/>
    
    <category term="iOS" scheme="https://israynotarray.com/tags/iOS/"/>
    
    <category term="Google Photos" scheme="https://israynotarray.com/tags/Google-Photos/"/>
    
  </entry>
  
  <entry>
    <title>Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具，Prompt 才寫得準</title>
    <link href="https://israynotarray.com/ai/20260429/8473628195/"/>
    <id>https://israynotarray.com/ai/20260429/8473628195/</id>
    <published>2026-04-29T06:00:00.000Z</published>
    <updated>2026-04-29T06:00:00.000Z</updated>
    
    <content type="html"><![CDATA[<figure class="my-4"><a href="https://images.israynotarray.com/blog/ai/claude-tools-agens/Gemini_Generated_Image_kwkdeakwkdeakwkd.png" data-fancybox="gallery" data-caption="Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具，Prompt 才寫得準"><img fetchpriority="high" class="cursor-zoom-in !mb-0" src="https://images.israynotarray.com/blog/ai/claude-tools-agens/Gemini_Generated_Image_kwkdeakwkdeakwkd.png" alt="Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具，Prompt 才寫得準"></a><figcaption class="text-center text-sm text-gray-500 dark:text-gray-400 mt-2 italic">Claude Code 內建 Tools 在做什麼？看懂它怎麼選工具，Prompt 才寫得準</figcaption></figure><h2 id="前言"><a href="#前言" class="headerlink" title="前言"></a>前言</h2><p>這一篇稍微來聊一下 Claude Code 的內建 Tools。如果你有用過 Claude Code 應該會發現一件事，有時候你叫它讀一個檔案它直接 Read 打開，有時候你問它「這個專案有哪些 component」它跑去 Glob、Grep，可是偶爾它會繞遠路跑去用 Bash 的 <code>cat</code>、<code>grep</code>，讓人很困惑為什麼不直接用內建工具。其實 Claude 是有一套自己的工具選擇邏輯的，這篇就來聊一下 Claude Code 有哪些內建 Tools、它怎麼決定要用哪個、以及怎麼從工具使用模式反推回去調 Prompt。</p><blockquote><p><strong>Note</strong><br>如果你還不熟悉 Claude Code 的基本操作，可以先去看 <a href="https://israynotarray.com/ai/20260128/374686091/">想用 Claude Code 開發？這篇帶你從入門到進階</a>，裡面有很完整的入門介紹。本篇所有工具名稱都對照官方的 <a href="https://code.claude.com/docs/en/tools-reference">Tools reference</a>。</p></blockquote><h2 id="為什麼要搞懂內建-Tools"><a href="#為什麼要搞懂內建-Tools" class="headerlink" title="為什麼要搞懂內建 Tools"></a>為什麼要搞懂內建 Tools</h2><p>你可能會想說：我又不是 Claude 本人，幹嘛搞懂它用什麼工具？</p><p>簡單來講，當你看得懂 Claude 選了什麼工具，你就反推得出來你的 Prompt 哪裡沒講清楚。</p><p>舉個實際的例子，假設你叫 Claude「幫我看一下登入邏輯」，結果它連續開了七八個 Glob 都沒找到，最後才用 Grep 搜到。如果你看不懂它在做什麼那只會覺得「怎麼這麼慢」，可是如果你知道 Glob 是用檔名 pattern、Grep 是用內容字串搜，你就會發現問題出在你的描述「登入邏輯」是個抽象概念，檔名根本不會直接寫 <code>login</code>，下次你就會直接給它 <code>src/auth/</code> 這個路徑，事情瞬間變快。</p><p>另外一個原因是 Claude Code 的 Hook、Skill 的 <code>allowed-tools</code>、permissions 的 deny 規則這些設定，全部都是建立在工具名稱上面的，你如果不知道哪些工具存在、各自做什麼，那這些進階設定根本沒辦法寫對。</p><h2 id="Claude-Code-有哪些內建-Tools"><a href="#Claude-Code-有哪些內建-Tools" class="headerlink" title="Claude Code 有哪些內建 Tools"></a>Claude Code 有哪些內建 Tools</h2><p>先快速看一下整體有哪些，後面會挑常用的展開講。</p><div class="table-wrapper"><table><thead><tr><th>分類</th><th>工具名稱</th><th>主要用途</th></tr></thead><tbody><tr><td>讀檔</td><td>Read</td><td>讀取檔案內容</td></tr><tr><td>讀檔</td><td>Glob</td><td>用檔名 pattern 找檔案</td></tr><tr><td>讀檔</td><td>Grep</td><td>用內容搜尋（底層是 ripgrep）</td></tr><tr><td>改檔</td><td>Edit</td><td>對檔案做精確的字串替換</td></tr><tr><td>改檔</td><td>Write</td><td>整個檔案覆寫</td></tr><tr><td>改檔</td><td>NotebookEdit</td><td>修改 Jupyter Notebook 的 cell</td></tr><tr><td>執行</td><td>Bash</td><td>跑 Shell 指令</td></tr><tr><td>執行</td><td>PowerShell</td><td>跑 PowerShell 指令（Windows 為主）</td></tr><tr><td>執行</td><td>Monitor</td><td>背景執行指令並把每行輸出回傳給 Claude</td></tr><tr><td>委派</td><td>Agent</td><td>派出 Subagent 處理任務</td></tr><tr><td>程式碼</td><td>LSP</td><td>透過 Language Server 做型別檢查、跳轉、找參考</td></tr><tr><td>任務</td><td>TaskCreate &#x2F; TaskUpdate &#x2F; TaskList</td><td>管理任務清單</td></tr><tr><td>互動</td><td>AskUserQuestion</td><td>主動問使用者多選題問題</td></tr><tr><td>計畫</td><td>EnterPlanMode &#x2F; ExitPlanMode</td><td>進入或離開 Plan Mode</td></tr><tr><td>排程</td><td>CronCreate &#x2F; CronDelete &#x2F; CronList</td><td>session 內的排程任務</td></tr><tr><td>網路</td><td>WebFetch</td><td>抓網頁內容</td></tr><tr><td>網路</td><td>WebSearch</td><td>搜尋網路</td></tr><tr><td>其他</td><td>Skill</td><td>執行 Skill</td></tr><tr><td>其他</td><td>ToolSearch</td><td>載入延遲載入的工具定義</td></tr></tbody></table></div><p>看起來很多，可是日常你會大量看到的就是 Read、Glob、Grep、Edit、Write、Bash、Agent 這幾個，其他比較偶爾才會出現。接下來會挑這幾個重點工具來聊，最後再補充幾個值得認識的。</p><h2 id="讀檔三劍客：Read、Glob、Grep"><a href="#讀檔三劍客：Read、Glob、Grep" class="headerlink" title="讀檔三劍客：Read、Glob、Grep"></a>讀檔三劍客：Read、Glob、Grep</h2><p>很多人剛開始用 Claude Code 都會搞不懂為什麼讀檔需要三個工具，明明就是讀檔不是嗎？其實這三個東西在做的事情完全不一樣，搞混了 Claude 就會選錯。</p><h3 id="Read"><a href="#Read" class="headerlink" title="Read"></a>Read</h3><p>Read 是最直接的讀檔工具，給它一個絕對路徑就會把檔案內容回傳，而且回傳的時候會自動帶行號，長得像這樣：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs txt">1    function login(email, password) &#123;<br>2      // ...<br>3    &#125;<br></code></pre></td></tr></table></figure><p>這個行號不是裝飾，後面 Edit 工具會用到，因為 Edit 需要精確的行內容才有辦法做替換。</p><p>Read 還有幾個特性要注意：</p><ul><li>一次最多讀 2000 行，超過要用 <code>offset</code> 跟 <code>limit</code> 分段讀</li><li>可以讀圖片、PDF、Jupyter Notebook，圖片會用視覺方式呈現給 Claude</li><li>路徑必須是絕對路徑，不能寫相對路徑</li></ul><p>那為什麼有些時候 Claude 不直接用 Bash 的 <code>cat</code>？因為 <code>cat</code> 不會給行號，後續做 Edit 就會卡住，而且 <code>cat</code> 一次回所有內容沒辦法分段，大檔案直接灌爆 Context。所以官方的系統提示有特別寫「能用 Read 就不要用 cat」這條規則。</p><h3 id="Glob"><a href="#Glob" class="headerlink" title="Glob"></a>Glob</h3><p>Glob 是用檔名 pattern 找檔案，支援星號的通配符，像是這樣：</p><figure class="highlight txt"><table><tr><td class="gutter"><pre><span class="line">1</span><br><span class="line">2</span><br><span class="line">3</span><br></pre></td><td class="code"><pre><code class="hljs txt">**/*.tsx          # 所有 tsx 檔<br>src/**/index.ts   # src 底下任何 index.ts<br>*.config.&#123;js,ts&#125;  # 根目錄的 config 檔<br></code></pre></td></tr></table></figure><p>回傳結果會按照檔案的修改時間排序，最近改的會在前面。這個排序其實很實用，因為當你在 debug 某個功能的時候，最近改過的檔案通常就是你要排查的對象。</p><p>簡單來講 Glob 是你已經知道檔名長什麼樣子的時候用的。如果你要找的是包含某段內容的檔案，那就要用 Grep 了。</p><h3 id="Grep"><a href="#Grep" class="headerlink" title="Grep"></a>Grep</h3><p>Grep 底層是包裝 <a href="https://github.com/BurntSushi/ripgrep">ripgrep</a>，所以速度比 Bash 的 <code>grep</code> 快很多，而且支援完整的 Regex。</p><p>它有三種輸出模式：</p><ul><li><code>files_with_matches</code>（預設）：只回傳有符合的檔案路徑</li><li><code>content</code>：回傳實際匹配到的那幾行內容</li><li><code>count</code>：回傳每個檔案匹配到幾次</li></ul><p>以我自己來講最常用的是 <code>content</code> 模式搭配 <code>-n</code>（顯示行號）跟 <code>-C</code>（前後 context），這樣 Claude 一眼就能看到搜尋結果跟前後幾行的脈絡，不用再多開一次 Read 確認。</p><blockquote><p><strong>Note</strong><br>Grep 預設只搜單行，如果你要搜跨行的 pattern（像是「一個 function 開頭到第一個 return」這種），要設 <code>multiline: true</code>。這個常被忽略，所以 Claude 在搜跨行結構時偶爾會找不到，記得提醒它一下。</p></blockquote><h3 id="三個工具的差異"><a href="#三個工具的差異" class="headerlink" title="三個工具的差異"></a>三個工具的差異</h3><p>聊完這三個工具後簡單總結一下時機：</p><ul><li>你已經知道路徑：用 Read 直接打開</li><li>你知道檔名 pattern 但不知道在哪：用 Glob 找</li><li>你只知道內容裡有某些字串：用 Grep 搜</li></ul><p>如果你下指令的時候直接點明這件事，例如「Grep 一下整個專案有沒有 <code>LEGACY_AUTH</code> 這個常數」，Claude 就不會浪費時間先 Glob 一輪，直接 Grep 出結果。</p><h2 id="改檔的-Edit-跟-Write-不是同一件事"><a href="#改檔的-Edit-跟-Write-不是同一件事" class="headerlink" title="改檔的 Edit 跟 Write 不是同一件事"></a>改檔的 Edit 跟 Write 不是同一件事</h2><p>讀完接下來就是改了。Claude Code 提供兩個改檔的工具：Edit 跟 Write，很多人會以為這兩個差不多，其實差很多。</p><h3 id="Edit-是精確替換"><a href="#Edit-是精確替換" class="headerlink" title="Edit 是精確替換"></a>Edit 是精確替換</h3><p>Edit 的運作方式是傳入一段 <code>old_string</code> 跟一段 <code>new_string</code>，Claude Code 會去檔案裡面找出 <code>old_string</code> 完全一致的位置然後換掉。重點在『完全一致』這四個字，連空白、縮排、換行符都要對。</p><p>也因為這樣 Edit 有一個重要的限制：<strong>你必須先 Read 過那個檔案才能 Edit</strong>。沒讀過直接 Edit 會被擋掉，這是為了避免 Claude 用過時的記憶亂改檔案。</p><p>如果同樣的字串在檔案裡面出現多次，Edit 預設會直接報錯叫你提供更多上下文讓字串唯一。如果你就是想全部改掉，可以加 <code>replace_all: true</code>。</p><h3 id="Write-是整檔覆寫"><a href="#Write-是整檔覆寫" class="headerlink" title="Write 是整檔覆寫"></a>Write 是整檔覆寫</h3><p>Write 就是直接把整個檔案重寫，路徑存在的話會把舊內容全部蓋掉。</p><p>聽起來很方便對吧？可是這個工具其實滿危險的，因為它會把你手動改過 Claude 沒讀到的內容全部清空。假設你開兩個視窗一邊在 Claude Code 一邊在 VSCode 改檔案，結果 Claude 用 Write 覆蓋，你剛剛在 VSCode 改的東西就直接消失了。</p><p>所以官方的系統提示會強烈建議「能用 Edit 就不要用 Write」，Write 大部分只用在「建立全新檔案」這個場景。</p><p>如果你發現 Claude 一直在用 Write 而不是 Edit，通常是因為它記憶中的檔案內容跟磁碟上的不一致（你中間自己改過），導致 Edit 怎麼配對都失敗，最後 fallback 到 Write。這時候叫它先重新 Read 一次再 Edit 通常就會好。</p><h2 id="Bash：什麼時候-Claude-會繞道用它"><a href="#Bash：什麼時候-Claude-會繞道用它" class="headerlink" title="Bash：什麼時候 Claude 會繞道用它"></a>Bash：什麼時候 Claude 會繞道用它</h2><p>Bash 工具就是你想的那樣，跑 Shell 指令。可是 Claude Code 的系統提示會特別提醒它：能用內建工具就不要用 Bash。原因主要是 <code>cat</code>、<code>grep</code>、<code>find</code> 這類指令都有對應的內建版本，內建工具的 UX 會比較好，使用者要審查跟授權也比較直覺，回傳格式對 Claude 後續處理也更友善。</p><p>那 Claude 什麼時候還是會用 Bash 呢？大概有以下幾種情況：</p><ul><li>你叫它跑測試 or 建置：例如 <code>pnpm test</code>、<code>pnpm build</code></li><li>操作 Git：<code>git status</code>、<code>git diff</code>、<code>git log</code> 這類（Claude Code 把唯讀的 git 指令直接列為免授權）</li><li>啟動 dev server：例如 <code>pnpm dev</code></li><li>跑套件管理指令：<code>pnpm install</code></li><li>處理一些內建工具沒有的東西：例如複雜的 pipe（找出最大的五個檔案之類的）</li></ul><p>Bash 還有一個 <code>run_in_background</code> 參數可以讓指令背景執行，這個對長時間任務很有用，像是跑 dev server or 長時間的 build，Claude 不需要呆等就可以繼續做別的事，背景指令完成之後它會被自動通知。</p><blockquote><p><strong>Note</strong><br>這邊有一個有趣的細節：<code>Read</code> 跟 <code>Edit</code> 的 deny 規則只會擋內建工具，不會擋 Bash 子進程。也就是說你寫 <code>Read(./.env)</code> 進 deny，Claude 用 Read 讀 <code>.env</code> 會被擋掉，可是它跑 <code>cat .env</code> 還是會通過。如果要從 OS 層面真的擋住，需要去設 sandboxing。</p></blockquote><h2 id="Agent：把任務丟給-Subagent"><a href="#Agent：把任務丟給-Subagent" class="headerlink" title="Agent：把任務丟給 Subagent"></a>Agent：把任務丟給 Subagent</h2><p>這是最多人不懂、用了威力卻最大的工具。</p><h3 id="Subagent-跟主對話差在哪"><a href="#Subagent-跟主對話差在哪" class="headerlink" title="Subagent 跟主對話差在哪"></a>Subagent 跟主對話差在哪</h3><p>簡單來講，Agent 工具會開一個全新的 Subagent 去處理某個任務，這個 Subagent 有幾個特點：</p><ul><li>有自己獨立的 Context Window，跟主對話完全不共享</li><li>完成任務後只回傳一個摘要結果給主對話</li><li>它看不到主對話的歷史，所以你要給它的指令必須完整自包含</li><li>每個 Subagent 都繼承父對話的權限再加上額外的工具限制</li><li>可以指定 Subagent 類型，後面會講</li></ul><p>如果用比較生活面的例子來講的話，Subagent 就像你叫實習生去做事，你只給他任務描述跟所需的背景資料，他做完之後只回報結論給你，中間翻過幾本書、查過什麼資料你完全不會看到。</p><h3 id="內建的三種-Subagent"><a href="#內建的三種-Subagent" class="headerlink" title="內建的三種 Subagent"></a>內建的三種 Subagent</h3><p>Claude Code 預設提供三種內建 Subagent，每種的設計用途不太一樣：</p><div class="table-wrapper"><table><thead><tr><th>Subagent</th><th>模型</th><th>工具權限</th><th>適合的場景</th></tr></thead><tbody><tr><td>Explore</td><td>Haiku（速度快）</td><td>唯讀（不能 Write&#x2F;Edit）</td><td>大範圍搜檔、理解 codebase</td></tr><tr><td>Plan</td><td>跟主對話相同</td><td>唯讀（不能 Write&#x2F;Edit）</td><td>Plan Mode 用來研究專案的小幫手</td></tr><tr><td>general-purpose</td><td>跟主對話相同</td><td>全部工具</td><td>複雜任務需要邊讀邊改</td></tr></tbody></table></div><p>從這張表可以看出來 Explore 是最常被自動觸發的，因為它便宜又快，而且只負責探索不會亂改檔案。Claude 在派 Explore 的時候還會指定一個 thoroughness 等級（quick、medium、very thorough），Prompt 越具體它就越能用低 thoroughness 完成任務、省更多 Token。</p><p>除了內建的之外你也可以在 <code>.claude/agents/</code> 底下定義自己的 Subagent，這就比較進階了，這篇就不展開講。</p><h3 id="什麼任務適合丟-Agent"><a href="#什麼任務適合丟-Agent" class="headerlink" title="什麼任務適合丟 Agent"></a>什麼任務適合丟 Agent</h3><p>需要讀很多檔案才能得出結論的任務最適合丟 Agent：</p><ul><li>「掃描整個專案，告訴我有沒有用到舊版的某個 API」</li><li>「分析這個功能的實作流程，從 router 一路追到 service」</li><li>「這個 codebase 哪裡有測試、哪裡沒測試」</li></ul><p>如果這種任務你直接讓主對話做，那讀過的所有檔案都會塞到主 Context 裡面，後面的工作 Token 就所剩無幾。改用 Agent 派出 Subagent 之後，主對話只會收到一個精簡的摘要，Context 就保得住。</p><p>之前在 <a href="https://israynotarray.com/ai/20260409/1057131696/">你的 Claude Code Context 是不是都被吃光了？AGENTS.md 與 Context 管控全攻略</a> 這篇我也有提到這個技巧，這邊再多補充一個觀念：你可以一次叫 Claude 派出多個 Agent 平行跑，例如「同時找出所有 API endpoint 跟所有 React component」，兩個 Subagent 會同時運作，比一個一個慢慢做快很多。</p><h3 id="什麼任務不要丟-Agent"><a href="#什麼任務不要丟-Agent" class="headerlink" title="什麼任務不要丟 Agent"></a>什麼任務不要丟 Agent</h3><p>可是 Agent 也不是萬能的，有些情況不適合用：</p><ul><li><strong>需要來回討論的任務</strong>：Subagent 看不到主對話歷史，每次派任務都要把背景重打一次，需要反覆討論的事情用 Subagent 反而拖時間</li><li><strong>目標已經很明確的單檔修改</strong>：直接 Read 跟 Edit 就好，丟給 Subagent 反而是繞遠路</li><li><strong>任務本身需要主對話的脈絡</strong>：你又懶得把背景重打一次的話就直接讓主對話做，省事</li></ul><blockquote><p><strong>Note</strong><br>如果你不希望 Claude 自動派某種 Subagent，可以在 <code>permissions.deny</code> 加上 <code>Agent(Explore)</code> 這種規則來擋掉它，這個語法只會擋對應的 Subagent，其他 Subagent 還是能用。</p></blockquote><h2 id="其他偶爾會用到的"><a href="#其他偶爾會用到的" class="headerlink" title="其他偶爾會用到的"></a>其他偶爾會用到的</h2><p>前面講的是日常會大量看到的，這邊再補幾個比較少出現可是值得知道的。</p><h3 id="WebFetch-跟-WebSearch"><a href="#WebFetch-跟-WebSearch" class="headerlink" title="WebFetch 跟 WebSearch"></a>WebFetch 跟 WebSearch</h3><p>兩個工具名稱看起來很像可是用途不同。WebFetch 是給定一個 URL 把網頁抓下來轉成 Markdown，適合用在「我有一個官方文件 URL，請你照著做」這種場景。WebSearch 則是真的去搜尋關鍵字，適合用在「最新版本的某個 API 怎麼用」這種需要找資訊的場景。</p><p>如果你叫 Claude「查一下 Vue 3.5 的最新功能」，它通常會先用 WebSearch 找出官方文件的 URL，然後再用 WebFetch 把詳細內容抓下來，這是滿常見的組合技。</p><h3 id="AskUserQuestion"><a href="#AskUserQuestion" class="headerlink" title="AskUserQuestion"></a>AskUserQuestion</h3><p>當 Claude 遇到不確定的選擇時，它有時候會主動跳出來問你問題，這時候用的就是 AskUserQuestion 工具。它是多選題形式的，比如你叫它建立一個專案可是沒講要用 React 還是 Vue，它就可能會先停下來給你幾個選項而不是自己猜。</p><p>如果你不希望它常常停下來問，可以在 CLAUDE.md 裡面寫清楚預設的選擇，這樣它就會自己決定不再彈出來問。</p><h3 id="LSP"><a href="#LSP" class="headerlink" title="LSP"></a>LSP</h3><p>LSP 是比較新的工具（透過 Language Server Protocol），適合做程式碼智慧的事情，像是跳轉到函式定義、找出某個 symbol 在哪裡被引用、列出某個 interface 的實作等等。比較有感的一點是 Claude 每次 Edit 完之後 LSP 會自動回報那個檔案的型別錯誤跟警告，所以 Claude 不需要再特別跑一次 build 就知道有沒有打錯字。</p><p>不過這個工具要先安裝對應語言的 Code Intelligence Plugin 才會啟用，沒裝的話 Claude 是看不到的。</p><h3 id="Monitor"><a href="#Monitor" class="headerlink" title="Monitor"></a>Monitor</h3><p>Monitor 是讓 Claude 在背景跑一個指令，把每一行輸出即時回傳給它，適合用來：</p><ul><li>盯著一個 log 檔，遇到 error 就跳出來提醒</li><li>監控 CI 任務的狀態變化</li><li>看某個資料夾有沒有新檔案產生</li></ul><p>跟 <code>Bash</code> 的 <code>run_in_background</code> 差別在於 Monitor 是「持續餵資料給 Claude」，Bash background 則是「跑完才通知一次」。</p><h3 id="TaskCreate-TaskUpdate-TaskList"><a href="#TaskCreate-TaskUpdate-TaskList" class="headerlink" title="TaskCreate &#x2F; TaskUpdate &#x2F; TaskList"></a>TaskCreate &#x2F; TaskUpdate &#x2F; TaskList</h3><p>這幾個工具是 Claude 自己用來管理任務清單的，當任務比較複雜的時候它會先建立一份待辦，然後一項一項標記完成。你會在 Claude Code 介面看到 Claude 在拉清單，背後就是這幾個工具在運作。</p><p>這邊有個小細節：互動式 session（你日常用的 <code>claude</code> 指令）用的是 TaskCreate &#x2F; TaskUpdate &#x2F; TaskList 這組，至於 Headless 模式跟 Agent SDK 用的則是另一個叫做 TodoWrite 的舊工具，兩個概念上是同一件事不同實作。</p><h2 id="Claude-怎麼決定要用哪個工具"><a href="#Claude-怎麼決定要用哪個工具" class="headerlink" title="Claude 怎麼決定要用哪個工具"></a>Claude 怎麼決定要用哪個工具</h2><p>聊完工具本身，那 Claude 到底是怎麼決定要用哪個的呢？</p><p>簡單來講有兩個來源：</p><p>第一個是<strong>系統提示（System Prompt）</strong>。Claude Code 啟動時會載入一份很長的系統提示，裡面就明確寫了哪些工具優先、哪些工具有什麼限制，例如「能用內建工具就不要用 Bash」、「Edit 之前必須先 Read」這類規則。所以你可以理解成 Claude 的工具選擇邏輯有一部分是事先被教好的。</p><p>第二個是 <strong>Claude 自己的判斷</strong>。模型會根據你的請求內容、目前對話的脈絡、Context 中的檔案資訊來推測哪個工具最適合。這部分就比較吃 Prompt 寫得清不清楚，Prompt 越模糊 Claude 越容易繞遠路。</p><p>舉個實際的例子比較好懂：</p><ul><li>你說「打開 <code>src/auth/login.ts</code>」 → Claude 直接 Read，因為路徑明確</li><li>你說「找一下登入邏輯」 → Claude 可能會先 Glob 找 <code>**/login*</code> or <code>**/auth/**</code>，找不到再 Grep</li><li>你說「掃整個專案找用到 deprecated API 的地方」 → Claude 比較聰明的話會直接派 Explore Subagent</li></ul><p>從這個角度看，<strong>Prompt 給得越具體 Claude 用工具就越精準</strong>，這也是為什麼前面要建議大家學會看工具使用模式。</p><h2 id="從工具使用模式反推-Prompt-哪裡寫差了"><a href="#從工具使用模式反推-Prompt-哪裡寫差了" class="headerlink" title="從工具使用模式反推 Prompt 哪裡寫差了"></a>從工具使用模式反推 Prompt 哪裡寫差了</h2><p>這部分是這篇最核心的東西，前面講的一堆工具其實都是為了讓你看懂這個。</p><h3 id="Claude-一直-Read-不同檔案找不到目標"><a href="#Claude-一直-Read-不同檔案找不到目標" class="headerlink" title="Claude 一直 Read 不同檔案找不到目標"></a>Claude 一直 Read 不同檔案找不到目標</h3><p>通常代表你給的描述太抽象，它在猜檔案位置。例如你說「修一下首頁」，可是它根本不知道首頁是哪個檔案，就一直 Read 各種看起來像首頁的東西。</p><p>解法是 Prompt 直接給路徑 or 精確的描述，例如「修一下 <code>src/pages/Home.vue</code> 裡面的搜尋邏輯」。</p><h3 id="Claude-一直-Glob-都找不到"><a href="#Claude-一直-Glob-都找不到" class="headerlink" title="Claude 一直 Glob 都找不到"></a>Claude 一直 Glob 都找不到</h3><p>代表你描述的關鍵字跟實際檔名對不起來。例如你說「找登入相關的檔案」，可是專案實際是用 <code>Auth*</code> 命名的，Glob 一輪都找不到 <code>*login*</code>。</p><p>解法是直接告訴它命名慣例，or 改用 Grep 搜 import 的字串會比較準。</p><h3 id="Claude-Edit-一直失敗回去用-Write"><a href="#Claude-Edit-一直失敗回去用-Write" class="headerlink" title="Claude Edit 一直失敗回去用 Write"></a>Claude Edit 一直失敗回去用 Write</h3><p>如果你看到 Claude 試了幾次 Edit 都報找不到 <code>old_string</code>，最後改用 Write，通常有兩個原因：</p><ol><li>你中間用 IDE 自己改過檔案，Claude 記憶中的內容跟磁碟不一致</li><li>縮排 or trailing whitespace 不一致（特別是 tab vs space）</li></ol><p>解法是叫它先重新 Read 一次再 Edit，or 你直接點明「請先 Read 一次最新內容再改」。</p><h3 id="Claude-不用-Subagent-自己硬讀很多檔"><a href="#Claude-不用-Subagent-自己硬讀很多檔" class="headerlink" title="Claude 不用 Subagent 自己硬讀很多檔"></a>Claude 不用 Subagent 自己硬讀很多檔</h3><p>如果任務本來該用 Agent 派出去（例如全專案掃描），結果 Claude 自己一個一個 Read，那是因為它沒判斷出這是個大任務。</p><p>解法是你直接在 Prompt 裡面寫「請用 Explore subagent 幫我掃整個專案」，明確要求它派 Agent。</p><h3 id="Claude-一直繞道用-Bash"><a href="#Claude-一直繞道用-Bash" class="headerlink" title="Claude 一直繞道用 Bash"></a>Claude 一直繞道用 Bash</h3><p>如果該用 Read 它跑去 <code>cat</code>、該用 Grep 它跑去 <code>grep</code>，多半是因為以下幾個原因：</p><ul><li><code>permissions.deny</code> 把內建工具擋掉了，逼 Claude fallback 到 Bash（前面有提過 deny 規則只擋內建工具不擋 Bash）</li><li>CLAUDE.md 裡面有不必要的引導指令</li><li>它要做的事情真的需要 pipe 組合，例如「找出最大的五個 log 檔」</li></ul><p>前兩個情況用 <code>/permissions</code> 跟檢查 CLAUDE.md 就能修，第三個是合理的不用管。</p><h2 id="總結"><a href="#總結" class="headerlink" title="總結"></a>總結</h2><p>最後整理一下這篇的重點：</p><ul><li>Claude Code 內建 Tools 主要分成讀檔（Read、Glob、Grep）、改檔（Edit、Write）、執行（Bash）、委派（Agent）這四類，外加 LSP、Monitor、TaskCreate 等比較新的工具</li><li>Read 必看行號，Edit 必先 Read，Write 盡量少用，免得蓋掉手動改的東西</li><li>Glob 是找檔名 pattern，Grep 是找內容字串，搞清楚才不會選錯</li><li>Agent 是最強大也最被忽略的工具，Claude Code 內建 Explore、Plan、general-purpose 三種 Subagent，需要讀很多檔案的任務丟給 Explore 最划算</li><li><code>permissions.deny</code> 的 Read&#x2F;Edit 規則只擋內建工具，不擋 Bash 子進程，要從 OS 層擋要走 sandboxing</li><li>看懂 Claude 用什麼工具，就能反推你的 Prompt 哪裡沒講清楚</li></ul><p>希望這一篇有讓你對 Claude Code 的內建 Tools 有更近一步的認識，下次它選錯工具的時候你就知道該怎麼調 Prompt 了哩～</p>]]></content>
    
    
    <summary type="html">這一篇稍微來聊一下 Claude Code 的內建 Tools。如果你有用過 Claude Code 應該會發現一件事，有時候你叫它讀一個檔案它直接 Read 打開，有時候你問它「這個專案有哪些 component」它跑去 Glob、Grep，可是偶爾它會繞遠路跑去用 Bash 的 cat、grep，讓人很困惑為什麼不直接用內建工具。其實 Claude 是有一套自己的工具選擇邏輯的，這篇就來聊一下 Claude Code 有哪些內建 Tools、它怎麼決定要用哪個、以及怎麼從工具使用模式反推回去調 Prompt。</summary>
    
    
    
    <category term="AI" scheme="https://israynotarray.com/categories/ai/"/>
    
    
    <category term="AI" scheme="https://israynotarray.com/tags/AI/"/>
    
    <category term="Claude Code" scheme="https://israynotarray.com/tags/Claude-Code/"/>
    
    <category term="AI Agent" scheme="https://israynotarray.com/tags/AI-Agent/"/>
    
    <category term="AI 開發" scheme="https://israynotarray.com/tags/AI-%E9%96%8B%E7%99%BC/"/>
    
    <category term="Claude Code Tools" scheme="https://israynotarray.com/tags/Claude-Code-Tools/"/>
    
  </entry>
  
</feed>
