TL;DR,建立一個代理應用程式大部分是基礎架構的設定工作:工具、狀態、防護措施,以及從單一代理擴展到多個代理。CUGA(透過 pip install cuga 安裝),全名為 Configurable Generalist Agent,是 IBM 專為企業打造的代理框架,它能處理這些繁瑣事務,讓您只需編寫工具清單和提示詞。

我們建立了二十多個單一檔案的應用程式來證明這一點。您可以從頭到尾閱讀其中一個範例,然後了解相同的代理程式如何在無需重寫的情況下,在生產環境中自主且受控地運行。

大多數代理應用程式在代理程式執行任何有用功能之前,都需要花費一週時間進行基礎架構設定。您需要選擇一個框架、連接模型客戶端、編寫工具轉接器、建立將狀態串流至使用者介面的方法,並在此過程中決定代理程式的實際用途。有趣的部分總是最後才出現。

CUGA 顛覆了這種模式。它是 IBM 的開源代理框架,負責處理規劃、執行迴圈、工具呼叫和狀態管理。剩下的就是真正屬於您的部分:代理程式可以存取哪些工具,以及您告訴它做什麼。為了展示實際體驗,我們建立了 cuga-apps:二十多個小型、實用的應用程式,每個都是一個單一的 FastAPI 檔案,包裝了一個 CugaAgent,從電影推薦器到 IBM Cloud 架構顧問應有盡有。它們的存在是為了供人閱讀和複製。您可以點擊進入即時展示區。

本文將介紹其中一個應用程式,說明該框架如何減輕您的負擔,並展示當您需要將相同的程式碼用於生產環境時,如何進行管理。無需先學習新的框架。如果您曾經編寫過 FastAPI 路由,那麼您就能讀懂每一行程式碼。

為何是框架而非一般工具集

在這個領域中,一個合理的問題是它能為您省下多少編寫工作。CUGA 的答案是:您每次都會重複建構的模型協調邏輯。

它在行動前會先進行規劃,然後透過工具呼叫和生成程式碼(CodeAct)的組合來執行。在一個需要執行二十個步驟的長任務中,大多數代理程式失敗的原因是追蹤不到中間結果,並在下一個回合中重新推導(通常是錯誤的); CUGA 會保留該狀態並執行一個反思步驟,可以捕捉到錯誤的呼叫並重新規劃,而不是盲目推進。

正是這種機制使其在 AppWorld 和 WebArena 等代理基準測試中名列前茅,而不是您手動調整的結果。

您還可以透過配置而非程式碼來設定成本/延遲權衡:快速、平衡和精確的推理模式,以及在您信任的任何沙盒中執行程式碼(本地、Docker/Podman 或 E2B 雲端)。相同的代理定義,不同的設定。這個設定比聽起來更重要。大多數框架假設底層是前沿模型,並依賴它在計畫出錯時進行恢復;CUGA 則自行完成這項工作。

規劃、反思步驟、變數追蹤以保持長期運行的正確性,這些都是框架承擔了模型原本必須承擔的負擔,這使得較小的開源模型也能在通常無法勝任的情況下表現出色。這就是為什麼託管應用程式運行在 gpt-oss-120b 而非前沿 API 上。運行您可以呼叫的最大模型是常見的選擇;CUGA 的選擇是,一個較小的開源模型就足夠了。

這些獨立的組件都不是 CUGA 獨有的。不同之處在於它們是預先組裝好的,因此您可以配置它們,而不是將它們連接起來。您接觸的 API 很小,用工具清單和提示詞建立一個 CugaAgent,然後 await agent.invoke(...)。這行程式碼以下的所有內容都由框架處理。

具體來說,這包括可互換的工具(OpenAPI、MCP 和 LangChain 函數都以相同的方式綁定)、具有變數管理和自我修正的長期規劃(這是 07/25 - 02/26 AppWorld 和 02/25 - 09/25 WebArena 排名第一的幕後機制)、宣告式防護措施、透過 A2A 進行多代理委派、由 Docling 驅動的 RAG,以及單一環境變數供應商切換(pip install cuga,然後是 OpenAI、watsonx、Ollama 等),這些都是您原本需要自行建構的功能。

