<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Workflow｜林鼎淵 Dean Lin</title>
    <link>https://deanlin.net/tags/workflow/</link>
    <image><url>https://deanlin.net/images/author_hu_aa256fc934e9a932.jpg</url><title>Workflow｜林鼎淵 Dean Lin</title><link>https://deanlin.net/tags/workflow/</link><width>144</width><height>144</height></image>
    <description>林鼎淵（Dean Lin）的部落格：生成式 AI 企業內訓講師、外商資安公司軟體專家、7 本書作者。400 多篇文章用白話分享生成式 AI 實戰、Claude Code 與 Vibe Coding 教學、團隊合作與職場成長。</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>© 2020–2026 林鼎淵 Dean Lin</copyright>
    <lastBuildDate>Sat, 15 Aug 2026 11:30:05 +0800</lastBuildDate>
    <atom:link href="https://deanlin.net/tags/workflow/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>遇到問題，直接問 AI 不香嗎？</title>
      <link>https://deanlin.net/blog/202608/just-ask-ai/</link>
      <pubDate>Sat, 15 Aug 2026 11:30:05 +0800</pubDate>
      <guid>https://deanlin.net/blog/202608/just-ask-ai/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>AI 實戰</category>
      <description>「香！」這是大多數人心中的答案。</description>
      <media:content url="https://deanlin.net/blog/202608/just-ask-ai/images/just-ask-ai-og_hu_e54fe4728f5d95ca.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p><strong>現在 AI 的使用門檻很低，打開視窗，把問題丟進去，稍等一下就有答案了。</strong></p>
<p>但我在金融、醫療、行銷、電子、通訊等產業講了上百場內訓，發現大家就算已經在工作中使用 AI，還是會問：</p>
<ul>
<li>「為什麼開新視窗後，AI 不記得我們過去的對話？」</li>
<li>「專案資訊散落在信箱、行事曆跟雲端硬碟，每次都要找好久。」</li>
<li>「我只是要 AI 彙整報表、生成簡報，結果每次格式都不一樣！」</li>
</ul>
<blockquote class="pullquote"><p>產業不同、職位不同，卡住的地方卻幾乎一模一樣。</p>
</blockquote>
<p>每件事都需要：「重新交代背景 → 來回複製貼上 → 反覆調整格式」</p>
<p>但這些明明是 AI 能接手的機械式工作，只是我們已經習慣了這樣用它。</p>
<hr>
<h2 id="那要怎麼跳出這個習慣">那要怎麼跳出這個習慣？<a class="h-anchor" href="#%e9%82%a3%e8%a6%81%e6%80%8e%e9%ba%bc%e8%b7%b3%e5%87%ba%e9%80%99%e5%80%8b%e7%bf%92%e6%85%a3" aria-label="這個段落的連結"></a></h2>
<p>分享一下我在內訓現場的做法。</p>
<p>課程開始前，我會先跟同學說：「如果發現今天的案例有可以優化的地方，請直接舉手告訴我；我知道怎麼用 AI，但你們才是產業的專家。」</p>
<blockquote class="pullquote"><p>真實世界裡，正確答案往往不只一種，更多時候我們是在做取捨。</p>
</blockquote>
<p>但很多人用 AI，只想拿到「一個答案」。</p>
<p>拿到不符預期的結果，就覺得是 AI 不好用。</p>
<p><strong>你大概也遇過這種長官，交代任務時丟下一句「幫我處理」就走了。</strong></p>
<p><strong>什麼細節都問不到的狀態下，你只能用猜的；但交出去的成果往往換來一句「這不是我要的」。</strong></p>
<p>我們對 AI，常常就是那個長官：「產業、角色、能動用的資源都沒交代，卻期待它一次給出對的答案。」</p>
<p>所以我跟 AI 協作時，不會直接要答案，而是反過來：</p>
<p><strong>「請它反問我缺什麼資訊 → 從不同面向給幾個版本 → 挑最接近的那個往下改」</strong></p>
<p><strong>需求一開始講不清楚沒關係，重點是每輪討論都離你的期待更近一點。</strong></p>
<hr>
<h2 id="等你和-ai-討論出符合期待的成果還可以更進一步">等你和 AI 討論出符合期待的成果，還可以更進一步。<a class="h-anchor" href="#%e7%ad%89%e4%bd%a0%e5%92%8c-ai-%e8%a8%8e%e8%ab%96%e5%87%ba%e7%ac%a6%e5%90%88%e6%9c%9f%e5%be%85%e7%9a%84%e6%88%90%e6%9e%9c%e9%82%84%e5%8f%af%e4%bb%a5%e6%9b%b4%e9%80%b2%e4%b8%80%e6%ad%a5" aria-label="這個段落的連結"></a></h2>
<p>還記得開頭那三個問題嗎？它們各自有解。</p>
<h3 class="h-nomark" id="1-開新視窗就失憶">1️⃣ 開新視窗就失憶？<a class="h-anchor" href="#1-%e9%96%8b%e6%96%b0%e8%a6%96%e7%aa%97%e5%b0%b1%e5%a4%b1%e6%86%b6" aria-label="這個段落的連結"></a></h3>
<p>→ 依主題建立「Project」，專案背景、指令，交代一次就好。</p>
<h3 class="h-nomark" id="2-資料散落在信箱行事曆雲端">2️⃣ 資料散落在信箱、行事曆、雲端？<a class="h-anchor" href="#2-%e8%b3%87%e6%96%99%e6%95%a3%e8%90%bd%e5%9c%a8%e4%bf%a1%e7%ae%b1%e8%a1%8c%e4%ba%8b%e6%9b%86%e9%9b%b2%e7%ab%af" aria-label="這個段落的連結"></a></h3>
<p>→ 接上「Connector」，讓 AI 自己去讀 Gmail、Google Drive、Notion，不用再複製貼上。</p>
<h3 class="h-nomark" id="3-每次產出的格式都不一樣">3️⃣ 每次產出的格式都不一樣？<a class="h-anchor" href="#3-%e6%af%8f%e6%ac%a1%e7%94%a2%e5%87%ba%e7%9a%84%e6%a0%bc%e5%bc%8f%e9%83%bd%e4%b8%8d%e4%b8%80%e6%a8%a3" aria-label="這個段落的連結"></a></h3>
<p>→ 把流程封裝成「Skill」，教會 AI 一次，它就永遠記得。</p>
<p>很多事 AI 做得到，只是我們沒想到可以這樣用。</p>
<hr>
<p>我開設這堂線上課程的初衷，是希望能解決你的真實痛點、並打造適合自己的 AI 工作流。</p>
<p>把雜事交給 AI：先學會跟它對話、和它討論，再用 Project、Connector、Skill 打好地基，最後把電腦裡的文件也交給它彙整。</p>
<p>課程後半段，我還想帶你做過去要「工程師」才能做到的事。</p>
<p>就算第一次接觸程式，也能用 Claude Code 做出解決自己痛點的小工具。</p>
<p>並分享如何搭配 Git 來為每個進度建立「存檔點」，像打電動一樣，改壞了就讀檔重來。</p>
<hr>
<h2 id="也許有人會想這些資源網路上不是都有嗎">也許有人會想，這些資源網路上不是都有嗎？<a class="h-anchor" href="#%e4%b9%9f%e8%a8%b1%e6%9c%89%e4%ba%ba%e6%9c%83%e6%83%b3%e9%80%99%e4%ba%9b%e8%b3%87%e6%ba%90%e7%b6%b2%e8%b7%af%e4%b8%8a%e4%b8%8d%e6%98%af%e9%83%bd%e6%9c%89%e5%97%8e" aria-label="這個段落的連結"></a></h2>
<p>是的，都有。</p>
<p>現在外面有太多看起來很炫的 AI 示範，但如果你真的有嘗試過，你會發現實際能落地非常少。</p>
<p>那是因為，他們沒有從零開始、只講成果跟目標；但當你想實作時，很容易遇到問題、卡關。</p>
<p>我自己在 Medium 也寫了上百篇文章，拍了幾十部 YT 教學影片。</p>
<p>但文章和影片有個先天限制：「一次只能專注在單一主題」</p>
<p>它適合讓你快速認識一個工具，卻很難帶你把整套工作流從零建起來。</p>
<p>如果你也想把 AI 從「有在用」，變成一套真正融入工作的流程，這堂課會告訴你每一步該怎麼做。</p>
<figure class="figure"><img src="https://deanlin.net/blog/202608/just-ask-ai/images/just-ask-ai-01.webp" width="1400" height="1400" alt="Claude 企業級自動化實戰" fetchpriority="high" decoding="async">
  <figcaption>Claude 企業級自動化實戰</figcaption>
</figure>

