Files
ewoooc/migrations/025_create_mcp_calls_and_budgets.sql
OoO 4648673423 db(p1): ai_calls/mcp_calls/budgets schema + bge-m3 signature
migrations 024/025/026 — 統一 LLM 遙測 + 預算告警 + RAG 一致性護欄
- 024: ai_calls 表 + 5 索引 + 6 CHECK constraint(H1/H2/M3/L3)
- 025: mcp_calls + ai_call_budgets + 10 種子預算(含 ollama_secondary)
- 026: ai_insights.embedding_signature + pgcrypto + CONCURRENTLY index

A11 critic 三輪審查記錄完整保留:
- Phase 1 schema review: 2 BLOCKER + 4 HIGH + 6 MEDIUM 全處理
- Phase 1 final sign-off: 0 BLOCKER + 2 HIGH + 4 MEDIUM
- Phase 6 ADR review: 5 BLOCKER + 6 HIGH 全修

Operation Ollama-First v5.0 / Phase 0+1+6 護欄

Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
2026-05-03 23:04:42 +08:00

205 lines
9.5 KiB
SQL
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
-- =============================================================================
-- Migration 025: mcp_calls + ai_call_budgets
-- Operation Ollama-First v5.0 — Phase 1 (Phase 10 MCP / Phase 9 預算告警 預備)
-- 日期: 2026-05-03 台北
-- =============================================================================
-- 說明:
-- mcp_calls — Phase 10 引入 5 個 MCP server 後的遙測表Schema 先到位。
-- 與 ai_calls 分表,因 MCP 沒有 token 概念、計費邏輯不同。
-- ai_call_budgets — Phase 9 預算告警表;種子資料即立即可用。
--
-- 設計決策:
-- 1. mcp_calls.insight_id 不加 FK避免 cascadePhase 11 ai_insights 會頻繁 archive
-- 改用「軟連結」+ 應用層 join保留可被 NULL 化的彈性。
-- 2. ai_call_budgets.provider NULL = 全供應商總額UNIQUE constraint 用 (period, provider)
-- 若 NULL 行為不一致需保護,由應用層強制單例)
-- 註: PostgreSQL 預設 NULL != NULL所以同 period 多筆 provider=NULL 會通過 UNIQUE
-- 需應用層自律或改用部分索引(見下方)。
--
-- 回滾腳本:
-- DROP INDEX IF EXISTS idx_mcp_calls_called_at;
-- DROP INDEX IF EXISTS idx_mcp_calls_caller_called_at;
-- DROP INDEX IF EXISTS idx_mcp_calls_server_tool;
-- DROP INDEX IF EXISTS idx_mcp_calls_status_called_at;
-- DROP INDEX IF EXISTS uq_ai_call_budgets_period_null_provider;
-- DROP TABLE IF EXISTS mcp_calls;
-- DROP TABLE IF EXISTS ai_call_budgets;
-- =============================================================================
-- ─────────────────────────────────────────────────────────────────────────────
-- mcp_calls — MCP Server 呼叫遙測Phase 10 預備)
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS mcp_calls (
id BIGSERIAL PRIMARY KEY,
called_at TIMESTAMPTZ NOT NULL DEFAULT NOW(),
-- 與 ai_calls.caller 同一張白名單,便於跨表 trace
caller VARCHAR(64) NOT NULL,
-- omnisearch / firecrawl / postgres / playwright / filesystem / git
server VARCHAR(64) NOT NULL,
-- search / scrape / query / read_file / git_log / ...
tool VARCHAR(128) NOT NULL,
input_args JSONB,
output_size INTEGER, -- bytes異常巨大可警示
duration_ms INTEGER,
-- M1: NOT NULL 對齊 ai_calls
status VARCHAR(16) NOT NULL,
error TEXT,
cost_usd NUMERIC(10,6) NOT NULL DEFAULT 0,
cache_hit BOOLEAN NOT NULL DEFAULT FALSE,
-- M6: 跨 ai_calls/mcp_calls 串鏈用Phase 10 後 LLM→MCP→LLM 鏈不可斷)
request_id VARCHAR(64),
-- 軟連結:若 MCP 結果被 embed 寫入 ai_insights記錄 insight_id 但不加 FK
insight_id BIGINT,
-- ─────── critic-A11 修補:白名單 + PII/膨脹護欄 ───────
-- M1: status 白名單
CONSTRAINT chk_mcp_calls_status CHECK (
status IN ('ok','error','timeout','rate_limited','cache_only')
),
-- L3: duration 範圍
CONSTRAINT chk_mcp_calls_duration_range CHECK (
duration_ms IS NULL OR (duration_ms >= 0 AND duration_ms <= 600000)
),
-- H2: input_args / error 大小護欄postgres-mcp 可能含 SQL含 PII 風險)
CONSTRAINT chk_mcp_calls_args_size CHECK (
input_args IS NULL OR octet_length(input_args::text) <= 16384
),
CONSTRAINT chk_mcp_calls_error_size CHECK (
error IS NULL OR octet_length(error) <= 4096
)
);
CREATE INDEX IF NOT EXISTS idx_mcp_calls_called_at
ON mcp_calls (called_at DESC);
CREATE INDEX IF NOT EXISTS idx_mcp_calls_caller_called_at
ON mcp_calls (caller, called_at DESC);
CREATE INDEX IF NOT EXISTS idx_mcp_calls_server_tool
ON mcp_calls (server, tool, called_at DESC);
-- M5: 異常監控 partial 精確列舉
CREATE INDEX IF NOT EXISTS idx_mcp_calls_status_called_at
ON mcp_calls (status, called_at DESC)
WHERE status IN ('error','timeout','rate_limited');
-- M6: request_id 串鏈部分索引sparse 不全建)
CREATE INDEX IF NOT EXISTS idx_mcp_calls_request_id
ON mcp_calls (request_id)
WHERE request_id IS NOT NULL;
GRANT ALL PRIVILEGES ON mcp_calls TO momo;
GRANT USAGE, SELECT ON SEQUENCE mcp_calls_id_seq TO momo;
-- ─────────────────────────────────────────────────────────────────────────────
-- ai_call_budgets — 預算與告警閾值Phase 9 預算守門)
-- ─────────────────────────────────────────────────────────────────────────────
CREATE TABLE IF NOT EXISTS ai_call_budgets (
id SERIAL PRIMARY KEY,
period VARCHAR(16) NOT NULL, -- daily / weekly / monthly
provider VARCHAR(32), -- NULL = 全供應商總額
budget_usd NUMERIC(10,2) NOT NULL,
alert_pct INTEGER NOT NULL DEFAULT 80, -- 達此百分比觸發 Telegram 告警
updated_at TIMESTAMPTZ DEFAULT NOW(),
CONSTRAINT chk_ai_budget_period
CHECK (period IN ('daily', 'weekly', 'monthly')),
CONSTRAINT chk_ai_budget_alert_pct
CHECK (alert_pct BETWEEN 1 AND 100),
CONSTRAINT chk_ai_budget_amount
CHECK (budget_usd > 0)
);
-- 部分唯一索引:分別處理 provider IS NULL 與 NOT NULL避免 NULL != NULL 漏洞
CREATE UNIQUE INDEX IF NOT EXISTS uq_ai_call_budgets_period_provider
ON ai_call_budgets (period, provider)
WHERE provider IS NOT NULL;
CREATE UNIQUE INDEX IF NOT EXISTS uq_ai_call_budgets_period_null_provider
ON ai_call_budgets (period)
WHERE provider IS NULL;
GRANT ALL PRIVILEGES ON ai_call_budgets TO momo;
GRANT USAGE, SELECT ON SEQUENCE ai_call_budgets_id_seq TO momo;
-- ─────────────────────────────────────────────────────────────────────────────
-- 種子資料(戰役 v5.0 規格 + critic-A11 H3 補 nim/nim_via_elephant/ollama
-- M2: ON CONFLICT 配 partial unique index 會炸;改用 WHERE NOT EXISTS 確保冪等
-- ─────────────────────────────────────────────────────────────────────────────
-- 全供應商總額period, provider=NULL
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'daily', NULL, 1.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='daily' AND provider IS NULL
);
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'weekly', NULL, 5.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='weekly' AND provider IS NULL
);
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', NULL, 20.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider IS NULL
);
-- 個別供應商(含 H3 修補:補 nim / nim_via_elephant / ollama 雙線)
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'claude', 10.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='claude'
);
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'gemini', 8.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='gemini'
);
-- H3: NIM 兩條獨立計費鏈NemoTron 配額 + ElephantAlpha 49B各設預算
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'nim', 5.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='nim'
);
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'nim_via_elephant', 5.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='nim_via_elephant'
);
-- H3: OpenRouterPPT deepseek-v3.2
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'openrouter', 3.00, 80
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='openrouter'
);
-- Ollama 雙線(免費,但設極低預算 + alert=100% 統一告警邏輯,異常激增可警示)
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'gcp_ollama', 0.01, 100
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='gcp_ollama'
);
INSERT INTO ai_call_budgets (period, provider, budget_usd, alert_pct)
SELECT 'monthly', 'ollama_111', 0.01, 100
WHERE NOT EXISTS (
SELECT 1 FROM ai_call_budgets WHERE period='monthly' AND provider='ollama_111'
);
DO $$
BEGIN
RAISE NOTICE 'Migration 025 done: mcp_calls + ai_call_budgets + 10 seed budgets (Operation Ollama-First v5.0 P1, critic-A11 fixes B2/H1/H2/H3/M1/M2/M5/M6/L3 applied)';
END $$;