V10.567 收斂 MCP 市場洞察 fallback
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
All checks were successful
CD Pipeline / deploy (push) Successful in 1m6s
This commit is contained in:
@@ -402,7 +402,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.566"
|
||||
SYSTEM_VERSION = "V10.567"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -104,6 +104,7 @@
|
||||
- 2026-05-31 起,`V10.506` 新增市場情報 MCP Fetch Candidate Queue Writer Review Decision Approval gate:在 review decision 通過後只審核 operator human approval 摘要,要求 decision linkage、approval identity、target table、row count、dedupe keys、`approved_for_writer_preflight` approval result、decision/approval evidence refs、artifact paths、matched row exact-identity/variant/overwrite guard 與 operator confirmation 對齊;仍不讀 token、不執行 CLI、不開 DB、不寫 approval record、不寫 decision record、不更新 review_state、不寫 match result、不補 queue、不掛 scheduler,只放行到後續 writer preflight 設計。
|
||||
- 2026-05-31 起,`V10.509` 新增市場情報 MCP Fetch Candidate Queue Writer Review Decision Approval Writer Preflight gate:在 human approval 通過後只審核 operator writer preflight 摘要,要求 approval linkage、writer_preflight_id、target operation、row count、dedupe keys、approved decision 到 target review_state 的逐列映射、decision/approval/preflight evidence refs、matched row exact-identity/variant/overwrite guard 與 operator boundary;仍不讀 token、不執行 CLI、不開 DB、不寫 preflight/approval/decision/match、不更新 review_state、不補 queue、不掛 scheduler,只放行到後續 CLI review / run package 設計。
|
||||
- 2026-06-01 起,`V10.566` 新增市場情報 Professional Source Governance gate:將 robots/REP、sitemap/lastmod、JSON-LD / schema.org structured data、canonical URL、rate limit、公開資料邊界、provenance、snapshot hash 與 idempotency key 納入 source contract,並接上 `/api/market_intel/mcp_professional_source_governance`、UI preview panel、deployment readiness check 與 production smoke target;仍不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不掛 scheduler。
|
||||
- 2026-06-02 起,`V10.567` 將 MCP 市場洞察 fallback 收斂為 GCP-A / GCP-B only,不再讓 111 承接非即時市場分析長任務;預設 timeout 25 秒、`num_predict` 500,GCP 不可用時直接保守降級,避免 Elephant Alpha 60 秒 timeout 與 111 負載尖峰。
|
||||
|
||||
## 3. 12 Agent 決策信封整合
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-06-01:PChome 比價新鮮度操作閉環
|
||||
- **V10.567 MCP 市場洞察 GCP-only fallback**: `MCPCollectorService._ollama_topic_fallback()` 改成只使用 GCP-A / GCP-B Ollama,失敗後保守回本地 fallback,不再把市場洞察長分析轉嫁到 111。預設 timeout 收斂為 25 秒、`num_predict` 收斂為 500,避免 Elephant Alpha 在 GCP-A/GCP-B 短暫不可用時撞 60 秒總上限或造成 111 負載尖峰;Gemini 仍維持 `GEMINI_API_HARD_DISABLED=true` 預設硬封鎖。
|
||||
- **V10.565 PChome 覆蓋率操作建議**: 補強 `/api/ai/pchome-match/backfill/status`,將低覆蓋率拆成 `operation_backlog`:刷新舊 identity、重評近門檻、補抓未配對、人工覆核、單位價覆核與過期搜尋救援預覽;並新增 `recommended_next_action`,Dashboard 狀態摘要會直接顯示建議下一步,避免使用者只看到低覆蓋率卻不知道該按哪條產線。
|
||||
- **V10.563 正式 preview 假可救候選收斂**: 針對正式 `retryable_candidate_preview` 露出的 M.A.C 蜜粉與 SAUGELLA 菁萃潔浴凝露案例補 guard。M.A.C 單邊明確色號(如 `#絕絕紫`)會進 `variant_selection_review`,維持 `true_low_confidence`;SAUGELLA 潤澤 / 日用型 / 加強 / 黃金女郎型互斥,直接 hard veto,避免同品線不同私密清潔款式被當成 recoverable low_score。
|
||||
- **V10.566 市場情報 Professional Source Governance**: 新增 `/api/market_intel/mcp_professional_source_governance`、preview service/gates/sample 與市場情報頁卡片,將 robots/REP、sitemap/lastmod、JSON-LD / schema.org structured data、canonical URL、rate limit、公開資料邊界、provenance、snapshot hash 與 idempotency key 納入 source contract。此 gate 只審核操作員治理摘要,不抓外站、不讀 robots/sitemap、不開 DB、不寫檔、不掛 scheduler;deployment readiness 同步新增 preview-safe 檢查與 production smoke target。
|
||||
|
||||
@@ -33,10 +33,13 @@ from services.gemini_guard import (
|
||||
logger = logging.getLogger(__name__)
|
||||
|
||||
MCP_CACHE_TTL_HOURS = int(os.getenv("MCP_CACHE_TTL_HOURS", "24"))
|
||||
# MCP router 是即時情報主路徑;router 不可用時先走 Ollama 三主機級聯做離線洞察,
|
||||
# MCP router 是即時情報主路徑;router 不可用時先走 GCP Ollama 做離線洞察。
|
||||
# 市場洞察屬非即時必需批次工作,不把長分析轉嫁到 111 fallback。
|
||||
# Gemini Grounding 僅作最後備援,避免再次回到 Gemini-first。
|
||||
MCP_MODEL = os.getenv("MCP_GEMINI_MODEL", "gemini-2.0-flash")
|
||||
MCP_FALLBACK_MODEL = "gemini-1.5-flash"
|
||||
MCP_OLLAMA_TIMEOUT = int(os.getenv("MCP_OLLAMA_TIMEOUT", "25"))
|
||||
MCP_OLLAMA_NUM_PREDICT = int(os.getenv("MCP_OLLAMA_NUM_PREDICT", "500"))
|
||||
|
||||
try:
|
||||
from services.ollama_service import OllamaService
|
||||
@@ -266,11 +269,11 @@ class MCPCollectorService:
|
||||
return self._fallback_topic_content(topic, f"即時外部搜尋暫不可用:{type(e).__name__}")
|
||||
|
||||
def _ollama_topic_fallback(self, topic: str, query: str) -> Optional[str]:
|
||||
"""MCP/搜尋不可用時先走 Ollama 三主機級聯;Gemini 只留最後備援。"""
|
||||
"""MCP/搜尋不可用時先走 GCP Ollama;111 不承接市場洞察長任務。"""
|
||||
if not _OLLAMA_AVAILABLE:
|
||||
return None
|
||||
try:
|
||||
logger.info("[MCP] Using GCP-A/GCP-B/111 Ollama for market insight fallback topic=%s", topic)
|
||||
logger.info("[MCP] Using GCP-A/GCP-B Ollama for market insight fallback topic=%s", topic)
|
||||
ollama_model = os.getenv('OPENCLAW_OLLAMA_MODEL', 'qwen2.5-coder:7b')
|
||||
ollama_prompt = (
|
||||
f"你是一位精通台灣電商市場的分析師。目前無法取得即時搜尋結果,"
|
||||
@@ -282,15 +285,16 @@ class MCPCollectorService:
|
||||
prompt=ollama_prompt,
|
||||
model=ollama_model,
|
||||
temperature=0.4,
|
||||
timeout=45,
|
||||
options={'num_predict': 800},
|
||||
timeout=MCP_OLLAMA_TIMEOUT,
|
||||
options={'num_predict': MCP_OLLAMA_NUM_PREDICT},
|
||||
allow_111_fallback=False,
|
||||
)
|
||||
content = (resp.content or '').strip() if resp.success else ''
|
||||
if content and not self._looks_unreliable(content):
|
||||
# 不進快取,因為這是預測性內容。
|
||||
return content
|
||||
if not resp.success:
|
||||
logger.warning("[MCP] Ollama cascade fallback failed: %s", resp.error)
|
||||
logger.warning("[MCP] GCP Ollama fallback failed: %s", resp.error)
|
||||
except Exception as exc:
|
||||
logger.warning("[MCP] Ollama fallback failed: %s", exc)
|
||||
return None
|
||||
|
||||
@@ -35,4 +35,35 @@ def test_mcp_collector_uses_ollama_before_gemini(monkeypatch):
|
||||
assert "基於歷史趨勢" in result
|
||||
assert calls
|
||||
assert calls[0]["model"]
|
||||
assert calls[0]["options"]["num_predict"] == 800
|
||||
assert calls[0]["allow_111_fallback"] is False
|
||||
assert calls[0]["timeout"] == mcp_mod.MCP_OLLAMA_TIMEOUT
|
||||
assert calls[0]["options"]["num_predict"] == mcp_mod.MCP_OLLAMA_NUM_PREDICT
|
||||
|
||||
|
||||
def test_mcp_collector_market_insight_does_not_use_111_fallback(monkeypatch):
|
||||
import services.mcp_collector_service as mcp_mod
|
||||
|
||||
service = mcp_mod.MCPCollectorService()
|
||||
monkeypatch.setattr(mcp_mod, "_OLLAMA_AVAILABLE", True)
|
||||
calls = []
|
||||
|
||||
class FakeOllamaService:
|
||||
def __init__(self, model=None):
|
||||
self.model = model
|
||||
|
||||
def generate(self, **kwargs):
|
||||
calls.append(kwargs)
|
||||
return SimpleNamespace(
|
||||
success=False,
|
||||
content="",
|
||||
error="111 fallback disabled; no approved GCP Ollama host available",
|
||||
)
|
||||
|
||||
monkeypatch.setattr(mcp_mod, "OllamaService", FakeOllamaService)
|
||||
|
||||
result = service._ollama_topic_fallback("market_trends", "台灣電商促銷趨勢")
|
||||
|
||||
assert result is None
|
||||
assert calls
|
||||
assert calls[0]["allow_111_fallback"] is False
|
||||
assert calls[0]["timeout"] <= 25
|
||||
|
||||
Reference in New Issue
Block a user