沒有迴圈,就沒有 agent
Claude Code 的整個秘密可以寫成一行:while stop_reason == "tool_use"
一個 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 在迴圈。