你讓兩個 AI Agent 各自開一個 Git Worktree 改程式,畫面上像是兩張互不干擾的工作桌。但它們的資料庫連線都指向同一個測試庫:Agent A 改庫存,Agent B 的測試立刻讀到 A 的半成品。更麻煩的是,A 已經分 50 次呼叫資料庫工具、每次都提交成功;現在想反悔,一句 ROLLBACK 還來得及嗎?這正是 AI Agent 資料庫回滾 要解決的問題。
這篇寫給第一次讓 Agent 碰資料庫的讀者。你會用兩個 Worktree、兩份 PostgreSQL 測試資料庫、各自的低權限角色,走完「隔離、留快照、記錄 50 次寫入、整批丟棄、人工審核」五步。文中的數量與帳號全是假資料;即使你還不熟 SQL,也能先看懂每道閘門要驗證什麼。
先說結論:AI Agent 資料庫回滾靠「一桌一庫一快照」
一句話記住:安全的平行 Agent=各自程式碼+各自資料庫+事前快照+可審查的變更紀錄。Worktree 是分開的檔案夾,資料庫則像放在旁邊的帳本;若兩張桌子仍共用同一本帳,分開檔案夾也擋不住資料互相覆蓋。Git 官方的 Worktree 文件說的是多個工作目錄與 Git 中繼資料,並不替外部資料庫建立副本。
- 單次交易出錯:還沒提交時用
ROLLBACK;已提交的前 49 次不會跟著倒帶。 - 整個實驗不要了:停掉 Agent,丟棄它專用的測試庫,再從快照建立一份。
- 結果值得留下:只把經過檢查的程式碼、遷移檔與必要資料變更帶到正式部署流程;不把整個測試庫覆蓋正式庫。
為何 AI Agent 資料庫回滾不能只靠 Git 或 SQL ROLLBACK?
假設 A 和 B 都從庫存 qty=10 開始。A 做五十輪測試,每輪把數量加一;B 在自己的分支測另一套定價。若兩邊用同一個資料庫,B 的結果混入 A 的修改,失敗時也難判斷該回到誰的狀態。這和「程式碼 merge 有沒有衝突」是兩回事;想先看 Worktree 的連接埠隔離,可讀 Portless Worktree 教學,想理解 Agent 開工前的意圖衝突,可讀 Foremerge 教學。
依 PostgreSQL 交易文件,BEGIN 到 COMMIT 是一筆交易;ROLLBACK 取消的是尚未提交的這筆交易。若 Agent 每次工具呼叫都各自提交,50 次就是 50 筆已完成交易,最後再送一個 ROLLBACK 無法清除前面的結果。快照在這裡像「開工前影印帳本」:出錯後丟掉 A 的整本測試帳,從那張影本重開。

