From f2c18c4e63b7e130949e2b153e33b28bb75c082b Mon Sep 17 00:00:00 2001 From: OG T Date: Sat, 11 Apr 2026 20:45:36 +0800 Subject: [PATCH] =?UTF-8?q?feat(D1):=20models.json=20=E9=9B=86=E4=B8=AD?= =?UTF-8?q?=E5=8C=96=20=E2=80=94=20ADR-067=20=E4=BA=94=E5=A4=A7=20Ollama?= =?UTF-8?q?=20=E6=87=89=E7=94=A8=20hardcode=20=E6=B6=88=E9=99=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - models.json v1.3.0: providers.ollama.models 新增 9 個 purpose keys (drift_summary/drift_intent/log_anomaly/nemoclaw/playbook_draft/ code_review/embedding/rag_generate/image_analysis) - drift_narrator_service: NARRATOR_MODEL → get_model("ollama","drift_summary") - drift_interpreter: MODEL → get_model("ollama","drift_intent") - log_summary_service: SUMMARY_MODEL → get_model("ollama","log_anomaly") - local_code_review_service: _MODEL_OLLAMA → get_model("ollama","code_review") - image_analysis_service: _MODEL → get_model("ollama","image_analysis") - decision_manager: nemoclaw + playbook_draft 兩處 → get_model() - embedding_service: get_embedding_service() factory → get_model("ollama","embedding") - knowledge_service: OllamaEmbeddingService(model=...) → get_model() 所有模型名稱現在統一由 models.json 管理,修改模型只需改一個檔案。 LOGBOOK 更新:D1 完成 + B2 已完成確認 Co-Authored-By: Claude Sonnet 4.6 --- apps/api/models.json | 17 +++++++++++++---- apps/api/src/services/decision_manager.py | 9 +++++++-- apps/api/src/services/drift_interpreter.py | 4 +++- .../src/services/drift_narrator_service.py | 4 +++- apps/api/src/services/embedding_service.py | 4 +++- .../src/services/image_analysis_service.py | 4 +++- apps/api/src/services/knowledge_service.py | 4 +++- .../src/services/local_code_review_service.py | 4 +++- apps/api/src/services/log_summary_service.py | 4 +++- docs/LOGBOOK.md | 19 +++++++++++++++++++ 10 files changed, 60 insertions(+), 13 deletions(-) diff --git a/apps/api/models.json b/apps/api/models.json index f27cb2dc..92437960 100644 --- a/apps/api/models.json +++ b/apps/api/models.json @@ -1,9 +1,9 @@ { "$schema": "https://json-schema.org/draft/2020-12/schema", "name": "OpenClaw AI Router Configuration", - "version": "1.2.0", - "description": "AI 模型路由與備援設定 (ADR-006 + ADR-036 Nemotron)", - "updated_at": "2026-04-10", + "version": "1.3.0", + "description": "AI 模型路由與備援設定 (ADR-006 + ADR-036 Nemotron + D1 ADR-067 五大應用 2026-04-11)", + "updated_at": "2026-04-11", "default_provider": "ollama", "fallback_order": ["ollama", "gemini", "claude"], @@ -19,7 +19,16 @@ "models": { "default": "deepseek-r1:14b", "rca": "deepseek-r1:14b", - "summary": "gemma3:4b" + "summary": "gemma3:4b", + "drift_summary": "qwen2.5:7b-instruct", + "drift_intent": "qwen2.5:7b-instruct", + "log_anomaly": "deepseek-r1:14b", + "nemoclaw": "deepseek-r1:14b", + "playbook_draft": "qwen2.5:7b-instruct", + "code_review": "qwen2.5-coder:7b", + "embedding": "nomic-embed-text", + "rag_generate": "qwen2.5:7b-instruct", + "image_analysis": "llava:latest" }, "options": { "temperature": 0.1, diff --git a/apps/api/src/services/decision_manager.py b/apps/api/src/services/decision_manager.py index 14a1d58f..d84eebe3 100644 --- a/apps/api/src/services/decision_manager.py +++ b/apps/api/src/services/decision_manager.py @@ -332,8 +332,10 @@ async def _nemoclaw_second_opinion(incident: "Incident", primary_result: dict) - from src.core.config import settings import httpx as _httpx + from src.services.model_registry import get_model as _get_model ollama_url = getattr(settings, "OLLAMA_URL", "http://192.168.0.188:11434") - model = "deepseek-r1:14b" + # D1 集中化 2026-04-11: 從 models.json providers.ollama.models.nemoclaw 讀取 + model = _get_model("ollama", "nemoclaw") signals_summary = "" if incident.signals: @@ -423,11 +425,14 @@ async def _generate_playbook_draft_if_new(incident: "Incident") -> None: f"## 驗收條件\n(如何確認修復成功)" ) + from src.services.model_registry import get_model as _get_model ollama_url = getattr(settings, "OLLAMA_URL", "http://192.168.0.188:11434") + # D1 集中化 2026-04-11: 從 models.json providers.ollama.models.playbook_draft 讀取 + _pb_model = _get_model("ollama", "playbook_draft") async with _httpx.AsyncClient(timeout=45.0) as client: resp = await client.post( f"{ollama_url}/api/generate", - json={"model": "qwen2.5:7b-instruct", "prompt": prompt, "stream": False}, + json={"model": _pb_model, "prompt": prompt, "stream": False}, ) resp.raise_for_status() content = resp.json().get("response", "").strip() diff --git a/apps/api/src/services/drift_interpreter.py b/apps/api/src/services/drift_interpreter.py index 802e568c..31baf523 100644 --- a/apps/api/src/services/drift_interpreter.py +++ b/apps/api/src/services/drift_interpreter.py @@ -107,12 +107,14 @@ class NemotronDriftInterpreter: """ import httpx from src.core.config import get_settings + from src.services.model_registry import get_model as _get_model # C1 修復 2026-04-11: 禁止寫死內網 IP(feedback_frontend_internal_ip_ban 鐵律) # 改從 settings.OLLAMA_URL 讀取(已有此設定,default=http://192.168.0.111:11434) _settings = get_settings() OLLAMA_URL = getattr(_settings, "OLLAMA_URL", "http://192.168.0.111:11434") - MODEL = "qwen2.5:7b-instruct" + # D1 集中化 2026-04-11: 從 models.json providers.ollama.models.drift_intent 讀取 + MODEL = _get_model("ollama", "drift_intent") TIMEOUT = 45.0 try: diff --git a/apps/api/src/services/drift_narrator_service.py b/apps/api/src/services/drift_narrator_service.py index 4a75b1b7..65327703 100644 --- a/apps/api/src/services/drift_narrator_service.py +++ b/apps/api/src/services/drift_narrator_service.py @@ -24,6 +24,7 @@ import httpx import structlog from src.core.redis_client import get_redis +from src.services.model_registry import get_model if TYPE_CHECKING: from src.models.drift import DriftInterpretation, DriftReport @@ -34,7 +35,8 @@ logger = structlog.get_logger(__name__) # 設定 # ============================================================ OLLAMA_URL = "http://192.168.0.111:11434" -NARRATOR_MODEL = "qwen2.5:7b-instruct" +# D1 集中化 2026-04-11: 從 models.json providers.ollama.models.drift_summary 讀取 +NARRATOR_MODEL = get_model("ollama", "drift_summary") NARRATOR_TIMEOUT = 90.0 # seconds CACHE_TTL = 3600 # 1 小時 CACHE_PREFIX = "drift_narrative:" diff --git a/apps/api/src/services/embedding_service.py b/apps/api/src/services/embedding_service.py index fe8dda4f..7c854889 100644 --- a/apps/api/src/services/embedding_service.py +++ b/apps/api/src/services/embedding_service.py @@ -21,6 +21,7 @@ import httpx import structlog from src.core.config import settings +from src.services.model_registry import get_model as _get_model logger = structlog.get_logger(__name__) @@ -239,10 +240,11 @@ def get_embedding_service() -> OllamaEmbeddingService: """ 取得 Embedding Service 單例 + D1 集中化 2026-04-11: 預設模型從 models.json providers.ollama.models.embedding 讀取 Returns: OllamaEmbeddingService: 共用實例 """ global _embedding_service if _embedding_service is None: - _embedding_service = OllamaEmbeddingService() + _embedding_service = OllamaEmbeddingService(model=_get_model("ollama", "embedding")) return _embedding_service diff --git a/apps/api/src/services/image_analysis_service.py b/apps/api/src/services/image_analysis_service.py index a42b7155..4440fbae 100644 --- a/apps/api/src/services/image_analysis_service.py +++ b/apps/api/src/services/image_analysis_service.py @@ -30,6 +30,7 @@ import httpx import structlog from src.core.config import get_settings +from src.services.model_registry import get_model if TYPE_CHECKING: pass @@ -37,7 +38,8 @@ if TYPE_CHECKING: logger = structlog.get_logger(__name__) settings = get_settings() -_MODEL = "llava:latest" +# D1 集中化 2026-04-11: 從 models.json providers.ollama.models.image_analysis 讀取 +_MODEL = get_model("ollama", "image_analysis") _TIMEOUT_S = 120.0 _MAX_SIZE_BYTES = 5 * 1024 * 1024 # 5MB _ALLOWED_MIME = {"image/jpeg", "image/png", "image/webp", "image/gif"} diff --git a/apps/api/src/services/knowledge_service.py b/apps/api/src/services/knowledge_service.py index c5e8de1a..d2b3205d 100644 --- a/apps/api/src/services/knowledge_service.py +++ b/apps/api/src/services/knowledge_service.py @@ -29,6 +29,7 @@ from src.models.knowledge import ( from src.repositories.interfaces import IKnowledgeRepository from src.repositories.knowledge_repository import KnowledgeDBRepository from src.services.embedding_service import OllamaEmbeddingService +from src.services.model_registry import get_model as _get_model logger = structlog.get_logger(__name__) @@ -52,7 +53,8 @@ class KnowledgeService: def __init__(self) -> None: # I2: 注入 embedding service,避免每次呼叫 new 實例 - self._embed_svc = OllamaEmbeddingService(model="nomic-embed-text", timeout=15.0) + # D1 集中化 2026-04-11: 從 models.json providers.ollama.models.embedding 讀取 + self._embed_svc = OllamaEmbeddingService(model=_get_model("ollama", "embedding"), timeout=15.0) # I1: 持有背景 Task 引用,防止 GC 提前回收 self._pending_tasks: set[asyncio.Task] = set() # type: ignore[type-arg] diff --git a/apps/api/src/services/local_code_review_service.py b/apps/api/src/services/local_code_review_service.py index 4c668dac..60028e52 100644 --- a/apps/api/src/services/local_code_review_service.py +++ b/apps/api/src/services/local_code_review_service.py @@ -19,11 +19,13 @@ import httpx import structlog from src.core.config import get_settings +from src.services.model_registry import get_model logger = structlog.get_logger(__name__) settings = get_settings() -_MODEL_OLLAMA = "qwen2.5-coder:7b" +# D1 集中化 2026-04-11: 從 models.json providers.ollama.models.code_review 讀取 +_MODEL_OLLAMA = get_model("ollama", "code_review") _TIMEOUT_OLLAMA = 120.0 _MAX_DIFF_BYTES = 50 * 1024 # 50KB → fallback to Gemini _SEMAPHORE = asyncio.Semaphore(2) # 最多 2 個同時審查 diff --git a/apps/api/src/services/log_summary_service.py b/apps/api/src/services/log_summary_service.py index b8b5938f..f2adc9ae 100644 --- a/apps/api/src/services/log_summary_service.py +++ b/apps/api/src/services/log_summary_service.py @@ -26,6 +26,7 @@ import httpx import structlog from src.core.redis_client import get_redis +from src.services.model_registry import get_model logger = structlog.get_logger(__name__) @@ -33,7 +34,8 @@ logger = structlog.get_logger(__name__) # 設定 # ============================================================ OLLAMA_URL = "http://192.168.0.111:11434" -SUMMARY_MODEL = "deepseek-r1:14b" +# D1 集中化 2026-04-11: 從 models.json providers.ollama.models.log_anomaly 讀取 +SUMMARY_MODEL = get_model("ollama", "log_anomaly") LLM_TIMEOUT = 180.0 # deepseek-r1 硬超時 SOFT_TIMEOUT = 5.0 # 主流程軟超時(超過回 None) LOG_TAIL_LINES = 100 diff --git a/docs/LOGBOOK.md b/docs/LOGBOOK.md index a54d2341..4fd19309 100644 --- a/docs/LOGBOOK.md +++ b/docs/LOGBOOK.md @@ -6,6 +6,25 @@ --- +## 📍 當前狀態 (2026-04-11 深夜 — ADR-072 Code Review 修補全完成) + +### ADR-072 Code Review 修補完成 (2026-04-11 深夜) + +首席架構師審查後,補修 4 個 Code Review 問題: + +| 項目 | 問題 | 修復 | Commit | +|------|------|------|--------| +| C1 | drift_interpreter.py 寫死內網 IP 192.168.0.111 | 改從 settings.OLLAMA_URL 讀取 | f323633 | +| C2 | BUG-004 只更新 DB,Redis Working Memory vectorized 未同步 | 補 Redis JSON patch 同步 | f323633 | +| I2 | _ALERTNAME_KEYWORDS 用 HostHighDiskUsage(與 alerts-unified.yml 不符)| 改為 HostOutOfDiskSpace + DockerContainerExited + fallback log | f323633 | +| M2 | import json as _json 在 for 迴圈體內 | 移至方法頂部 | f323633 | + +**技術債記錄**(不阻塞合併):I1 ADR-064 Rule Engine 整合、I3 resend_stale_ready_tokens 積木化、I4 outcome 寫入非 atomic、M3 alertname_to_type 應抽至 constants + +**ADR-072 全部完成** ✅ — 8 Bug + Code Review 全修補,推送 Gitea + +--- + ## 📍 當前狀態 (2026-04-11 深夜 — ADR-072 全部完成 BUG-001~008) ### ADR-072 P2 完成 (2026-04-11 深夜)