名稱的第一個詞說明了一切:Configurable(可配置的);困難的部分已經處理好,所以您的工作只是任務本身。

一個應用程式,從頭到尾

這是一個 IBM Cloud 顧問,一個為架構推薦真實 IBM Cloud 服務的代理程式。整個應用程式都放在一個檔案中:一個 main.py 包含代理工廠、工具和提示詞,以及一個小型使用者介面。

整個代理程式程式碼如下:

def make_agent():

from cuga import CugaAgent

from _llm import create_llm

return CugaAgent(

model=create_llm(

provider=os.getenv("LLM_PROVIDER"),

model=os.getenv("LLM_MODEL"),

),

tools=_make_tools(),

special_instructions=_SYSTEM,

cuga_folder=str(_DIR / ".cuga"),

)

有四個參數。模型來自一個小型工廠 (create_llm),它根據環境變數與 OpenAI、Anthropic、watsonx、LiteLLM 或 Ollama 進行通訊。應用程式程式碼中沒有任何部分知道其背後是哪個模型。cuga_folder 是此應用程式儲存其狀態和任何政策的地方。承載應用程式的兩個參數是 tools 和 special_instructions。

工具混合了本地函數和託管函數:

def _make_tools():

from langchain_core.tools import tool

@tool

def search_ibm_catalog(query: str) -> str:

"""Search the IBM Cloud Global Catalog for real IBM Cloud services.

Always call this before recommending services to verify they exist."""

... # hits the catalog API, returns JSON

from _mcp_bridge import load_tools

web_tools = load_tools(["web"])

return [search_ibm_catalog, *web_tools]

這裡有一個模式適用於所有應用程式:MCP 工具和內嵌工具之間的區分。通用的、無狀態的功能來自共享的 MCP 伺服器;load_tools(["web"]) 引入了網路搜尋,而無需您託管任何內容。任何特定於此應用程式的內容都定義為正常的 Python 函數,例如 search_ibm_catalog,其文件字串是代理程式決定何時呼叫它的依據。您只需編寫屬於您的一個工具,其餘的則借用。

雲端顧問的提示詞指示代理程式在命名任何服務之前先搜尋目錄,推薦三到七個服務,並說明每個服務在設計中的作用,且絕不虛構服務名稱。最後一條規則非常重要:一個推薦不存在的 IBM Cloud 服務的代理程式比沒有代理程式更糟糕,因此提示詞強制每個推薦都必須先經過目錄查詢。以有序步驟編寫並明確「不要虛構」規則的提示詞會表現良好;以人設編寫的提示詞則容易偏離主題。

這就是應用程式。一個工具、一個程序、四行建構函數。圍繞它的 FastAPI 路由是普通的網路程式碼:瀏覽器向 /ask 發送問題,即時面板輪詢 /session/{thread_id} 端點以獲取狀態。沒有資料庫;狀態是一個每個執行緒 ID 的 Python 字典,只有代理程式透過其工具寫入。

代理程式在運行中途呼叫工具的那一刻,面板就會重新繪製。使用者介面不是邏輯的第二個副本;它是代理程式變異的狀態的視圖。

承擔繁重工作的約定

有一個細節很容易被忽略,但卻是關鍵:每個內嵌工具都返回相同的小型封裝。成功看起來像 {"ok": true, "data": {...}};失敗看起來像 {"ok": false, "code": "...", "error": "..."}。

這看起來像樣板程式碼。但它不是。CUGA 的規劃器會優雅地處理宣告的失敗(「地理編碼沒有返回任何內容,跳過該部分並繼續」),並在未宣告的失敗時卡住,此時原始堆疊追蹤會在規劃中途冒出,導致運行脫軌。在所有應用程式中,那些可靠運行的應用程式,其工具從未向代理程式拋出裸露的異常。這是一個無聊的約定,但它是一個可以恢復的代理程式和一個會失敗的代理程式之間的區別。

上述區分只有在通用部分已經在某處運行時才有效。應用程式反覆使用的功能,網路搜尋、Wikipedia/arXiv、地理編碼和天氣、金融報價以及其他一些功能,都存在於 IBM Code Engine 上託管的 7 個公共 MCP 伺服器(36 個工具)中,無需身份驗證。

