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>
6.4 KiB
Phase 2 部署驗證劇本(ADR-027 真正落地)
Date: 2026-05-03 Phase: Operation Ollama-First v5.0 — Phase 2(A6 debugger) 修補項: B1 / B2 / B3 / B4 / N2 / N3 修改檔:
config.py/services/ollama_service.py/services/aider_heal_executor.py/services/code_review_pipeline_service.py新檔:tests/test_ollama_resolve.py(13 tests,本機已通過)
一、部署前 dry-run(本機)
1.1 語法檢查
cd "/Users/ooo/Library/Mobile Documents/com~apple~CloudDocs/momo-pro-system"
python3 -m py_compile config.py services/ollama_service.py \
services/aider_heal_executor.py services/code_review_pipeline_service.py \
tests/test_ollama_resolve.py && echo "PYCOMPILE_OK"
期望:PYCOMPILE_OK(已驗證)
1.2 Unit test
MOMO_ALLOW_INSECURE_CONFIG_FOR_TESTS=true /opt/anaconda3/bin/python3 -m pytest \
tests/test_ollama_resolve.py \
tests/test_phase3f_cleanup_contracts.py \
tests/test_app_startup_contracts.py \
tests/test_ai_call_logger.py \
tests/test_code_review_pipeline_security.py \
tests/test_auto_heal_safety.py -v
期望:56 passed(13 新 + 43 既有)。已驗證。
1.3 import 一致性
MOMO_ALLOW_INSECURE_CONFIG_FOR_TESTS=true /opt/anaconda3/bin/python3 -c "
from config import get_ollama_host, get_hermes_url, get_embedding_host
from services.ollama_service import resolve_ollama_host, mark_unhealthy
print('get_ollama_host =', get_ollama_host())
print('get_hermes_url =', get_hermes_url())
print('get_embedding_host =', get_embedding_host())
print('resolve_ollama_host=', resolve_ollama_host())
"
期望(網路通時):四行都印 http://34.21.145.224:11434(GCP 可達)或 http://192.168.0.111:11434(GCP 不可達)。
不可出現 https://ollama.wooo.work/ollama(舊寫死 URL)。
二、部署後驗證(SSH 188)
2.1 容器健康
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
docker ps --format '{{.Names}} | {{.Status}}' | grep momo-; \
docker exec momo-pro python3 -c 'from config import get_ollama_host; print(get_ollama_host())' 2>&1\""
期望:
momo-pro | Up(重啟後新容器)- 列印的 host 不是
https://ollama.wooo.work/ollama
2.2 OllamaHost 解析 log(B3 HTTP probe 驗證)
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
docker logs momo-pro --since 10m 2>&1 | grep -E 'OllamaHost' | tail -20\""
期望(GCP 可達):
[OllamaHost] GCP 主機可用,使用 Primary: http://34.21.145.224:11434
期望(GCP 掛時):
[OllamaHost] GCP 主機無法連線,自動切換 Fallback: http://192.168.0.111:11434
罕見(process 卡死,TCP 通但 HTTP 掛):
[OllamaHost] GCP HTTP 探測失敗但 TCP 仍通,疑似 process 卡死:http://34.21.145.224:11434
[OllamaHost] GCP 主機無法連線,自動切換 Fallback: http://192.168.0.111:11434
第三種日誌是 Phase 2 修補後才會看見的新觀測能力,舊版純 TCP 探測不會印。
2.3 mark_unhealthy 觸發(B4 驗證)
當 LLM generate 真的失敗時,會看見:
[OllamaHost] 主機標記為 unhealthy(30s 跳過):http://34.21.145.224:11434
立刻在下一次任何 ollama 呼叫的 log 看:
[OllamaHost] Primary http://34.21.145.224:11434 仍在 unhealthy TTL 內,跳過直接 fallback: http://192.168.0.111:11434
2.4 AiderHeal OLLAMA_API_BASE 動態化(N2 驗證)
下次 AiderHeal 觸發時 grep:
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
docker logs momo-pro --since 30m 2>&1 | grep 'aider_ollama_api_base' | tail -5\""
期望:
event=aider_ollama_api_base host=http://34.21.145.224:11434
(GCP 可達時)或 host=http://192.168.0.111:11434(fallback)。
絕不可 仍顯示 http://192.168.0.111:11434 當 GCP 是可達的。
2.5 Code Review provider tag(N3 驗證)
下次 Code Review pipeline 觸發後:
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
docker exec momo-postgres psql -U momo -d momo_analytics -c \
\\\"SELECT caller, provider, meta->>'host' AS host \
FROM ai_calls \
WHERE caller = 'code_review_hermes' \
ORDER BY created_at DESC LIMIT 5;\\\"\""
期望(GCP 通時):
caller | provider | host
code_review_hermes | gcp_ollama | http://34.21.145.224:11434
絕不可仍標 ollama_111 當 host 是 GCP。
三、模擬故障驗證(選做)
3.1 模擬 GCP 不可達 → 5s 內 fallback
在 188 上臨時封鎖 GCP IP:
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
sudo iptables -A OUTPUT -d 34.21.145.224 -j DROP\""
立即觸發 sales copy(or 任何 LLM 入口),看 log:
- 第一次呼叫應 timeout(2s 內 _is_reachable 失敗)→ 切 fallback
- 之後 30s 內所有呼叫直接走 fallback
- 30s 後 cache TTL 過期,會重新探測(仍封鎖則繼續 fallback;解封後恢復 GCP)
恢復:
ssh wooo@192.168.0.110 "ssh ollama@192.168.0.188 \"\
sudo iptables -D OUTPUT -d 34.21.145.224 -j DROP\""
此項屬統帥權限,debugger 不執行。
四、回滾 SOP
若部署後出問題,最快回滾:
git revert <this-commit-sha>
git push origin main
# 等 Gitea CD 自動部署
也可以單獨回退 ollama_service.py:
git checkout HEAD~1 -- services/ollama_service.py config.py
(其他三檔變更可獨立保留)
五、commit 草稿
[V-New] ADR-027 Phase 2:Ollama 主機解析全鏈 lazy + HTTP probe + unhealthy 標記
修補 6 項讓 ADR-027「GCP 優先」真正 100% 落地:
B1 — config.OLLAMA_HOST 改 lazy resolve(移除寫死 ollama.wooo.work URL)
B2 — config.EMBEDDING_HOST / HERMES_URL 改 lazy(避免 import-time freeze)
B3 — _is_reachable 改 HTTP probe (/api/version, 2s timeout),TCP 改作觀測點
B4 — 新增 mark_unhealthy(),generate / embedding 失敗時標 30s,cache 失效
N2 — aider_heal_executor.OLLAMA_API_BASE 改 lazy resolve(每次 execute 重評估)
N3 — code_review_pipeline_service Hermes scan 改 get_hermes_url() 取代 freeze
新增:tests/test_ollama_resolve.py(13 tests)
變更:config.py / services/ollama_service.py /
services/aider_heal_executor.py / services/code_review_pipeline_service.py
驗證:56 tests 全綠(13 新 + 43 既有 regression),py_compile 全綠。
驗證劇本:docs/phase2_deploy_verify_20260503.md(給統帥 SSH 188 跑)。