V10.418 skip 111 in embedding consistency checks
This commit is contained in:
@@ -342,6 +342,7 @@ RAG_DEFAULT_TOP_K=5
|
||||
RAG_EMBED_MODEL=bge-m3:latest
|
||||
RAG_EMBED_DIM=1024
|
||||
RAG_EMBED_NORMALIZE=true
|
||||
EMBED_CONSISTENCY_INCLUDE_111=false
|
||||
PPT_VISION_ENABLED=true
|
||||
PPT_VISION_MODEL=minicpm-v:latest
|
||||
PPT_VISION_TIMEOUT=120
|
||||
|
||||
@@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.417"
|
||||
SYSTEM_VERSION = "V10.418"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **最後更新**: 2026-05-24 (台北時間)
|
||||
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯,Gemini 備援預設關閉
|
||||
> **適用版本**: V10.417
|
||||
> **適用版本**: V10.418
|
||||
|
||||
---
|
||||
|
||||
@@ -21,6 +21,7 @@
|
||||
- Code Review Hermes LLM scan 啟用時才使用本地模型矩陣,且預設只跑 GCP-A `qwen2.5-coder:7b` → GCP-B `gemma3:4b`;`CODE_REVIEW_ALLOW_111_FALLBACK=true` 時才允許落到 111,並由 `OllamaService` 降級到 `llama3.2:latest`。不啟用 Gemini 備援,本地掃描失敗時只回空 findings 並交由 OpenClaw 本地矩陣續跑。
|
||||
- Code Review OpenClaw assessment 預設只跑 GCP-A → GCP-B:GCP-A `qwen2.5-coder:7b`、GCP-B `gemma3:4b`;primary timeout 預設 `15s`、secondary timeout 預設 `60s`,讓 A 掛時快速讓位給 B,且 B 有足夠時間完成審查 prompt。111 是最後救急節點,但部署後重分析預設不打 111;只有 `CODE_REVIEW_ALLOW_111_FALLBACK=true` 才允許 111 接手,並降級到 `llama3.2:latest`。Code Review 的 Ollama `keep_alive` 預設為 `5m`,不得再用 `24h` 長駐 runner 壓住 GCP-B/111。GCP-A/GCP-B 都失敗且 Claude/Gemini 未顯式開啟時,必須回 deterministic 本地降級摘要,不呼叫 Gemini、不落 111、不走其他雲端模型。
|
||||
- Embedding / semantic RAG 背景任務預設只跑 GCP-A → GCP-B:`OpenClawLearningService` embedding worker 與 `RAGService` 查詢 embedding 呼叫 `OllamaService.generate_embedding(..., allow_111_fallback=False)`;111 只可作人工明確指定的救急路徑,不承接 `bge-m3` 背景批次。`OLLAMA_EMBED_TIMEOUT` / `OLLAMA_EMBED_MAX_TIMEOUT` 預設 `15s`、`OLLAMA_EMBED_KEEP_ALIVE=1m`、`OLLAMA_EMBED_MAX_CHARS=4000`,避免 embedding worker 長時間卡住 GCP-B 或 111。
|
||||
- BGE-M3 一致性檢查是監測任務,不是 fallback 壓測;預設只比對 GCP-A / GCP-B。111 Mac fallback 只有 `EMBED_CONSISTENCY_INCLUDE_111=true` 時才納入,避免每週背景檢查把 `bge-m3` 載入 111。
|
||||
- OpenClaw Telegram Q&A 主路徑也不得綁單一 host:`_call_qwen3_qa()` 必須透過 `OllamaService` 跑 GCP-A → GCP-B → 111,並把實際落點寫入 `ai_calls.provider`。
|
||||
- OpenClaw Telegram 圖片商品辨識也必須 Ollama-first:`_identify_product_name_with_ollama_vision()` 透過 `OllamaService` 嘗試 GCP-A → GCP-B → 111;Gemini 只允許以 `openclaw_bot_image_gemini` caller 作為失敗後備援。
|
||||
- OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-first;Gemini caller 只能帶 `_gemini_fallback` 或明確 fallback caller 語意,且不得先於 Ollama/NIM 被呼叫。OpenClaw strategy 的 Ollama `keep_alive` 預設為 `5m`,避免報告型任務把 GCP-B/111 runner 長駐 24h。
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-05-24:PChome 近門檻身份回收第二輪
|
||||
- **V10.418 bge-m3 一致性檢查不打 111**: `verify_embedding_consistency()` 預設只比對 GCP-A / GCP-B,不再每週把 111 Mac 納入 bge-m3 背景驗證;新增 `EMBED_CONSISTENCY_INCLUDE_111=false` 預設,只有救急需要驗證 fallback 模型時才 opt-in。這補上 V10.417 後仍會由監測任務載入 111/GCP-B embedding runner 的缺口。
|
||||
- **V10.417 Embedding/RAG 背景負載保護**: `OllamaService.generate_embedding()` 新增 `allow_111_fallback`、timeout cap、輸入長度 cap 與 `/api/embed keep_alive=1m`;OpenClaw learning worker 與 RAG 查詢預設只跑 GCP-A → GCP-B,不再把 `bge-m3` 背景 embedding / semantic RAG 轉嫁到 111。預設 `OLLAMA_EMBED_TIMEOUT=15`、`OLLAMA_EMBED_MAX_TIMEOUT=15`、`OLLAMA_EMBED_MAX_CHARS=4000`,避免 embedding worker 在 GCP-B/111 長時間常駐或拖住 runner。
|
||||
- **V10.416 私密清潔 / 彩妝用途 / 棉棒 / 蘭蔻品線防錯配**: marketplace matcher 追加窄範圍 hard-veto guard,讓 SAUGELLA 日用/加強 vs 黃金女郎型、Lactacyd 清新舒涼 vs 生理呵護、LUNASOL 頰彩 vs 眼彩、MUJI 細軸棉棒 vs 黑色棉棒、LANCOME 超極光晶露 vs 超極限肌因精華露不再停留在模糊 `true_low_confidence`,而是以 `*_variant_conflict` / `makeup_usage_conflict` / `lancome_line_conflict` 明確拒絕;不調整 `MIN_MATCH_SCORE`,也不放寬真同款進 matched 的門檻。
|
||||
- **V10.416 production pilot**: 正式回刷 7 筆近門檻錯配樣本,SAUGELLA 2 筆、LUNASOL 頰彩 vs 眼彩、LANCOME 超極光 vs 超極限、我的心機兒童防曬 vs 海洋友善防曬、Lactacyd 清新舒涼 vs 生理呵護、MUJI 細軸棉棒 vs 黑色棉棒皆更新為 `identity_veto`;`matched` 維持 1619、`true_low_confidence` 759→753、`recoverable_low_score` 1→0、`identity_veto` 4004→4011,無正式 `competitor_prices` 覆寫。
|
||||
|
||||
@@ -189,10 +189,10 @@ def _register_schedules():
|
||||
schedule.every(4).hours.do(run_expire_stale_reviews)
|
||||
logger.info("📅 每 4 小時:expire_stale_reviews(24h 無回應降權 0.5)")
|
||||
|
||||
# Phase 11.0 護欄 #3:BGE-M3 跨主機一致性驗證(ADR-033)
|
||||
# Phase 11.0 護欄 #3:BGE-M3 跨 GCP Ollama 一致性驗證(ADR-033)
|
||||
# 每週一次足夠(驗證模型版本未漂移;不需每次啟動)
|
||||
schedule.every().sunday.at("04:30").do(run_embed_consistency_check)
|
||||
logger.info("📅 每週日 04:30:bge-m3 跨主機一致性驗證")
|
||||
logger.info("📅 每週日 04:30:bge-m3 GCP-A/GCP-B 一致性驗證(111 opt-in)")
|
||||
|
||||
# Phase 42: 三主機 Ollama 健康探針(即使無人開觀測台頁面也持續累積歷史)
|
||||
schedule.every(15).minutes.do(run_host_health_probe)
|
||||
@@ -980,10 +980,11 @@ def run_roi_monthly_report_if_new_month():
|
||||
|
||||
|
||||
def run_embed_consistency_check():
|
||||
"""每週日 04:30 — BGE-M3 跨主機一致性驗證(ADR-033 護欄 #3)。
|
||||
"""每週日 04:30 — BGE-M3 跨 GCP Ollama 一致性驗證(ADR-033 護欄 #3)。
|
||||
|
||||
跑 verify_embedding_consistency,不一致時 logger.error;ok 時 logger.info。
|
||||
每週一次足夠(驗證模型版本未漂移;過頻會打三主機 Ollama 浪費)。
|
||||
每週一次足夠(驗證模型版本未漂移;過頻會打 Ollama 浪費)。
|
||||
111 Mac fallback 預設不參與,避免背景檢查載入 bge-m3 壓住 16GB 主機。
|
||||
"""
|
||||
try:
|
||||
from services.rag_service import verify_embedding_consistency
|
||||
@@ -1003,7 +1004,7 @@ def run_embed_consistency_check():
|
||||
)
|
||||
logger.error(
|
||||
"[EmbedConsistency] ⚠️ INCONSISTENT — RAG 召回率將下降;"
|
||||
"檢查三主機 bge-m3 模型版本是否同步(ollama list)"
|
||||
"檢查 GCP-A/GCP-B bge-m3 模型版本是否同步(ollama list)"
|
||||
)
|
||||
_notify_scheduler_failure(
|
||||
"run_embed_consistency_check",
|
||||
|
||||
@@ -129,6 +129,10 @@ def get_embedding_signature(
|
||||
EMBED_CONSISTENCY_TEST_TEXT = "momo電商競品分析測試向量一致性檢查"
|
||||
EMBED_CONSISTENCY_MAX_DIFF = 1e-4 # cosine 距離上限(浮點誤差容忍)
|
||||
EMBED_CONSISTENCY_TIMEOUT_SEC = 10.0 # 各主機 embedding 探測 timeout
|
||||
EMBED_CONSISTENCY_INCLUDE_111 = os.getenv(
|
||||
'EMBED_CONSISTENCY_INCLUDE_111',
|
||||
'false',
|
||||
).strip().lower() in ('true', '1', 'yes', 'on')
|
||||
|
||||
|
||||
def _cosine_distance(vec_a: List[float], vec_b: List[float]) -> float:
|
||||
@@ -147,11 +151,13 @@ def verify_embedding_consistency(
|
||||
test_text: str = EMBED_CONSISTENCY_TEST_TEXT,
|
||||
max_diff: float = EMBED_CONSISTENCY_MAX_DIFF,
|
||||
) -> Dict[str, Any]:
|
||||
"""跨三主機(GCP Primary / Secondary / 111)BGE-M3 embedding 一致性驗證。
|
||||
"""跨 GCP Ollama 節點 BGE-M3 embedding 一致性驗證。
|
||||
|
||||
Owen v5.0 護欄 #3(ADR-033)— RAG 啟動時驗證;不一致則 log warning。
|
||||
fail-safe:任何主機失敗(連線、超時)都跳過,只比對能拿到的 embeddings。
|
||||
最少 2 個主機可達才能比對;只有 1 個 → 回 ok=True + warning「無法比對」。
|
||||
111 是 Mac final fallback,預設不參與背景一致性檢查;只有
|
||||
EMBED_CONSISTENCY_INCLUDE_111=true 才納入救急驗證,避免載入 bge-m3 壓住 111。
|
||||
|
||||
回傳:
|
||||
{
|
||||
@@ -171,8 +177,9 @@ def verify_embedding_consistency(
|
||||
hosts = {
|
||||
'gcp_ollama': OLLAMA_HOST_PRIMARY,
|
||||
'ollama_secondary': OLLAMA_HOST_SECONDARY,
|
||||
'ollama_111': OLLAMA_HOST_FALLBACK,
|
||||
}
|
||||
if EMBED_CONSISTENCY_INCLUDE_111:
|
||||
hosts['ollama_111'] = OLLAMA_HOST_FALLBACK
|
||||
|
||||
embeddings: Dict[str, List[float]] = {}
|
||||
errors: List[str] = []
|
||||
@@ -185,6 +192,7 @@ def verify_embedding_consistency(
|
||||
model=RAG_EMBED_MODEL,
|
||||
host=host, # 顯式指定(避免 retry 鏈干擾驗證)
|
||||
timeout=int(EMBED_CONSISTENCY_TIMEOUT_SEC),
|
||||
allow_111_fallback=(label == 'ollama_111'),
|
||||
)
|
||||
elapsed = time.monotonic() - t0
|
||||
if vec and len(vec) == RAG_EMBED_DIM:
|
||||
|
||||
@@ -278,6 +278,53 @@ class TestEmbeddingSignature:
|
||||
assert len(result.hits) == 1
|
||||
|
||||
|
||||
class TestEmbeddingConsistencyRouting:
|
||||
def test_consistency_check_skips_111_by_default(self, monkeypatch):
|
||||
from services import rag_service as rs
|
||||
from services import ollama_service as oss
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_embed(text, model, host, timeout, **kwargs):
|
||||
calls.append((host, kwargs))
|
||||
return _fake_embedding()
|
||||
|
||||
monkeypatch.setattr(rs, 'EMBED_CONSISTENCY_INCLUDE_111', False)
|
||||
monkeypatch.setattr(oss.ollama_service, 'generate_embedding', fake_embed)
|
||||
|
||||
result = rs.verify_embedding_consistency()
|
||||
|
||||
hosts = [host for host, _kwargs in calls]
|
||||
assert result['ok'] is True
|
||||
assert hosts == [oss.OLLAMA_HOST_PRIMARY, oss.OLLAMA_HOST_SECONDARY]
|
||||
assert oss.OLLAMA_HOST_FALLBACK not in hosts
|
||||
assert all(kwargs.get('allow_111_fallback') is False for _host, kwargs in calls)
|
||||
|
||||
def test_consistency_check_can_include_111_when_explicitly_enabled(self, monkeypatch):
|
||||
from services import rag_service as rs
|
||||
from services import ollama_service as oss
|
||||
|
||||
calls = []
|
||||
|
||||
def fake_embed(text, model, host, timeout, **kwargs):
|
||||
calls.append((host, kwargs))
|
||||
return _fake_embedding()
|
||||
|
||||
monkeypatch.setattr(rs, 'EMBED_CONSISTENCY_INCLUDE_111', True)
|
||||
monkeypatch.setattr(oss.ollama_service, 'generate_embedding', fake_embed)
|
||||
|
||||
result = rs.verify_embedding_consistency()
|
||||
|
||||
hosts = [host for host, _kwargs in calls]
|
||||
assert result['ok'] is True
|
||||
assert hosts == [
|
||||
oss.OLLAMA_HOST_PRIMARY,
|
||||
oss.OLLAMA_HOST_SECONDARY,
|
||||
oss.OLLAMA_HOST_FALLBACK,
|
||||
]
|
||||
assert calls[-1][1].get('allow_111_fallback') is True
|
||||
|
||||
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
# Test 4: fire-and-forget log 失敗不影響主流程
|
||||
# ─────────────────────────────────────────────────────────────────────────────
|
||||
|
||||
Reference in New Issue
Block a user