diff --git a/app/api/search.py b/app/api/search.py index e1badb3..8c0c517 100644 --- a/app/api/search.py +++ b/app/api/search.py @@ -518,8 +518,10 @@ async def ask( # 거의 모든 classifier 호출 timeout → conservative_refuse(no_classifier) 경로. 15s 로 상향 — classifier # 가 실제 작동하도록 (단, ask 전체 응답 시간 상한 영향: ev_ms + max(classifier_wait, evidence_extract) + # synth_ms + verifier 누적). + # 2026-05-17 B-3: 15s 도 동시 부하 시 부족 (classifier_service LLM_TIMEOUT_MS 30s 와 misalign). + # 30s 로 align → classifier 동작 안정. ask 응답 latency 상한 ↑ 의도. try: - classifier_result = await asyncio.wait_for(classifier_task, timeout=15.0) + classifier_result = await asyncio.wait_for(classifier_task, timeout=30.0) except (asyncio.TimeoutError, Exception): classifier_result = ClassifierResult("timeout", None, [], [], 0.0) @@ -637,8 +639,10 @@ async def ask( verifier_task = asyncio.create_task( verify(q, sr.answer or "", evidence) ) + # 2026-05-17 B-3: 4s outer wait_for 가 verifier_service LLM_TIMEOUT_MS (10s) 를 override + # → classifier 와 동일 패턴 (search.py:522 가 6s→15s swap 했던 case). 10s 로 align. try: - verifier_result = await asyncio.wait_for(verifier_task, timeout=4.0) + verifier_result = await asyncio.wait_for(verifier_task, timeout=10.0) except (asyncio.TimeoutError, Exception): verifier_result = VerifierResult("timeout", [], 0.0) diff --git a/app/services/search/evidence_service.py b/app/services/search/evidence_service.py index 84ee264..453b29c 100644 --- a/app/services/search/evidence_service.py +++ b/app/services/search/evidence_service.py @@ -78,7 +78,7 @@ SPAN_MIN_CHARS = 80 # 이 미만이면 window enlarge SPAN_ENLARGE_TARGET = 120 # enlarge 시 재윈도우 target_chars SPAN_MAX_CHARS = 300 # 이 초과면 cut (synthesis token budget 보호) -LLM_TIMEOUT_MS = 15000 +LLM_TIMEOUT_MS = 30000 # 2026-05-17 B-3: 15s 시 ev_ms=15005 timeout 빈발 — classifier (30s) 와 align PROMPT_VERSION = "v2-triage" # B-2: primary(26B MLX) → triage path 전환. PR #20 이후 triage/primary 동일 endpoint (Mac mini 26B). # 확장 여지 — None 이면 비활성 (baseline). 실측 후 0.8 등으로 켠다. diff --git a/app/services/search/query_analyzer.py b/app/services/search/query_analyzer.py index e407f50..105383c 100644 --- a/app/services/search/query_analyzer.py +++ b/app/services/search/query_analyzer.py @@ -44,7 +44,7 @@ logger = setup_logger("query_analyzer") PROMPT_VERSION = "v2" # prompts/query_analyze.txt 축소판 CACHE_TTL = 86400 # 24h CACHE_MAXSIZE = 1000 -LLM_TIMEOUT_MS = 15000 # async 구조 (background), 동기 경로 금지 +LLM_TIMEOUT_MS = 30000 # 2026-05-17 B-3: 동시 부하 시 query_analyze 45s 측정 (fastapi log) — 15s 부족, classifier (30s) 와 align. async 구조 (background), 동기 경로 금지 # ↑ 실측: gemma-4-26b-a4b-it-8bit MLX, 축소 프롬프트(prompt_tok=802) 7~11초. # generation이 dominant (max_tokens 무효, 자연 EOS ~289 tok 생성). # background 실행이라 15초도 안전. 상향 필요 시 여기서만 조정. diff --git a/app/services/search/synthesis_service.py b/app/services/search/synthesis_service.py index 51248a6..f0e5452 100644 --- a/app/services/search/synthesis_service.py +++ b/app/services/search/synthesis_service.py @@ -40,7 +40,7 @@ logger = setup_logger("synthesis") # ─── 상수 (plan 영구 룰) ───────────────────────────────── PROMPT_VERSION = "v2" -LLM_TIMEOUT_MS = 15000 +LLM_TIMEOUT_MS = 30000 # 2026-05-17 B-3: 15s 시 동시 부하 (Mac mini 26B classifier+evidence+synthesis serialized) 빈발 timeout — classifier (30s) 와 align CACHE_TTL = 3600 # 1h (answer 는 원문 변경에 민감 → query_analyzer 24h 보다 짧게) CACHE_MAXSIZE = 300 MAX_ANSWER_CHARS = 600 diff --git a/app/services/search/verifier_service.py b/app/services/search/verifier_service.py index 8cda6bd..757986b 100644 --- a/app/services/search/verifier_service.py +++ b/app/services/search/verifier_service.py @@ -31,7 +31,7 @@ if TYPE_CHECKING: logger = setup_logger("verifier") -LLM_TIMEOUT_MS = 3000 +LLM_TIMEOUT_MS = 10000 # 2026-05-17 B-3: 3s 시 동시 부하 시 verifier 빈발 skip → grounding 약화. Mac mini 26B 가 verifier-style 짧은 LLM call 도 concurrent 호출 시 3s 초과 빈번 — 10s 로 raise CIRCUIT_THRESHOLD = 5 CIRCUIT_RECOVERY_SEC = 60