AI 開發

OpenAI Agents API 如何處理長時間執行的 AI Agent?Context、Tools、Subagents 與 JSON 狀態管理完整教學

閱讀約 16 分鐘

9 月 10 日公測的賣點不是「再開一個 chat」。官方把 Codex 上跑了幾天的 harness 交出來:Session 續著,上下文會被 compact,subagent 自帶一份上下文。本文只寫長任務怎麼活:你存什麼 JSON,OpenAI 替你壓什麼,斷線後看哪段 items。

閱讀前速覽

物件 做什麼 你該存什麼
Session 可續的實例,跨小時、跨視窗 session_id
Turn 一次輸入啟動的非同步工作 turn_id / 完成事件
Items 已儲存的訊息和工具呼叫 斷線後回放用
Compact 接近視窗上限時自動壓縮 關鍵欄位另存
Subagent 獨立上下文,並行干活 並行上限

空閒的 session 不等於這一輪成功。看 turn 事件和 items,不要看最後一句自然語言。

長任務活在 Session 裡,不活在更長的 prompt 裡

Agents API 處理長時間執行的辦法很具體:你建一個 durable session,後續輸入都打到同一個 session_id。接近上下文上限時,harness 自動 compact 早先的內容。獨立子任務可以派給 subagent。你不用自己拼完整聊天紀錄。

  • 續跑:把 session_id 跟業務會話綁在一起。同一條會話,不要新建第二個 session。
  • 斷線:事件流不重放。把 session 和 items 拉回來,不要假設用戶端還記得上一跳。
  • 錢和單號:compact 會丟掉細節。訂單號、金額、審批結果寫在你自己的 JSON 裡。

Session 和工具 JSON 怎麼接,見 OpenAI Agents API 完整解析。循環該不該交給託管,見 Agents API vs Responses API。若 Agent 和 Function Calling 還糊在一起,先讀 AI Agent 完整指南。

Session 續著,Turn 非同步跑

建立時一次宣告模型、工具、環境和是否開 multi_agent。官方建立口是 POST /v1/agents/sessions,header 帶 OpenAI-Beta: agents=v1。託管沙箱寫 openai_hosted:

{
  "agent": {
    "model": "gpt-6-astra",
    "instructions": "Look up orders. Confirm before any refund. Delegate independent lookups to subagents.",
    "tools": [
      {
        "type": "function",
        "name": "lookup_order",
        "parameters": {
          "type": "object",
          "properties": {
            "order_id": { "type": "string", "pattern": "^[A-Z]-\\d{4}$" }
          },
          "required": ["order_id"],
          "additionalProperties": false
        }
      },
      { "type": "web_search" }
    ],
    "multi_agent": { "enabled": true, "max_concurrent_subagents": 3 }
  },
  "environment": { "type": "openai_hosted", "workspace_directory": "/workspace" },
  "input": [
    {
      "role": "user",
      "content": [{ "type": "input_text", "text": "Look up A-1042 and keep the case open." }]
    }
  ]
}

同一條 session 上再發 agent.session.input.message。空閒就開新 turn;還在跑就轉向當前 turn。取消用 agent.session.input.cancel,session 和已儲存工作還在:

{
  "events": [
    {
      "type": "agent.session.input.message",
      "input": [
        {
          "role": "user",
          "content": [
            { "type": "input_text", "text": "Warehouse marked A-1042 packed. Continue." }
          ]
        }
      ]
    }
  ]
}

看結局,不要看「現在閒不閒」。官方事件是 agent.session.turn.completed、failed、cancelled。turn 完成也不等於每個工具都成功。需要函數結果時,去讀 required_actions。

工具鏈跨小時,回寫還是 JSON

長任務裡,模型仍然只填參數。輪到你的 function,harness 停住,你按 turn_id / call_id 交結果。四小時後回寫,形狀不變:

{
  "type": "tool_result",
  "turn_id": "turn_4",
  "call_id": "call_3c91",
  "success": true,
  "output": "{\"order_id\":\"A-1042\",\"status\":\"packed\"}"
}

function 要你執行;MCP 和 web_search 可以由 harness 自己走。subagent 繼承 MCP 和網頁搜尋,不繼承 function 工具。需要查自家訂單的副作用,留在主 Agent。

門還是 Schema。寬 object 在長任務裡更危險:compact 之後模型更容易填錯單號。MCP 那一行只是另一張工具表,邊界見 MCP Tool Schema vs Function Calling。

Compact 壓的是視窗,不是你的帳簿

官方寫明:接近上下文上限時,Agents API 自動 compact 早先內容,讓工作跨多個視窗。你不必自己實作壓縮。這和 Responses 上的 context_management / /responses/compact 不是同一層。

  • Agents API:session 自己壓。你拿回 items,核對單號還在不在。
  • Responses:要自己開 compact,或呼叫 compact 端點。加密的 compaction item 當下一窗的輸入,不要手剪。
  • 兩邊都成立:壓縮丟細節。金額、確認、放行仍由宿主寫。

把 order_id 只活在對話裡,等於把帳簿交給摘要。Schema 怎麼當門,見 AI 為什麼需要 JSON Schema。

