fix(ai): fallback PixelRAG platform probe runtime gaps

This commit is contained in:
ogt
2026-07-10 10:03:22 +08:00
parent 4982819493
commit 39d9918f12
5 changed files with 143 additions and 8 deletions

View File

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

View File

@@ -120,6 +120,7 @@
- 2026-07-09 起 PixelRAG VLM route readiness 必須在 execute 前可讀回:`/api/ai-automation/pixelrag-vlm-route-readiness``scripts/ops/report_pixelrag_vlm_route_readiness.py` 只讀 approved Ollama `/api/tags`,輸出 configured model、candidate model、reachable host、model_route_ready 與 next machine action`/api/ai-automation/smoke` 需包含 `PixelRAG VLM route readiness``/api/ai-automation/scheduled-health-summary` 需輸出 `pixelrag_vlm_route_readiness` family。此 readback 不呼叫 `/api/generate`、不讀 secret、不寫 DBworker execute 必須使用它自動避開缺模型路由,完全缺候選時寫 `model_route_not_ready` artifact receipt。
- 2026-07-10 起 PixelRAG platform probe 必須把 blocked / interstitial receipt 轉成機器可執行下一步:`/api/ai-automation/pixelrag-platform-probe``scripts/ops/report_pixelrag_platform_probe.py` 需讀取 capture receipts 與 VLM replay receipts辨識 Shopee language / region / generic marketplace slogan / traffic verification 與 Coupang 403 / access denied輸出 public browser context probe、structured source fallback、platform backoff、blocked page not product data 與 no-write 邊界;`/api/ai-automation/smoke` 需包含 `PixelRAG platform probe readiness``/api/ai-automation/scheduled-health-summary` 需輸出 `pixelrag_platform_probe` family。此 readback 不抓外站、不讀 secret/cookie/session、不寫 DB、不寫 `ai_insights`、不寫正式價格表capture worker 可消化 `public_browser_context` 的 locale/timezone/Accept-Language但必須濾掉 Cookie、Authorization、token/secret/key 類 header。
- 2026-07-10 起 PixelRAG platform probe 必須有 controlled worker`/api/ai-automation/pixelrag-platform-probe-worker``scripts/ops/run_pixelrag_platform_probe_worker.py` 預設 dry-run`execute=true&write_receipt=true` 才會對 Shopee language / traffic / generic landing 執行 public empty-context capture並對 Coupang 403 / access denied 自動產生 structured-source/backoff artifact receipt`/api/ai-automation/smoke` 需包含 `PixelRAG platform probe worker``/api/ai-automation/scheduled-health-summary` 需輸出 `pixelrag_platform_probe_worker` family。此 worker 不登入、不讀 cookie/session/secret、不寫 DB、不寫 `ai_insights`、不寫正式價格表capture 成功後下一步固定為 RAG candidate replaystructured fallback 只產生機器可讀 package不把 blocked page 當商品資料。
- 2026-07-10 起 PixelRAG platform probe worker 必須自動處理 capture runtime gap若 production app 容器缺 Playwright`execute=true&write_receipt=true` 不得退回人工審核或 critical dead-endworker 必須寫 `capture_runtime_unavailable_structured_fallback_package` artifact receipt標記 `network_call=false``writes_database=false``primary_human_gate_count=0`,並把下一步改為 `run_structured_source_or_install_pixelrag_capture_runtime`
- 2026-07-02 起 `/ai_intelligence` 商品明細與單品作戰詳情的四格價格證據必須可測PChome 價格、MOMO 參考價、差距、可信度需以 `data-evidence` 固定,並以 `aria-label="價格證據"` 對應可掃描區塊;候選待確認或缺資料只能顯示「候選待確認 / 待補」,不得捏造價格或讓使用者打開 raw payload 才知道判斷依據。
- 2026-07-02 起 `/ai_intelligence` 必須是密集 AI 工作台不得退回大段文字說明頁首屏與明細可見內容只保留短狀態、數字、四格證據與下一步按鈕KPI note、benchmark detail、alert 副句、策略說明、decision copy、來源長句與單品 reason list 不得佔用第一層視覺。`tests/test_ai_intelligence_text_density_guardrails.py` 必須鎖住 `data-density-guardrail="compact-ai-workbench"`、短任務文案、detail meta 與 hidden explanatory copy。
- 2026-07-02 起 `/observability/overview` 也必須採密集 AI 觀測工作台:首屏以 `data-density-guardrail="compact-observability-workbench"``AI 觀測 / 風險優先 / 下一步` 與 golden signals 先呈現狀態、數字與操作入口hero lede、signal note、route desc、host meta 與資料來源長句不得佔用第一層視覺。`tests/test_observability_text_density_guardrails.py` 必須鎖住 compact marker 與 hidden explanatory copy。
@@ -892,6 +893,7 @@ POSTGRES_HOST=momo-db
| 2026-07-10 | PixelRAG VLM language note / generic marketplace slogan 必須視為 interstitial | V10.758 起若 VLM notes 或 title 顯示 `Language selection page`、語言/地區選擇,或只抽到 Shopee `蝦皮購物 | 花得更少買得更好` 這類網站 slogan 且 price/shop 缺失worker validation 會標記 `interstitial_signal_detected` / `generic_marketplace_title_detected``non_product_or_interstitial_detected=true`run-level 與 item-level 下一步都輸出 `run_platform_probe_or_use_structured_api`。 |
| 2026-07-10 | PixelRAG platform probe 必須自動消化 marketplace interstitial / blocked receipt | V10.759 起 `/api/ai-automation/pixelrag-platform-probe``scripts/ops/report_pixelrag_platform_probe.py``/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary` 必須輸出 platform probe / `pixelrag_platform_probe` familyShopee traffic / language / generic landing 會轉成 `run_shopee_public_context_probe_then_structured_source_fallback`Coupang 403 / access denied 會轉成 `use_structured_source_or_platform_backoff_policy`。probe plan 只產生 public empty browser context、structured adapter fallback 與 no-write contract不讀或注入 cookie/session/login不寫 DB、不寫 `ai_insights`、不寫正式價格表capture worker 只允許 locale/timezone/非敏感 header 並濾掉 Cookie / Authorization / token / secret / key 類 header。 |
| 2026-07-10 | PixelRAG platform probe worker 必須把 probe plan 變成 controlled automation | V10.760 起 `/api/ai-automation/pixelrag-platform-probe-worker``scripts/ops/run_pixelrag_platform_probe_worker.py``/api/ai-automation/smoke``/api/ai-automation/scheduled-health-summary` 必須輸出 controlled platform probe worker / `pixelrag_platform_probe_worker` familydry-run 只規劃 capture 或 structured fallbackexecute 模式才執行 public empty-context capture 或寫 structured-source/backoff artifact receipt。此 worker 明確標記 `network_call` 只在 capture execute 時為 true、`model_call=false``writes_database=false``writes_ai_insights=false``writes_price_tables=false``secret_read=false``raw_cookie_or_session_read=false``primary_human_gate_count=0`capture 成功後必須回到 PixelRAG RAG candidate replay不得直接寫正式價格或把 blocked page 當商品資料。 |
| 2026-07-10 | PixelRAG platform probe worker 缺 capture runtime 時必須自動降級 | V10.761 起若 `run_pixelrag_platform_probe_worker.py --execute --write-receipt` 在 app 容器偵測到 `ModuleNotFoundError: playwright`worker 會自動產生 `capture_runtime_unavailable_structured_fallback_package` receipt`network_call=false``artifact_write=true``writes_database=false``primary_human_gate_count=0`,不再把 Shopee/Coupang platform probe runtime gap 變成人工主流程。 |
| 2026-06-29 | PChome DB apply 授權 lane 必須先通過 no-write guard / decision preflight / decision closeout / issuer gate / signing-decision preflight / signing-decision closeout / signing-issuer guard | V10.725 的 PChome mapping backlog auto-policy 已新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-lane-guard``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-preflight``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-decision-closeout``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-issuer-gate``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-preflight``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-decision-closeout``/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-guard`;這些 endpoint 只驗證 final exact request package、same-run production truth requirement、secret rejection、rollback boundary、lane entry requirements、decision input requirements、rejection policy、post-apply verifier、future authorization decision package、final nonsecret authorization envelope、signing decision preflight inputs、unsigned signing decision package 與 signable request boundary不讀 secret、不執行 shell/SQL、不寫 DB也不簽發 database apply authorization。 |
| 2026-06-29 | PChome DB apply 授權簽署發行者 lane 必須先產出 final signable request package | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-issuer-closeout`;此 endpoint 只把 signing-issuer guard 的 signable request boundary 收斂成 final signable request package 與 closeout contract確認 fresh production truth、post-apply verifier、migration hash、secret boundary 與 no-side-effect checks不讀 secret、不簽發 authorization、不執行 shell/SQL、不寫 DB也不代表正式 DB apply 已授權。 |
| 2026-06-29 | PChome DB apply 授權簽署執行 lane 必須先通過 operator-held secret boundary preflight | V10.725 的 PChome mapping backlog auto-policy 新增 `/api/ai/pchome-growth/mapping-backlog/auto-policy-db-apply-authorization-signing-execution-preflight`;此 endpoint 只把 final signable request package 轉成 future signing execution preflight package、operator-held secret boundary contract、nonsecret signing inputs、command-shape preview、rollback boundary 與 abort conditions不讀 secret、不接受 plaintext secret、不簽發 authorization、不執行 shell/SQL、不寫 DB也不代表正式 DB apply 已授權。 |

