feat(infra): Phase 1.5 진단 도구 3개 + trace 정리

scheduler_status, queue_status, run_verify 추가.
MCP 10개 도구 + NanoClaude wrapper + pre-route 키워드.
worker.py trace print 제거.

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
Hyungi Ahn
2026-04-13 14:27:19 +09:00
parent 476cebcf88
commit d47c04317c
6 changed files with 198 additions and 5 deletions
+10 -4
View File
@@ -190,7 +190,7 @@ def _pre_route(message: str) -> dict | None:
return {"action": "tools", "tool": "document", "operation": "search", "params": {"query": query}}
# 인프라 도구 키워드
infra_keywords = ["docker", "컨테이너", "디스크", "용량", "헬스체크", "tailscale", "ollama 모델", "mlx 모델"]
infra_keywords = ["docker", "컨테이너", "디스크", "용량", "헬스체크", "tailscale", "ollama 모델", "mlx 모델", "스케줄러", "scheduler", "큐 상태", "queue", "처리 큐", "verify", "검증"]
if any(k in msg for k in infra_keywords):
# docker/컨테이너 상태
if any(k in msg for k in ["docker", "컨테이너"]):
@@ -212,6 +212,15 @@ def _pre_route(message: str) -> dict | None:
if any(k in msg for k in ["ollama 모델", "mlx 모델"]):
host = "mlx" if "mlx" in msg else "gpu"
return {"action": "tools", "tool": "infra", "operation": "models", "params": {"host": host}}
# 스케줄러
if any(k in msg for k in ["스케줄러", "scheduler"]):
return {"action": "tools", "tool": "infra", "operation": "scheduler", "params": {}}
# 큐
if any(k in msg for k in ["큐 상태", "queue", "처리 큐"]):
return {"action": "tools", "tool": "infra", "operation": "queue", "params": {}}
# 검증
if any(k in msg for k in ["verify", "검증"]):
return {"action": "tools", "tool": "infra", "operation": "verify", "params": {"check_name": "gpu-snapshot"}}
# 시스템 상태 질문 — 마커만 반환 (worker에서 비동기 조회)
if any(k in msg for k in ["추론 모델", "gemma", "젬마", "서버 상태", "시스템 상태"]) or \
@@ -254,11 +263,9 @@ async def run(job: Job) -> None:
# --- 사전 라우팅 (키워드 기반, EXAONE 스킵) ---
pre = _pre_route(job.message)
classify_latency = 0
print(f"[TRACE] Job {job.id} pre_route result: {pre}", flush=True)
if pre:
classification = pre
print(f"[TRACE] Job {job.id} PRE-ROUTED: {pre.get('tool','')}.{pre.get('operation','')}", flush=True)
logger.info("Job %s pre-routed: %s.%s", job.id, pre.get("tool", ""), pre.get("operation", pre.get("action", "")))
else:
# --- EXAONE 분류기 호출 ---
@@ -291,7 +298,6 @@ async def run(job: Job) -> None:
response_text = classification.get("response", "")
route_prompt = classification.get("prompt", "")
print(f"[TRACE] Job {job.id} final action='{action}' classification={classification}", flush=True)
logger.info("Job %s classified as '%s'", job.id, action)
# 대화 기록: 사용자 메시지
+17
View File
@@ -14,6 +14,8 @@ from infra.core.health import service_health, VALID_SERVICES
from infra.core.system import disk_usage
from infra.core.network import tailscale_status
from infra.core.models import ollama_models, mlx_models
from infra.core.docserver import scheduler_status as _scheduler_status, queue_status as _queue_status
from infra.core.verify import run_verify as _run_verify, VERIFY_COMMANDS
logger = logging.getLogger(__name__)
@@ -111,3 +113,18 @@ async def models(host: str = "gpu") -> dict:
summary = f"{result.source} on {result.host}: {len(result.models)}개 모델"
return {"ok": True, "tool": "infra", "operation": "models",
"data": data, "summary": summary, "error": ""}
async def scheduler() -> dict:
"""Document Server scheduler status."""
return await _scheduler_status()
async def queue() -> dict:
"""Document Server queue status."""
return await _queue_status()
async def verify(check_name: str = "gpu-snapshot") -> dict:
"""Run predefined verify command."""
return await _run_verify(check_name)
+7 -1
View File
@@ -21,7 +21,7 @@ ALLOWED_OPS = {
"calendar": {"today", "search", "create_draft", "create_confirmed"},
"email": {"search", "read"},
"document": {"search", "read"},
"infra": {"status", "health", "disk", "network", "models"},
"infra": {"status", "health", "disk", "network", "models", "scheduler", "queue", "verify"},
}
# payload hard limit
@@ -113,6 +113,12 @@ async def _exec_infra(operation: str, params: dict) -> dict:
return await infra_tool.network()
elif operation == "models":
return await infra_tool.models(params.get("host", "gpu"))
elif operation == "scheduler":
return await infra_tool.scheduler()
elif operation == "queue":
return await infra_tool.queue()
elif operation == "verify":
return await infra_tool.verify(params.get("check_name", "gpu-snapshot"))
return _error("infra", operation, "미구현")