<p>線上課程正在籌備中，讓我更瞭解你的需求</p>
<p>填寫問卷領取折扣 👉<a href="https://lihi1.me/R678K">https://lihi1.me/R678K</a></p>
]]></content:encoded>
    </item>
    <item>
      <title>【Antigravity Workflow 實戰】建立自動化測試，解決 Vibe Coding 痛點！</title>
      <link>https://deanlin.net/blog/202601/antigravity-workflow-testing/</link>
      <pubDate>Mon, 19 Jan 2026 12:02:37 +0800</pubDate>
      <guid>https://deanlin.net/blog/202601/antigravity-workflow-testing/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>AI 實戰</category>
      <description>市場不會為爛產品買單，而自動化測試，是讓 Vibe Coding 成果，從玩具走向產品的過程！</description>
      <media:content url="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-og_hu_cd70e7e9f035e9e.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-01.webp" width="1400" height="788" alt="【Antigravity Workflow 實戰】建立自動化測試，解決 Vibe Coding 痛點！（圖 1）" fetchpriority="high" decoding="async"></p>
<p>如果你已經開始嘗試用 Vibe Coding 開發專案，一定會遇到以下問題：</p>
<h3 id="穩定性">穩定性<a class="h-anchor" href="#%e7%a9%a9%e5%ae%9a%e6%80%a7" aria-label="這個段落的連結"></a></h3>
<p>你一定遇過請 AI 解決目前遇到的問題，結果改完後發現過去正常的功能被改壞了。</p>
<h3 id="-複雜度">複雜度<a class="h-anchor" href="#-%e8%a4%87%e9%9b%9c%e5%ba%a6" aria-label="這個段落的連結"></a></h3>
<p>隨著專案不斷完善，功能肯定會持續增加；如果還是依靠人工去確認流程的正確性，是非常浪費時間、沒有效率的。</p>
<h3 id="-擴充性">擴充性<a class="h-anchor" href="#-%e6%93%b4%e5%85%85%e6%80%a7" aria-label="這個段落的連結"></a></h3>
<p>Vibe Coding 在初期可以加速開發，但隨著專案架構逐漸複雜，功能之間開始有相依性時，任何修改都可能引發連鎖影響；最怕的就是程式看起來可以跑，但背後的商業邏輯一團糟，出狀況時，你連問題都不知道如何定位。</p>
<blockquote class="pullquote"><p>如果這幾個問題能引起你的共鳴，先給自己比個讚，因為這代表你已經進入了下個階段。</p>
</blockquote>
<p>而今天這篇文章，就是要跟大家分享如何導入「自動化測試」，讓 Vibe Coding 的成果更加可靠。</p>
<p>並一口氣解決前面提到「穩定性、複雜度、擴充性」的問題。</p>
<blockquote>
<p>完整的 Demo，可以觀看 YT 喔 👉</p>
<p>對 AI 應用感興趣的朋友，歡迎按下訂閱按鈕，未來我計畫分享一系列 AI 工具的應用教學。</p>
</blockquote>
<div class="yt" data-yt="Rb0_LutRIaw" data-title="【Antigravity Workflow 實戰】市場不會為爛產品買單！建立自動化測試，解決 Vibe Coding 痛點">
  <a class="yt-link" href="https://www.youtube.com/watch?v=Rb0_LutRIaw" target="_blank" rel="noopener" aria-label="播放影片：【Antigravity Workflow 實戰】市場不會為爛產品買單！建立自動化測試，解決 Vibe Coding 痛點">
    <img src="https://i.ytimg.com/vi/Rb0_LutRIaw/hqdefault.jpg" alt="" width="480" height="360" loading="lazy" decoding="async">
    <span class="yt-play" aria-hidden="true"><svg viewBox="0 0 24 24" fill="currentColor" aria-hidden="true" focusable="false"><path d="M8 5.2v13.6L19 12z"/></svg></span>
    <span class="yt-title">【Antigravity Workflow 實戰】市場不會為爛產品買單！建立自動化測試，解決 Vibe Coding 痛點</span>
  </a>
</div>

<p><strong>假使你有把 Vibe Coding 成果產品化的打算，我強烈建議大家把這篇文章儲存下來，這樣之後才能得到喔！</strong></p>
<p>我會透過實際專案，帶你掌握以下知識：</p>
<p>① 哪些地方需要寫測試</p>
<p>② 怎麼設計工作流，讓 AI 幫你寫測試</p>
<p>③ 如何設計 GitHub Action 建立自動化測試</p>
<p>④ 建立 Rule 保護主分支的技巧</p>
<blockquote class="pullquote"><p><strong>記住，市場不會為爛產品買單；而加入自動化測試，是讓 Vibe Coding 的成果，從玩具走向產品的過程！</strong></p>
</blockquote>
<hr>
<h2 id="一不寫測試才會沒有時間">一、不寫測試，才會沒有時間<a class="h-anchor" href="#%e4%b8%80%e4%b8%8d%e5%af%ab%e6%b8%ac%e8%a9%a6%e6%89%8d%e6%9c%83%e6%b2%92%e6%9c%89%e6%99%82%e9%96%93" aria-label="這個段落的連結"></a></h2>
<p>這邊我用 Vibe Coding 寫了一個有「登入頁、儀表板、管理後台、Mock API」的前端專案。</p>
<p><a href="https://github.com/deancourse/vibe-coding-testing-practice">https://github.com/deancourse/vibe-coding-testing-practice</a></p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-02.webp" width="1400" height="709" alt="一、不寫測試，才會沒有時間（圖 2）" loading="lazy" decoding="async"></p>
<p>如果你有興趣從頭開始，我有把專案初始化的提示詞放在 GitHub 的專案上，可以直接複製貼上體驗。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-03.webp" width="1145" height="765" alt="一、不寫測試，才會沒有時間（圖 3）" loading="lazy" decoding="async"></p>
<p>不過今天文章的主軸是「自動化測試」，所以我建議你可以直接下載我放在 GitHub 的專案，然後跟著練習。</p>
<blockquote>
<p>如果你是第一次接觸 Git 與 Node.js 的朋友，<a href="https://youtu.be/5DEV_K0JauQ">可以參考我過去拍的影片，裡面有詳細的操作教學</a>。</p>
</blockquote>
<p>這邊我建議大家點擊 fork，將專案複製到自己的帳號下。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-04.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 4）" loading="lazy" decoding="async"></p>
<p>然後點擊 Code，從裡面複製專案網址。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-05.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 5）" loading="lazy" decoding="async"></p>
<p>接著打開你熟悉的程式碼編輯器，這邊我以 Google 最近推出的 Antigravity 來示範。</p>
<blockquote>
<p>第一次接觸程式碼編輯器的朋友也不用擔心，可以參考我過去分享的影片，不管 <a href="https://youtu.be/kVniOF36GEk">Cursor</a> 還是 <a href="https://youtu.be/-FW5DgQEV0M">Antigravity</a> 都有對應的教學。</p>
</blockquote>
<p>好的，我們再把注意力轉回 Antigravity，點擊 <code>Clone Repository</code>，貼上剛剛複製的網址。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-06.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 6）" loading="lazy" decoding="async"></p>
<p>選擇下載的路徑後就會開始下載，把專案下載好後，把視角切換到 <code>package.json</code>，其中 <code>dependencies</code> 與 <code>devDependencies</code> 是專案用到的套件。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-07.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 7）" loading="lazy" decoding="async"></p>
<p>我們打開終端機，輸入 <code>npm install</code> 就能夠安裝這些套件，安裝好的套件會存放到 <code>node_modules</code> 的資料夾中。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-08.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 8）" loading="lazy" decoding="async"></p>
<p>另外上面 scripts 的部分，則是專案能使用的指令，輸入 <code>npm run dev</code> 就可以啟動專案了。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-09.webp" width="1400" height="815" alt="一、不寫測試，才會沒有時間（圖 9）" loading="lazy" decoding="async"></p>
<p>當然你也可以直接打開與 AI 的對話視窗，跟他說 <code>幫我安裝套件並啟動專案</code> 就能搞定一切；但多了解專案的架構，Vibe Coding 可以走得更遠。</p>
<h3 id="-登入頁面">登入頁面<a class="h-anchor" href="#-%e7%99%bb%e5%85%a5%e9%a0%81%e9%9d%a2" aria-label="這個段落的連結"></a></h3>
<p>這邊先從登入頁開始，跟大家說明系統功能，以及要測試的情境：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-10.webp" width="1400" height="815" alt="登入頁面（圖 10）" loading="lazy" decoding="async"></p>
<p>可以看到要輸入「電子郵件」與「密碼」才能登入系統，看起來很簡單，但其實這裡是<strong>第一道防線</strong>。</p>
<p>以網頁系統來說，會分成「前端」跟「後端」，我們看到的視覺頁面是前端。</p>
<p>假使使用者輸入的「電子郵件格式錯誤」，或者「密碼沒有英數字混合並大於 8 碼」，<strong>因為格式錯誤，所以根本不需要呼叫後端 API，來增加伺服器的負擔</strong>。</p>
<p>至於有沒有傳送到後端，我們可以打開 F12，或是點擊滑鼠右鍵，選擇檢查，來開啟「開發人員工具」，點擊「網路」的分頁。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-11.webp" width="1400" height="815" alt="登入頁面（圖 11）" loading="lazy" decoding="async"></p>
<p>在基礎格式錯誤的狀態下，可以用前端驗證擋住；所以你會發現，即使按下登入也不會呼叫後端 API。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-12.webp" width="1400" height="815" alt="登入頁面（圖 12）" loading="lazy" decoding="async"></p>
<p>將兩者格式改為正確之後，才會呼叫 API：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-13.webp" width="1400" height="815" alt="登入頁面（圖 13）" loading="lazy" decoding="async"></p>
<p>以剛才的操作來說，就至少涵蓋了 3 種測試情境：</p>
<ul>
<li>使用者格式輸入錯誤的時候，前端會擋住</li>
<li>不同的格式錯誤，會有對應錯誤訊息</li>
<li>只有格式正確時，才會呼叫 API</li>
</ul>
<p>在程式加入這些測試情境後，未來請 AI 修改程式時，你才知道是否會影響到既有功能。</p>
<h3 id="-使用-mock-api-模擬情境">使用 Mock API 模擬情境<a class="h-anchor" href="#-%e4%bd%bf%e7%94%a8-mock-api-%e6%a8%a1%e6%93%ac%e6%83%85%e5%a2%83" aria-label="這個段落的連結"></a></h3>
<p>另外在真實的世界中，呼叫後端 API 並不會永遠成功，以登入來說，可能會有密碼錯誤、帳號不存在、系統出問題等狀況。</p>
<p>所以我這邊用 <strong>Mock Service Worker</strong> 來模擬後端，讓我們可以測試不同的情境，比如：</p>
<ul>
<li>密碼錯誤時，有錯誤提示</li>
<li>帳號不存在時，有錯誤提示</li>
<li>並確認成功後，有沒有拿到 Token</li>
</ul>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-14.webp" width="1400" height="815" alt="使用 Mock API 模擬情境（圖 14）" loading="lazy" decoding="async"></p>
<blockquote class="pullquote"><p>Mock API 給我們帶來的幫助，就是除了測試成功情境外，還能模擬錯誤情境。</p>
</blockquote>
<p>而且實際與後端串接時，如果 API 回傳有問題，也可以即時釐清問題。</p>
<h3 id="-角色權限與憑證">角色權限與憑證<a class="h-anchor" href="#-%e8%a7%92%e8%89%b2%e6%ac%8a%e9%99%90%e8%88%87%e6%86%91%e8%ad%89" aria-label="這個段落的連結"></a></h3>
<p>登入後，我們把分頁切換到「應用程式 → 本機儲存空間 → localhost 的網址」</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-15.webp" width="1400" height="815" alt="角色權限與憑證（圖 15）" loading="lazy" decoding="async"></p>
<p>這邊可以看到有一個「msw_user_role」的金鑰，系統的前端就是用它來判斷<strong>使用者的權限，決定前端頁面要如何渲染。</strong></p>
<p>像我們身份為 admin 的狀態下，頁面才會「管理後台」，至於「auth_token 」這個欄位，則是你取得的登入憑證，以測試來說，可以有一個基礎的判斷：</p>
<ul>
<li><strong>有 Token</strong>，才會用它來呼叫後續的 API</li>
<li><strong>沒有 Token</strong>，直接導向到登入頁</li>
</ul>
<p>Token 是決定呼叫後端 API 時，能取得哪些資源的憑證，比如這邊我使用「清除 Token」後，可以看到除了 auth_token 移除外，頁面也重新導向到登入頁。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-16.webp" width="1400" height="815" alt="角色權限與憑證（圖 16）" loading="lazy" decoding="async"></p>
<p><strong>因為對系統來說，你已經沒有憑證了。</strong></p>
<p>接著我把權限切換到一般使用者，登入後你會發現<strong>「msw_user_role」的角色切換成了 user，上方的「管理後台」也不見了</strong>：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-17.webp" width="1400" height="815" alt="角色權限與憑證（圖 17）" loading="lazy" decoding="async"></p>
<p>這邊示範的就是權限測試的重要性：</p>
<ul>
<li>要確保一般用戶不會看到管理者功能</li>
<li>並且不能讓一般用戶誤闖管理者後台</li>
</ul>
<blockquote class="pullquote"><p><strong>權限的設計如果不完善，那對系統來說就是重大事故！</strong></p>
</blockquote>
<p>在這總結一下，我剛剛跟大家示範的流程涵蓋： 「<strong>登入功能、API 回傳、角色權限、Token 驗證、介面差異</strong>」 這些情境都應該設計對應的測試案例。</p>
<p>因為在 Vibe Coding 的世界， 我們很常改流程、改權限、改 API，<strong>如果少了測試案例，那你根本無法判斷這次修改 會不會動到其他系統功能。</strong></p>
<p>千萬不要嫌寫測試很浪費時間，別忘記現在已經有 AI 可以幫忙了。</p>
<p><strong>根據我過去十幾年的開發經驗，其實寫測試是在幫助你加速開發；因為他讓你可以更放心新增與修改功能，在遇到問題時也可以快速定位。</strong></p>
<p>另外測試案例不太可能一次到位，他會隨著規格變更、實際操作而有所調整；但有了它，需求規格的內容才能真正被驗證。</p>
<p><strong>同時提醒一下大家，我們在前端做的格式驗證（ex:帳號格式、密碼格式），後端也不能少。</strong></p>
<p>因為知道 API 路徑的人，也能直接繞過前端打 API，這些都是設計上要注意的細節。</p>
<hr>
<h2 id="二用-ai-撰寫測試程式">二、用 AI 撰寫測試程式<a class="h-anchor" href="#%e4%ba%8c%e7%94%a8-ai-%e6%92%b0%e5%af%ab%e6%b8%ac%e8%a9%a6%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h2>
<p>這篇文章我使用 Google 最新推出的 Antigravity 來給大家做示範。</p>
<p>目前登入 Google 帳號就可以使用，但如果付費升級到 Pro 會有更高的使用額度。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-18.webp" width="1400" height="815" alt="二、用 AI 撰寫測試程式（圖 18）" loading="lazy" decoding="async"></p>
<blockquote>
<p>但如果你熟悉的是 Cursor 或者是其他 AI 編輯器，也可以跟著操作。</p>
</blockquote>
<p>這邊我們點擊右上角的「…」，選擇「Customizations」來做客製化。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-19.webp" width="1400" height="815" alt="二、用 AI 撰寫測試程式（圖 19）" loading="lazy" decoding="async"></p>
<p>Rules 的部分，我是參考 Cursor 內部員工的 Rules 範例，其實提示詞都是通用的：</p>
<pre tabindex="0"><code>DO NOT GIVE ME HIGH LEVEL SHIT, IF I ASK FOR FIX OR EXPLANATION, I WANT ACTUAL CODE OR EXPLANATION!!! I DON&#39;T WANT &#34;Here&#39;s how you can blablabla&#34;

- Be casual unless otherwise specified
- Be terse
- Suggest solutions that I didn&#39;t think about—anticipate my needs
- Treat me as an expert
- Be accurate and thorough
- Give the answer immediately. Provide detailed explanations and restate my query in your own words if necessary after giving the answer
- Value good arguments over authorities, the source is irrelevant
- Consider new technologies and contrarian ideas, not just the conventional wisdom
- You may use high levels of speculation or prediction, just flag it for me
- No moral lectures
- Discuss safety only when it&#39;s crucial and non-obvious
- If your content policy is an issue, provide the closest acceptable response and explain the content policy issue afterward
- Cite sources whenever possible at the end, not inline
- No need to mention your knowledge cutoff
- No need to disclose you&#39;re an AI
- Please respect my prettier preferences when you provide code.
- Split into multiple responses if one response isn&#39;t enough to answer the question.

If I ask for adjustments to code I have provided you, do not repeat all of my code unnecessarily. Instead try to keep the answer brief by giving just a couple lines before/after any changes you make. Multiple code blocks are ok.
</code></pre><p>而這次撰寫測試的部分，我們要使用 Workflow，讓 AI 根據我們的流程來執行。</p>
<p>如果你是下載我提供的練習專案，你會看到<strong>兩個在 Workspace 底下的 Workflow，這代表 Workflow 只會在這個專案中執行：</strong></p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-20.webp" width="1400" height="815" alt="二、用 AI 撰寫測試程式（圖 20）" loading="lazy" decoding="async"></p>
<p>這邊我點進去跟大家說明，主要的 Workflow 是 <code>gen-test-cases</code>，Description 是這個工作流的描述，我就寫 <code>生成測試案例工作流</code> ，而 Content 則是說明這個工作流要做的事。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-21.webp" width="1400" height="815" alt="二、用 AI 撰寫測試程式（圖 21）" loading="lazy" decoding="async"></p>
<h3 id="-角色扮演">角色扮演<a class="h-anchor" href="#-%e8%a7%92%e8%89%b2%e6%89%ae%e6%bc%94" aria-label="這個段落的連結"></a></h3>
<p><code>扮演一位經驗豐富的軟體測試專家，根據以下步驟進行協作</code></p>
<p>首先我讓他扮演一位經驗豐富的軟體測試專家。</p>
<h3 id="-step-1-建立存放文件的資料夾">STEP 1: 建立存放文件的資料夾<a class="h-anchor" href="#-step-1-%e5%bb%ba%e7%ab%8b%e5%ad%98%e6%94%be%e6%96%87%e4%bb%b6%e7%9a%84%e8%b3%87%e6%96%99%e5%a4%be" aria-label="這個段落的連結"></a></h3>
<p><code>STEP 1: 檢查專案底下是否建立 `doc/test` 資料夾，若無則建立</code></p>
<p>一開始先請他檢查專案底下是否有建立 test 資料夾，我們會在這裡面保存 Markdown 格式的測試清單，方便日後閱讀檢視。</p>
<h3 id="-step-2-撰寫測試案例清單">STEP 2: 撰寫測試案例清單<a class="h-anchor" href="#-step-2-%e6%92%b0%e5%af%ab%e6%b8%ac%e8%a9%a6%e6%a1%88%e4%be%8b%e6%b8%85%e5%96%ae" aria-label="這個段落的連結"></a></h3>
<p><code>STEP 2: 根據選擇範圍撰寫測試案例清單，撰寫格式請參考 `.agent/workflows/test/template.md`，並將發想的結果用 Markdown 格式寫入 `doc/test` 底下，完成後進行 Reivew 不要直接生成測試程式，確認符合預期後再下一步</code></p>
<p>然後請他撰寫測試案例清單，這邊用了一個技巧叫做 reference，直接給他一個參考模板，避免 AI 每次生成的格式都不一樣。</p>
<p>我們開一下這個模板，這就是寫給 AI 參考的撰寫範例，要有<strong>測試的類型、說明、範例輸入、期待輸出</strong>等資訊。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-22.webp" width="1400" height="815" alt="STEP 2: 撰寫測試案例清單（圖 22）" loading="lazy" decoding="async"></p>
<p>同時用了一個小技巧，就是以「狀態」的概念來管理，有測試通過才會標記；並給他幾個測試類型的範例參考。</p>
<p>接著我們再回到一開始的工作流，這邊我有要求他要把這些發想的測試清單，寫到 test 資料夾下， 然後再完成這個任務後，我要求他將這個 Markdown 文件存到專案資料夾中，並讓我們 Review，這樣我們才能確認 AI 給的測試情境是否有遺漏的狀況。</p>
<h3 id="-step-3-ai-撰寫測試程式">STEP 3: AI 撰寫測試程式<a class="h-anchor" href="#-step-3-ai-%e6%92%b0%e5%af%ab%e6%b8%ac%e8%a9%a6%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h3>
<pre tabindex="0"><code>STEP 3: 參考上一步完成的 `doc/test` 撰寫測試程式，不同頁面需建立獨立檔案

**重要規則：**
- 第二層 `describe()` 必須為「測試類型」，下面每一個 `it()` 描述必須為對應的測試案例的「測試說明」
- `it()` 內的文字描述請「直接使用 Markdown 的原文，不需翻譯、不需重新命名」
</code></pre><p>在我們確認要測試的範圍符合需求後，就可以讓 AI 開始撰寫測試程式了。</p>
<p>這邊有一個特別的「重要規則」，就是要求他程式內的描述，要跟剛剛文件撰寫的測試說明一致，這樣才方便我們比對。</p>
<h3 id="-step-4-驗證測試程式">STEP 4: 驗證測試程式<a class="h-anchor" href="#-step-4-%e9%a9%97%e8%ad%89%e6%b8%ac%e8%a9%a6%e7%a8%8b%e5%bc%8f" aria-label="這個段落的連結"></a></h3>
<p><code>STEP 4: 驗證測試程式，若結果符合預期，請去 doc/test 底下，將 Markdown 打勾</code></p>
<p>在 AI 生成測試程式完成後，我們要求他自主驗證程式正確性，若通過要去文件內打勾。</p>
<h3 id="-step-5-限制測試次數">STEP 5: 限制測試次數<a class="h-anchor" href="#-step-5-%e9%99%90%e5%88%b6%e6%b8%ac%e8%a9%a6%e6%ac%a1%e6%95%b8" aria-label="這個段落的連結"></a></h3>
<p><code>STEP 5: 若測試不符預期，重複 STEP 3–4，最多 5 次，仍失敗就討論原因</code></p>
<p>因為撰寫的程式可能會有問題，所以我讓他可以重複 STEP 3 跟 4 的動作，但最多嘗試 5 次；<strong>這個限制是為了避免 AI 在錯誤的方向狂奔</strong>，我們適時地介入，去確認問題發生的原因。</p>
<blockquote>
<p>這套工作流主要是用來撰寫前端專案的單元測試為主，你可以根據自己的需求自行調整。</p>
</blockquote>
<p>再設計好 Workflow 並儲存後，點擊「Back to Agent」回到對話窗，此時我們按下「/」就會看到剛剛設計好的 Workflow。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-23.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 23）" loading="lazy" decoding="async"></p>
<p>這邊我用 <code>gen-test-cases</code> 示範給大家看，我們把視角移到左側的專案，我們專案的三個頁面，都被放到 pages 的資料夾底下。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-24.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 24）" loading="lazy" decoding="async"></p>
<p><strong>我建議大家請 AI 撰寫測試程式時，不要讓他一口氣生成，而是先放一個檔案。</strong></p>
<p>確認他執行的結果與我們的期待相符，否則一口氣生成，如果結果不是你要的，那不但浪費時間還浪費 Token。</p>
<p>這邊把檔案拖曳進去後，只要按下 Enter，AI 就會開始閱讀程式碼根據工作流開始運作了。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-25.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 25）" loading="lazy" decoding="async"></p>
<p>首先我們會看到，他有要求我們建立對應的「 doc/test」資料夾來存放文件，點擊 Accept 接受就好。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-26.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 26）" loading="lazy" decoding="async"></p>
<p>接著稍等一段時間，他就參考我們提供的 Markdown 文件來撰寫測試案例文件，可以看到初始的狀態都是空的。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-27.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 27）" loading="lazy" decoding="async"></p>
<p>並且有根據測試類型進行分類，這可以幫我們快速理解測試的意圖，然後可以從輸入輸出的描述，判斷他想測試的情境跟你的期待是否相符。</p>
<p>如果覺得有缺少，就可以直接跟他討論；覺得沒問題，就請他<code>繼續</code> 。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-28.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 28）" loading="lazy" decoding="async"></p>
<p>但我也在這邊跟大家說，<strong>AI 給予的測試案例，可能會有錯誤或者多餘的狀況，建議大家看一下</strong>。</p>
<p>並提醒一下大家，<strong>如果你看不懂 AI 執行的指令，建議把指令複製起來詢問一下比較保險。全部無腦點擊 Accept，有可能會出大事</strong>。</p>
<p>因為有時 AI 會執行一些危險的操作，之前就曾經有網友遇過 <strong>AI 直接把整個 D 槽硬碟刪除的悲劇</strong>：</p>
<figure class="figure"><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-29.webp" width="1400" height="815" alt="【Antigravity Workflow 實戰】建立自動化測試，解決 Vibe Coding 痛點！（圖 2）" loading="lazy" decoding="async">
  <figcaption><a href="https://www.reddit.com/r/google_antigravity/comments/1p82or6/google_antigravity_just_deleted_the_contents_of/">https://www.reddit.com/r/google_antigravity/comments/1p82or6/google_antigravity_just_deleted_the_contents_of/</a></figcaption>
</figure>

<p>稍等一段時間後，AI 就幫我們把測試程式完成了，點擊 Accept 讓他執行測試前，我們先看一下他有沒有按照工作流的指示撰寫。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-30.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 30）" loading="lazy" decoding="async"></p>
<p>在「describe」這邊有幫我們做基礎的分組，另外「it」的描述也與文件吻合；這次的話很幸運，一次就成功了；如果有問題他就會多嘗試幾次。</p>
<blockquote>
<p>這邊也提醒一下大家，儘管我們的 Workflow 說最多嘗試 5 次，但隨著執行次數增加；AI 可能會忽略這個規則，這是需要注意的。</p>
</blockquote>
<p>AI 完成一個頁面的測試案例，並驗證通過後，就會回頭更新測試清單，這邊我們可以看到上面都標注通過了。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-31.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 31）" loading="lazy" decoding="async"></p>
<p>如果你做到了這一步，請給自己一點掌聲，恭喜你用 AI 撰寫了測試程式，並跑完了一個頁面的功能驗證！</p>
<p>一般來說，我會建議每個頁面都有獨立的測試程式，這樣日後修改遇到問題時，可以快速定位。</p>
<p>在第一個頁面的測試程式撰寫並測試完成後，<strong>其實這個工作流你要放兩個檔案進去也是可以的。</strong></p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-32.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 32）" loading="lazy" decoding="async"></p>
<p>稍等一段時間後，對應的測試程式就完成了，並且對應的驗證邏輯也通過了；你可以看到光是 3 個功能不多的頁面，就能涵蓋這麼多的測試情境。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-33.webp" width="1400" height="815" alt="STEP 5: 限制測試次數（圖 33）" loading="lazy" decoding="async"></p>
<p><strong>如果用人工測試，即便是小專案；你都要花個好幾分鐘才能手動把流程跑過一遍，甚至可能還會有遺漏的項目。</strong></p>
<p><strong>但加入自動化測試後，你放給他跑就好了；隨著專案擴增，你更會體會到他給你帶來的幫助。</strong></p>
<p>因為以市面上的大型專案來說，功能動輒成千上萬，彼此間又有各種複雜的關聯性。</p>
<p>沒有自動化測試，專案就像是個危樓，任何不起眼的改動，都可能導致問題在你想不到的地方炸裂。</p>
<blockquote>
<p>今天分享的這個工作流只是一個雛形，如果大家有更好的方案，也歡迎交流分享。</p>
</blockquote>
<hr>
<h2 id="三用-github-action-執行自動化測試">三、用 GitHub Action 執行自動化測試<a class="h-anchor" href="#%e4%b8%89%e7%94%a8-github-action-%e5%9f%b7%e8%a1%8c%e8%87%aa%e5%8b%95%e5%8c%96%e6%b8%ac%e8%a9%a6" aria-label="這個段落的連結"></a></h2>
<p>完成專案的測試案例後，下一步就要把測試的步驟加入 GitHub Action。</p>
<p>這樣我們每次更新程式時，都會自動執行測試，確保基礎功能的基礎流程都是正常的。</p>
<p>不過在這之前，我先跟大家簡介一下 Git Flow，Git 是用來版本控制的工具，Git Flow 則是分支合作的策略。</p>
<p>目前我們還在 main 的這個主分支上：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-34.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 34）" loading="lazy" decoding="async"></p>
<p><strong>你可以想一下，如果把邏輯錯誤，或功能不完善的版本更新上去，那產品不就壞掉了嗎？</strong></p>
<p><strong>所以在開發上，我會建議至少要有一個 develop 的開發分支，日常更新推送到上面就好，等功能完善後再合併到 main 這個主分支。</strong></p>
<p>雖然 Git Flow 還有新增功能用的 feature 分支、發佈上線版本的 release 分支、以及處理緊急問題的 hotfix 分支。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-35.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 35）" loading="lazy" decoding="async"></p>
<p>但如果你是第一次接觸，可以先從 main 分支出 develop，來了解為什麼需要用不同分支開發。</p>
<p>這邊先打開終端機，在下面輸入 <code>git checkout -b develop</code> ，就可以從 main 切出一個 develop 的分支。</p>
<p>然後到與 AI 的對話窗中輸入以下內容：</p>
<pre tabindex="0"><code>我希望在 GitHub Action 加入自動化測試的流程
每一個分支將更新推送到 GitHub 都會觸發一次自動化測試
測試完畢後，要生成覆蓋率報告讓我下載
</code></pre><p>等 AI 將 GitHub Action 內容更新完畢後，我們就可以將變更推送到 GitHub 上面</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-36.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 36）" loading="lazy" decoding="async"></p>
<p>Commit 的部分可以由 AI 自動生成，或是自己手動輸入，這邊我選擇貼上準備好的訊息：</p>
<pre tabindex="0"><code>1. 撰寫 pages 頁面下的測試案例
2. 在 GitHub Action 增加自動測試的流程
</code></pre><p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-37.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 37）" loading="lazy" decoding="async"></p>
<p>送出去後，我們把視角切換到 GitHub，點擊 Action 的分頁，這邊你可以看到 GitHub Action 執行的狀況，成功成的話會是綠色勾勾，失敗則是紅色叉叉。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-38.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 38）" loading="lazy" decoding="async"></p>
<p>這邊我們很幸運的看到是綠色勾勾，點進去後，你可以看到測試的細節：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-39.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 39）" loading="lazy" decoding="async"></p>
<p>回到 Summary，在「Artifacts」的部分會有測試報告可以下載：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-40.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 40）" loading="lazy" decoding="async"></p>
<p>這樣每次有新功能完成後，如果主管想要看測試報告，就有東西可以給他們參考嚕。</p>
<p>下載後會得到一個 zip 檔案，解壓縮後點擊「index.html」檔案就可以看到專案的測試覆蓋率了：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-41.webp" width="1400" height="815" alt="三、用 GitHub Action 執行自動化測試（圖 41）" loading="lazy" decoding="async"></p>
<p>透過這份報告，你就可以了解專案中哪些功能測試比較多，比如像是剛剛撰寫測試程式的 pages 就接近覆蓋率 100%。</p>
<p>而其他沒有被測試到的頁面，我們就可以再去補一些測試情境。</p>
<blockquote class="pullquote"><p>但提醒一下大家，在測試覆蓋率這塊，我們沒必要追求 100%；而是重要的邏輯都要測試到，這才是最重要的。</p>
</blockquote>
<hr>
<h2 id="四設計-rules-保護專案測試通過才能合併">四、設計 Rules 保護專案，測試通過才能合併<a class="h-anchor" href="#%e5%9b%9b%e8%a8%ad%e8%a8%88-rules-%e4%bf%9d%e8%ad%b7%e5%b0%88%e6%a1%88%e6%b8%ac%e8%a9%a6%e9%80%9a%e9%81%8e%e6%89%8d%e8%83%bd%e5%90%88%e4%bd%b5" aria-label="這個段落的連結"></a></h2>
<p>我們剛剛更新的分支是 develop，要透過 pull request 這個操作，才能將更新合併到 main 這個主分支。</p>
<p>但如果你希望只有「通過測試」的分支才能合併到主分支，你需要多做一設定。</p>
<p>我們回到 GitHub 頁面，選擇 Settings，然後選擇 Rules 下面的「Ruleset」，這邊我們要根據分支來做設計，所以選擇「New branch ruleset」。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-42.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 42）" loading="lazy" decoding="async"></p>
<p>name 的部分你可以輸入「Protect main branch」，然後「Enforcement status」這個記得要切換到 Active 才會生效。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-43.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 43）" loading="lazy" decoding="async"></p>
<p>Target branch 選擇「Include default branch」</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-44.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 44）" loading="lazy" decoding="async"></p>
<p>然後把「Require a pull request before merging」這個必須「用 PR 才能合併的選項」打勾。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-45.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 45）" loading="lazy" decoding="async"></p>
<p>並將「Require status checks to pass」打勾，以及下面的「Require branches to be up to date before merging」也打勾，這是在設定「測試必須通過才能合併」。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-46.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 46）" loading="lazy" decoding="async"></p>
<p>接著點擊「Add Checks」，搜尋 <code>test</code>，然後把他打勾，這就是要檢查的項目。</p>
<blockquote>
<p>如果「test」搜尋不到結果，可以到 Github 這個 Repository 的 Actions 下，確認命名。</p>
</blockquote>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-47.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 47）" loading="lazy" decoding="async"></p>
<p>完成以上設定後，我們滑到最下面，點擊「Create」保存設定。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-48.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 48）" loading="lazy" decoding="async"></p>
<p>接著回到 Antigravity，故意做一個測試失敗的，看看「pull request」是否會無法合併。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-49.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 49）" loading="lazy" decoding="async"></p>
<p>我們到專案挑一個測試案例，故意把裡面的結果弄壞；此時如果在終端機執行 <code>npm run test</code>，就會看到有一個錯誤。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-50.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 50）" loading="lazy" decoding="async"></p>
<p>然後我們將這個錯誤更新上去，commit 就填寫 <code>test PR failure condition</code> ，推上去後回到 GitHub，此時點擊「pull request」。</p>
<p>如果你是 fork 我的專案，這邊要特別注意，base 你要改成自己的帳號：</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-51.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 51）" loading="lazy" decoding="async"></p>
<p>然後 compare 要選擇 develop，因為我們的目標是把 develop 的內容更新到 main。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-52.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 52）" loading="lazy" decoding="async"></p>
<p>在建立 pull request 的時候，建議填寫裡面的 description，這是為了幫助其他人，以及未來的自己了解我們做了什麼更新。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-53.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 53）" loading="lazy" decoding="async"></p>
<p>你會發現因為測試有錯誤，所以合併的按鈕是灰色，符合我們的預期。</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-54.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 54）" loading="lazy" decoding="async"></p>
<p>接著回到專案，把這個問題修正回來，commit 就填寫 <code>test PR success condition</code> 。</p>
<p>更新後再回到 GitHub，稍等一段時間，你會發現合併的按鈕變成綠色，可以順利合併了！</p>
<p><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-55.webp" width="1400" height="815" alt="四、設計 Rules 保護專案，測試通過才能合併（圖 55）" loading="lazy" decoding="async"></p>
<p>有了 Rule 保護專案的主分支後，我們至少能保證合併進來的功能都是經過基礎驗證的。</p>
<hr>
<h2 id="結語用自動化測試守住系統的底線">結語：用自動化測試，守住系統的底線<a class="h-anchor" href="#%e7%b5%90%e8%aa%9e%e7%94%a8%e8%87%aa%e5%8b%95%e5%8c%96%e6%b8%ac%e8%a9%a6%e5%ae%88%e4%bd%8f%e7%b3%bb%e7%b5%b1%e7%9a%84%e5%ba%95%e7%b7%9a" aria-label="這個段落的連結"></a></h2>
<p>測試的領域博大精深，今天的分享只是帶大家入門而已。</p>
<p>同時提醒大家，加入測試，不代表專案無懈可擊，但至少能守住專案的底線；也相信把影片看到這裡的你，充分了解到 Vibe Coding 是需要學習的。</p>
<p><strong>也許用一行指令就能生成系統，但能穩定運行、並且有擴充性的系統才是有價值的。</strong></p>
<p>突然想到前陣子跟一些老闆聊天時，有聊到<strong>目前企業導入 AI 遇到的瓶頸</strong>。</p>
<p>其中一個是邀請講師來企業內訓的時候，同仁都用得很開心；但隔天上班，想實際應用到工作時卻感覺非常卡。</p>
<p>當時我就跟他說：「<strong>因為講師在設計教材時，給予的是最佳體驗路徑；但換個情境，一定會遇到許多問題。</strong>」</p>
<p>就像今天這支影片，如果你直接使用我提供的專案操作，通常可以順利完成每個步驟。</p>
<p><strong>但這只代表你對測試有了初步的概念，並不代表你已經掌握了自動化測試。</strong></p>
<p>把我的指令搬到其他專案執行時，可能會遇到不一樣的問題；這個時候不要氣餒，因為你遇到的問題才是屬於自己的護城河。</p>
<p><strong>學習 AI 並不是搜集一大堆指令複製貼上就好；更重要的是了解應用情境後，透過實踐調整為適合自己的工作流。</strong></p>
<blockquote class="pullquote"><p>讓 AI 成為你的助手，而不是取代自己的思考與判斷。</p>
</blockquote>
<p>最後總結一下，為什麼測試對專案來說如此重要：</p>
<ul>
<li>因為有測試，你才有辦法驗證規格書上的功能你都有做到。</li>
<li>因為有測試，當需求變更時你才敢大膽修改程式，不怕改壞原有功能。</li>
<li>因為有測試，當遇到錯誤時，你可以快速定位問題，而不是一個個慢慢嘗試。</li>
</ul>
<p>再了解測試的好處後，跟大家介紹一種開發模式，叫做 TDD(Test-Driven Development)，也就是測試驅動開發。</p>
<p>相比於大家熟悉的先完成功能再寫測試，他則是先撰寫測試再去開發，之後有機會再跟大家深入分享。</p>
<p><strong>如果過去沒有寫過測試程式，我認為先養成撰寫測試的習慣才是最重要的。</strong></p>
<blockquote class="pullquote"><p>建立自動化測試，你才知道 AI 是否改動到邏輯；掌握關鍵知識，你才能走得更遠。</p>
</blockquote>
<p>另外今天這支影片，我都是用 Mock API 來代替後端；所以在接下來的 Vibe Coding 系列影片中，我打算在這支影片的基礎上做延伸。</p>
<p><strong>跟大家分享後端與資料庫的設計，帶大家把這些模擬的 API 都變成真實可用的。</strong></p>
<p>如果這篇文章對你有幫助，可以順手按個讚，把文章分享給正在 Vibe Coding 的朋友們。</p>
<figure class="figure"><img src="https://deanlin.net/blog/202601/antigravity-workflow-testing/images/antigravity-workflow-testing-56.webp" width="1400" height="914" alt="林鼎淵著作《工程師下班有約：企業內訓講師帶你認清職涯真相！》" loading="lazy" decoding="async">
  <figcaption><a href="https://www.tenlong.com.tw/products/9786264142083">https://www.tenlong.com.tw/products/9786264142083</a></figcaption>
