到現在為止,我一直覺得 AI agent 周圍籠罩著一種神祕的光環。沒有人真的知道它們在做什麼,它們大概很快就要接管世界了;而如果你想自己做一個……嗯,顯然你需要一個框架。

如果我告訴你這不是真的呢?你可以在大約 80 行程式碼內做出自己的 AI agent。

我得承認,這週不太好過。工作上發生了很多事。除此之外,我還收到其中一個 CFP 的自動拒絕信。正常來說,這大概只會佔據我五分鐘的心思,因為會議被拒本來就是家常便飯。

只不過……那場會議其實是邀請我去的。「你已經被接受了,只需要在 CFP 裡填交演講細節。」因為那個邀請,我還推掉了另外兩個會議機會。唉,至少我現在九月有空了。😉

不管怎樣,生活還是要繼續。今天是我的生日,所以當作送給自己、也送給大家的一份小禮物,我寫了這篇文章。😄 希望你會喜歡!

我們真的需要框架嗎?

直接切入正題。

像 LangChain、CrewAI 或 Mastra 這類框架,並不是在施魔法。它們只是把像是對話記憶、工具執行、重試、備援等事情簡化了。

一旦你理解底層機制,就會更容易判斷什麼時候框架真的值得用。即使最後還是會用框架,你也會知道幕後到底發生了什麼。

所以我決定做一個小型 demo,看看 AI agent 到底只需要多少程式碼。

我用 Node.js 寫了一個 AI agent,大約只用了 80 行程式碼……好吧好吧,核心迴圈大概是 80 行。還是有工具、供應商抽象層,以及一些周邊邏輯。不過老實說,「80 行寫出一個 AI agent」 聽起來就是比較厲害。😄

這是專案倉庫:

https://github.com/sylwia-lask/code-review-agent

而且今天是我的生日……如果你喜歡這個專案,歡迎幫我點個 ⭐。當然,前提是你真的喜歡。😄

認識 Steve

這個應用程式是一個簡單的程式碼審查 agent。嗯……其實也不完全是。

認識一下 Steve:一位有 15 年經驗、專門審查別人程式碼的軟體工程師。Steve 不會照單全收。沒錯,他有時候可能有點酸……但他的結論通常很難反駁。

以下是他的一個程式碼審查範例:

Steve 的 agent 程式碼審查

Steve 的 agent 程式碼審查 - 第 2 部分

或者當你有空的 diff 時:

空 diff 的審查

目前 Steve 會審查本機的 Git diff。這基本上就表示……他在審查他自己。所以我想,我大概是做出了一個傳說中的自我修復 agent 原型;根據 @nitsancohen770 的說法,這東西有一天會把我的工作搶走。

如你所見,我基本上是在幫自己自動化失業。也許我真的該開始考慮退休了。😄

「Agent 就只是一個 while 迴圈」

在我上一篇文章裡,我曾開玩笑說,有些人認為 AI agent 不過就是一個 while 迴圈。嗯……我的甚至連 while 都不是。它是個 for 迴圈,因為我想避免不小心做出無限迴圈,然後在 token 上燒掉太多錢。😄

好吧,公平地說,這個迴圈本身其實沒有真的什麼。它只是負責協調流程。不過有趣的是,大多數 agent 框架在底層做的事情其實也非常類似。

這個迴圈本身很容易寫。真正的挑戰,其實就是你猜得到的那一塊。

做 AI agent 的第一步就是……選擇 LLM。😄 這次我選了 Gemini API,因為這類專案的 token 真的便宜到誇張。我確實很想下一版改用本機模型來做,但……先等等。😄

我立刻遇到一個問題:有些 Gemini 模型負載過高,所以我一直收到 503 回應。這代表我必須實作一些框架通常開箱即有的功能——簡單的重試機制。

我的版本刻意做得非常基本。正式環境的框架通常還會提供更多功能,例如指數退避、抖動(jitter),或自動切換到另一個模型。