View File

@@ -20,7 +20,7 @@
| Completed | PixelRAG application portfolio and integration lanes | `/api/ai-automation/pixelrag-application-portfolio` and `scripts/ops/report_pixelrag_application_portfolio.py` expose commerce, RAG, UX, ops, marketing, and governance uses with priority, status, next machine action, and forbidden guardrails. |
| Completed | PixelRAG Ollama-first VLM replay worker | `/api/ai-automation/pixelrag-vlm-replay-worker` and `scripts/ops/run_pixelrag_vlm_replay_worker.py` dry-run or execute ready visual receipts against approved Ollama VLM routes, emit evidence-bound artifact receipts, and keep blocked pages out of product data. |
| Completed | PixelRAG VLM route readiness and auto model select | `/api/ai-automation/pixelrag-vlm-route-readiness` and `scripts/ops/report_pixelrag_vlm_route_readiness.py` read approved Ollama `/api/tags`, expose configured/candidate model readiness, and let execute mode avoid missing-model blind generate calls. |
| Completed | PixelRAG platform probe worker | `/api/ai-automation/pixelrag-platform-probe-worker` and `scripts/ops/run_pixelrag_platform_probe_worker.py` turn platform probe plans into dry-run/execute automation: Shopee interstitials go to public empty-context capture, Coupang 403/access denied goes to structured-source/backoff artifact receipts. |
| Completed | PixelRAG platform probe worker | `/api/ai-automation/pixelrag-platform-probe-worker` and `scripts/ops/run_pixelrag_platform_probe_worker.py` turn platform probe plans into dry-run/execute automation: Shopee interstitials go to public empty-context capture when runtime is available, missing Playwright auto-falls back to structured-source/backoff receipt, and Coupang 403/access denied goes to structured-source/backoff artifact receipts. |
| In progress | MCP/RAG runtime health in AI automation smoke | `/api/ai-automation/smoke` and `/api/ai-automation/scheduled-health-summary` include external MCP/RAG integration, PixelRAG RAG candidate replay, PixelRAG OCR/VLM replay contract, PixelRAG VLM route readiness, PixelRAG VLM replay worker, PixelRAG platform probe, and PixelRAG platform probe worker families. |
| In progress | Formal production deploy/readback discipline | Every mainline change must update version, push Gitea main/dev, deploy to 188 without touching `momo-db`, and read back `/health` plus new endpoints. |