五步演練:兩個 Worktree,各用一份 PostgreSQL 測試庫
下列命令以已安裝、已啟動的 PostgreSQL 16 與 psql 為前提。資料庫管理員負責建庫與授權,Agent 只能用自己的角色連線。先在空白、可丟棄的本機測試叢集練習,不要把正式資料或正式憑證接進這段流程。PostgreSQL 的 CREATE DATABASE 文件也提醒:從模板複製時,模板庫不能有其他連線,資料庫層級的授權不會跟著複製。
① 開兩張桌:工作目錄和資料庫一起分開
在 Git 專案執行 git worktree add ../agent-a -b agent/a 與 git worktree add ../agent-b -b agent/b。管理員另建一份只放假資料的 seed_db,例如 inventory(id, qty) 裡有 (1,10),以及 change_log(run_id,step,recorded_at)。接著在管理庫 postgres 執行 CREATE DATABASE agent_a TEMPLATE seed_db;、CREATE DATABASE agent_b TEMPLATE seed_db;。每個 Worktree 的本機、未追蹤環境設定指向對應的資料庫;開工前先顯示 SELECT current_database(), current_user;,不要只相信檔名。模板與副本建立期間應停止應用連線。
② 限定鑰匙:A 的帳號只進 A 的庫
管理員建立 agent_a、agent_b 兩個 LOGIN NOCREATEDB NOCREATEROLE 角色,對兩個測試庫各執行 REVOKE CONNECT ON DATABASE agent_a FROM PUBLIC; 及對應的 B 版,再分別 GRANT CONNECT ON DATABASE agent_a TO agent_a;、GRANT CONNECT ON DATABASE agent_b TO agent_b;。在 A 庫內只給 A SELECT、UPDATE(qty) 與紀錄表的 INSERT;B 庫內只給 B 同樣的有限權限。PostgreSQL 權限文件明列資料庫 CONNECT 與資料表權限是不同層;兩層都要核對。
驗收時,psql -X -v ON_ERROR_STOP=1 -U agent_a -d agent_b -c 'SELECT 1' 應被拒絕,而 A 登入 agent_a 應成功。這裡的本機演練使用私人 Unix socket 的測試驗證方式;真正接到共享主機時,仍須配合密碼驗證、連線設定與秘密管理。REVOKE CONNECT 限的是角色的資料庫連線,不能代替網路與身分驗證設定。
想把權限一次抄對,可先在空白測試叢集以管理員身分進入 psql -X -v ON_ERROR_STOP=1 -d postgres,逐行執行下列最小範例。\password 會互動式設定角色密碼,\connect 會切換資料庫;表由管理員擁有,Agent 只取得指定欄位和紀錄表權限。資料庫名稱或角色已存在時,先換一個全新測試叢集。
CREATE ROLE agent_a LOGIN NOCREATEDB NOCREATEROLE;
CREATE ROLE agent_b LOGIN NOCREATEDB NOCREATEROLE;
\password agent_a
\password agent_b
CREATE DATABASE seed_db;
REVOKE CONNECT ON DATABASE seed_db FROM PUBLIC;
\connect seed_db
REVOKE CREATE ON SCHEMA public FROM PUBLIC;
CREATE TABLE public.inventory (id integer PRIMARY KEY, qty integer NOT NULL);
INSERT INTO public.inventory VALUES (1,10);
CREATE TABLE public.change_log (run_id text, step integer, recorded_at timestamptz DEFAULT now(), PRIMARY KEY(run_id,step));
\connect postgres
CREATE DATABASE agent_a TEMPLATE seed_db;
CREATE DATABASE agent_b TEMPLATE seed_db;
REVOKE CONNECT ON DATABASE agent_a FROM PUBLIC;
REVOKE CONNECT ON DATABASE agent_b FROM PUBLIC;
GRANT CONNECT ON DATABASE agent_a TO agent_a;
GRANT CONNECT ON DATABASE agent_b TO agent_b;
\connect agent_a
GRANT SELECT ON public.inventory, public.change_log TO agent_a;
GRANT UPDATE(qty) ON public.inventory TO agent_a;
GRANT INSERT ON public.change_log TO agent_a;
\connect agent_b
GRANT SELECT ON public.inventory, public.change_log TO agent_b;
GRANT UPDATE(qty) ON public.inventory TO agent_b;
GRANT INSERT ON public.change_log TO agent_b;
③ 按下存檔:先留 A 的整庫快照,再記 run_id
兩位 Agent 都停在開工前狀態時,管理員從 postgres 執行 CREATE DATABASE agent_a_before TEMPLATE agent_a;,並撤掉快照庫對 PUBLIC 的連線權。agent_a_before 是 A 這輪的還原點;B 的庫保持獨立。快照庫的權限、名稱與保留期限要由管理員管理,Agent 不拿 CREATEDB 或刪庫權。若模板仍有連線,建快照會失敗;先停相關應用並重試,不要在有寫入時假裝複製完成。
替這次任務定一個 run_id=run_demo_001。每次寫入用同一筆 SQL 交易包住「改庫存+新增一筆 change_log」,例如 BEGIN; UPDATE public.inventory SET qty=qty+1 WHERE id=1; INSERT INTO public.change_log(run_id,step) VALUES ('run_demo_001',1); COMMIT;。第 2 至 50 輪只換 step,每輪從新的 psql 連線送出。這張紀錄表是逐輪對帳線索;若允許 Agent 繞過封裝直接改表,它就不是不可竄改的稽核證據。
要完整重演 50 個獨立呼叫,先建快照,再於終端機執行這個迴圈。ON_ERROR_STOP 讓 SQL 出錯時 psql 回傳失敗,迴圈就停下。若伺服器要求密碼,可依 PostgreSQL 密碼檔文件使用只允許本人讀寫的 ~/.pgpass;不要把密碼放進指令或 Git。
for i in $(seq 1 50); do
psql -X -v ON_ERROR_STOP=1 -U agent_a -d agent_a -c "BEGIN; UPDATE public.inventory SET qty=qty+1 WHERE id=1; INSERT INTO public.change_log(run_id,step) VALUES ('run_demo_001',$i); COMMIT;" >/dev/null || break
done
④ 整批撤銷:丟掉 A 的實驗庫,從快照重建
先用 SELECT qty FROM public.inventory WHERE id=1; 與 SELECT count(*) FROM public.change_log WHERE run_id='run_demo_001'; 記下 A、B 的讀值。本機 PostgreSQL 16.15 演練將 50 次寫入分成 50 個獨立連線提交:A 讀到 qty=60、紀錄 50 筆;B 仍是 qty=10、紀錄 0 筆。這只是單列假資料的功能驗證,沒有測效能或大量資料恢復時間。
確定 A 的工作程序與連線已停,管理員連到 postgres,依序執行 DROP DATABASE agent_a;、CREATE DATABASE agent_a TEMPLATE agent_a_before;,再重新執行 REVOKE CONNECT ON DATABASE agent_a FROM PUBLIC; 與 GRANT CONNECT ON DATABASE agent_a TO agent_a;。重建後,A 回到 10/0,B 保持 10/0。依 PostgreSQL 刪庫文件,刪庫不可復原、不能在目標庫連線或交易區塊內執行;所以必須先核對目標名稱與快照,再由管理員操作。整庫還原也會抹掉快照以後 A 庫的其他變更,這正是要讓 A 庫只有這輪實驗的原因。
⑤ 只晉升審過的變更:正式庫另走部署流程
若 A 的方案值得保留,先把程式碼差異、資料遷移 SQL、run_id 下的寫入清單與測試結果放進審核單。讓審核者回答:改了哪些列?新舊資料如何對應?測試是否在與正式結構一致的預備環境通過?失敗時要往前修補,還是能用已準備的復原程序?只有人工批准後,部署流程才使用另一組受控憑證在預備環境與正式環境套用必要變更。Agent 的測試庫連線字串不進正式部署環境。
這道門尤其重要:DROP DATABASE 是測試庫整批撤銷,不是正式資料的回滾指令。正式資料一旦同時有使用者寫入,直接覆蓋整庫會丟掉別人的合法變更;對跨付款、Email 或外部 API 的動作,還需逐項對帳與補償。若要理解後者,可接著看 Agentic Transaction 與補償;它處理的是跨工具副作用,本篇處理的是單個可丟棄測試資料庫。
三個常見坑:快照、紀錄和權限各有邊界
- 快照不是持續同步:它固定在建立那一刻;複製前的資料來源要先停寫,且不要將真客戶資料複製給 Agent。用合成或已批准的去識別資料建立種子庫。
- run_id 不是魔法橡皮擦:它能幫你找出同一輪寫入,卻不能自動逆轉已提交 SQL,更不能撤回寄出的信或外部請款。
- 資料庫角色不是唯一防線:若 Agent 仍拿得到管理員連線字串,表上的小權限毫無意義。執行環境、秘密管理、網路與人工批准要一起設計。
AI Agent 資料庫回滾常見問題
Q1:Worktree 已分開,還要分資料庫嗎?
要,看連線目標。Worktree 分的是工作目錄;若 A、B 的 current_database() 相同,資料狀態仍共用。
Q2:50 次修改後送 ROLLBACK 就好?
不行,若 50 次已各自提交。ROLLBACK 只作用於當前尚未提交的交易;整輪測試要靠快照重建或事先設計的補償程序。
Q3:能把 50 次操作包成一筆超長交易嗎?
技術上可以,通常先問是否真的需要。同一連線的單筆交易可在提交前回滾,但長時間持有交易會影響並行操作與維護。這篇用 50 個已提交連線,刻意演練不能靠單筆交易收尾的情況。
Q4:只想撤回 A 的部分列,還能整庫還原嗎?
先別整庫還原。整庫重建會捨棄快照以後的全部寫入;選擇性撤銷要有事前紀錄、明確的反向 SQL 與逐列驗證。
Q5:快照可以直接用正式資料嗎?
不要直接照搬。本教學用合成假資料;正式資料的複製須另處理授權、個資與存取邊界,不能因為是測試庫就交給任意 Agent。
Q6:run_id 寫進表裡,就能保證完整稽核嗎?
不能單靠這張表。它記錄遵守封裝流程的寫入;若 Agent 能跳過封裝或改紀錄,還要補上資料庫觸發器、權限限制與外部不可竄改日誌。
Q7:為何重建資料庫後要再 GRANT?
資料庫層級的權限不隨模板複製。重建後重新撤銷 PUBLIC 連線,再授予 A,最後用 A 與 B 的角色各做一次正反驗收。
Q8:哪一步可以接到正式環境?
只有第⑤步審核通過後的部署流程。它帶走經檢查的程式碼與必要變更,不帶走 Agent 的測試庫或測試憑證。
給新手的三個驗收讀值
current_database(), current_user:A、B 是否真的拿不同的庫與角色?qty與change_log:A 的 50 筆是否只留在 A,B 是否保持原值?- 重建後的
qty、紀錄筆數與登入測試:A 是否回到快照,權限是否仍只授給 A?
接著閱讀
左右滑動查看更多推薦
下一步:先做一個會被拒絕的連線測試
拿一列假資料,先確認 Agent A 連不上 B 的庫,再讓 A 做一次寫入、用快照重建。當你親眼看到「A 變、B 不變、A 又回到原點」,就抓住了本文的核心:一桌一庫一快照,正式環境另開審核門。如果你準備把這套流程接進自己的 Agent,可以從 AlphaLab 課程繼續學執行層與驗收設計。






