This commit is contained in:
@@ -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 # 用於模板顯示
|
||||
|
||||
|
||||
@@ -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-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 才能作緊急備援。
|
||||
- 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:14b;111 fallback 降級 llama3.2;NIM fallback | GCP-A → GCP-B → 111;NVIDIA NIM 備援 | Ollama 零;NIM 配額內免費 | NIM 80 |
|
||||
| OpenClaw 策略師 | qwen3:14b;111 fallback 降級 llama3.2;Gemini 鎖定場景 | Ollama-first;Gemini 備援 | Ollama 零;Gemini 需控管 | — |
|
||||
| OpenClaw 策略師 | qwen2.5-coder:7b / qwen3:14b;111 fallback 降級 llama3.2 | Ollama-first;Gemini emergency fallback only | Ollama 零;Gemini 預設封鎖 | — |
|
||||
| ElephantAlpha 編排者 | ElephantAlpha | 依部署環境 | 受控 | HITL / 任務制 |
|
||||
|
||||
---
|
||||
|
||||
@@ -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 價格懲罰豁免。
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -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"
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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 → 回 default(caller 用既有 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):
|
||||
|
||||
Reference in New Issue
Block a user