From e786307a071c2b5d8a0778546ff7347cc6ab8a3d Mon Sep 17 00:00:00 2001 From: Hyungi Ahn Date: Mon, 6 Apr 2026 13:43:01 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20JSON=20=ED=8C=8C=EC=8B=B1=20=EA=B2=AC?= =?UTF-8?q?=EA=B3=A0=ED=99=94=20+=20=EB=B6=84=EB=A5=98=EA=B8=B0=20?= =?UTF-8?q?=ED=94=84=EB=A1=AC=ED=94=84=ED=8A=B8=20=EA=B0=95=ED=99=94?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - _parse_classification: 어떤 형태든 첫{~마지막} 추출, 백틱 잔재 제거 - 분류기: 판단 예시 추가 (일정→tools, 인사→direct 등), 백틱/코드블록 금지 명시 Co-Authored-By: Claude Opus 4.6 (1M context) --- nanoclaude/services/backend_registry.py | 17 ++++++++++++---- nanoclaude/services/worker.py | 27 +++++++++++++------------ 2 files changed, 27 insertions(+), 17 deletions(-) diff --git a/nanoclaude/services/backend_registry.py b/nanoclaude/services/backend_registry.py index b1b4a98..8f332ab 100644 --- a/nanoclaude/services/backend_registry.py +++ b/nanoclaude/services/backend_registry.py @@ -35,13 +35,22 @@ JSON 형식: - direct/route/clarify: {"action": "...", "response": "...", "prompt": "..."} - tools: {"action": "tools", "tool": "calendar|email|document", "operation": "...", "params": {...}} -규칙: -- JSON 외 텍스트는 절대 출력하지 마라 +중요 규칙: +- 반드시 순수 JSON만 출력. 백틱, 코드블록, 설명 텍스트 금지 - 날짜는 YYYY-MM-DD, 시간은 HH:MM - pending_draft가 있다고 [대화 이력]에 표시되어 있을 때만 create_confirmed 사용 -- 오늘 날짜 정보가 [현재 시간]에 있으니 참고하라 +- [현재 시간]의 날짜를 참고하여 "오늘", "내일", "이번주" 등을 YYYY-MM-DD로 변환하라 -���의 이름은 '이드'. 상냥하고 친근하게 대화한다. +판단 예시: +- "오늘 일정" → tools: calendar.today() +- "이번주 일정" → tools: calendar.search(이번주 월~일) +- "내일 3시 회의" → tools: calendar.create_draft(...) +- "최근 메일" → tools: email.search("", 7) +- "문서 찾아줘" → tools: document.search(...) +- "안녕" → direct +- "양자역학 설명해줘" → route + +너의 이름은 '이드'. 상냥하고 친근하게 대화한다. 대화 이력이 있으면 맥락을 고려하라.\ """ diff --git a/nanoclaude/services/worker.py b/nanoclaude/services/worker.py index b7113d5..d73e986 100644 --- a/nanoclaude/services/worker.py +++ b/nanoclaude/services/worker.py @@ -70,20 +70,21 @@ async def _stream_with_cancel(adapter, message: str, job: Job, collected: list[s def _parse_classification(raw: str) -> dict: """EXAONE JSON 응답 파싱. 실패 시 direct fallback.""" raw = raw.strip() - # JSON 블록 추출 (```json ... ``` 감싸는 경우 대응) - if "```" in raw: - start = raw.find("{") - end = raw.rfind("}") + 1 - if start >= 0 and end > start: - raw = raw[start:end] - try: - result = json.loads(raw) - if "action" in result: - return result - except json.JSONDecodeError: - pass + # 어떤 형태든 첫 번째 { ~ 마지막 } 추출 + start = raw.find("{") + end = raw.rfind("}") + if start >= 0 and end > start: + json_str = raw[start:end + 1] + try: + result = json.loads(json_str) + if "action" in result: + return result + except json.JSONDecodeError: + pass # JSON 파싱 실패 → direct로 취급 (raw 텍스트가 직접 응답) - return {"action": "direct", "response": raw, "prompt": ""} + # 마크다운/코드블록 잔재 제거 + cleaned = raw.replace("```json", "").replace("```", "").replace("`json", "").replace("`", "").strip() + return {"action": "direct", "response": cleaned, "prompt": ""} async def _send_callback(job: Job, text: str) -> None: