<?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/">
  <channel>
    <title>Agent Loop on AI 輔助維運工程</title>
    <link>https://eeit.github.io/ai-devops-blog/tags/agent-loop/</link>
    <description>Recent content in Agent Loop on AI 輔助維運工程</description>
    <generator>Hugo</generator>
    <language>zh-tw</language>
    <copyright>2026 鄧景仁 (Scott Teng) · 授權：CC BY-NC 4.0</copyright>
    <lastBuildDate>Mon, 13 Apr 2026 00:00:00 +0000</lastBuildDate>
    <atom:link href="https://eeit.github.io/ai-devops-blog/tags/agent-loop/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Agent Loop:AI 助理的心跳機制</title>
      <link>https://eeit.github.io/ai-devops-blog/posts/02-agent-loop/</link>
      <pubDate>Mon, 13 Apr 2026 00:00:00 +0000</pubDate>
      <guid>https://eeit.github.io/ai-devops-blog/posts/02-agent-loop/</guid>
      <source url="https://eeit.github.io/ai-devops-blog/posts/02-agent-loop/">AI 輔助維運工程</source>
      <description>拆解 Claude Code 的核心運作機制 —— Agent Loop。搞懂一次對話背後發生了什麼,以及為什麼 token 會這樣消耗、為什麼長任務會越跑越慢。</description><content:encoded><![CDATA[<blockquote>
<p>本文為《AI 輔助維運工程:從 Claude Code 機制到企業落地》系列第 2 篇。前一篇談了為什麼 Claude Code 是不一樣的工具,這一篇開始進入它的運作核心。</p>
</blockquote>
<h2 id="為什麼先懂這個">為什麼先懂這個</h2>
<p>你可能以為你在「跟 Claude Code 對話」。</p>
<p>實際上,你是在<strong>驅動一個迴圈</strong>。</p>
<p>這個差異不是修辭。搞懂之後,後面所有的疑問都會有答案:</p>
<ul>
<li>為什麼同樣一句話,有時候 3 秒結束、有時候跑 5 分鐘?</li>
<li>為什麼 token 帳單會爆炸,而且完全看不出錢花在哪?</li>
<li>為什麼一個任務進行到一半會卡住?</li>
<li>為什麼「thinking&hellip;」有時候真的在想,有時候只是在處理資料?</li>
</ul>
<p>這些問題的根源都在 Agent Loop。這篇文章不是教你怎麼用 Claude Code,是要你<strong>建立一個關於它運作方式的心智模型</strong>,這個模型會貫穿整個系列。</p>
<hr>
<h2 id="三句話版本的-agent-loop">三句話版本的 Agent Loop</h2>
<p>如果要用最精簡的方式描述,Claude Code 做的事情就是這三件:</p>
<ol>
<li><strong>接收輸入</strong>,組裝成模型看得懂的 prompt</li>
<li><strong>呼叫模型</strong>,拿到回應</li>
<li><strong>判斷要不要繼續</strong>,要繼續就執行工具,拿結果回到第 1 步</li>
</ol>
<p>聽起來很簡單。但魔鬼在細節裡,尤其是第 3 步的「判斷要不要繼續」。這個判斷讓 Claude Code 從「問答工具」變成「任務執行者」。</p>
<hr>
<h2 id="一次對話實際發生了什麼">一次對話實際發生了什麼</h2>
<p>我們用一個具體情境拆解:你在 Claude Code 裡輸入「幫我看看今天早上 nginx 的錯誤日誌,有沒有 5xx 的異常」,然後按下 Enter。</p>
<p>接下來發生的事情,可以分成六個階段。</p>
<h3 id="階段一你按下-enter-之後的-01-秒">階段一:你按下 Enter 之後的 0.1 秒</h3>
<p>訊息送進 Claude Code 的處理管線之前,先經過一道<strong>輸入處理</strong>:</p>
<ul>
<li>如果你輸入的是 slash command(像 <code>/compact</code>、<code>/init</code>),會被攔截走另一條路徑</li>
<li>如果你附加了圖片或檔案,會被轉成對應的 message block</li>
<li>如果你用 <code>@</code> 引用了檔案(<code>@src/main.ts</code>),會把檔案內容塞進 context</li>
<li>純文字就直接進入下一步</li>
</ul>
<p>這一步很快,但它決定了<strong>你送進去的不只是你打的那幾個字</strong>。</p>
<h3 id="階段二system-prompt-的組裝">階段二:System Prompt 的組裝</h3>
<p>這是很多人忽略的關鍵環節。</p>
<p>每次你送訊息,Claude Code 會動態組裝一個 system prompt,這個 prompt 由好幾個部分拼成:</p>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="p">[</span><span class="err">核心身份與工具說明</span><span class="p">]</span>          <span class="err">←</span> <span class="n">Claude</span> <span class="n">Code</span> <span class="err">本身的</span> <span class="n">prompt</span><span class="p">,</span><span class="err">固定</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">環境資訊</span><span class="p">]</span>                    <span class="err">←</span> <span class="err">作業系統、</span><span class="n">shell</span><span class="err">、目前工作目錄</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">專案上下文</span><span class="p">]</span>                  <span class="err">←</span> <span class="err">你的</span> <span class="n">CLAUDE</span><span class="o">.</span><span class="n">md</span> <span class="err">和</span> <span class="err">@</span><span class="n">import</span> <span class="err">的檔案</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">MCP</span> <span class="err">工具描述</span><span class="p">]</span>                <span class="err">←</span> <span class="err">你裝的</span> <span class="n">MCP</span> <span class="n">servers</span> <span class="err">的</span> <span class="k">tool</span> <span class="n">schemas</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">內建工具描述</span><span class="p">]</span>                <span class="err">←</span> <span class="n">Read</span><span class="o">/</span><span class="n">Write</span><span class="o">/</span><span class="n">Bash</span><span class="o">/</span><span class="n">Grep</span> <span class="err">等工具的說明</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="n">Git</span> <span class="err">狀態</span><span class="p">]</span>                    <span class="err">←</span> <span class="err">目前</span> <span class="n">branch</span><span class="err">、</span><span class="n">uncommitted</span> <span class="n">changes</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">過去對話歷史</span><span class="p">]</span>                <span class="err">←</span> <span class="err">如果是延續</span> <span class="n">session</span><span class="p">,</span><span class="err">過去的</span> <span class="n">turns</span> <span class="err">都在這</span>
</span></span></code></pre></div><p>這個組裝順序和內容會影響兩件事:</p>
<ol>
<li><strong>Token 消耗</strong>:每次送出的 prompt 越長,輸入 token 越多。</li>
<li><strong>模型行為</strong>:你 CLAUDE.md 怎麼寫,直接決定模型會怎麼回應你。</li>
</ol>
<p>這也是為什麼<strong>減少 CLAUDE.md 裡不必要的內容,我自己觀察是 token 優化裡很容易見效的一個手段</strong>。把 CLAUDE.md 寫成長篇大論,等於每一輪都在付那個長篇大論的錢。</p>
<h3 id="階段三api-call-與-streaming">階段三:API Call 與 Streaming</h3>
<p>組裝好的 prompt 送到 Anthropic 的 API,模型開始回應。</p>
<p>你注意過嗎?Claude Code 的回應是<strong>一個字一個字跑出來</strong>的,不是整段突然出現。這叫做 <strong>streaming</strong>,模型一邊思考一邊吐字,Claude Code 一邊接一邊顯示。</p>
<p>Streaming 有幾個現實影響:</p>
<ul>
<li><strong>你可以在中途按 Esc 取消</strong>:模型還沒講完,你就可以打斷它,不用等它講完再操作。</li>
<li><strong>延遲感知比較低</strong>:即使整個回應要 30 秒,你看到第一個字通常只要 1-2 秒。</li>
<li><strong>Tool use 也是 stream 的一部分</strong>:當模型決定要呼叫工具,那個指令也是 stream 出來的,Claude Code 接到就準備執行。</li>
</ul>
<h3 id="階段四tool-的識別與執行">階段四:Tool 的識別與執行</h3>
<p>這是 Agent Loop 最核心的一步。</p>
<p>模型的回應裡可能有兩種東西:</p>
<ul>
<li><strong>純文字</strong>:給你看的說明、分析、結論</li>
<li><strong>Tool Use Block</strong>:「我需要執行某個工具,請幫我跑」的指令</li>
</ul>
<p>當 Claude Code 偵測到 tool use block,它會:</p>
<ol>
<li>檢查這個工具是否在白名單(對應上一篇講的三道關卡)</li>
<li>如果需要你確認(ask 規則),跳出確認對話</li>
<li>實際執行這個工具(讀檔、跑 bash、呼叫 MCP 等)</li>
<li>把執行結果包成 tool_result,準備送回模型</li>
</ol>
<p>用我們的 nginx 例子:</p>
<ul>
<li>模型第一輪可能先發出 <code>Bash: ls /var/log/nginx/</code> 想看有哪些 log 檔</li>
<li>Claude Code 執行後,把結果送回模型</li>
<li>模型第二輪可能發出 <code>Bash: grep -E &quot; 5[0-9]{2} &quot; /var/log/nginx/access.log | head -50</code> 篩出 5xx</li>
<li>執行後結果再送回</li>
<li>模型第三輪可能已經分析完,直接用文字回答你</li>
</ul>
<p>你看到的「Claude 幫我查完了」,其實是<strong>至少 3 次 API call + 2 次工具執行</strong>的結果。</p>
<h3 id="階段五判斷要不要繼續">階段五:判斷要不要繼續</h3>
<p>這是 loop 的關鍵節點。</p>
<p>每一輪模型回應結束後,Claude Code 會問:<strong>回應裡還有 tool use block 嗎?</strong></p>
<ul>
<li><strong>有</strong> → 執行工具,把結果送回,進入下一輪</li>
<li><strong>沒有</strong>(只有文字) → loop 結束,把控制權交還給你</li>
</ul>
<p>這個判斷聽起來簡單,但它是整個 agentic 行為的基礎。模型自己決定「我需要更多資訊」或「我完成了」,不需要你每一步都按「繼續」。</p>
<h3 id="階段六終止條件">階段六:終止條件</h3>
<p>除了模型自主判斷完成,loop 還有幾個強制終止的條件:</p>
<ul>
<li><strong>Max turns</strong>:預防無限迴圈,達到上限會停</li>
<li><strong>使用者中斷</strong>:你按 Esc</li>
<li><strong>權限拒絕</strong>:你拒絕了某個工具的 ask,而模型沒有備案</li>
<li><strong>錯誤</strong>:API 失敗、tool 執行錯誤且模型無法處理</li>
</ul>
<p>這解釋了為什麼<strong>你有時候覺得 Claude Code「沒做完就停了」</strong> —— 它其實是碰到了某個終止條件。看錯誤訊息或 verbose mode 可以知道是哪一個。</p>
<hr>
<h2 id="一張圖總結agent-loop-的完整流程">一張圖總結:Agent Loop 的完整流程</h2>
<div class="highlight"><pre tabindex="0" class="chroma"><code class="language-gdscript3" data-lang="gdscript3"><span class="line"><span class="cl"><span class="err">你按下</span> <span class="n">Enter</span>
</span></span><span class="line"><span class="cl">    <span class="err">│</span>
</span></span><span class="line"><span class="cl">    <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">輸入處理</span><span class="p">]</span> <span class="n">slash</span> <span class="n">command</span><span class="err">?</span> <span class="err">附件?</span> <span class="err">@引用?</span>
</span></span><span class="line"><span class="cl">    <span class="err">│</span>
</span></span><span class="line"><span class="cl">    <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="p">[</span><span class="err">組裝</span> <span class="n">system</span> <span class="n">prompt</span><span class="p">]</span> <span class="err">固定部分</span> <span class="o">+</span> <span class="err">環境</span> <span class="o">+</span> <span class="n">CLAUDE</span><span class="o">.</span><span class="n">md</span> <span class="o">+</span> <span class="n">tools</span> <span class="o">+</span> <span class="n">git</span> <span class="o">+</span> <span class="err">歷史</span>
</span></span><span class="line"><span class="cl">    <span class="err">│</span>
</span></span><span class="line"><span class="cl">    <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="err">┌───</span> <span class="err">進入</span> <span class="n">Query</span> <span class="n">Loop</span> <span class="err">───┐</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>                        <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>  <span class="p">[</span><span class="err">呼叫</span> <span class="n">API</span><span class="p">,</span><span class="n">streaming</span> <span class="err">回應</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>           <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>           <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>  <span class="p">[</span><span class="err">識別</span> <span class="n">tool_use</span> <span class="n">block</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>           <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>     <span class="err">有</span> <span class="err">─┴─</span> <span class="err">沒有</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">│</span>       <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">▼</span>       <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>  <span class="p">[</span><span class="err">執行工具</span><span class="p">]</span>  <span class="p">[</span><span class="err">結束</span> <span class="n">loop</span><span class="p">,</span><span class="err">回傳給你</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">▼</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>  <span class="p">[</span><span class="err">結果送回模型</span><span class="p">]</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>      <span class="err">└──→</span> <span class="p">(</span><span class="err">下一輪</span><span class="p">)</span>
</span></span><span class="line"><span class="cl"><span class="err">│</span>
</span></span><span class="line"><span class="cl"><span class="err">└────────────────────────┘</span>
</span></span></code></pre></div><p>這就是每次你跟 Claude Code 互動時,底層真正發生的事。</p>
<hr>
<h2 id="tool-執行序列還是平行">Tool 執行:序列還是平行?</h2>
<p>模型在一次回應中可以發出<strong>多個</strong> tool use block。當這發生時,Claude Code 要決定:這些工具能不能同時跑?</p>
<p>判斷邏輯大致是:</p>
<table>
  <thead>
      <tr>
          <th>情境</th>
          <th>執行方式</th>
          <th>原因</th>
      </tr>
  </thead>
  <tbody>
      <tr>
          <td>多個 Read 操作</td>
          <td>平行</td>
          <td>只讀,不互相影響</td>
      </tr>
      <tr>
          <td>多個 Grep / Glob</td>
          <td>平行</td>
          <td>搜尋也是只讀</td>
      </tr>
      <tr>
          <td>多個 Bash command</td>
          <td>序列</td>
          <td>可能有副作用(改檔案、改狀態)</td>
      </tr>
      <tr>
          <td>Read + Write 同一個檔案</td>
          <td>序列</td>
          <td>必須先 Read 再 Write,有依賴</td>
      </tr>
      <tr>
          <td>多個獨立的 Write</td>
          <td>通常序列</td>
          <td>謹慎起見,避免競態</td>
      </tr>
  </tbody>
</table>
<p><strong>為什麼這個區分重要?</strong> 因為平行執行會大幅加速任務。當 Claude Code 一次叫 10 個 Read 去看 10 個檔案,平行跑和序列跑的體感差距是 10 倍以上。</p>
<p>這也是為什麼 Claude Code 有「Explore」這類<strong>唯讀</strong> sub-agent —— 唯讀保證可以平行,速度快,而且不會搞壞東西。(Sub-agent 的主題會在第 4 篇展開)</p>
<hr>
<h2 id="這些機制對你實際操作的意義">這些機制對你實際操作的意義</h2>
<p>搞懂 loop 之後,很多「奇怪的現象」都有答案了。</p>
<h3 id="為什麼長任務會越跑越慢">為什麼長任務會越跑越慢</h3>
<p>每一輪 loop,Claude Code 都要把<strong>整段對話歷史</strong>重新送進 API。對話越長,每一輪要傳的東西越多,API 呼叫越慢,也越貴。</p>
<p>這就是為什麼到了某個臨界點,任務會明顯變慢。原因不是「AI 累了」,是 context 逼近上限,<strong>每一輪的 overhead 越來越大</strong>。</p>
<p>這個問題的解方就是 context 壓縮,下一篇會整個展開。</p>
<h3 id="為什麼thinking有時候很久">為什麼「thinking&hellip;」有時候很久</h3>
<p>「thinking&hellip;」其實有兩種:</p>
<ol>
<li><strong>真的在 extended thinking</strong>:模型在做內部推理,這部分會花時間但也確實在產出思考</li>
<li><strong>在處理 tool result</strong>:前一個工具剛跑完,結果很大(例如讀了一個 500 行的檔案),模型正在消化</li>
</ol>
<p>如果你覺得 thinking 太久,可以開 verbose mode 看它到底在做什麼。通常第二種比較常見。</p>
<h3 id="為什麼某些操作會卡住等你">為什麼某些操作會卡住等你</h3>
<p>這是權限系統介入的時刻。當模型想做的事情在 <code>ask</code> 規則裡,Claude Code 會跳出確認對話,loop 在你回應之前是停住的。</p>
<p>這不是 bug,是設計 —— 確保你對破壞性操作有最後一次確認機會。</p>
<h3 id="為什麼同樣一句話效果差很多">為什麼同樣一句話,效果差很多</h3>
<p>兩個使用者問一樣的問題,結果天差地遠的主因不是「模型不穩」,而是:</p>
<ul>
<li>CLAUDE.md 寫得不一樣</li>
<li>當前 working directory 不一樣</li>
<li>已經在 session 裡累積的 context 不一樣</li>
<li>裝的 MCP tools 不一樣</li>
</ul>
<p>System prompt 的組裝階段(階段二)就決定了結果差異中很大一部分。這也是為什麼我覺得<strong>團隊裡把 CLAUDE.md 寫好、統一管理,通常會比讓每個人自己摸索更可預期一些</strong>。</p>
<hr>
<h2 id="我自己經歷的一次觀念轉換">我自己經歷的一次觀念轉換</h2>
<p>我自己一開始使用 AI 助理的心理模型是這樣:</p>
<blockquote>
<p>「我在問 AI 一個問題,它回答我。」</p>
</blockquote>
<p>用 Claude Code 一段時間後,我自己慢慢轉成這樣:</p>
<blockquote>
<p>「我啟動了一個迴圈,這個迴圈會執行多輪,直到達成我給的目標或觸發終止條件。我的任務是<strong>設計好這個迴圈的起點、邊界、和終止條件</strong>。」</p>
</blockquote>
<p>這個轉換之後,我開始問自己不一樣的問題:</p>
<ul>
<li>我這句 prompt 會讓 loop 跑幾輪?會不會太多?</li>
<li>這個任務需要的 context 是否都在 loop 開始時就備好了?</li>
<li>如果 loop 跑太久,我該怎麼設計 checkpoint?</li>
<li>什麼情況下應該主動終止 loop,換個方式再來?</li>
</ul>
<p>這些是我自己覺得在「熟練使用 Claude Code」之後比較常想的事。</p>
<hr>
<h2 id="下一篇預告">下一篇預告</h2>
<p>搞懂 loop 之後,下一個繞不開的主題就是 <strong>context 管理</strong>。</p>
<p>在這篇裡我們看到:每一輪 loop 都要把整個 context 送進 API。當 context 逼近上限會發生什麼?Claude Code 怎麼在你不察覺的情況下,把空間清出來?</p>
<p>這個系統比表面看起來精巧 —— 有四層壓縮管線,每一層用不同的策略,該什麼時候哪一層啟動都有明確規則。</p>
<p>下一篇,我們把這個黑盒打開。</p>
<hr>
<h2 id="本篇重點整理">本篇重點整理</h2>
<ul>
<li>Claude Code 不是對話工具,是<strong>任務執行迴圈</strong> —— Agent Loop</li>
<li>每次你按 Enter,會經過六個階段:輸入處理 → 組裝 system prompt → API call → 識別 tool use → 執行工具 → 判斷是否繼續</li>
<li>Loop 的終止由<strong>模型自主判斷</strong>或<strong>強制條件</strong>(max turns、權限拒絕、錯誤、使用者中斷)觸發</li>
<li>Tool 可以<strong>平行或序列執行</strong>,唯讀類通常平行、有副作用的通常序列</li>
<li>長任務變慢的主因是 <strong>context 越來越大</strong>,每一輪 overhead 放大</li>
<li>心智模型上的轉換:從「我在問 AI」到「我在驅動一個迴圈」</li>
</ul>
<hr>
<p><em>本文作者:鄧景仁 (Scott Teng) | 資訊服務業 infra 工程師,專注於 Azure / Linux / 安全維運。如需討論可聯繫 <a href="mailto:st333117@gmail.com">st333117@gmail.com</a>。</em></p>
<p><em>本系列所有內容為個人學習與實務心得整理,不代表任職機構立場。本文對 Claude Code 內部機制的描述,基於社群對 Claude Code 的公開分析材料與筆者實務觀察,並非 Anthropic 官方文件。</em></p>
<hr /><p style="font-size:0.9em;opacity:0.8;margin-top:2em;">本文原刊於 <a href="https://eeit.github.io/ai-devops-blog/posts/02-agent-loop/" rel="canonical">https://eeit.github.io/ai-devops-blog/posts/02-agent-loop/</a>，作者：鄧景仁 (Scott Teng)。<br />授權條款：<a href="https://creativecommons.org/licenses/by-nc/4.0/deed.zh_TW" rel="license">CC BY-NC 4.0</a> · 禁止商業使用；分享請署名並註明原文 URL。<br />商業授權請聯絡：st333117@gmail.com</p>]]></content:encoded>
    </item>
  </channel>
</rss>