接下來的挑戰當然就是,寫出正確的提示詞。之後,一切就變得出乎意料地順利。

這個 agent 到底是怎麼運作的?

比你想的簡單多了。

步驟 1:送出提示詞和可用工具

我們送兩樣東西給模型:

  • 使用者訊息,
  • 模型可以使用的工具清單。

請求長這樣:

{
  "model": "gemini-2.5-flash",
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Please review the current git diff."
        }
      ]
    }
  ],
  "config": {
    "systemInstruction": "You are Steve, a senior software engineer with 15 years of experience...",
    "tools": [
      {
        "functionDeclarations": [
          {
            "name": "getDiff",
            "description": "Get the git diff of the current repository...",
            "parameters": {
              "type": "OBJECT",
              "properties": {},
              "required": []
            }
          },
          {
            "name": "getFile",
            "description": "Read a file from the repository...",
            "parameters": {
              "type": "OBJECT",
              "properties": {
                "path": {
                  "type": "STRING",
                  "description": "Path to the file relative to the repository root"
                }
              },
              "required": ["path"]
            }
          },
          {
            "name": "listFiles",
            "description": "List files and directories at a given path...",
            "parameters": {
              "type": "OBJECT",
              "properties": {
                "path": {
                  "type": "STRING",
                  "description": "Directory path relative to the repository root"
                }
              },
              "required": ["path"]
            }
          }
        ]
      }
    ]
  }
}

步驟 2:等待模型回應

模型可以用三種方式回應:

  • 純文字,
  • 要求呼叫某個工具,
  • 或兩者都有。

如果它回傳純文字,我們就完成了:那就是最後的程式碼審查結果。如果它要求我們呼叫工具,就進入步驟 3。

例如,我們可能會收到:

{
  "candidates": [
    {
      "content": {
        "role": "model",
        "parts": [
          {
            "text": "Let's see what damage we're dealing with today..."
          },
          {
            "functionCall": {
              "id": "call_001",
              "name": "getDiff",
              "args": {}
            }
          }
        ]
      }
    }
  ]
}

步驟 3:在本機執行工具

接下來輪到我們的應用程式。

我們在本機執行要求的工具。例如執行 git diff 或讀取倉庫中的某個檔案,然後把結果作為對話中的另一則訊息送回給模型。

這個 demo 可用的工具有:

  • getDiff
  • getFile
  • listFiles

換句話說,這就是一個好的程式碼審查者需要的一切。😄

步驟 4:重複直到模型完成

然後我們只要回到步驟 2。為了避免卡進無限迴圈,我把最大迭代次數限制在 10 次。

不過,還有一個重要細節。

看看下一個請求。注意我們把整段對話歷史一起送回模型:

{
  "contents": [
    {
      "role": "user",
      "parts": [
        {
          "text": "Please review the current git diff."
        }
      ]
    },
    {
      "role": "model",
      "parts": [
        {
          "text": "Let's see what damage we're dealing with today..."
        },
        {
          "functionCall": {
            "id": "call_001",
            "name": "getDiff",
            "args": {}
          }
        }
      ]
    },
    {
      "role": "user",
      "parts": [
        {
          "functionResponse": {
            "id": "call_001",
            "name": "getDiff",
            "response": {
              "result": "diff --git a/src/auth.ts b/src/auth.ts\n--- a/src/auth.ts\n+++ b/src/auth.ts\n@@ -12,7 +12,7 @@\n-  if (password === storedHash) {\n+  if (password == storedHash) {\n"
            }
          }
        }
      ]
    }
  ]
}

就這樣而已。LLM 會決定要用哪個工具,以及什麼時候結束。其他所有事情——執行工具、處理重試、限制迭代次數、以及協調整個迴圈——都是我們應用程式的責任。

很簡單,對吧?如果你喜歡視覺化,ChatGPT 已經幫我們準備了一張圖 😉