Subagent:並行,但上下文分開

打開 multi_agent.enabled 後,harness 自帶建立、傳話、等待、打斷。你不用自己宣告這些工具。items 裡會出現協調動作:

{
  "data": [
    {
      "type": "function_call",
      "turn_id": "turn_4",
      "call_id": "call_3c91",
      "name": "lookup_order",
      "arguments": "{\"order_id\":\"A-1042\"}"
    },
    {
      "type": "create_subagent_call",
      "agent_id": "agent_main",
      "task": "Compare A-1042 packing notes with the invoice."
    }
  ]
}
  • 獨立上下文:每個 subagent 自己一份。主 Agent 用 create / message / wait / interrupt 收口。
  • 並行要封頂:預設最多 6 個(不含協調者)。生產裡寫死更小的數。
  • 完成項 ≠ 子任務結束:create_subagent_call 完成,只說明派出去了。看 agent.session.subagent.created 和該 subagent 自己的 items。

handoff 該是可審計的 JSON,不是一句「請交給帳單組」。怎麼收成 Schema,見 JSON Schema 與 AI Agent 工作流。

你持有的狀態,不要指望 compact 替你記

OpenAI 替你存 session 設定、對話和已儲存工作。你至少還要一份業務狀態,斷線也能重建:

{
  "session_id": "sess_8f21",
  "order_id": "A-1042",
  "last_turn_id": "turn_4",
  "last_call_id": "call_3c91",
  "awaiting": "tool_result",
  "compact_checkpoint": "after_turn_3"
}

這份物件跟 Session、SDK session、Responses conversation、sandbox 都不是同一個資源。刪一份不會自動清另一份。資料駐留目前只在美國;自建沙箱也不等於 Zero Data Retention。

五步把跨視窗任務跑完

不要從示範影片倒推。按物件走:

  1. 建立一次 session,把 session_id 寫進你的會話。工具表和 Schema 一次收緊。
  2. 訂事件再發後續輸入。先訂流,再打 agent.session.input.message,否則早期事件會丟。
  3. 停在 required_actions 就回寫。用同一對 turn_id / call_id,不要另開 session 重試。
  4. 斷線拉 items。流不重放。按 order=asc 列出,對上單號和等待中的呼叫。
  5. compact 之後再核一次帳簿。摘要裡沒有 A-1042,下一跳就會填錯。

五步都落在物件上,長任務才是可恢復的。少一步,你就是在賭視窗還沒滿。

複盤長任務,先攤開兩段 JSON

出問題的多半不是模型變笨,是物件對不上。

  • 把 session items 和你本地狀態 格式化,確認 session_id / call_id 對得上。
  • 用 JSONPath 提取 抽出 order_id、turn_id、subagent 的 agent_id。
  • compact 前後的 items JSON Diff。單號漂了,先看壓縮,再怪模型。
  • 主 Agent 的 items 和某個 subagent 的 items 分開列。混在一頁裡,協調動作會對錯人。
  • 訂單、session、內網 MCP 不要貼進公共格式化站。JSONSort 在瀏覽器本地跑。

兩段軌跡都能重建,你才知道任務是續上的,不是重新猜的。

常見誤區

  • 以為空閒就是成功。看 turn.completed / failed / cancelled,再看工具結果。
  • 斷線後等流重放。官方寫明流不重放。拉 session 和 items。
  • 把單號只活在對話裡。compact 之後,摘要可能丟掉 A-1042。
  • 開 multi_agent 卻不設並行上限。預設 6 個也會扇出 token 和副作用。
  • 讓 subagent 去跑你的 function。子代理不支援 function 工具。查單留在主 Agent。
  • 把 Session 和 Conversation 當成同一份。刪一份不會自動清另一份。

FAQ

長時間執行是不是把 prompt 寫得更長?

不是。靠的是 durable session、自動 compact、以及你自己存的業務 JSON。更長的系統提示解決不了視窗上限。

Agents API 的 compact 和 Responses 的 compact 能混用嗎?

不要混。Agents session 自己壓上下文。Responses 用 context_management 或 /responses/compact。兩套資源,兩套下一窗輸入。

斷線後怎麼恢復?

用存下來的 session_id 取回 session 和 items。流不會重放錯過的事件。先對齊 last_turn_id 和未完成的 call_id,再決定回寫還是續訊息。

subagent 能不能呼叫我的 lookup_order?

不能。官方寫明 subagent 不支援 function 工具。它們繼承 MCP、網頁搜尋和環境裡的檔案 / 命令。查自家訂單,留在主 Agent。

自建沙箱是不是就能做 Zero Data Retention?

不能。官方寫明 Agents API 目前只支援美國資料駐留,也不支援 ZDR。選 self-hosted 沙箱不會讓它變成 ZDR。

結論

長時間執行不是更聰明的模型。它是一份可續的 Session、一次自動 compact、以及你自己握住的 JSON 狀態。工具參數、turn 事件、subagent items,都只是這條鏈上的物件。

先寫清你斷線後還能不能重建 A-1042。能,任務才算活著。模型會換;session_id 和 Schema 應該先穩定。

延伸阅读

更新紀錄: 首次發布