阻止 Gemini 成為推薦主路徑
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s

This commit is contained in:
OoO
2026-05-21 16:18:21 +08:00
committed by AiderHeal Bot
parent de10712586
commit e3da4ffbb3
9 changed files with 75 additions and 15 deletions

View File

@@ -325,7 +325,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
# ==========================================
# 系統版本與路徑
# ==========================================
SYSTEM_VERSION = "V10.376"
SYSTEM_VERSION = "V10.377"
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
public_url = PUBLIC_URL # 用於模板顯示

View File

@@ -2,7 +2,7 @@
> **最後更新**: 2026-05-21 (台北時間)
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地LLM 路由紅線升級為 Ollama-first 三主機級聯Gemini 備援預設關閉
> **適用版本**: V10.364
> **適用版本**: V10.377
---
@@ -25,6 +25,8 @@
- OpenClaw 週報、月報、Meta analysis、日報洞察、Telegram PPT 分析與 MCP fallback 也必須 Ollama-firstGemini 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 才能作緊急備援。
- 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=45``OLLAMA_111_NUM_CTX=4096` 封頂,避免 16GB RAM 主機被大 context runner 與 24h keep-alive 壓到 swap。
## 一、四 AI Agent 路由架構
@@ -66,7 +68,7 @@ SQL漏斗(~300筆)
|------|------|------|------|---------|
| Hermes 分析師 | hermes3:latest / bge-m3 | GCP-A → GCP-B → 111 Ollama | 零 | 無限 |
| NemoTron 派發器 | qwen3:14b111 fallback 降級 llama3.2NIM fallback | GCP-A → GCP-B → 111NVIDIA NIM 備援 | Ollama 零NIM 配額內免費 | NIM 80 |
| OpenClaw 策略師 | qwen3:14b111 fallback 降級 llama3.2Gemini 鎖定場景 | Ollama-firstGemini 備援 | Ollama 零Gemini 需控管 | — |
| OpenClaw 策略師 | qwen2.5-coder:7b / qwen3:14b111 fallback 降級 llama3.2 | Ollama-firstGemini emergency fallback only | Ollama 零Gemini 預設封鎖 | — |
| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | HITL / 任務制 |
---

View File

@@ -13,6 +13,7 @@
## 📅 詳細更新日誌 (考古存檔)
### 2026-05-21瀏覽器測試守門與 PChome 熱路徑優化
- **V10.377 Gemini 主路徑防漏補強**: `AIProviderService._get_recommended_provider()` 不再於 Ollama 不通時推薦 `gemini` 作為主提供者;`llm_model_router``ea_engine` 即使 caller 傳入 `gemini-2.0-flash` default也會改回 `hermes3:latest`,需要深推理才升 `deepseek-r1:14b``ElephantAlphaOrchestrator` 的 OpenClaw registry / system prompt 改為 Ollama-first避免 L3 HITL prompt 繼續把 Gemini 當主模型描述。同步補 AI SOT 與防回歸測試。
- **V10.376 Recipe Box 同款防曬漂移比對**: `services/marketplace_product_matcher.py` 對 Recipe Box 多效提亮防曬霜新增 shared identity anchor 加分,當 MOMO 長標含兒童/無毒/天然彩妝等行銷詞、PChome 以「韓兔 多效提亮防曬霜」呈現時仍可判定同款;同步測試鎖住 `shared_identity_anchor_recipe_box_line`,避免平台名稱漂移讓同款價格告警漏報。
- **V10.375 過期活動爬蟲排程 opt-in**: `run_scheduler.py` 將固定 LPN 的 `edm_task` / `festival_task` 改為 `MOMO_ENABLE_LEGACY_EDM_SCHEDULE=true` 才註冊,季節活動 `mothers_day_2026` / `valentine_520_2026` / `labor_day_2026` 改為 `MOMO_ENABLE_SEASONAL_PROMO_SCHEDULE=true` 才註冊;`services/data/crawler_config.json` 同步暫停已失效的 mothers_day LPN避免 scheduler 定時打過期 MOMO 活動頁造成 Selenium browser loop 與無效負載。手動 API / CLI 指定 LPN 仍保留;同版整合 NIVEA/OPI 等比價搜尋 noise 與 identity anchor 補強。
- **V10.374 EDM 失效頁告警止血**: `scheduler.py` 新增 MOMO EDM alert guard`run_edm_task` / `run_festival_task` / `run_promo_event_task` 遇到「很抱歉此EDM不存在」時會接受 browser alert、寫入 `Skipped / edm_unavailable` stats且不再送 EventRouter failure避免 festival / mothers_day 過期活動頁重新累積 Telegram queue同版整合 REJURAN 麗駐蘭唇膏同款在價格比過寬時的 exact-identity 價格懲罰豁免。

View File

@@ -158,13 +158,11 @@ class AIProviderService:
return status
def _get_recommended_provider(self, ollama_ok: bool, gemini_ok: bool, elephant_ok: bool) -> str:
"""根據可用性推薦提供者"""
"""根據可用性推薦提供者Gemini 僅能備援,不可被推薦為主路徑。"""
if ollama_ok:
return 'ollama'
if self._default_provider == 'elephant' and elephant_ok:
return 'elephant'
if gemini_ok:
return 'gemini'
if elephant_ok:
return 'elephant'
return 'none'

View File

@@ -85,10 +85,9 @@ class ElephantAlphaOrchestrator:
),
"openclaw": AgentCapability(
name="OpenClaw Strategist",
# LOCKED-GEMINI: EA HITL 戰略決策影響統帥行動,要最高品質推理
# 未來可升 Claude Sonnet 4.6 (agentic 工具使用佳) — Phase 7 任務
# ADR-028 鎖定場景 #6
model="gemini-2.0-flash",
# Ollama-first: OpenClaw strategy/reporting must use the approved
# GCP-A → GCP-B → 111 cascade; Gemini is emergency fallback only.
model="qwen2.5-coder:7b",
strengths=["strategic_planning", "market_analysis", "insight_generation"],
limitations=["real_time_execution", "direct_actions"],
cost_per_token=0.0,
@@ -125,11 +124,16 @@ AGENT CAPABILITIES:
- Limitations: Limited strategic depth, short-term focus
- Best for: Executing actions, immediate responses, tool-based operations
3. OPENCLAW (gemini-2.0-flash)
3. OPENCLAW (qwen2.5-coder:7b / qwen3:14b via Ollama cascade)
- Strengths: Strategic planning, market analysis, insight generation
- Limitations: No direct execution capabilities, analysis-only
- Best for: Long-term strategy, market insights, recommendation generation
Gemini policy:
- Gemini is not a primary agent model.
- Gemini may only be used after the approved Ollama cascade fails and the
operator explicitly unlocks the emergency fallback guard.
YOUR SUPERVISORY CAPABILITIES:
- Cross-agent coordination and optimization
- Strategic long-term planning (weeks/months ahead)

View File

@@ -80,15 +80,34 @@ ROUTING_RULES: Dict[str, list] = {
'minicpm-v:latest'),
],
# 推理增強場景EA HITL 戰略決策;目前未啟用,預留
# 推理增強場景EA HITL 戰略決策;Gemini 不可作為預設模型
'ea_engine': [
(lambda ctx: bool(ctx.get('require_chain_of_thought', False)),
'deepseek-r1:14b'),
(lambda ctx: True,
None), # None → caller 用預設gemini-2.0-flash
'hermes3:latest'),
],
}
_CALLER_SAFE_DEFAULT_MODELS = {
'ea_engine': 'hermes3:latest',
}
def _sanitize_default_model(caller: str, default: Optional[str]) -> Optional[str]:
"""Model router must not hand Gemini back as an Ollama-route default."""
if default and default.strip().lower().startswith('gemini'):
safe_default = _CALLER_SAFE_DEFAULT_MODELS.get(caller)
if safe_default:
logger.warning(
"[ModelRouter] %s default=%s rejected; using %s",
caller,
default,
safe_default,
)
return safe_default
return default
def select_model(
caller: str,
@@ -107,6 +126,8 @@ def select_model(
flag OFF 時直接回 default不評估規則向下相容
"""
default = _sanitize_default_model(caller, default)
if not is_model_router_enabled():
return default

View File

@@ -55,3 +55,13 @@ def test_gemini_is_called_only_after_ollama_failure(monkeypatch):
assert result.success is True
assert result.provider == "gemini"
assert result.content == "gemini fallback"
def test_status_never_recommends_gemini_as_primary_provider():
service = AIProviderService(default_provider="ollama")
assert service._get_recommended_provider(
ollama_ok=False,
gemini_ok=True,
elephant_ok=False,
) == "none"

View File

@@ -486,3 +486,15 @@ def test_resource_optimization_cannot_use_legacy_autonomous_execution_template(m
asyncio.run(engine._notify_telegram_executed(decision, trigger))
assert sent == []
def test_elephant_alpha_openclaw_registry_is_ollama_first():
from services.elephant_alpha_orchestrator import ElephantAlphaOrchestrator
orchestrator = ElephantAlphaOrchestrator()
openclaw = orchestrator.agents["openclaw"]
assert not openclaw.model.startswith("gemini")
assert openclaw.model == "qwen2.5-coder:7b"
assert "Gemini is not a primary agent model" in orchestrator.system_prompt
assert "Ollama cascade" in orchestrator.system_prompt

View File

@@ -182,7 +182,7 @@ def test_ppt_vision_minicpm_unhealthy_routes_to_llava(monkeypatch):
# ═══════════════════════════════════════════════════════════════════════════
def test_ea_engine_no_cot_returns_default(monkeypatch):
"""規則命中但 model_name=None → 回 defaultcaller 用既有 Gemini"""
"""EA 不需要深推理時走免費 Ollama Hermes不得回 Gemini 預設。"""
monkeypatch.setenv('MODEL_ROUTER_ENABLED', 'true')
from services.llm_model_router import select_model
@@ -191,7 +191,19 @@ def test_ea_engine_no_cot_returns_default(monkeypatch):
context={'require_chain_of_thought': False},
default='gemini-2.0-flash',
)
assert result == 'gemini-2.0-flash'
assert result == 'hermes3:latest'
def test_ea_engine_rejects_gemini_default_even_when_router_disabled(monkeypatch):
monkeypatch.delenv('MODEL_ROUTER_ENABLED', raising=False)
from services.llm_model_router import select_model
result = select_model(
caller='ea_engine',
context={'require_chain_of_thought': False},
default='gemini-2.0-flash',
)
assert result == 'hermes3:latest'
def test_ea_engine_cot_routes_to_deepseek_r1(monkeypatch):