校準 GCP-B embedding timeout
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:
12
.env.example
12
.env.example
@@ -155,9 +155,9 @@ HERMES_ALLOW_111_FALLBACK=false
|
||||
|
||||
# [選填] Embedding 服務主機;留空時自動走同一條 Ollama 三主機級聯
|
||||
EMBEDDING_HOST=
|
||||
# [預設 15] Embedding API timeout;優先使用 Ollama /api/embed,舊節點 fallback /api/embeddings
|
||||
EMBEDDING_TIMEOUT=15
|
||||
OLLAMA_EMBED_MAX_TIMEOUT=15
|
||||
# [預設 30] Embedding API timeout;優先使用 Ollama /api/embed,舊節點 fallback /api/embeddings
|
||||
EMBEDDING_TIMEOUT=30
|
||||
OLLAMA_EMBED_MAX_TIMEOUT=30
|
||||
OLLAMA_EMBED_KEEP_ALIVE=1m
|
||||
OLLAMA_EMBED_MAX_CHARS=4000
|
||||
OLLAMA_EMBED_GCP_FAILURE_COOLDOWN_SEC=60
|
||||
@@ -410,8 +410,8 @@ OLLAMA_HOST_FALLBACK=http://192.168.0.111:11434
|
||||
OLLAMA_MODEL=gemma3:4b
|
||||
OLLAMA_TIMEOUT=120
|
||||
OLLAMA_COPY_TIMEOUT=180
|
||||
OLLAMA_EMBED_TIMEOUT=15
|
||||
OLLAMA_EMBED_MAX_TIMEOUT=15
|
||||
OLLAMA_EMBED_TIMEOUT=30
|
||||
OLLAMA_EMBED_MAX_TIMEOUT=30
|
||||
OLLAMA_EMBED_KEEP_ALIVE=1m
|
||||
OLLAMA_EMBED_MAX_CHARS=4000
|
||||
OLLAMA_EMBED_GCP_FAILURE_COOLDOWN_SEC=60
|
||||
@@ -419,7 +419,7 @@ OLLAMA_EMBED_GCP_FAILURE_NOTICE_SEC=30
|
||||
OLLAMA_HOST_HEALTH_MODEL_PROBE_ENABLED=true
|
||||
OLLAMA_HOST_HEALTH_MODEL_PROBE_INCLUDE_111=false
|
||||
OLLAMA_HOST_HEALTH_EMBED_MODEL=bge-m3:latest
|
||||
OLLAMA_HOST_HEALTH_EMBED_TIMEOUT=8
|
||||
OLLAMA_HOST_HEALTH_EMBED_TIMEOUT=30
|
||||
OLLAMA_HOST_HEALTH_EMBED_KEEP_ALIVE=1m
|
||||
# 111 是 Mac final fallback,不承接 7B+ / vision / long-context / 長輸出任務;落到 111 時自動降級與縮短常駐。
|
||||
OLLAMA_111_MODEL_FALLBACK=llama3.2:latest
|
||||
|
||||
@@ -4,6 +4,7 @@
|
||||
================================================================================
|
||||
|
||||
【已完成】
|
||||
- V10.471 依 GCP-B `bge-m3` 實測 latency 調整 embedding timeout,已部署正式環境並確認 `/health=V10.471`:GCP-B `/api/embed` 三次實測約 6.4s / 7.3s / 23.5s,原本 `OLLAMA_EMBED_MAX_TIMEOUT=15` 與 host health `8s` 會誤殺慢但成功的 embedding;已將背景 embedding cap 與 host health model probe timeout 預設調為 30s。正式 smoke 顯示容器內 embedding 回 1024 維、耗時 10.07s;手動 host health probe 後最新狀態為 GCP-A unhealthy、GCP-B healthy、111 healthy。路由安全不變:背景 embedding 仍只跑 GCP-A/GCP-B,不落 111。
|
||||
- V10.470 強化 Ollama host health probe,已部署正式環境並確認 `/health=V10.470`:scheduler 與觀測台 host health 對 GCP-A / GCP-B 除 `/api/tags` 外,再做短 `bge-m3` `/api/embed` 實作探針;可抓出 GCP-B「tags/version 正常但 embedding runner 8s timeout」這類假健康。111 預設不做背景 embedding probe,避免監控任務把 `bge-m3` 載入 fallback Mac。正式 smoke 後 `host_health_probes` 最新狀態為 GCP-A unhealthy、GCP-B unhealthy、111 healthy。
|
||||
- V10.469 將背景 embedding 的 GCP-only 全失敗改為專業降級語意,已部署正式環境並確認 `/health=V10.469`:`allow_111_fallback=False` 時若 GCP-A/GCP-B 都不可用,開啟 failure circuit 並記 WARNING,不再把可預期的背景熔斷每分鐘打成 ERROR;同步 / 允許 fallback 的 embedding 全失敗仍保留 ERROR。Smoke 顯示 GCP-B `/api/version` 可用,但 `/api/embed` 仍可能 15s timeout,下一步需修 GCP-A primary 與 GCP-B runner/model 負載。
|
||||
- V10.468 補 Ollama import-time 防凍結與背景 embedding GCP failure circuit,已部署正式環境並確認 `/health=V10.468`:`config.OLLAMA_HOST` / `HERMES_URL` / `EMBEDDING_HOST` 舊相容常數不再於 import 時 probe network,也不會因 GCP-A/GCP-B 暫時拒連而 freeze 到 111;動態 caller 仍走 `get_*()` / `OllamaService` 三主機級聯。當 `allow_111_fallback=False` 且 GCP-A/GCP-B 皆失敗時,短暫熔斷 60 秒,不重複打兩台 GCP、不落 111,降低 app/scheduler 因連續 embedding timeout 造成的 log 與 worker 壓力;部署 smoke 時 GCP-B `/api/version` 已恢復 200 並成為動態路由落點,GCP-A `22/11434` 仍拒連,需後續用 GCP 權限修復 primary Ollama 主機。
|
||||
|
||||
@@ -350,7 +350,7 @@ YOUTUBE_API_KEY = os.getenv('YOUTUBE_API_KEY', '')
|
||||
# ==========================================
|
||||
# 系統版本與路徑
|
||||
# ==========================================
|
||||
SYSTEM_VERSION = "V10.470"
|
||||
SYSTEM_VERSION = "V10.471"
|
||||
LOG_FILE_PATH = os.path.join(BASE_DIR, 'logs/system.log')
|
||||
public_url = PUBLIC_URL # 用於模板顯示
|
||||
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
> **最後更新**: 2026-05-25 (台北時間)
|
||||
> **狀態**: 🟢 四 AI Agent 自動化閉環已落地;LLM 路由紅線升級為 Ollama-first 三主機級聯,Gemini 備援預設關閉
|
||||
> **適用版本**: V10.470
|
||||
> **適用版本**: V10.471
|
||||
|
||||
---
|
||||
|
||||
@@ -21,11 +21,11 @@
|
||||
- Code Review Hermes scan 預設不呼叫 LLM,改用 deterministic fast static scan,避免部署後先卡三段 Ollama timeout;需要 LLM 掃描時才以 `CODE_REVIEW_HERMES_LLM_SCAN_ENABLED=true` 啟用本地矩陣。
|
||||
- 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-B:GCP-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。
|
||||
- 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` 預設 `30s`、`OLLAMA_EMBED_KEEP_ALIVE=1m`、`OLLAMA_EMBED_MAX_CHARS=4000`;此上限依 GCP-B `bge-m3` 實測 6–23s 波動調整,避免慢但成功的 embedding 被 15s cap 誤殺。
|
||||
- `allow_111_fallback=False` 時,若 resolver 因 unhealthy cache 回傳 111,不得直接結束 embedding;必須強制改試尚未嘗試的 GCP-A / GCP-B,避免正式 log 出現 `tried=[]` 或只試單台 GCP-B。
|
||||
- `allow_111_fallback=False` 且 GCP-A / GCP-B 皆失敗時,背景 embedding 會開啟短暫 GCP failure circuit(預設 60 秒),期間不重複打兩台 GCP、不落 111,避免 worker 與 log 被連續失敗拖慢;GCP 恢復後會自然再試。
|
||||
- 背景 embedding 的 GCP-only 熔斷屬於可降級背景能力,應記錄為明確 WARNING 與 circuit 狀態,不應每次污染 ERROR 通道;真正允許三主機 fallback 的同步 embedding 全失敗仍保留 ERROR。
|
||||
- Scheduler host health probe 不只看 `/api/tags`;GCP-A / GCP-B 節點必須再通過短 `bge-m3` `/api/embed` 實作探針,才算 healthy。111 預設不納入這個背景 embedding 探針,避免監測任務把 fallback Mac 載入 `bge-m3`。
|
||||
- Scheduler host health probe 不只看 `/api/tags`;GCP-A / GCP-B 節點必須再通過 `bge-m3` `/api/embed` 實作探針,才算 healthy。探針 timeout 預設 30s,111 預設不納入這個背景 embedding 探針,避免監測任務把 fallback Mac 載入 `bge-m3`。
|
||||
- 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 → 111;Gemini 只允許以 `openclaw_bot_image_gemini` caller 作為失敗後備援。
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
- 2026-05-25 12:27 CST 狀態:已部署 `V10.468` 到 188,正式 `/health` 為 `V10.468`。本輪 recreate `momo-app`、`scheduler`、`telegram-bot`;未使用 `--remove-orphans`,未碰 `momo-db`。Smoke 通過:三個 app 容器 healthy、`/`、`/daily_sales`、`/growth_analysis`、`/observability/ppt_audit_history`、PChome review queue API `/api/pchome-review/queue` HTTP 200;容器內 mock smoke 證明背景 embedding 在 GCP-A / GCP-B 全失敗後會開啟 60 秒 failure circuit,第二筆不再重複打兩台 GCP,且不落 111。GCP 維運盤點:GCP-A `22/11434` refused;GCP-B `22` open 但現有 key publickey denied,部署 smoke 時 GCP-B `11434` 已恢復 200、`get_ollama_host()` 選到 GCP-B;111 `/api/version` 可用,但 111 仍不得承接背景 `bge-m3`。
|
||||
- 2026-05-25 12:39 CST 狀態:已部署 `V10.469` 到 188,正式 `/health` 為 `V10.469`。本輪 recreate `momo-app`、`scheduler`、`telegram-bot`;未使用 `--remove-orphans`,未碰 `momo-db`。Smoke 通過:三個 app 容器 healthy、首頁 / daily / growth / PChome review queue HTTP 200、Gemini hard disabled;`allow_111_fallback=False` 時 GCP-only embedding 全失敗會開啟 failure circuit 並記 WARNING,不再把預期內的背景熔斷打進 ERROR 通道。觀測到 GCP-B `/api/version` 200,但 `/api/embed` 仍可能 15s timeout,下一步需修 GCP-A primary 與 GCP-B runner/model 負載。
|
||||
- 2026-05-25 12:53 CST 狀態:已部署 `V10.470` 到 188,正式 `/health` 為 `V10.470`。本輪 recreate `momo-app`、`scheduler`、`telegram-bot`;未使用 `--remove-orphans`,未碰 `momo-db`。Smoke 通過:三容器 healthy、host health page HTTP 200 並顯示 Runtime 狀態、scheduler probe 寫入 DB。最新 `host_health_probes`:GCP-A unhealthy(11434 refused)、GCP-B unhealthy(`EmbedProbe ReadTimeout`, `/api/tags` 仍可列出 4 模型)、111 healthy;這補上「HTTP API 活著但模型 runtime 卡住」的假健康監控缺口。
|
||||
- 2026-05-25 13:38 CST 狀態:已部署 `V10.471` 到 188,正式 `/health` 為 `V10.471`。本輪 recreate `momo-app`、`scheduler`、`telegram-bot`;未使用 `--remove-orphans`,未碰 `momo-db`。Smoke 通過:三容器 healthy、首頁 / daily / growth / host_health / ppt_audit_history / PChome review queue HTTP 200。GCP-B `bge-m3` `/api/embed` 直接實測約 6.4s、7.3s、23.5s,原 `OLLAMA_EMBED_MAX_TIMEOUT=15` 與 host health `OLLAMA_HOST_HEALTH_EMBED_TIMEOUT=8` 會誤殺慢但成功的 embedding;預設改為 30s。正式容器內 embedding smoke 回 1024 維、耗時 10.07s;手動 host health probe 後最新狀態為 GCP-A unhealthy、GCP-B healthy、111 healthy。背景 embedding 路由安全不變:GCP-A → GCP-B,不落 111。
|
||||
- 2026-05-25 12:05 CST 狀態:`main` 已部署到 188,正式 `/health` 為 `V10.467`,待推 Gitea。兩段變更已合併驗證:V10.466 rescore duplicate 改看 latest-state,7 筆 SKU 最新 attempt 全為 `rescore_accepted_current`,`competitor_prices` / `competitor_price_history` 目標計數未變;V10.467 focused exact matcher 在容器內回 `exact / total_price / price_alert_exact`。本輪 recreate `momo-app`、`scheduler`、`telegram-bot`;未使用 `--remove-orphans`,未碰 `momo-db`。Smoke 通過:三容器 healthy、PChome rescore queue API HTTP 200、Gemini 24 小時無 provider 紀錄、Ollama env 順序維持 GCP-A → GCP-B → 111、3 分鐘三容器 log 未見 Traceback / ERROR / CRITICAL / IntegrityError。
|
||||
|
||||
## 1. MOMO / PChome 核心比價準確率
|
||||
@@ -79,6 +80,7 @@
|
||||
- 2026-05-25 08:48 CST 起,`OllamaService.generate_embedding(..., allow_111_fallback=False)` 若 resolver 回 111,會強制改試尚未嘗試的 GCP-A/GCP-B,不再讓背景 embedding 在 111 disabled 情境直接退出或只試單台 GCP-B;111 仍不承接背景 `bge-m3`。
|
||||
- 2026-05-25 12:27 CST 起,背景 embedding 在 GCP-A/GCP-B 全掛時開啟短暫 failure circuit;這是降載保護,不代表 primary 已恢復。部署 smoke 時 GCP-B `/api/version` 已恢復 200,下一步仍需恢復 GCP-A Ollama 或更新 110 的可用 SSH/GCP 操作憑證。
|
||||
- 2026-05-25 12:37 CST 起,背景 embedding GCP-only failure circuit 改用 WARNING 記錄,避免可預期降級污染 ERROR 告警通道。
|
||||
- 2026-05-25 13:35 CST 起,GCP-B `bge-m3` 實測 P95 波動已超過 15s,背景 embedding / host health model probe timeout 預設改 30s;若 30s 仍常 timeout,需進一步處理 GCP-B runner/CPU/模型併發,而不是再把 111 納入背景任務。
|
||||
|
||||
## 4. 業績分析資料與圖表修復
|
||||
|
||||
|
||||
@@ -13,6 +13,7 @@
|
||||
## 📅 詳細更新日誌 (考古存檔)
|
||||
|
||||
### 2026-05-24:PChome 近門檻身份回收第二輪
|
||||
- **V10.471 GCP-B embedding timeout 校準**: GCP-B `bge-m3` `/api/embed` 直接實測約 6.4s / 7.3s / 23.5s,原 `OLLAMA_EMBED_MAX_TIMEOUT=15` 與 host health `OLLAMA_HOST_HEALTH_EMBED_TIMEOUT=8` 會誤判慢但成功的 embedding;預設改為 30s。背景 embedding 仍只跑 GCP-A/GCP-B,不落 111。
|
||||
- **V10.470 Ollama host health 實作探針**: `run_host_health_probe()` 對 GCP-A / GCP-B 在 `/api/tags` 成功後追加短 `bge-m3` `/api/embed` probe,避免 GCP-B 出現 tags/version 正常、但實際 embedding runner 20s timeout 時仍被標 healthy;111 預設不做背景 embedding probe,避免監測任務把 fallback Mac 載入 `bge-m3`。
|
||||
- **V10.469 Background embedding 降級語意修正**: `OllamaService.generate_embedding(..., allow_111_fallback=False)` 在 GCP-A/GCP-B 全失敗時會開啟短暫 failure circuit 並記 WARNING,不再把背景 `bge-m3` 降級熔斷每分鐘寫成 ERROR;同步或允許三主機 fallback 的 embedding 全失敗仍維持 ERROR,保留真正阻塞型故障訊號。
|
||||
- **V10.468 Ollama import-time / embedding 熔斷治理**: `config.OLLAMA_HOST`、`HERMES_URL`、`EMBEDDING_HOST` 舊相容常數改成靜態核准 env reader,不再於 import 時呼叫 `resolve_ollama_host()`,避免 GCP-A/GCP-B 短暫拒連時把 process 常數 freeze 到 111。`generate_embedding(..., allow_111_fallback=False)` 在 GCP-A/GCP-B 都失敗後會開短暫 GCP embedding circuit,避免背景任務每筆重打兩台故障主機;111 仍不承接背景 `bge-m3`。維運盤點曾見 110 proxy 11435/11436 因 GCP 11434 refused 回 502;部署 smoke 時 GCP-B `/api/version` 已恢復 200 並成為動態路由落點,GCP-A 22/11434 仍 refused,後續需以 GCP 權限恢復 primary Ollama 主機或 SSH key。
|
||||
|
||||
@@ -22,7 +22,7 @@ def host_health_model_probe_enabled(label: str) -> bool:
|
||||
def probe_ollama_embedding_runtime(requests_module, host: str) -> tuple[bool, str | None]:
|
||||
"""Verify Ollama can serve a tiny embedding, not just answer /api/tags."""
|
||||
model = os.getenv("OLLAMA_HOST_HEALTH_EMBED_MODEL", "bge-m3:latest")
|
||||
timeout = float(os.getenv("OLLAMA_HOST_HEALTH_EMBED_TIMEOUT", "8"))
|
||||
timeout = float(os.getenv("OLLAMA_HOST_HEALTH_EMBED_TIMEOUT", "30"))
|
||||
keep_alive = os.getenv("OLLAMA_HOST_HEALTH_EMBED_KEEP_ALIVE", "1m")
|
||||
try:
|
||||
resp = requests_module.post(
|
||||
|
||||
@@ -56,7 +56,7 @@ DEFAULT_MODEL = os.getenv('OLLAMA_MODEL', 'llama3.1:8b') # 較快速的模型
|
||||
TIMEOUT = int(os.getenv('OLLAMA_TIMEOUT', '120')) # 秒 - 2 分鐘
|
||||
COPY_TIMEOUT = int(os.getenv('OLLAMA_COPY_TIMEOUT', '180')) # 文案生成專用超時 - 3 分鐘
|
||||
EMBED_TIMEOUT = int(os.getenv('OLLAMA_EMBED_TIMEOUT', os.getenv('EMBEDDING_TIMEOUT', '45')))
|
||||
EMBED_MAX_TIMEOUT = int(os.getenv('OLLAMA_EMBED_MAX_TIMEOUT', '15'))
|
||||
EMBED_MAX_TIMEOUT = int(os.getenv('OLLAMA_EMBED_MAX_TIMEOUT', '30'))
|
||||
EMBED_KEEP_ALIVE = os.getenv('OLLAMA_EMBED_KEEP_ALIVE', '1m')
|
||||
EMBED_MAX_CHARS = int(os.getenv('OLLAMA_EMBED_MAX_CHARS', '4000'))
|
||||
EMBED_GCP_FAILURE_COOLDOWN_SEC = int(os.getenv('OLLAMA_EMBED_GCP_FAILURE_COOLDOWN_SEC', '60'))
|
||||
|
||||
@@ -154,6 +154,33 @@ def test_host_health_probe_verifies_gcp_embedding_runtime(monkeypatch):
|
||||
assert run_scheduler._host_health_model_probe_enabled("Fallback (111)") is False
|
||||
|
||||
|
||||
def test_host_health_embedding_probe_uses_30s_default_timeout(monkeypatch):
|
||||
run_scheduler = _load_run_scheduler(monkeypatch)
|
||||
monkeypatch.delenv("OLLAMA_HOST_HEALTH_EMBED_TIMEOUT", raising=False)
|
||||
seen = {}
|
||||
|
||||
class Resp:
|
||||
status_code = 200
|
||||
|
||||
def json(self):
|
||||
return {"embeddings": [[0.1, 0.2, 0.3]]}
|
||||
|
||||
class Requests:
|
||||
@staticmethod
|
||||
def post(*_args, **kwargs):
|
||||
seen["timeout"] = kwargs["timeout"]
|
||||
return Resp()
|
||||
|
||||
ok, err = run_scheduler._probe_ollama_embedding_runtime(
|
||||
Requests,
|
||||
"http://34.21.145.224:11434",
|
||||
)
|
||||
|
||||
assert ok is True
|
||||
assert err is None
|
||||
assert seen["timeout"] == 30.0
|
||||
|
||||
|
||||
def test_host_health_probe_reports_embedding_runtime_failure(monkeypatch):
|
||||
run_scheduler = _load_run_scheduler(monkeypatch)
|
||||
|
||||
|
||||
Reference in New Issue
Block a user