fix(api): enforce global ollama endpoint order
All checks were successful
Code Review / ai-code-review (push) Successful in 11s
CD Pipeline / tests (push) Successful in 5m13s
CD Pipeline / build-and-deploy (push) Successful in 3m31s
CD Pipeline / post-deploy-checks (push) Successful in 1m18s

This commit is contained in:
Your Name
2026-05-19 12:31:56 +08:00
parent 5fa0e1452c
commit 45cd55b2da
7 changed files with 359 additions and 228 deletions

View File

@@ -4,7 +4,7 @@ Knowledge Extractor Service — KB Phase 2-A
Incident resolved 後自動萃取 KB 草稿。
設計原則:
- 強制使用 Ollama llama3.2:3b(本地推理,符合 Phase 24 D7 隱私規則)
- 使用 Ollama llama3.2:3b,依全域順序 GCP-A → GCP-B → 111 嘗試
- fire-and-forget失敗不影響 resolve 主流程
- logger.exception 保留完整 Stack Trace 供 Prompt 調優
@@ -15,11 +15,11 @@ import structlog
logger = structlog.get_logger(__name__)
# 2026-05-05 Codex: KB 萃取走 111 lane避免污染 GCP alert-fast lane
def _get_ollama_base() -> str:
from src.services.ollama_endpoint_resolver import resolve_ollama_endpoint
# 2026-05-19 Codex: 統帥校正,全 Ollama workload 固定 GCP-A → GCP-B → 111。
def _get_ollama_endpoints():
from src.services.ollama_endpoint_resolver import resolve_ollama_order
return resolve_ollama_endpoint("deep_rca")
return resolve_ollama_order("deep_rca")
_EXTRACT_MODEL = "llama3.2:3b"
_EXTRACT_TIMEOUT = 30.0 # 秒,容忍慢速
@@ -160,36 +160,54 @@ class KnowledgeExtractorService:
不走 AIRouter 是刻意設計:
- KB 萃取是背景工作,不需要完整的路由/閘門/Cache 邏輯
- 強制本地,不允許 fallback 到 cloud provider
- Ollama endpoint 固定依 GCP-A → GCP-B → 111 嘗試
"""
import httpx
try:
async with httpx.AsyncClient(timeout=_EXTRACT_TIMEOUT) as client:
r = await client.post(
f"{_get_ollama_base()}/api/generate",
json={
"model": _EXTRACT_MODEL,
"prompt": prompt,
"stream": False,
"options": {
"temperature": 0.3, # 低溫:減少幻覺
"num_predict": 800, # 控制長度
"stop": ["\n\n\n"], # 防止無限生成
endpoints = _get_ollama_endpoints()
async with httpx.AsyncClient(timeout=_EXTRACT_TIMEOUT) as client:
for endpoint in endpoints:
if not endpoint.url:
continue
try:
r = await client.post(
f"{endpoint.url}/api/generate",
json={
"model": _EXTRACT_MODEL,
"prompt": prompt,
"stream": False,
"options": {
"temperature": 0.3, # 低溫:減少幻覺
"num_predict": 800, # 控制長度
"stop": ["\n\n\n"], # 防止無限生成
},
},
},
)
r.raise_for_status()
text = r.json().get("response", "").strip()
return text or None
)
r.raise_for_status()
text = r.json().get("response", "").strip()
if text:
logger.info(
"kb_ollama_call_success",
model=_EXTRACT_MODEL,
provider=endpoint.provider_name,
base=endpoint.url,
)
return text
except Exception as e:
logger.warning(
"kb_ollama_call_failed",
model=_EXTRACT_MODEL,
provider=endpoint.provider_name,
base=endpoint.url,
error=str(e),
)
except Exception:
logger.exception(
"kb_ollama_call_failed",
model=_EXTRACT_MODEL,
base=_get_ollama_base(),
)
return None
logger.error(
"kb_ollama_all_endpoints_failed",
model=_EXTRACT_MODEL,
attempted=[endpoint.provider_name for endpoint in endpoints],
)
return None
def _extract_title(self, markdown: str, incident) -> str:
"""