View File

@@ -238,14 +238,40 @@ def _run_capture_subprocess(
payload = json.loads(completed.stdout)
except json.JSONDecodeError:
payload = {}
stderr_excerpt = completed.stderr[:500]
if len(completed.stderr) > 500:
stderr_excerpt = f"{completed.stderr[:250]}...{completed.stderr[-250:]}"
return {
"returncode": completed.returncode,
"stdout_json": payload,
"stdout_excerpt": completed.stdout[:500],
"stderr_excerpt": completed.stderr[:500],
"stderr_excerpt": stderr_excerpt,
}
def _capture_runtime_unavailable(result: Mapping[str, Any]) -> bool:
haystack = " ".join(
[
str(result.get("stdout_excerpt") or ""),
str(result.get("stderr_excerpt") or ""),
]
).lower()
return (
"modulenotfounderror" in haystack
and "playwright" in haystack
) or "no module named 'playwright'" in haystack or (
"from playwright.sync_api" in haystack
and "traceback" in haystack
) or (
"playwright" in haystack
and (
"executable doesn't exist" in haystack
or "please run" in haystack
or "browsertype.launch" in haystack
)
)
def _capture_item(
item: Mapping[str, Any],
*,
@@ -302,8 +328,25 @@ def _capture_item(
and capture_payload.get("success") is True
and capture_payload.get("status") == "captured"
)
runtime_missing = not captured_ok and _capture_runtime_unavailable(result)
structured_fallback_available = bool(
(item.get("structured_source_fallback") or {}).get("available")
)
capture_fallback = not captured_ok and structured_fallback_available
base.update({
"worker_status": "executed_capture_ok" if captured_ok else "capture_worker_error",
"worker_status": (
"executed_capture_ok"
if captured_ok
else (
(
"capture_runtime_unavailable_structured_fallback_package"
if runtime_missing
else "capture_failed_structured_fallback_package"
)
if capture_fallback
else "capture_worker_error"
)
),
"ready_for_execution": True,
"capture_execution_ready": True,
"capture_manifest_id": manifest.get("manifest_id"),
@@ -314,14 +357,39 @@ def _capture_item(
else 0,
"capture_tile_file_count": tile_file_count,
"capture_receipt_path": receipt_path,
"network_call_performed": True,
"network_call_performed": bool(not runtime_missing),
"artifact_write_performed": bool(captured_ok),
"structured_source_fallback": item.get("structured_source_fallback") or {},
"structured_source_package": (
{
"adapter_code": (item.get("structured_source_fallback") or {}).get("adapter_code"),
"available": True,
"source_count": len(list((item.get("structured_source_fallback") or {}).get("sources") or [])),
"network_request_allowed": bool(
(item.get("structured_source_fallback") or {}).get("network_request_allowed")
),
"database_write_allowed": False,
"dry_run_only": True,
"blocked_page_not_product_data": True,
"capture_runtime_unavailable": runtime_missing,
}
if capture_fallback
else None
),
"capture_stdout_excerpt": result["stdout_excerpt"] if not captured_ok else "",
"capture_stderr_excerpt": result["stderr_excerpt"] if not captured_ok else "",
"next_machine_action": (
"run_pixelrag_rag_candidate_replay_after_probe_capture"
if captured_ok
else "repair_pixelrag_platform_probe_worker_capture_runtime_or_use_structured_source"
else (
(
"run_structured_source_or_install_pixelrag_capture_runtime"
if runtime_missing
else "run_structured_source_or_retry_platform_capture"
)
if capture_fallback
else "repair_pixelrag_platform_probe_worker_capture_runtime_or_use_structured_source"
)
),
})
if write_receipt:
@@ -408,7 +476,7 @@ def run_pixelrag_platform_probe_worker(
ready_count = sum(1 for item in probe_items if item.get("probe_ready"))
dry_run_count = sum(1 for item in worker_items if str(item.get("worker_status") or "").startswith("dry_run_"))
structured_count = sum(
1 for item in worker_items if "structured_source_fallback" in str(item.get("worker_status") or "")
1 for item in worker_items if "structured" in str(item.get("worker_status") or "")
)
capture_ready_count = sum(1 for item in worker_items if item.get("capture_execution_ready"))
capture_execute_count = sum(
@@ -417,7 +485,10 @@ def run_pixelrag_platform_probe_worker(
capture_ok_count = sum(1 for item in worker_items if item.get("worker_status") == "executed_capture_ok")
capture_error_count = sum(1 for item in worker_items if item.get("worker_status") == "capture_worker_error")
executed_structured_count = sum(
1 for item in worker_items if item.get("worker_status") == "executed_structured_source_fallback_package"
1
for item in worker_items
if "structured" in str(item.get("worker_status") or "")
and not str(item.get("worker_status") or "").startswith("dry_run_")
)
executed_count = capture_execute_count + executed_structured_count
skipped_count = sum(1 for item in worker_items if str(item.get("worker_status") or "").startswith("skipped_"))

View File

@@ -147,6 +147,68 @@ def test_pixelrag_platform_probe_worker_execute_capture_writes_receipt(tmp_path,
assert receipt["receipt_path"] == str(receipt_path)
def test_pixelrag_platform_probe_worker_missing_playwright_falls_back_to_structured_receipt(
tmp_path,
monkeypatch,
):
from services import pixelrag_platform_probe_worker_service as service
shopee_receipt = _write_receipt(
tmp_path,
platform="shopee_tw",
manifest_id="shopee-traffic",
title="蝦皮購物 | 花得更少買得更好",
url="https://shopee.tw/search?keyword=sunscreen",
)
_patch_receipt(
shopee_receipt,
page_metrics={
"final_url": "https://shopee.tw/verify/traffic/error?home_url=https%3A%2F%2Fshopee.tw",
"title": "蝦皮購物 | 花得更少買得更好",
},
)
monkeypatch.setattr(
service,
"_run_capture_subprocess",
lambda **kwargs: {
"returncode": 1,
"stdout_json": {},
"stdout_excerpt": "",
"stderr_excerpt": "ModuleNotFoundError: No module named 'playwright'",
},
)
payload = service.run_pixelrag_platform_probe_worker(
artifact_root=tmp_path,
output_root=tmp_path / "worker_receipts",
platform="shopee_tw",
execute=True,
write_receipt=True,
)
assert payload["status"] == "ok"
assert payload["summary"]["capture_error_count"] == 0
assert payload["summary"]["structured_fallback_count"] == 1
assert payload["summary"]["executed_structured_count"] == 1
assert payload["summary"]["network_call_performed"] is False
assert payload["summary"]["receipt_written_count"] == 1
item = payload["worker_items"][0]
assert item["worker_status"] == "capture_runtime_unavailable_structured_fallback_package"
assert item["structured_source_package"]["capture_runtime_unavailable"] is True
assert item["next_machine_action"] == "run_structured_source_or_install_pixelrag_capture_runtime"
receipt_path = (
tmp_path
/ "worker_receipts"
/ "shopee_tw"
/ "shopee-traffic"
/ "platform_probe_worker_receipt.json"
)
receipt = json.loads(receipt_path.read_text(encoding="utf-8"))
assert receipt["worker_status"] == "capture_runtime_unavailable_structured_fallback_package"
assert receipt["artifact_write_performed"] is True
assert receipt["network_call_performed"] is False
def test_pixelrag_platform_probe_worker_execute_structured_fallback_writes_receipt(tmp_path):
from services.pixelrag_platform_probe_worker_service import run_pixelrag_platform_probe_worker