一個小型橋接器會自動解析其 URL,即時展示區還提供了一個 MCP 工具探索器,讓您在將任何工具連接到代理程式之前,可以從表單中呼叫它們。

一個函式庫,而非示範

有二十多個精美的應用程式,其原因比任何單一應用程式都重要:一旦您閱讀了雲端顧問,您就閱讀了所有應用程式。它們共享一個骨架,電影推薦器將 IBM 目錄工具替換為知識 MCP 伺服器,網路研究員幾乎完全依賴網路,所以 cuga-apps 實際上是一個起點目錄。

您複製儲存庫,找到最接近您想法的應用程式,然後編輯其工具清單和提示詞(HOW_TO_BUILD_AN_APP_FAST.md 和 ADDING_AN_APP.md 確切地說明了這一點)。有些應用程式甚至是由程式碼助理透過一個規格檔案和一行簡短說明生成的,模型足以重現的規律性意味著您也足以學習。在複製任何內容之前,您可以點擊即時展示區中的每一個應用程式。

它們也分佈在不同的家族中,因此無論您正在建構什麼,總有一個應用程式已經練習了您需要的部分。有一個研究群組(Paper Scout 根據引用次數對 arXiv 論文進行排名;Wiki Dive 和 Web Researcher 進行引用綜合)、一個日常生產力套件(城市簡報、旅行、食譜、步道)、一個對 PDF、音訊和視訊進行 RAG 的文件和媒體群組、一個監控即時指標的維運專區,以及一個針對真實 IBM 產品文件的企業範例。

Ouroboros 是一個七代理的潛在客戶開發系統;打開它以了解多代理的形狀。而 Meetup Finder 則透過 Playwright 驅動無頭 Chromium 從 Meetup、Luma 和 Eventbrite 提取結構化事件(所有這些都終止了其公共搜尋 API);打開它以了解瀏覽器自動化,這是 CUGA 的起點,也是其在 WebArena 中取得優異成績的關鍵。

在您複製之前有兩個注意事項。真實的目錄位於內部 cuga-apps/cuga-apps/apps/ 目錄中,而不是外部目錄。並非每個應用程式都同樣精美,因此使用者介面將它們標記為「可發布」、「待處理」或「探索性」,並預設為「可發布」;從雲端顧問或電影推薦器開始,以獲得一個可運行的基準。

將您的代理程式保持在界限內

一個搜尋目錄的示範代理程式風險很低。將相同的模式指向寫入檔案、運行 shell 命令或觸及生產環境的事物時,問題就變了:您如何阻止它做您會後悔的事情?

CUGA 在運行時回答這個問題,而不是在您之後添加的包裝器中。開源代理程式附帶一個政策系統,您可以將政策附加到相同的代理物件:

await agent.policies.add_intent_guard(

name="Block force-push",

keywords=["--force", "--no-verify"],

response="Blocked: destructive git flags are not permitted.",

)

這是一個意圖防護(Intent Guard),是六種政策類型之一,每種政策都回答了團隊在放任代理程式之前會問的問題:

意圖防護(Intent Guard),它可以直接拒絕請求嗎?

工具核准(Tool Approval),在執行有風險的工具之前,它可以暫停等待人工確認嗎?

工具指南(Tool Guide),我可以在不重寫特定工具的情況下,引導其使用方式嗎?

行動手冊(Playbook),我可以為重複性任務固定一個已知良好的程序嗎?

輸出格式化工具(Output Formatter),我可以強制最終回應符合所需的格式嗎?

第六種類型 CustomPolicy 是當上述政策都不適用時的應變措施。時機值得仔細考慮,因為它並非全部在一個階段發生:意圖防護在代理程式選擇工具之前檢查請求,工具核准在代理程式生成其程式碼之後運行並檢查該程式碼使用了哪些工具,而輸出格式化工具僅在最終訊息存在後才觸發。

觸發器也超越了關鍵字匹配:它們儲存在 sqlite-vec 儲存庫中並進行語義匹配,因此政策會根據使用者的意圖觸發,而不僅僅是精確的關鍵字。