適合代理的資料庫結構描述,會把工作、操作及稽核事件與業務資料列分開記錄,以限制條件執行租戶範圍與冪等性、協調並行 worker,並保留足夠歷史,讓系統在逾時後復原,而不會把模型對話記錄當成證據。
資料庫不需要理解提示詞,它需要讓不安全的狀態轉換與重複副作用難以發生。
從工作與操作開始
工作是使用者層級的工作流程,操作則是該工作嘗試的一項外部或內部副作用。兩者要分開,因為一項工作可能多次重試同一操作,也可能在操作間等待。
CREATE TABLE agent_tasks (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
workflow_version text NOT NULL,
state text NOT NULL,
state_version bigint NOT NULL DEFAULT 0,
current_step text,
wake_at timestamptz,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (tenant_id, id)
);
CREATE TABLE agent_operations (
id uuid PRIMARY KEY,
tenant_id uuid NOT NULL,
task_id uuid NOT NULL,
idempotency_key text NOT NULL,
action text NOT NULL,
input_hash text NOT NULL,
state text NOT NULL,
provider_reference text,
result jsonb,
created_at timestamptz NOT NULL DEFAULT now(),
updated_at timestamptz NOT NULL DEFAULT now(),
UNIQUE (tenant_id, idempotency_key),
FOREIGN KEY (tenant_id, task_id) REFERENCES agent_tasks (tenant_id, id)
);
複合外鍵會防止操作指向另一租戶的工作。唯一冪等鍵則防止兩個 worker 重複建立同一個邏輯操作。回傳既有結果前,先比較保存的輸入雜湊,避免同一個鍵在不知情的情況下授權不同參數。
使用限制條件執行模型絕不能協商的規則
在資料庫中執行唯一性、外鍵、不可為 null 的所有權、合法 enum 值及版本檢查。應用程式驗證仍能改善錯誤訊息,但最終限制可以保護每一個寫入者,包括遷移指令碼或新生成的工具。
對租戶隔離,確保每張租戶擁有的子資料表都能透過租戶範圍鍵連接。多租戶代理基礎設施指南涵蓋資料列、結構描述及資料庫隔離的選擇。
記錄目前狀態及僅附加事件
目前狀態資料表讓讀取與排程保持簡單;僅附加事件資料表則解釋狀態如何改變。
在同一個 transaction 中寫入狀態更新與事件:
UPDATE agent_tasks
SET state = $1, state_version = state_version + 1, updated_at = now()
WHERE tenant_id = $2 AND id = $3 AND state_version = $4;
INSERT INTO agent_events
(tenant_id, task_id, event_type, actor_id, policy_version, payload)
VALUES ($1, $2, $3, $4, $5, $6);
提交前要求更新影響一筆資料列。更新零筆代表另一個 worker 已推進工作,過期的 worker 必須重新載入,不能覆寫。
不要在事件承載資料中放入密鑰、完整提示詞或無限大小的供應商回應。儲存遮蔽後的事實或參照,並使用獨立的保留政策。
使用 outbox 可靠地傳送下游事件
當資料庫更新必須發布事件或將工作排入佇列時,在同一個 transaction 寫入 outbox 資料列。Relay 傳送等待中的資料列,再標記為已傳送。Relay 若在遺失確認後重複發布,消費者仍必須具備冪等性。
這能消除資料庫已提交,但程序在通知佇列前當機的缺口,也讓操作有可查詢的傳送狀態,而不必依賴日誌文字。
使用 lease 或版本檢查協調 worker
短暫的狀態轉換使用樂觀版本;worker 需要暫時擁有工作時則使用 lease。Lease 包含擁有者、取得時間及到期時間,工作期間持續續約,完成時則要求呼叫者是擁有者或持有較新的有效 lease。
模型思考或供應商回應期間,不要一直開著資料庫 transaction。先保留操作並提交、執行呼叫,再對應結果並更新。持久狀態指南說明這如何支援全新的 worker。
讓 JSON 保有彈性,但查詢事實要明確
JSONB 適合存放模型專屬中繼資料、供應商承載資料片段,以及依工作流程改變的步驟區域值。租戶所有權、狀態、操作身分、排程、保留規則,以及政策或索引使用的欄位則適合用資料行。
如果每次授權查詢或排程器都會使用某條 JSON 路徑,就將它提升成具型別的資料行。資料庫 introspection 對程式開發代理有用,但簡單的結構描述並不等於權限;應把工具限制在核准的陳述式或應用程式 API,而不是讓生成的 SQL 取得管理員連線。
連結追蹤,但不要把資料庫當成遙測儲存
在工作與操作中保存 trace 及 span 識別碼,詳細的模型與工具遙測則留在可觀察性系統。如此一來,調查人員可以從錯誤的業務資料列找到產生它的執行,又不會在 Postgres 重複儲存大型追蹤。
OpenTelemetry 的 GenAI 語意慣例定義通用的代理與工具屬性。這套結構描述仍在演進,因此要固定 instrumentation 所輸出的版本。
在重試與並行下證明結構描述
讓兩個 worker 同時處理相同工作;以相同及衝突參數重用一個冪等鍵;在業務 transaction 提交後、佇列確認前讓程序當機;在測試環境刪除一個租戶,並確認其工作、操作、成果及憑證會遵循預定政策。
下方提示詞會將這些保證對應到目前的結構描述。它要求提供查詢路徑與限制條件,因為「我們在代理中處理」不是資料庫不變條件。