tiny-agent從第一性原理打造可靠 Agent

第一部|最小閉環 · 第 1 章

從 LLM 到 Agent

拆開 LLM、Agent 與 coding agent,親手建立不可再刪的 model → tool → result 閉環。

約 14 分鐘1 / 8

LLM 能產生下一段文字;Agent 則把文字模型放進一個可以觀察世界、採取行動、接收結果並繼續判斷的閉環。這一章只留下不可再刪的部分。

LLM 不是 Agent

一次 LLM 呼叫可以抽象成 output = model(messages)。模型不會記得上一次呼叫,也不會真的讀檔、執行測試或修改系統。它只根據本次輸入預測輸出。所謂「記憶」,其實是 host 在下一次呼叫時,把先前的訊息再次送入。

LLM

  • 輸入訊息,輸出文字或 tool call
  • 單次呼叫無狀態
  • 不直接產生外部副作用

Agent

  • 保存 transcript
  • 執行模型選擇的 tool
  • 把結果送回模型,直到得到 final answer

不可再刪的閉環

async function runAgentLoop(userText: string) {
    messages.push({ role: "user", content: userText });

    for (;;) {
        const answer = await callModel(messages, toolDefinitions);
        messages.push(answer);

        if (!answer.toolCalls.length) return answer.text;

        for (const call of answer.toolCalls) {
            const result = await tools[call.name].execute(call.arguments);
            messages.push({ role: "tool", toolCallId: call.id, content: result });
        }
    }
}

這段程式不理解「修 bug」或「查營收」。它只負責機械性的控制:呼叫模型、執行能力、保存結果、決定是否繼續。領域能力在 tools;決策能力在 model;連續性在 messages。

Coding Agent 多了什麼

給 Agent 四個能力,就得到最小 coding agent:

  1. read:取得原始碼與檔案。
  2. write:建立或完整覆寫檔案。
  3. edit:精準修改既有內容。
  4. bash:搜尋、編譯、測試與執行命令。

真正困難的不是把 loop 寫出來,而是讓它在錯誤、取消、context 增長、程式 crash 與不確定外部 effect 下,仍保持 transcript 合法且能安全恢復。後面七章都在補上這些可靠性。

第一性原則的責任分配

責任 由誰負責 不該負責什麼
選擇下一步 Model 直接取得 host credential
控制迴圈 Agent runtime 理解每個領域的業務規則
產生副作用 Tool adapter 自行改變授權範圍
持續與恢復 Session 猜測未記錄的 effect 是否成功
介面與部署 CLI / trusted host 把 deployment security 假裝成 agent 功能

動手驗證

tiny-ts --plugin read "讀取 README.md,說明 agent loop"

tiny-ts --plugin edit "讀取 README.md"
# 第二個命令缺少 read 能力,Agent 應明確說明缺少能力,不能捏造結果。

這個對照揭示一個重要觀念:Agent 的能力不是 prompt 宣稱出來的,而是本次 request 實際提供的 tool definitions。

這個迴圈假設 messages 這個陣列永遠活著——它只存在 process 的記憶體裡,process 一死,這個陣列就跟著消失。下一章先處理它裡面裝的東西合不合法,再往後幾章處理它會不會突然消失。