supabase-postgres-best-practices (supabase/agent-skills)
用 Supabase 最佳實務指導 Postgres schema 設計、遷移、RLS、索引與查詢效能。
Supabase Postgres 最佳實務:在結構描述、審查與索引任務上,相較於單純使用 Codex 沒有增益(4.48 對 4.54)
Codex CLI · gpt-6-astra
24.4 / 30
相對未使用技能的提升:-0.06 · 視情況推薦 · 18 次執行
視情況推薦:提升幅度 -0.06 低於 +1.0。推薦條件為總分至少 22/30、產出品質至少 3.5、提升幅度至少 +1.0。
SOTA
透過使用 Codex 測試。
涵蓋 Codex, Claude Code
Codex CLI 0.166(SOTA 等級):18 次評分執行。依我們的等級規則,在這裡通過也涵蓋 Claude Code;但這不代表第二等級的結果。
分數
每個維度為 0 到 5 分,六個維度相加即為滿分 30 的總分。每個維度都是 5 分最好。成本 5 分代表便宜又快速;安裝 5 分代表容易安裝。
- 觸發可靠性
- 5.0
- 相對未使用技能的提升
- 1.9
- 產出品質
- 4.5
- Token 與時間成本
- 4.0
- 安裝與可攜性
- 4.0
- 安全性
- 5.0
安全審查
安全靜態掃描:0 CRITICAL / 4 HIGH / 0 MEDIUM。人工審查:可在無網路沙箱中安全執行(已說明掃描器命中的項目)。掃描結果不能證明技能一定安全。
安裝
npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices
固定 Commit: c9be0e931b7930f7d02126d04774d904c381e7d7
測試日期: 2026-11-02
我們測了什麼
- 含 RLS 的多租戶結構描述 — 為一個 Supabase(Postgres)多租戶專案追蹤系統撰寫 schema.sql:organizations、memberships(Supabase 身分驗證使用者以某個角色隸屬於某個組織)、projects、tasks(指派對象、狀態、到期日)以及任務留言。使用者只能看到與變更…
- 審查結構描述、查詢與 worker — 審查 schema.sql、queries.sql 和 worker.py(一個 Supabase/Postgres 客服單應用程式)。列出問題,最嚴重的排在最前面,每項都附上修正方式。不要修改任何檔案。在最終回覆中寫出清單。
- 為三個慢查詢建立索引 — slow_queries.md 中的三個查詢在 schema.sql 的資料表(120M 資料列)上很慢。撰寫 migration.sql,只包含你會做的索引變更(不要有其他內容),以及 notes.md(最多 120 字),說明哪個索引服務哪個查詢,以及任何注意事項。(沒有資料庫…
優點
- 技能即使未被點名也會被採用:6/6 次實驗組執行都讀取了技能檔案。
缺點
- 相對未使用技能沒有可測量的提升(-0.06,落在雜訊範圍內)。
- 一句話提示得分 4.75,使用技能則為 4.48(-0.27)。
- 每個任務的每個組別 n=2;約 1 分以內的差異屬於雜訊。
評測
在 Mac 測試機上執行 Codex CLI 0.166(gpt-6-astra,低推理),每次執行都使用全新的暫存專案與暫存的 HOME/TMPDIR/快取,技能分為已安裝(實驗組)、未安裝(對照組),以及未安裝但把技能核心要求濃縮成一句話附加在提示詞後(一句話提示組),每個任務的每個組別執行 2 次,關閉網路,封鎖 /tmp 寫入。提示詞從未提到技能(除了下方註明的措辭)。三個任務:撰寫含 RLS 的多租戶 Supabase 結構描述(檢查項目:每張表都有 RLS、auth.uid() 包在 select 中、每個外鍵都有索引、使用 timestamptz、identity 或 uuid 鍵、限制條件、名稱使用小寫),審查有缺陷的結構描述/查詢/worker 中的 11 個植入問題,以及為一個 120M 資料列的資料表上的三個慢查詢撰寫索引遷移與說明(複合索引、部分索引、CONCURRENTLY、最多 4 個索引)。
三組對照
| 組別 | 平均分數(0-5) | 每次執行 Token 用量 |
|---|---|---|
| 未使用技能 | 4.54 | 77k |
| 一句話提示 | 4.75 | 80k |
| 使用技能 | 4.48 | 93k |
以 Codex CLI 執行 18 次,採規則式檢查評分,不經 LLM 評審。總分 24.4 / 30,評級為 視情況推薦。
評定:視情況而定(24.4 / 30)。 來源:supabase/agent-skills,skills/supabase-postgres-best-practices,固定於提交 c9be0e9。安裝:npx skills@latest add supabase/agent-skills --skill supabase-postgres-best-practices。授權: MIT(儲存庫 LICENSE)。
這是什麼
supabase-postgres-best-practices 技能(來自 supabase/agent-skills):為 Supabase 專案提供 Postgres 效能與 schema 指引,約有 35 份參考檔案(查詢效能與索引、連線管理、安全性與 RLS、schema 設計、並行與鎖定、資料存取模式、監控),並由一份 440 字的 SKILL.md 為這些檔案建立索引。純 Markdown,沒有腳本。
測試方式
在 Mac 測試機上使用 Codex CLI 0.166(gpt-6-astra,低推理),每次執行都建立全新的臨時專案,並使用臨時的 HOME/TMPDIR/快取;分別為安裝技能(技能組)、未安裝(對照組),以及未安裝但把技能核心要求濃縮成一句話附加在提示詞後(一句話組);每項任務的每組執行 2 次;關閉網路,並封鎖對 /tmp 的寫入。提示詞完全不會提到技能名稱(以下註明的措辭除外)。
三項任務:撰寫含 RLS 的多租戶 Supabase schema(檢查項目:每張表都啟用 RLS、auth.uid() 以 select 包覆、每個外鍵都有索引、使用 timestamptz、identity 或 uuid 鍵、限制條件、小寫名稱);審查有缺陷的 schema/查詢/worker 中的 11 個預埋問題;以及為 120M 資料列資料表上的三個慢查詢撰寫索引遷移與說明(複合索引、部分索引、CONCURRENTLY、最多 4 個索引)。
- 含 RLS 的多租戶 schema — 請為一個 Supabase(Postgres)多租戶專案追蹤系統撰寫 schema.sql:organizations、memberships(Supabase 驗證使用者以某個角色隸屬於某個組織)、projects、tasks(指派對象、狀態、到期日)以及任務上的 comments。使用者只能查看與變更…
- 審查 schema、查詢與 worker — 請審查 schema.sql、queries.sql 與 worker.py(一個 Supabase/Postgres 客服工單應用程式)。列出問題,最嚴重的排前面,每項都要附上修正方式。請勿變更任何檔案。在最終回答中寫出清單。
- 三個慢查詢的索引 — slow_queries.md 中的三個查詢在 schema.sql 的資料表(120M 資料列)上很慢。請撰寫 migration.sql,只包含你會做的索引變更(不要有其他內容),以及 notes.md(最多 120 字),說明哪個索引對應哪個查詢,以及任何注意事項。(沒有資料庫…
計分採程式化方式(隱藏測試/預埋問題召回/限制條件檢查/視情況搭配渲染與 OCR),每次執行 0–5 分。分數並非由任何 LLM 評審決定。
結果
提供給第三組的一句話版本:「套用 Postgres 最佳實務:為每個外鍵以及 WHERE/ORDER BY 中用到的每個資料行建立索引(複合索引將等值條件欄位放前面、罕見狀態使用部分索引、大資料表使用 CREATE INDEX CONCURRENTLY);在每張對外暴露的資料表上啟用 RLS,並將 auth.uid() 包在 (select auth.uid()) 中;使用 timestamptz、identity 或 uuid 鍵以及小寫 snake_case 名稱;以 keyset 而非 offset 分頁;保持交易簡短(內部不要呼叫外部服務)。」
| 任務 | 對照組分數 | 一句話版本 | 技能組 | 對照組 token 數 | 一句話版本 token 數 | 技能組 token 數 | 技能讀取 |
|---|---|---|---|---|---|---|---|
| t1-schema | 4.52 | 4.79 | 4.16 | 104k | 121k | 124k | 2/2 |
| t2-review | 4.09 | 4.46 | 4.28 | 64k | 56k | 70k | 2/2 |
| t3-indexes | 5.0 | 5.0 | 5.0 | 60k | 61k | 84k | 2/2 |
平均分數:無技能 4.54,技能組 4.48(差異 -0.06)。每次執行 token 數:77k 對 93k(1.21 倍);時間 78 秒對 73 秒。一句話組:4.75(技能相較一句話版本的增益:-0.27;一句話版本相較無技能的增益:+0.21);一句話版本每次執行 80k token,因此技能的 token 成本是一句話版本的 1.16 倍。
實際表現
- 沒有差異:對照組平均 4.54、一句話版本 4.75、技能組 4.48。技能相較對照組為 -0.06,每次執行 93k token,對照組 77k(1.2 倍)。
- 索引任務在每次執行中都被每一組解決(所有組的六項索引檢查都以 6/6 通過,包括 CONCURRENTLY 與維持 4 個索引):單純的 Codex 本來就懂這些基礎。
- 在 schema 任務中,每一組都為所有外鍵建立索引、在所有資料表上啟用 RLS,並使用 uuid/identity 鍵。將 auth.uid() 包在 (select auth.uid()) 中的做法,在每一組都只套用於部分原則:對照組與技能組的 5–6 次出現中有 3 次,某次一句話版本則是 5 次中 5 次。一句話組在這條規則上表現最好(2 分中拿 1.6 分,對照組與技能組為 1.1 分)。有一次技能組的執行使用了普通的 timestamp 資料行(3.86),是所有執行中的最低分。
- 在審查任務中,所有組都找到 11 個預埋問題中的 8–9 個(2 項檢查中得 1.5–1.7 分);技能並沒有找到更多。
- 索引任務曾重新測試一次:第一批因為規格錯誤(測試樣本清單為空)而沒有提供輸入檔案,因此全部 6 次執行都說檔案不存在。我們修正規格後重跑了那 6 次執行;先前的結果不予計入。
各面向分數
| 面向 | 分數 | 原因 |
|---|---|---|
| 1_output_quality | 4.48 | 技能組平均分數 4.48/5(程式化檢查,詳見各任務 表格)。 |
| 2_lift_vs_no_skill | 1.94 | 相較無技能的 4.54 低 -0.06 分(每項任務 n=2;約 1 分 以下的差異屬於雜訊)。 |
| 3_trigger | 5.0 | 技能組 6/6 次執行都讀取了技能檔案。 |
| 4_instructions_portability | 4 | 純 Markdown 規則檔案;遵循開放的技能結構。 |
| 5_efficiency | 4 | token 為 1.21 倍(每次執行 93k 對 77k),時間為 0.93 倍(73 秒對 78 秒)。 |
| 6_safety_hygiene | 5 | 僅 Markdown;掃描器的四個 HIGH 命中,是資料表名稱
|
本測試的限制
- 小型檔案中的教科書式問題;RLS 以外的 Supabase 特定重點(連線集區、Realtime、儲存)並未測試。
- 項目是以正規表達式比對,因此措辭不同的修正會被算成漏掉。
- 技能關於鎖定、監控與資料存取模式的規則並未實際演練。
- 每項任務的每組 n = 2:約 1 分以下的差異屬於雜訊。
- 規則式計分只能衡量檢查項目看得見的部分,無法評判品味,也無法判斷結果是否為最佳解。
安全性
詳見 security.md。掃描:0 CRITICAL / 4 HIGH / 0 MEDIUM。評定:可在無網路沙箱中安全執行。掃描結果乾淨並不代表技能一定安全。
總結
在 schema 設計、審查與索引任務中,單純的 Codex 本來就會套用這項技能列出的實務做法,而技能不但沒有提高分數,還多花約 20% 的 token。對於這些測試未涵蓋、較不常見的 Supabase 主題,它或許仍是有用的參考資料。
完整紀錄
執行日誌:supabase-postgres-best-practices (Codex CLI, network off, per-run temp HOME/TMPDIR, one row per graded run.) 欄位:任務、組-輪次、得分(0-5)、token、秒、通過的檢查、代理開啟的 skill 檔案。 t1-schema control-1: 得分 4.57, token 105331, 秒 124.5, 檢查 6/7, 未開啟 t1-schema control-2: 得分 4.46, token 104624, 秒 130.1, 檢查 6/7, 未開啟 t1-schema oneliner-1: 得分 5.0, token 111153, 秒 140.4, 檢查 7/7, 未開啟 t1-schema oneliner-2: 得分 4.57, token 132693, 秒 145.1, 檢查 6/7, 未開啟 t1-schema treatment-1: 得分 3.86, token 123861, 秒 108.3, 檢查 5/7, 開啟 supabase-postgres-best-practices t1-schema treatment-2: 得分 4.46, token 124377, 秒 129.6, 檢查 6/7, 開啟 supabase-postgres-best-practices t2-review control-1: 得分 4.27, token 64780, 秒 65.4, 檢查 2/2, 未開啟 t2-review control-2: 得分 3.91, token 64695, 秒 71.5, 檢查 2/2, 未開啟 t2-review oneliner-1: 得分 3.91, token 45381, 秒 57.3, 檢查 2/2, 未開啟
18 次執行、3 個產出檔案
範例產出
SOTA · 装了 skill:schema.sql(t1-schema,第 1 次,Codex)
-- Fresh Supabase database: run once as postgres (trusted BYPASSRLS owner). -- Members manage tenant content; admins also manage settings and memberships. -- service_role bypasses RLS: keep its credentials exclusively on the backend. begin; create schema if not exists private; revoke all on schema private from public, anon; grant usage on schema private to authenticated; -- Keep private out of the Data API's exposed schemas. create table public.organizations ( id uuid primary key default gen_random_uuid(), name text not null check (length(btrim(name)) between 1 and 200), created_at timestamptz not null default now(), updated_at timestamptz not null default now() ); create table public.memberships ( organization_id uuid not null references public.organizations(id) on delete cascade, user_id uuid not null references auth.users(id) on delete cascade, role text not null default 'member' check (role in ('admin', 'member')), created_at timestamptz not null default now(), primary key (organization_id, user_id) ); -- Reverse lookup for the caller's tenants, RLS, and auth user deletion. create index memberships_user_idx on public.memberships (user_id, organization_id) include (role); create table public.projects ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, name text not null check (length(btrim(name)) between 1 and 200), description text not null default '', created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id) ); create index projects_list_idx on public.projects (organization_id, created_at desc, id desc); create table public.tasks ( id uuid primary key default gen_random_uuid(), organization_id uuid not null references public.organizations(id) on delete cascade, project_id uuid not null, title text not null check (length(btrim(title)) between 1 and 500), description text not null default '', assignee_id uuid, status text not null default 'todo' check (status in ('todo', 'in_progress', 'done', 'cancelled')), due_date date, created_at timestamptz not null default now(), updated_at timestamptz not null default now(), unique (organization_id, id), foreign key (organization_id, project_id) references public.projects (organization_id, id) on delete cascade, -- Unassign tasks before deleting a membership/auth user. Deferred checking -- permits deleting an entire organization and all its children atomically. foreign key (organization_id, assignee_id) references public.memberships (organization_id, user_id) on delete no action deferrable initially deferred ); create index tasks_project_list_idx on public.tasks (organization_id, project_id, created_at desc, id desc); create index tasks_board_idx on public.tasks (organization_id, project_id, status, created_at desc, id desc); create index tasks_assignee_idx on public.tasks (organization_id, assignee_id, status, due_date, id) where assignee_id is not null; create index tasks_open_due_idx on public.tasks (organization_id, due_date, id) where status in ('todo', 'in_progress') [... excerpt: first 3200 of 11674 characters]