V10.418 skip 111 in embedding consistency checks

This commit is contained in:
OoO
2026-05-24 15:03:10 +08:00
parent 353e565e52
commit 7090f08dba
7 changed files with 68 additions and 9 deletions

View File

@@ -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

View File

@@ -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 # 用於模板顯示

View File

@@ -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-BGCP-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 → 111Gemini 只允許以 `openclaw_bot_image_gemini` caller 作為失敗後備援。
- OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-firstGemini caller 只能帶 `_gemini_fallback` 或明確 fallback caller 語意,且不得先於 Ollama/NIM 被呼叫。OpenClaw strategy 的 Ollama `keep_alive` 預設為 `5m`,避免報告型任務把 GCP-B/111 runner 長駐 24h。

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-05-24PChome 近門檻身份回收第二輪
- **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` 覆寫。

View File

@@ -189,10 +189,10 @@ def _register_schedules():
schedule.every(4).hours.do(run_expire_stale_reviews)
logger.info("📅 每 4 小時expire_stale_reviews24h 無回應降權 0.5")
# Phase 11.0 護欄 #3BGE-M3 跨主機一致性驗證ADR-033
# Phase 11.0 護欄 #3BGE-M3 跨 GCP Ollama 一致性驗證ADR-033
# 每週一次足夠(驗證模型版本未漂移;不需每次啟動)
schedule.every().sunday.at("04:30").do(run_embed_consistency_check)
logger.info("📅 每週日 04:30bge-m3 跨主機一致性驗證")
logger.info("📅 每週日 04:30bge-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.errorok 時 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",

View File

@@ -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 / 111BGE-M3 embedding 一致性驗證。
""" GCP Ollama 節點 BGE-M3 embedding 一致性驗證。
Owen v5.0 護欄 #3ADR-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:

View File

@@ -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 失敗不影響主流程
# ─────────────────────────────────────────────────────────────────────────────