</figure>

]]></content:encoded>
    </item>
    <item>
      <title>[n8n 教學]掌握重要節點 (Node) 使用情境：Set、Loop Over Items、If、Code、HTTP Request、Wait</title>
      <link>https://deanlin.net/blog/202501/n8n-core-nodes/</link>
      <pubDate>Tue, 14 Jan 2025 21:55:10 +0800</pubDate>
      <guid>https://deanlin.net/blog/202501/n8n-core-nodes/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>AI 實戰</category>
      <description>n8n 是一款功能強大的自動化工具，但如果你對 n8n 的基礎知識與節點功能不夠理解，可能會遇到各種常見的坑（ex: Loop Over Items 無法重置迴圈、讀不到過去節點的結果..）。為了讓大家少踩坑，這篇文章將詳細介紹 n8n 節點的基礎操作</description>
      <media:content url="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-og_hu_84c6ba83840e8098.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>n8n 是一款功能強大的自動化工具，透過視覺化的操作介面，只需拖拉節點、設定參數，就能快速建立屬於你的工作流。</p>
<p>但如果你對 n8n 的基礎知識與節點功能不夠理解，可能會遇到各種常見的坑（ex: Loop Over Items 無法重置迴圈、讀不到過去節點的結果..）。</p>
<p>為了讓大家少踩坑，這篇文章將詳細介紹 n8n 節點的基礎操作，幫助你快速上手這款工具。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-01.webp" width="1400" height="789" alt="\[n8n 教學\]掌握重要節點 (Node) 使用情境：Set、Loop Over Items、If、Code、HTTP Request、Wait（圖 1）" fetchpriority="high" decoding="async"></p>
<pre tabindex="0"><code>文章大綱

