CodeAgents + 結構:執行操作的更好方式
今天我們分享的研究彌合了人工智慧代理設計中的兩個強大正規化:基於程式碼的表達能力和結構化生成的可靠性。我們的研究結果表明,強制 CodeAgents 以結構化 JSON 格式生成思想和程式碼,在多個基準測試中可以顯著優於傳統方法。
圖 1:三種方法(結構化 CodeAgent (藍色)、CodeAgent (橙色) 和 ToolCallingAgent (灰色))在 SmolBench (GAIA, MATH, SimpleQA 和 Frames) 上的準確性比較。誤差線表示 95% 置信區間。
🤔 代理行動的演變
人工智慧代理需要在世界中採取行動——無論是呼叫 API、處理資料還是推理複雜問題。代理表達這些行動的方式已經發展了幾個正規化。
傳統 JSON 代理:代理生成結構化 JSON 以呼叫工具。
{"tool": "get_weather", "arguments": {"city": "Paris"}}
這些代理透過從預定義工具列表中選擇並生成 JSON 格式的呼叫來操作。這種呼叫工具的方法已透過 OpenAI 的 函式呼叫 API 普及,此後成為最廣泛使用的工具呼叫方法。
它可靠,但受以下限制:
- 有限的行動集:代理可以採取的行動只能透過預定義的工具表達,這限制了其功能。
- 缺乏可組合性:如果任務需要組合來自多個來源的資訊,JSON 代理會遇到困難,因為它們缺乏對跨工具呼叫維護中間狀態的支援。儘管某些模型支援並行工具呼叫,但它們無法輕鬆處理一個工具的輸出決定下一個行動或需要比較和一起處理結果的場景。
- 僵硬的結構:在處理工具與需要完成的事情不完全匹配的情況下,非常有限。
程式碼代理:代理利用其固有的編碼能力,直接編寫可執行的 Python 程式碼。
# We can get the average temperature in 3 cities in 1 model call.
temperature_sum = 0
for city in ["Paris", "Tokyo", "New York"]:
temp = get_weather(city)
temperature_sum += temp
print(f"Average temperature: {temperature_sum / 3:.1f}°C")
這種轉變,首次在論文“可執行程式碼行動引發更好的 LLM 代理”中以 CodeAct 形式提出,使 AI 代理能夠靈活地編寫任意可執行 Python 程式碼,以及進行工具呼叫。
這裡的關鍵在於 工具直接從程式碼內部呼叫,這使得變數和狀態管理更加可靠。代理可以在迴圈、函式和條件語句中呼叫工具——本質上在每個行動中生成一個動態的工具執行圖!
使用 CodeAgent 的優點
- 智慧工具使用:代理根據當下發生的事情決定使用哪些工具。
- 無限靈活性:可以使用任何 Python 功能來實現目標。
- 測試思想的能力:代理可以提出假設並進行測試,從而使其行動更具靈活性。
然而,從 Markdown 中解析程式碼可能容易出錯,這引出了一個命題:為什麼不使用結構化生成來生成程式碼操作呢?
➡️ 為程式碼代理新增結構化輸出
透過結構化輸出,您可以強制 LLM 生成明確的思想和程式碼,作為 JSON blob。
// The "code" block gets parsed into executable Python
{
"thoughts": "I want to find the average temperature across 3 cities.",
"code": "temperature_sum = 0\nfor city in [\"Paris\", \"Tokyo\", \"New York\"]:\n temp = get_weather(city)\n temperature_sum += temp\n\nprint(f\"Average temperature: {temperature_sum / 3:.1f}°C\")"
}
關鍵區別在於生成是強制執行的:基本上,現在不再只是提示輸出思想,然後是程式碼,而是使用結構化輸出強制其遵守結構。
這種方法將結構化生成的可靠性與程式碼執行的靈活性相結合,從而實現兩全其美。
- 顯式推理:
thoughts
欄位強制代理在採取行動之前進行推理。 - 可靠解析:JSON 結構消除了 Markdown 解析錯誤
- 完整的程式碼表達能力:
code
欄位保持了程式碼代理的所有靈活性 - 更好的分離:規劃和執行之間清晰分離
🧪 基準測試結果
我們比較了這三種正規化在多個基準測試中的表現,包括 GAIA、MATH、SimpleQA 和 Frames。結果顯示了一個清晰的模式:程式碼操作 + 結構化生成持續地提高了高效能模型的效果。
在大多數有能力的模型中,結構化方法平均比常規 CodeAgent 方法高出 2-7 個百分點。
- OpenAI 模型:透過結構化顯示出最大的改進,特別是在推理密集型任務上。
- Claude 模型:受益於結構化,其中 Claude 3.7 Sonnet 顯示出特別強勁的結果。
- Qwen 模型:通常透過結構化得到改進,儘管對於較小的模型,“結構稅”(參見下一節)會悄然出現。
💡 為什麼結構(通常)有幫助
解析問題真實存在
我們在 smolagents 中 CodeAgent 的實現 從 LLM 輸出中提取 Python 程式碼,當出現以下情況時可能會失敗:
- Markdown 中的程式碼塊格式不完整或不正確
- 單個響應中出現多個程式碼塊
結構化生成透過可靠的 JSON 解析消除了這些問題。
為了理解為什麼結構化生成很重要,我們分析了基準測試中的 15,724 個代理軌跡。結果令人震驚:
- 2.4% 的軌跡在首次呼叫時出現解析錯誤。
- 有首次呼叫解析錯誤的軌跡:成功率為 42.3%。
- 無首次呼叫解析錯誤的軌跡:成功率為 51.3%。
沒有解析錯誤的代理軌跡的成功率比有解析錯誤的軌跡高出 21.3%。
這不僅僅是為了方便——解析錯誤會引發一連串的失敗,嚴重影響代理的整體效能。當代理由於格式錯誤的程式碼而無法執行其第一個操作時,它通常難以恢復,從而導致次優的問題解決路徑。
圖 2:第一步中的解析錯誤將代理的成功率降低了 21.3%,並將平均步驟從 3.18 增加到 4.63。
此外:強制推理過程
結構化生成和明確的 thoughts
的使用不僅提示代理,而且強制代理在行動前闡明其推理。這導致:
- 更好的規劃:代理更系統地思考問題。
- 增強的可靠性:明確的推理能及早發現邏輯錯誤。
結構開銷
我們的結果還揭示了一個清晰的能力閾值:模型需要足夠的指令遵循能力和其預訓練資料中的 JSON 覆蓋率才能從結構化生成中受益。這表明結構化方法最適用於:
- 大型、訓練良好的模型
- 具有強大指令遵循能力的模型
- 經過結構化生成微調的模型。
結構何時失效:真實示例
當一個較小的模型(例如 mistralai/Mistral-7B-Instruct-v0.3
)嘗試生成結構化程式碼時,認知負荷變得過重,就會發生以下情況:
{
"thought": "I need to find the height...",
"code": "web_search(query=\"Eiffel Tower height\")\", "
}
該模型生成了語法損壞的 Python 程式碼:web_search(query="Eiffel Tower height")",
——注意帶有額外引號和逗號的畸形字串。這會導致立即的語法錯誤和執行失敗。
這說明了“結構開銷”:較小的模型難以同時處理 JSON 格式、Python 語法和實際的問題解決邏輯。結構化生成的認知開銷可能會壓垮那些在更簡單的基於 Markdown 的程式碼生成中表現相當好的模型。
🚀 何時使用結構化 CodeAgents
✅ 在以下情況使用結構化 CodeAgent:
- 使用功能強大的模型(32B+ 引數或前沿模型)時。
- 任務需要複雜的推理和程式碼執行時。
- 需要可靠解析代理輸出時。
⚠️ 在以下情況考慮替代方案:
- 使用在結構化生成方面表現不佳的小型模型時。
- 簡單的預定義工作流就足夠時。
如何與 smolagents 一起使用:
非常簡單!只需使用 use_structured_outputs_internally:
啟用它。
from smolagents import CodeAgent, InferenceClientModel, GoogleSearchTool
# Configure agent for structured generation
agent = CodeAgent(
tools=[GoogleSearchTool(provider="serper")],
model=InferenceClientModel("Qwen/Qwen3-235B-A22B", provider='nebius'),
use_structured_outputs_internally=True # Enable structured output
)
result = agent.run("Calculate the time for a cheetah to run across the Golden Gate Bridge")
LLM 將生成如下內容:
{
"thoughts": "I need to find the length of the Golden Gate Bridge and the top speed of a cheetah, then calculate the time.",
"code": "bridge_info = web_search('Golden Gate Bridge length meters')\ncheetah_speed = web_search('Cheetah top speed') ..."
}
然後“程式碼”部分像往常一樣由代理執行:這是標準的 CodeAgent,但現在它具有 100% 的解析可靠性!
實施技巧
- 清晰的提示:確保您的提示明確指定了預期的 JSON 結構。
- 模型選擇:選擇具有強大結構化生成能力的模型。
- 選擇正確的提供商: 某些 API 提供商(如 OpenAI 或 Anthropic)原生支援結構化生成。如果您透過 Hugging Face 使用推理提供商,結構化生成的支援因提供商而異。以下是支援結構化生成的提供商列表:smolagents 中模型對結構化生成的支援‣
更大的圖景 - 接下來是什麼?
這項研究表明我們正在走向對代理架構更細緻的理解。這不僅僅是關於“代理能做什麼?”,而是“代理應該如何思考它正在做什麼?”
也許使推理過程更明確有助於模型保持正軌。或者,也許只是更容易解析。無論哪種方式,都是一個勝利。
但這僅僅是個開始。還有很多問題需要探索:
- 還有哪些結構改進會有幫助?
- 我們如何讓它在不同的模型架構中更好地工作,特別是小型模型?
- 這告訴我們關於人工智慧推理的本質是什麼?
目前,如果您正在使用 smolagents(或構建您自己的 CodeAgent 系統),請考慮嘗試結構化輸出。您的解析錯誤會減少,並且您可能會看到效能顯著提升!