From e7a86c81da0c8ff50acf7e3b032f7b7f12b0787f Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Thu, 16 Apr 2026 08:59:22 +0900 Subject: [PATCH] =?UTF-8?q?feat(nanoclaude):=20ask-first=20routing=20+=20?= =?UTF-8?q?=EC=84=9C=EA=B3=A0=20=EC=95=88=EB=82=B4=20=ED=86=B5=EC=9D=BC=20?= =?UTF-8?q?+=20document=20=EB=A1=9C=EA=B7=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - document 진입 시 기본 ask (근거 중심 답변) - 명시적 목록 요청만 search_full (목록/리스트/제목만 등) - 안내 문구 "서고를 확인하는 중입니다..." 통일 - document 호출 전후 라우팅/지연 로그 추가 Co-Authored-By: Claude Opus 4.6 (1M context) --- nanoclaude/services/worker.py | 31 ++++++++++++++----------------- 1 file changed, 14 insertions(+), 17 deletions(-) diff --git a/nanoclaude/services/worker.py b/nanoclaude/services/worker.py index bf3a58a..1f1fcb0 100644 --- a/nanoclaude/services/worker.py +++ b/nanoclaude/services/worker.py @@ -202,22 +202,10 @@ def _pre_route(message: str) -> dict | None: query = query.replace(rm, "") query = query.strip() if query: - # 질문형 강신호 - ask_signals = ["알려줘", "설명해", "차이", "비교", "절차", "요건", "무엇", "왜", "어떻게", "뭐야", "뭔가", "뭐지", "내용"] - # 탐색형 강신호 - search_signals = ["찾아", "검색", "목록", "최근", "업로드", "리스트"] - ask_score = sum(1 for s in ask_signals if s in msg) - search_score = sum(1 for s in search_signals if s in msg) - - # 도메인 키워드만으로 진입한 경우 질문형으로 간주 (+1 보너스) - if domain_hit and not doc_entry: - ask_score += 1 - - # 초기 운영 가드: ask는 강신호 2개 이상일 때만 - if ask_score >= 2 and ask_score > search_score: - operation = "ask" - else: - operation = "search_full" + # 명시적 목록 요청만 search_full, 나머지는 ask (근거 중심 답변 기본값) + list_signals = ["목록", "리스트", "검색 결과", "몇 개", "list", "제목만", "문서만"] + list_request = any(s in msg for s in list_signals) + operation = "search_full" if list_request else "ask" return {"action": "tools", "tool": "document", "operation": operation, "params": {"query": query}} # 인프라 도구 키워드 @@ -377,15 +365,24 @@ async def run(job: Job) -> None: else: # 문서 도구 호출 시 안내 문구 if tool_name == "document" and job.callback == "synology": - notice = "서고를 확인하는 중입니다..." if operation == "ask" else "문서를 검색하는 중입니다..." + notice = "서고를 확인하는 중입니다..." await send_to_synology(notice, raw=True) # 일반 도구 실행 (document.ask는 긴 timeout) timeout = DOCUMENT_ASK_TIMEOUT if (tool_name == "document" and operation == "ask") else TOOL_TIMEOUT + doc_start = time() + if tool_name == "document": + logger.info("Job %s document.%s domain_hit=%s list_request=%s query=%s", + job.id, operation, + any(k in job.message.lower() for k in ["산업안전", "위험성평가", "asme", "법령"]), + any(s in job.message.lower() for s in ["목록", "리스트", "제목만"]), + params.get("query", "")[:50]) try: result = await asyncio.wait_for(execute_tool(tool_name, operation, params), timeout=timeout) except asyncio.TimeoutError: result = {"ok": False, "tool": tool_name, "operation": operation, "data": [], "summary": "", "error": "⚠️ 서비스 응답 시간이 초과되었습니다."} + if tool_name == "document": + logger.info("Job %s document.%s ok=%s elapsed=%.1fs", job.id, operation, result.get("ok"), time() - doc_start) if not result["ok"]: response = result.get("error", "⚠️ 서비스를 사용할 수 없습니다.")