▋ Set Node: 設定 Workflow 共用的變數
▋ Loop Over Items Node: 執行重複任務
▋ HTTP Request: 呼叫 API
▋ Wait Node: 等待指定時間
▋ Code Node: 添加程式邏輯（ex: 格式化資料）
▋ 取得指定節點（Specific Node）的 Result
▋ 用 If Node 來 Reset Loop Over Items 資料
▋ 用 $getWorkflowStaticData 儲存資訊
</code></pre><blockquote>
<p>n8n 有<a href="https://n8n.io/">雲端</a>與本地（local）的開源版本，基於資訊安全與使用限制，筆者強烈建立在本地（lo<em>cal）建立環境，<a href="https://deanlin.net/blog/202501/n8n-docker-local-setup/">詳細的操作步驟可以參考這篇文章</a>。</em></p>
</blockquote>
<hr>
<h2 id="-set-node-設定-workflow-共用的變數">Set Node: 設定 Workflow 共用的變數<a class="h-anchor" href="#-set-node-%e8%a8%ad%e5%ae%9a-workflow-%e5%85%b1%e7%94%a8%e7%9a%84%e8%ae%8a%e6%95%b8" aria-label="這個段落的連結"></a></h2>
<p>Set Node 的主要功能是定義工作流中共用的變數，這樣當參數要調整時，只需要調整一個地方，不需要去多個地方調整，避免設定上的遺漏。</p>
<p>比如你想針對特定的 Google Sheet 做讀取、新增、修改的操作，就可以將 Google Sheet 的 URL 設定到 Set Node 的欄位中。</p>
<p>這樣後續要使用 Google Sheet URL 時，只要讀取這個 Set Node 的資訊即可，不需要在每個 Google Sheet 的節點重新設定。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-02.webp" width="1398" height="954" alt="Set Node: 設定 Workflow 共用的變數（圖 2）" loading="lazy" decoding="async"></p>
<blockquote>
<p><strong>小提醒</strong>: 在這邊設定的變數是「固定值」，如果想要設定可以更改的變數，請參考後面的「$getWorkflowStaticData」方案。</p>
</blockquote>
<hr>
<h2 id="-loop-over-items-node-執行重複任務">Loop Over Items Node: 執行重複任務<a class="h-anchor" href="#-loop-over-items-node-%e5%9f%b7%e8%a1%8c%e9%87%8d%e8%a4%87%e4%bb%bb%e5%8b%99" aria-label="這個段落的連結"></a></h2>
<p>Loop Over Items Node 可以針對獲得的每一筆資料（item）執行相同的操作。</p>
<p>像筆者在後續的範例中，因為要重複 Call Google 的 API 來取得關聯關鍵字，就會用到這個 Node:</p>
<ul>
<li><strong>loop</strong>: 放上你要執行的重複任務。</li>
<li><strong>done</strong>: 重複任務執行完畢後，會得到的累計結果。</li>
</ul>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-03.webp" width="772" height="405" alt="Loop Over Items Node: 執行重複任務（圖 3）" loading="lazy" decoding="async"></p>
<blockquote>
<p><strong>小提醒</strong>: Loop 迴圈是不會自動重置的，需要搭配 If Node 進行條件判斷，才能避免資料重複處理。</p>
</blockquote>
<hr>
<h2 id="-http-request-node-呼叫-api">HTTP Request Node: 呼叫 API<a class="h-anchor" href="#-http-request-node-%e5%91%bc%e5%8f%ab-api" aria-label="這個段落的連結"></a></h2>
<p>許多自動化流程都是需要呼叫 API 來取得權限（ex: token）的:</p>
<ul>
<li><strong>Method</strong>: 有 GET/POST/DELETE 等方法可以選擇</li>
<li><strong>URL</strong>: 貼上 API URL</li>
<li><strong>Authentication</strong>: 執行 API 需要的憑證。</li>
<li><strong>傳送參數</strong>: 可以選擇用 Query Parameters 或 Send Body 的方式來傳送參數。</li>
</ul>
<p>下面就用 Google 建議搜尋的 Get Method API 來向大家做個示範:</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-04.webp" width="1400" height="953" alt="HTTP Request Node: 呼叫 API（圖 4）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-code-node-添加程式邏輯ex-格式化資料">Code Node: 添加程式邏輯（ex: 格式化資料）<a class="h-anchor" href="#-code-node-%e6%b7%bb%e5%8a%a0%e7%a8%8b%e5%bc%8f%e9%82%8f%e8%bc%afex-%e6%a0%bc%e5%bc%8f%e5%8c%96%e8%b3%87%e6%96%99" aria-label="這個段落的連結"></a></h2>
<p>當 n8n 預設的功能無法滿足需求時，就需要 Code Node 來處理了。</p>
<p>比如上面 HTTP Request Node 的結果是一個比較複雜的格式，我們可以透過程式將它轉換為自己期待的格式，並且可以透過 console.log 來協助 debug。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-05.webp" width="1400" height="699" alt="Code Node: 添加程式邏輯（ex: 格式化資料）（圖 5）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-wait-node-等待指定時間">Wait Node: 等待指定時間<a class="h-anchor" href="#-wait-node-%e7%ad%89%e5%be%85%e6%8c%87%e5%ae%9a%e6%99%82%e9%96%93" aria-label="這個段落的連結"></a></h2>
<p>Wait Node 可以在工作流間加入一段延遲。</p>
<p>比如我們在批量 Call API 時，為了避免伺服器過載，或是減少自己被黑名單的機率，可以設定 5 秒的間隔。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-06.webp" width="1400" height="951" alt="Wait Node: 等待指定時間（圖 6）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-取得指定節點specific-node的-result">取得指定節點（Specific Node）的 Result<a class="h-anchor" href="#-%e5%8f%96%e5%be%97%e6%8c%87%e5%ae%9a%e7%af%80%e9%bb%9especific-node%e7%9a%84-result" aria-label="這個段落的連結"></a></h2>
<p>n8n 默認取得上一個節點（Previous Node）的 Result，但如果你想要取得更早之前 Node 的 Result，就要用到下面的方式：</p>
<ul>
<li><strong>Node</strong>: 使用 Expression <code>{{ $(&rsquo;node_name&rsquo;).item.json.feild_name }}</code></li>
</ul>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-07.webp" width="1398" height="954" alt="取得指定節點（Specific Node）的 Result（圖 7）" loading="lazy" decoding="async"></p>
<ul>
<li><strong>Code</strong>: <code>$node[&ldquo;node_name&rdquo;].json[&ldquo;feild_name&rdquo;];</code></li>
</ul>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-08.webp" width="1398" height="954" alt="取得指定節點（Specific Node）的 Result（圖 8）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-用-if-node-來-reset-loop-over-items-資料">用 If Node 來 Reset Loop Over Items 資料<a class="h-anchor" href="#-%e7%94%a8-if-node-%e4%be%86-reset-loop-over-items-%e8%b3%87%e6%96%99" aria-label="這個段落的連結"></a></h2>
<p>如果你有設計迴圈（Loop），可透過 If Node 設定條件來 Reset 資料，避免因未清空資料而導致流程錯誤。</p>
<p>如果你希望每次進入 Loop Over Items 時資料都清空，Workflow 可以參考下圖的結構來設計:</p>
<ul>
<li>將 If Node 放在上一個 Node 的後面，Loop Over Items 前面。</li>
<li>將 Loop Over Items 的 done 連結到 If Node，這樣才能協助 Reset 資料。</li>
</ul>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-09.webp" width="736" height="216" alt="用 If Node 來 Reset Loop Over Items 資料（圖 9）" loading="lazy" decoding="async"></p>
<p>If Node 我用 <code>{{ $runIndex }}</code> 跑第幾次來當作重置的判斷，如果次數小於上一步 items 的數量 <code>{{ $json.keys().length }}</code>，會走 True，進行重置。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-10.webp" width="1400" height="956" alt="用 If Node 來 Reset Loop Over Items 資料（圖 10）" loading="lazy" decoding="async"></p>
<p>為了讓重置的事件觸發，在 Loop Over Items Node，我們要在 Options 的 Reset 設定重置的邏輯為 <code>{{ $prevNode.name === &lsquo;if&rsquo; }}</code></p>
<p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-11.webp" width="1400" height="956" alt="用 If Node 來 Reset Loop Over Items 資料（圖 11）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-用-getworkflowstaticdata-儲存資訊">用 $getWorkflowStaticData 儲存資訊<a class="h-anchor" href="#-%e7%94%a8-getworkflowstaticdata-%e5%84%b2%e5%ad%98%e8%b3%87%e8%a8%8a" aria-label="這個段落的連結"></a></h2>
<p>當需要跨節點（Node）保存資料時，可透過 $getWorkflowStaticData 儲存並讀取與更新工作流的靜態數據。</p>
<p>下面用 Code Node 展示初始化變數資訊的方式:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> workflowStaticData <span style="color:#ff7b72;font-weight:bold">=</span> $getWorkflowStaticData(<span style="color:#a5d6ff">&#39;global&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">if</span> (<span style="color:#ff7b72;font-weight:bold">!</span>workflowStaticData.hasOwnProperty(<span style="color:#a5d6ff">&#39;keyword_1st&#39;</span>)) {
</span></span><span style="display:flex;"><span>  workflowStaticData.keyword_1st <span style="color:#ff7b72;font-weight:bold">=</span> []
</span></span><span style="display:flex;"><span>}
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span> [{}];
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-12.webp" width="1400" height="956" alt="用 $getWorkflowStaticData 儲存資訊（圖 12）" loading="lazy" decoding="async"></p>
<p>如果你想要讀取、更新資料(範例是在 Array 裏面 push 資料)，可以這麼做:</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-javascript" data-lang="javascript"><span style="display:flex;"><span><span style="color:#ff7b72">const</span> workflowStaticData <span style="color:#ff7b72;font-weight:bold">=</span> $getWorkflowStaticData(<span style="color:#a5d6ff">&#39;global&#39;</span>);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span>workflowStaticData.keyword_1st.push(<span style="color:#a5d6ff">&#39;bbb&#39;</span>);
</span></span><span style="display:flex;"><span>console.log(workflowStaticData.keyword_1st);
</span></span><span style="display:flex;"><span>
</span></span><span style="display:flex;"><span><span style="color:#ff7b72">return</span> [{}];
</span></span></code></pre></div><p><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-13.webp" width="1400" height="953" alt="用 $getWorkflowStaticData 儲存資訊（圖 13）" loading="lazy" decoding="async"></p>
<p>以上就是今天的分享，如果你剛開始使用 n8n，不妨從這些節點開始入手。有任何問題歡迎留言討論！讓我們一起學習更有效率地使用 n8n。</p>
<p><a href="https://deanlin.net/blog/202501/n8n-seo-keyword-matrix/">下一篇文章將會向大家分享呼叫 Google API 自動寫入 Google Sheet 來搜集關鍵字的 n8n 實戰範例！</a></p>
<p><strong>延伸閱讀</strong>:</p>
<ul>
<li><a href="https://deanlin.net/blog/202501/n8n-docker-local-setup/">[n8n 教學]用 Docker 在 Local 建立免費 &amp; 無限次數使用的自動化工具</a></li>
<li><a href="https://deanlin.net/blog/202501/n8n-seo-keyword-matrix/">[n8n 教學]在 Google Sheet 自動建立 SEO 關鍵字矩陣</a></li>
</ul>
<hr>
<a class="linkcard" href="https://deanlin.net/categories/ai/">
  <span class="lc-body">
    <span class="lc-title">AI 實戰</span>
    <span class="lc-desc">實測生成式 AI 工具、Vibe Coding 與自動化流程的第一手心得，把 AI 變成每天都用得上的即戰力。</span>
    <span class="lc-site"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6"/></svg>林鼎淵 Dean Lin</span>
  </span>