agent 如何運作的示意圖

接下來呢?

我未來想至少再寫兩篇延伸文章。一篇是把 Steve 接到 MCP,另一篇是用 本機 LLM 取代託管模型。

不過……先等等,一步一步來。😄

加碼:模型怎麼知道自己該呼叫函式?

如果你只是想學怎麼做 AI agent,那你大概可以看到這裡就停了。😄

這一段是給好奇的人看的。因為遲早會有人問:「Sylwia,你到底在說什麼?你說你在做自己的 AI agent,但你其實只是用了 Gemini SDK。你送進去 JSON,回來也是 JSON。」

而且,正如 @darkwiiplayer 常常指出的,LLM 基本上就是一個很厲害的下一個 token 預測器,不是什麼神奇的 JSON 產生器。

那麼……當我們送 JSON 給它時,模型到底怎麼知道該做什麼?如果不是 Gemini,而是一些老舊的本機 Llama 模型,會發生什麼事?

是 Gemini SDK 偷偷在前面加了一段特殊提示詞嗎?還是這些模型本身就有針對這種情境訓練?

老實說,我們並不知道所有細節。不過我們知道的是,像 GeminiGPT 這類模型原生支援工具呼叫。SDK 負責正確格式化請求並與 API 溝通,而模型本身理解工具宣告,並且在判斷需要時可以產生函式呼叫。

換句話說,如果我們拿一個較舊的 Llama 模型,也還是可以寫一段提示詞,說明如何解讀 JSON,並要求它以特定的 JSON 格式回應。

然後我們只要呼叫 JSON.parse()……

……前提是模型真的回傳有效的 JSON,而不是 Markdown、說明文字,或它決定額外加上的幾句「貼心」註解。😄

話雖如此,值得一提的是,較新的開源模型也越來越多原生支援工具呼叫了。

尾聲:由另一個 agent 寫成的 agent

老實說,現在是 2026 年。這個 agent 很大一部分是由……另一個 AI agent 寫出來的。😄

因為我已經當了一段時間的 AWS Community Builder,所以我取消了 Claude Code 訂閱,改用 Kiro:一個內建支援多種 LLM 的 AI IDE,包括 Claude、GPT 和 Gemini。價格差不多,但因為這個計畫,我可以免費使用。

到目前為止,我真的很喜歡它。它有點像是建構在 VS Code 上的 agent 驅動層,所以我很快就上手了。我也覺得我現在可能只用了它實際功能的 10%,所以我很確定未來還會再寫更多關於它的內容。

現在我只希望 AWS 終於會寄一些周邊給我。😄 我特別想要他們的一件 T 恤。好多年前,波蘭其實真的有一個名叫 AWS 的政黨,所以我超想看看一些年長者看到它時的表情。😄

AWS……如果你們正在看這篇……我在等你們!!😄

最後想法

如你所見,AI agent 不一定非得從框架開始。有時你只需要一個 LLM、一些工具、對話歷史,以及一個簡單的迴圈。其他的一切,都是便利性、正式環境強化,以及提升使用體驗的改良。

所以……

你覺得 Steve 怎麼樣?你又喜不喜歡這種打造 AI agent 的方式? 😄

如果你喜歡這篇文章,也可以在 LinkedIn 上追蹤我。


原文出處:https://dev.to/sylwia-lask/the-dirty-secret-behind-ai-agents-demo--273d


精選技術文章翻譯,幫助開發者持續吸收新知。

共有 0 則留言


精選技術文章翻譯,幫助開發者持續吸收新知。
🏆 本月排行榜
🥇
站長阿川
📝10   💬2   ❤️5
278
🥈
我愛JS
📝2   💬6   ❤️3
112
評分標準:發文×10 + 留言×3 + 獲讚×5 + 點讚×1 + 瀏覽數÷10
本數據每小時更新一次
📢 贊助商廣告 · 我要刊登