Keep embedding retry on GCP when 111 fallback disabled
All checks were successful
CD Pipeline / deploy (push) Successful in 1m5s

This commit is contained in:
OoO
2026-05-25 09:59:00 +08:00
parent f3a199434f
commit 3fd349c830
7 changed files with 44 additions and 5 deletions

View File

@@ -4,6 +4,7 @@
================================================================================
【已完成】
- V10.465 修正 embedding fallback-disabled 控制流:`allow_111_fallback=False` 時若 resolver 回 111不再直接退出或只試單台 GCP-B會強制改試尚未嘗試的 GCP-A/GCP-B背景 embedding 仍不落 111。
- V10.464 補 rescore audit 精準 SKU pilot`audit_competitor_match_attempt_rescore.py --sku` 可只掃指定 SKU再搭配 `--apply-accepted` 只把通過新版 matcher 的目標 SKU 追加到 `rescore_accepted_current` 人工覆核隊列,不寫正式價格表。
- V10.463 補 DR.WU / 達爾膚品牌 alias同規格 `DR.WU 達爾膚` 與 `DR.WU` 候選不再被當成 brandless identity review會以既有 exact_identity / total_price / price_alert_exact 閘門處理;未調整 `MIN_MATCH_SCORE`,保留 variant / hard veto 保護。
- V10.462 進一步收斂 PChome 補抓 UI 語意Dashboard 區塊標題改為「PChome 補抓產線」AI 中樞按鈕、前端確認與 API 訊息改為「補抓未搜尋 / 未搜尋補抓」,避免操作員把尚未搜尋的工作誤判成已有候選待審。

View File

@@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.464"
SYSTEM_VERSION = "V10.465"
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.464
> **適用版本**: V10.465
---
@@ -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。
- `allow_111_fallback=False` 時,若 resolver 因 unhealthy cache 回傳 111不得直接結束 embedding必須強制改試尚未嘗試的 GCP-A / GCP-B避免正式 log 出現 `tried=[]` 或只試單台 GCP-B。
- 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 作為失敗後備援。

View File

@@ -65,6 +65,10 @@
- 告警不得再輸出空泛「預期效益」必須帶資料品質、證據來源、HITL 邊界與 trace id。
- Agent 建議只能輔助排序與分析,不得繞過 matcher / feeder / review service 寫正式價格。
## 3.1 Ollama / Embedding 健康
- 2026-05-25 08:48 CST 起,`OllamaService.generate_embedding(..., allow_111_fallback=False)` 若 resolver 回 111會強制改試尚未嘗試的 GCP-A/GCP-B不再讓背景 embedding 在 111 disabled 情境直接退出或只試單台 GCP-B111 仍不承接背景 `bge-m3`
## 4. 業績分析資料與圖表修復
- 修正即時業績匯入 `snapshot_date text = date` 類型錯誤。

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-05-24PChome 近門檻身份回收第二輪
- **V10.465 Embedding GCP fallback 修正**: `OllamaService.generate_embedding(..., allow_111_fallback=False)` 若 resolver 因 unhealthy cache 回 111會強制改試尚未嘗試的 GCP-A/GCP-B不再直接 `break` 造成 `tried=[]` 或只試單台 GCP-B背景 embedding 仍不允許落 111。
- **V10.464 Rescore SKU pilot 篩選**: `audit_competitor_match_attempt_rescore.py``fetch_match_attempt_rescore_rows()` 增加 `--sku` / `skus` 篩選,可針對 DR.WU 這類明確 cohort 做 3-10 筆精準 materialize不必為了 pilot 掃整批 `true_low_confidence`
- **V10.463 DR.WU / 達爾膚品牌 alias**: `marketplace_product_matcher``DR.WU / DR WU / DRWU / 達爾膚` 正規化,讓正式樣本中同規格玻尿酸保濕精華乳、杏仁酸亮白煥膚精華不再因品牌 token 不同被降成 brandless identity review測試鎖住 exact / total_price / price_alert_exact。
- **V10.462 PChome 補抓 UI 語意收斂**: Dashboard 補抓區塊標題、AI 中樞按鈕、前端 confirm 與 API 回覆全數改用「PChome 補抓產線 / 補抓未搜尋 / 未搜尋補抓」,避免「待比對」殘留在操作入口,和低信心待人工覆核混淆。

View File

@@ -1056,8 +1056,15 @@ class OllamaService:
logger.warning("[Embed] 111 fallback disabled; ignoring EMBEDDING_HOST=%s", configured_host)
target_host = resolve_ollama_host().rstrip("/")
if not allow_111_fallback and _is_111_fallback_host(target_host):
logger.warning("[Embed] 111 fallback disabled; no approved GCP embedding host available")
break
next_host = next((candidate for candidate in allowed_hosts if candidate not in attempted_hosts), None)
if not next_host:
logger.warning("[Embed] 111 fallback disabled; no approved GCP embedding host available")
break
logger.warning(
"[Embed] 111 fallback disabled; forcing approved GCP embedding host=%s",
next_host,
)
target_host = next_host
if target_host in attempted_hosts:
next_host = None
if target_host in allowed_hosts:

View File

@@ -378,7 +378,32 @@ def test_embedding_can_disable_111_fallback_for_background_rag_work():
posted_hosts = [call.args[0].split('/api/embed')[0] for call in mock_post.call_args_list]
assert vec == []
assert posted_hosts == [oss.OLLAMA_HOST_SECONDARY]
assert posted_hosts == [oss.OLLAMA_HOST_SECONDARY, oss.OLLAMA_HOST_PRIMARY]
assert oss.OLLAMA_HOST_FALLBACK not in posted_hosts
def test_embedding_fallback_disabled_uses_gcp_chain_when_resolver_returns_111():
"""resolver 若因 unhealthy cache 回 111背景 embedding 仍要嘗試 GCP-A/GCP-B。"""
import requests
from services import ollama_service as oss
from services.ollama_service import OllamaService
svc = OllamaService()
with patch(
'services.ollama_service.resolve_ollama_host',
side_effect=[oss.OLLAMA_HOST_FALLBACK, oss.OLLAMA_HOST_FALLBACK],
), patch.dict('os.environ', {}, clear=False), patch(
'services.ollama_service.requests.post',
side_effect=requests.Timeout('gcp timeout'),
) as mock_post:
import os
os.environ.pop('EMBEDDING_HOST', None)
vec = svc.generate_embedding('test text', allow_111_fallback=False)
posted_hosts = [call.args[0].split('/api/embed')[0] for call in mock_post.call_args_list]
assert vec == []
assert posted_hosts == [oss.OLLAMA_HOST_PRIMARY, oss.OLLAMA_HOST_SECONDARY]
assert oss.OLLAMA_HOST_FALLBACK not in posted_hosts