From 2ae377dadf0f705850e9c785ed986554eae1ba4e Mon Sep 17 00:00:00 2001 From: OoO Date: Thu, 21 May 2026 20:14:19 +0800 Subject: [PATCH] V10.386 harden Gemini compose defaults --- config.py | 2 +- docker-compose.yml | 9 +++++++++ docs/AI_INTELLIGENCE_MODULE_SOT.md | 3 ++- docs/memory/history_logs.md | 1 + services/marketplace_product_matcher.py | 16 ++++++++++++++++ tests/test_docker_compose_runtime_mounts.py | 10 ++++++++++ tests/test_marketplace_product_matcher.py | 15 +++++++++++++++ 7 files changed, 54 insertions(+), 2 deletions(-) diff --git a/config.py b/config.py index 8ace58c..26c7919 100644 --- a/config.py +++ b/config.py @@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '') # ========================================== # 系統版本與路徑 # ========================================== -SYSTEM_VERSION = "V10.385" +SYSTEM_VERSION = "V10.386" LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log') public_url = PUBLIC_URL # 用於模板顯示 diff --git a/docker-compose.yml b/docker-compose.yml index 1f6e974..4b14e68 100644 --- a/docker-compose.yml +++ b/docker-compose.yml @@ -96,6 +96,9 @@ services: - OLLAMA_HOST_FALLBACK=${OLLAMA_HOST_FALLBACK:-http://192.168.0.111:11434} # EMBEDDING_HOST 若未設定,由 resolve_ollama_host() 自動決定(三主機級聯) - EMBEDDING_HOST=${EMBEDDING_HOST:-} + # Gemini 只能作緊急備援;即使 .env 有 API key,預設也不得產生付費出站。 + - GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true} + - GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false} # PPT 視覺 QA + 線上預覽:需要容器內 LibreOffice(Dockerfile 安裝 libreoffice-impress) - PPT_VISION_ENABLED=${PPT_VISION_ENABLED:-true} - PPT_VISION_MODEL=${PPT_VISION_MODEL:-minicpm-v:latest} @@ -226,6 +229,9 @@ services: - OLLAMA_HOST_SECONDARY=${OLLAMA_HOST_SECONDARY:-http://34.21.145.224:11434} - OLLAMA_HOST_FALLBACK=${OLLAMA_HOST_FALLBACK:-http://192.168.0.111:11434} - EMBEDDING_HOST=${EMBEDDING_HOST:-} + # Gemini 只能作緊急備援;即使 .env 有 API key,預設也不得產生付費出站。 + - GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true} + - GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false} - PPT_VISION_ENABLED=${PPT_VISION_ENABLED:-true} - PPT_VISION_MODEL=${PPT_VISION_MODEL:-minicpm-v:latest} env_file: @@ -286,6 +292,9 @@ services: - OLLAMA_HOST_SECONDARY=${OLLAMA_HOST_SECONDARY:-http://34.21.145.224:11434} - OLLAMA_HOST_FALLBACK=${OLLAMA_HOST_FALLBACK:-http://192.168.0.111:11434} - EMBEDDING_HOST=${EMBEDDING_HOST:-} + # Gemini 只能作緊急備援;即使 .env 有 API key,預設也不得產生付費出站。 + - GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true} + - GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false} env_file: - .env command: ["python", "run_telegram_bot.py"] diff --git a/docs/AI_INTELLIGENCE_MODULE_SOT.md b/docs/AI_INTELLIGENCE_MODULE_SOT.md index 7996f93..512a649 100644 --- a/docs/AI_INTELLIGENCE_MODULE_SOT.md +++ b/docs/AI_INTELLIGENCE_MODULE_SOT.md @@ -2,7 +2,7 @@ > **最後更新**: 2026-05-21 (台北時間) > **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯,Gemini 備援預設關閉 -> **適用版本**: V10.380 +> **適用版本**: V10.386 --- @@ -25,6 +25,7 @@ - OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-first;Gemini caller 只能帶 `_gemini_fallback` 或明確 fallback caller 語意,且不得先於 Ollama/NIM 被呼叫。 - OpenClaw 週報、月報、Meta analysis、日報洞察與每日報告的 Gemini/NIM 備援 caller 必須登錄在 caller registry、AI 觀測台 agent group 與 Telegram 狀態統計,避免 fallback 用量被歸類為未知或漏算。 - Gemini API 出站有第二道 kill switch:`GEMINI_FALLBACK_ENABLED` 預設為 `false`。即使 `GEMINI_API_KEY` 存在,通用 AI fallback、OpenClaw 報告/QA/PPT/圖片、MCP Grounding 與 Code Review L3 都不得呼叫 Gemini;只有操作員明確設為 `true` 時,Gemini 才能作緊急備援。 +- `docker-compose.yml` 的 `momo-app`、`scheduler`、`telegram-bot` 必須明確設定 `GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true}` 與 `GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false}`;`.env` 可保留 `GEMINI_API_KEY`,但不得因 key 存在就讓核心容器產生 Gemini 付費出站。 - Gemini 不可被任何狀態面板或 router 推薦為主提供者:`AIProviderService._get_recommended_provider()` 不得回傳 `gemini`,只能顯示為 fallback 狀態;`llm_model_router` 的 `ea_engine` 若收到 `gemini-*` default 必須改回 `hermes3:latest`,需要深推理時才升本地 `deepseek-r1:14b`。 - ElephantAlpha prompt / agent registry 不得再把 OpenClaw 描述為 Gemini 主模型;OpenClaw 是 `qwen2.5-coder:7b` / `qwen3:14b` Ollama-first 策略師,Gemini 僅能在 guard 顯式解鎖後作 emergency fallback。 - 111 `192.168.0.111` 只是最後一道 Mac fallback,不承接 7B+、vision、long-context 模型長駐;`OllamaService.generate()` 落到 111 時會將 `qwen3`、`deepseek-r1`、`hermes3`、`qwen2.5*`、`gemma3`、`llava`、`minicpm-v` 與 7B+ 模型依 `OLLAMA_111_MODEL_DOWNGRADE_PATTERNS` 降級到 `OLLAMA_111_MODEL_FALLBACK=llama3.2:latest`,並以 `OLLAMA_111_KEEP_ALIVE=5m`、`OLLAMA_111_MAX_TIMEOUT=20`、`OLLAMA_111_NUM_CTX=4096`、`OLLAMA_111_NUM_PREDICT=512` 封頂,避免 16GB RAM 主機被大 context runner、長輸出與 24h keep-alive 壓到 swap。 diff --git a/docs/memory/history_logs.md b/docs/memory/history_logs.md index 4db891c..31faf1c 100644 --- a/docs/memory/history_logs.md +++ b/docs/memory/history_logs.md @@ -13,6 +13,7 @@ ## 📅 詳細更新日誌 (考古存檔) ### 2026-05-21:瀏覽器測試守門與 PChome 熱路徑優化 +- **V10.386 Gemini compose hard default / KATE 唇膏比對**: `docker-compose.yml` 針對 `momo-app`、`scheduler`、`telegram-bot` 明確釘住 `GEMINI_API_HARD_DISABLED=true` 與 `GEMINI_FALLBACK_ENABLED=false` 的預設,讓 `.env` 保留 API key 時也不會自動產生 Gemini 付費出站;AI SOT 與 compose 測試同步鎖定此契約。同版 marketplace matcher 補 KATE/凱婷「柔霧裸唇膏」identity anchor,避免 MOMO 長標含東京夜喫茶系列與任選文案時漏掉 PChome 同款短標。 - **V10.385 Lactacyd / MAQuillAGE 櫃別同款比對**: Marketplace matcher 補強 Lactacyd 私密潔浴露多款任選長標與 PChome 短標同款放行,並橋接「資生堂東京櫃」與 `MAQuillAGE 心機彩妝` 在「心機星魅蜜光圈潤唇膏」上的櫃別/品牌 alias,避免真同款被 `brand_conflict` 擋掉。 - **V10.384 Karadium 無規格眼影棒同款放行**: Marketplace matcher 對 Karadium「閃亮珍珠眼影棒」新增品牌 + 強 identity anchor 加分,當 PChome 標題省略 1.4g 規格但品名/品牌高度一致、無變體衝突與 hard veto 時仍可進入 exact identity 告警候選,避免同款因規格缺字漏報。 - **V10.383 EA JSON fallback / EDM cache 自癒 / 比對別名補強**: Elephant Alpha 協調器現在可容忍 fenced JSON 與混文字 JSON;若仍無法解析,會改用 DB/Hermes 實證產生保守人工覆核決策,不再輸出舊式 OpenClaw 策略 plan 或自動調價暗示。EDM promo dashboard shared cache 遇到損毀 pickle 會自動刪除並重建,避免每個 worker 重複噴 `UnicodeDecodeError`。Marketplace matcher 補上 Curel/珂潤、Karadium 與兩個強 identity anchor 測試,降低真同款漏報。 diff --git a/services/marketplace_product_matcher.py b/services/marketplace_product_matcher.py index db2acc8..3e94d0a 100644 --- a/services/marketplace_product_matcher.py +++ b/services/marketplace_product_matcher.py @@ -299,11 +299,13 @@ SEARCH_NOISE_TOKENS = { "美甲", "3d", "多色", + "提亮", "兩入組", "櫻花輕盈版", } SEARCH_IDENTITY_ANCHORS = ( + "柔霧裸唇膏", "潤浸保濕清爽身體乳液", "閃亮珍珠眼影棒", "智能光感應無線自動除臭芳香噴霧機", @@ -1839,6 +1841,20 @@ def score_marketplace_match( ): score += 0.10 reasons.append("shared_identity_anchor_lactacyd_wash") + if ( + "柔霧裸唇膏" in shared_anchor + and {"kate", "凱婷"} & (left.brand_tokens | right.brand_tokens) + and brand_score >= 0.95 + and not hard_veto + and price_penalty == 0 + and type_score >= 0.55 + and spec_score >= 0.45 + and token_score >= 0.50 + and sequence_score >= 0.50 + and not variant_descriptor_conflict + ): + score += 0.15 + reasons.append("shared_identity_anchor_kate_bare_lip") if ( "閃亮珍珠眼影棒" in shared_anchor and {"karadium"} <= (left.brand_tokens | right.brand_tokens) diff --git a/tests/test_docker_compose_runtime_mounts.py b/tests/test_docker_compose_runtime_mounts.py index ae81d12..dc86b3f 100644 --- a/tests/test_docker_compose_runtime_mounts.py +++ b/tests/test_docker_compose_runtime_mounts.py @@ -41,6 +41,16 @@ def test_core_runtime_services_keep_memory_limits_and_healthchecks(): assert 'curl", "-f", "http://localhost:80/health"' in _service_block(compose, "momo-app") +def test_core_runtime_services_pin_gemini_fallback_off_by_default(): + compose = DOCKER_COMPOSE.read_text(encoding="utf-8") + core_services = ("momo-app", "scheduler", "telegram-bot") + + for service_name in core_services: + block = _service_block(compose, service_name) + assert "- GEMINI_API_HARD_DISABLED=${GEMINI_API_HARD_DISABLED:-true}" in block + assert "- GEMINI_FALLBACK_ENABLED=${GEMINI_FALLBACK_ENABLED:-false}" in block + + def test_optional_compose_services_stay_behind_profiles(): compose = DOCKER_COMPOSE.read_text(encoding="utf-8") expected_profiles = { diff --git a/tests/test_marketplace_product_matcher.py b/tests/test_marketplace_product_matcher.py index 0a25fdd..f7a380b 100644 --- a/tests/test_marketplace_product_matcher.py +++ b/tests/test_marketplace_product_matcher.py @@ -608,6 +608,21 @@ def test_marketplace_matcher_bridges_maquillage_shiseido_counter_alias(): assert "brand_conflict" not in diagnostics.reasons +def test_marketplace_matcher_promotes_kate_bare_lip_line_with_series_copy(): + from services.marketplace_product_matcher import score_marketplace_match + + diagnostics = score_marketplace_match( + "即期品【KATE 凱婷】柔霧裸唇膏-東京夜喫茶系列(裸色系霧面唇膏/4色任選)", + "【KATE 凱婷】柔霧裸唇膏 (2.3g)", + momo_price=320, + competitor_price=320, + ) + + assert diagnostics.score >= 0.76 + assert diagnostics.hard_veto is False + assert "shared_identity_anchor_kate_bare_lip" in diagnostics.reasons + + def test_marketplace_matcher_rejects_refill_core_vs_case_only_pack(): from services.marketplace_product_matcher import score_marketplace_match