Lesson 01 · 基礎

沒有循環,就沒有 agent

Claude Code 的整個秘密可以寫成一行:while stop_reason == "tool_use"

⏱ 約 10 分鐘 · 📝 3 個可互動元件 · 🧑‍💻 基於 shareAI-lab · s01_agent_loop.py

一個 agent 到底在做什麼?

當你在終端機裡跑 Claude Code、讓它「把所有 TODO 註解整理成一份清單」,你看到的是:它自己決定先 grep、再 cat 幾個文件、然後輸出 Markdown。 模型本身不會執行程式碼-它只會請求執行。真正讓它看起來「有手有腳」的,是它外面那一圈 30 行左右的膠水代碼。

這一課就是要把那圈膠水拆開看清楚。它叫做 agent loop(智能體循環),結構就是:

while response.stop_reason == "tool_use":
    response = LLM(messages, tools)      # 1. ask the model
    execute_tools(response.tool_calls)   # 2. run what it asked for
    messages.append(tool_results)        # 3. feed results back

就這樣。沒了。 production 的 Claude Code 在這上面疊了權限、hook、子 agent、worktree 隔離、記憶壓縮——但核心還是這四行。

關鍵直覺:模型每次回覆要不是「我想調工具」,就是「我說完了」。只要還在前一種狀態,循環就繼續;一旦進入後一種狀態,循環就退出。 判據是回應裡的 stop_reason 欄位。

看 messages[] 一步步長出來

下面這個循環模擬的任務是:「目前目錄有哪些檔案?然後讀 package.json」。點 Step,每按前進一個循環內的動作。左邊是給人看的對話氣泡,右邊是真正塞給模型的 messages[] 陣列——注意看它如何增長。

工具結果必須重新進入訊息歷史

新手最容易踩的坑,是把「執行工具」當成一個副作用-執行了就完事了。但模型下一輪推理只能看到 messages[]不在裡面的東西它就不知道發生過。少 append 一步,整個循環就錯了。

錯法不是都一樣的。常見兩種:

  • 忘記 append:模型下一輪看不到結果,它會再問一次同一個工具--於是你得到一個無限循環。
  • append 了但丟掉 tool_use_id:Anthropic API 會直接報錯 tool_result must have tool_use_id,循環崩在 API 呼叫那一步。

同一個任務:「統計專案裡有多少個 Python 檔案」。兩個版本並排跑,看結局差。

讀懂 stop_reason

模型每次回都會帶一個 stop_reason。決定要不要繼續循環,就看這個欄位:

  • tool_use — 模型想調工具,繼續循環
  • end_turn — 模型覺得說完了,退出循環
  • max_tokens — 生成到了 token 上限被截斷,退出循環(一般當異常處理,提示用戶輸出不完整)。
  • stop_sequence — 碰到自訂停止串,退出循環

寫錯成「只要沒 tool_use 就繼續」,或忽略 max_tokens 直接當作正常 end,都是常見 bug。

動手跑一下

本地克隆 shareAI-lab/learn-claude-code:

git clone https://github.com/shareAI-lab/learn-claude-code
cd learn-claude-code
pip install -r requirements.txt
cp .env.example .env  # fill in ANTHROPIC_API_KEY
python agents/s01_agent_loop.py

然後讓它做點真事:帮我数一下这个仓库里有多少 .py 文件。你會看到它發一條 ls -R、看到輸出、再發一條 find . -name "*.py" | wc -l、然後告訴你答案。整個過程就是這個 loop 在循環。

Interactive

Widget 1 · Loop Stepper · messages[] 生長

看懂:工具結果是以特殊格式(tool_result 塊,帶 tool_use_id)被塞回 messages 的;這是模型下一輪推理時唯一知道“工具執行完了、結果是 X”的途徑。

对话视图未开始
点 Step 开始 →
messages[] JSONlength: 0
[]
stop_reason:
Interactive

Widget 2 · Break the Loop · 正確 vs 忘記 append

看懂:只要漏一行 messages.append(tool_results),整個 agent 就不再是 agent——它變成一台每輪忘記前一輪的點讀機。

while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    msgs.append({"role":"user",
                 "content": results})
模拟运行
while True:
    resp = LLM(msgs, tools)
    msgs.append({"role":"assistant",
                 "content": resp.content})
    if resp.stop_reason != "tool_use":
        return
    results = execute(resp.tool_uses)
    # BUG: forgot to append results
    # msgs.append(...)
模拟运行
Interactive

Widget 3 · Spot the stop_reason · 判斷 4 個真實回應

每個都是真實可能的模型反應片段。點「繼續」還是「退出」,即時看對錯。

答对 0 / 4