</a>

<p><strong>想將 AI 導入自己的職場與生活嗎？歡迎<a href="https://www.books.com.tw/products/0011004216">購買筆者出版的新書</a></strong>，幫助您<strong>在 AI 的浪潮中站穩腳步，並在未來的職場競爭中佔據優勢！</strong></p>
<figure class="figure"><img src="https://deanlin.net/blog/202501/n8n-core-nodes/images/n8n-core-nodes-14.webp" width="1400" height="319" alt="林鼎淵著作《ChatGPT 與 AI 繪圖效率大師（新增 Copilot、Gamma、Runway、Suno）》" loading="lazy" decoding="async">
  <figcaption><a href="https://www.books.com.tw/products/0011004216">https://www.books.com.tw/products/0011004216</a></figcaption>
</figure>

]]></content:encoded>
    </item>
    <item>
      <title>[n8n 教學]用 Docker 在 Local 建立免費 &amp; 無限次數使用的自動化工具</title>
      <link>https://deanlin.net/blog/202501/n8n-docker-local-setup/</link>
      <pubDate>Tue, 14 Jan 2025 21:22:38 +0800</pubDate>
      <guid>https://deanlin.net/blog/202501/n8n-docker-local-setup/</guid>
      <dc:creator>林鼎淵</dc:creator>
      <category>AI 實戰</category>
      <description>n8n 是款能把你從重複的例行性任務中，拯救出來的自動化工具。 使用者可以透過視覺化的介面，用拖拉節點、設定參數的方式來建立符合自己需求的工作流。這篇文章會分享本地部署的詳細步驟。</description>
      <media:content url="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-og_hu_48df8f790a9b80dd.jpg" medium="image" type="image/jpeg" width="1200" height="675" />
      <content:encoded><![CDATA[<p>n8n 是款能把你從重複的例行性任務中，拯救出來的自動化工具。</p>
<p>使用者可以透過視覺化的介面，用拖拉節點、設定參數的方式來建立符合自己需求的工作流。</p>
<p>他有<a href="https://n8n.io/">雲端</a>與本地（local）的版本，考量到許多工作流程需要放上私鑰（ex: Google API Key、OpenAI API Key），以及雲端版本至少要付費 20 歐元（限制工作流程執行 2500 次）；所以筆者選擇了本地部署的方案，這篇文章會分享詳細的操作步驟。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-01.webp" width="1400" height="785" alt="\[n8n 教學\]用 Docker 在 Local 建立免費 &amp; 無限次數使用的自動化工具（圖 1）" fetchpriority="high" decoding="async"></p>
<h2 id="-step-1-安裝-docker">STEP 1: 安裝 Docker<a class="h-anchor" href="#-step-1-%e5%ae%89%e8%a3%9d-docker" aria-label="這個段落的連結"></a></h2>
<p>前往 Docker 官網: <a href="https://www.docker.com/">https://www.docker.com/</a></p>
<p>根據自己的作業系統選擇對應的版本下載。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-02.webp" width="1400" height="686" alt="STEP 1: 安裝 Docker（圖 2）" loading="lazy" decoding="async"></p>
<hr>
<h2 id="-step-2-使用-docker-composeyml-安裝-n8n">STEP 2: 使用 docker-compose.yml 安裝 n8n<a class="h-anchor" href="#-step-2-%e4%bd%bf%e7%94%a8-docker-composeyml-%e5%ae%89%e8%a3%9d-n8n" aria-label="這個段落的連結"></a></h2>
<p>你可以直接 git clone <a href="https://github.com/dean9703111/n8n-google-sheet-exmaple">筆者的 GitHub 專案</a>，或者建立一個 <code>n8n</code> 的資料夾，新增 <code>docker-compose.yml</code> 檔案。</p>
<div class="highlight"><pre tabindex="0" style="color:#e6edf3;background-color:#0d1117;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;"><code class="language-yaml" data-lang="yaml"><span style="display:flex;"><span><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">n8n_storage</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#7ee787">services</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">  </span><span style="color:#7ee787">n8n</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">image</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">n8nio/n8n:latest</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">restart</span>:<span style="color:#6e7681"> </span><span style="color:#a5d6ff">always</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">ports</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span>- <span style="color:#a5d6ff">&#34;127.0.0.1:5678:5678&#34;</span><span style="color:#6e7681"> </span><span style="color:#8b949e;font-style:italic"># 根據實際需求設定</span><span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">    </span><span style="color:#7ee787">volumes</span>:<span style="color:#6e7681">
</span></span></span><span style="display:flex;"><span><span style="color:#6e7681">      </span>- <span style="color:#a5d6ff">n8n_storage:/home/node/.n8n</span><span style="color:#6e7681">
</span></span></span></code></pre></div><p>貼上內容後，在終端機（Terminal）輸入 <code>docker compose up -d</code> 即可啟動</p>
<figure class="figure"><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-03.webp" width="1400" height="193" alt="如果要關閉，輸入 `docker cpose down` 即可" loading="lazy" decoding="async">
  <figcaption>如果要關閉，輸入 `docker cpose down` 即可</figcaption>
</figure>

<hr>
<h2 id="-step-3-註冊-n8n-帳號">STEP 3: 註冊 n8n 帳號<a class="h-anchor" href="#-step-3-%e8%a8%bb%e5%86%8a-n8n-%e5%b8%b3%e8%99%9f" aria-label="這個段落的連結"></a></h2>
<p>進入網址: <a href="http://127.0.0.1:5678/">http://127.0.0.1:5678</a></p>
<p>如果看到下面的帳號設定畫面就代表你 n8n 安裝成功了！</p>
<p><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-04.webp" width="1281" height="886" alt="STEP 3: 註冊 n8n 帳號（圖 4）" loading="lazy" decoding="async"></p>
<p>帳號設定完成後，你會看到下面的初始畫面。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-05.webp" width="1281" height="961" alt="STEP 3: 註冊 n8n 帳號（圖 5）" loading="lazy" decoding="async"></p>
<p>這邊要特別提醒的是，<code>你一定要記得自己的 Email 跟 Password</code>，因為官方 Forgot Password 的功能只是擺飾用的。</p>
<p><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-06.webp" width="1281" height="412" alt="STEP 3: 註冊 n8n 帳號（圖 6）" loading="lazy" decoding="async"></p>
<p>到這一步，你就完成 n8n local 環境的初始化了！<a href="https://deanlin.net/blog/202501/n8n-core-nodes/">下一篇文章將會向讀者分享 n8n Node（節點）的使用方法。</a></p>
<p><strong>延伸閱讀</strong>:</p>
<ul>
<li><a href="https://deanlin.net/blog/202501/n8n-core-nodes/">[n8n 教學]掌握重要節點 (Node) 使用情境：Set、Loop Over Items、If、Code、HTTP Request、Wait</a></li>
<li><a href="https://deanlin.net/blog/202501/n8n-seo-keyword-matrix/">[n8n 教學]在 Google Sheet 自動建立 SEO 關鍵字矩陣</a></li>
</ul>
<hr>
<a class="linkcard" href="https://deanlin.net/categories/ai/">
  <span class="lc-body">
    <span class="lc-title">AI 實戰</span>
    <span class="lc-desc">實測生成式 AI 工具、Vibe Coding 與自動化流程的第一手心得，把 AI 變成每天都用得上的即戰力。</span>
    <span class="lc-site"><svg viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.7" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true" focusable="false"><path d="M5 12h14M13 6l6 6-6 6"/></svg>林鼎淵 Dean Lin</span>
  </span>
</a>

<p><strong>想將 AI 導入自己的職場與生活嗎？歡迎<a href="https://www.books.com.tw/products/0011004216">購買筆者出版的新書</a></strong>，幫助您<strong>在 AI 的浪潮中站穩腳步，並在未來的職場競爭中佔據優勢！</strong></p>
<figure class="figure"><img src="https://deanlin.net/blog/202501/n8n-docker-local-setup/images/n8n-docker-local-setup-07.webp" width="1400" height="319" alt="林鼎淵著作《ChatGPT 與 AI 繪圖效率大師（新增 Copilot、Gamma、Runway、Suno）》" loading="lazy" decoding="async">
  <figcaption><a href="https://www.books.com.tw/products/0011004216">https://www.books.com.tw/products/0011004216</a></figcaption>
</figure>

]]></content:encoded>
    </item>
  </channel>
</rss>
