From f8ae813c05feca69809047ba3d4e089c548d1a18 Mon Sep 17 00:00:00 2001 From: ttipo Date: Sun, 21 Jun 2026 12:27:31 +0900 Subject: [PATCH] =?UTF-8?q?fix:=20AI=20=EC=B6=94=EC=B2=9C=20=ED=86=A0?= =?UTF-8?q?=ED=81=B0=20=ED=95=9C=EB=8F=84=20=EB=B6=80=EC=A1=B1=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=9D=91=EB=8B=B5=20=EC=9E=98=EB=A6=BC=20=E2=86=92?= =?UTF-8?q?=20=EC=B6=94=EC=B2=9C=20=EC=8B=A4=ED=8C=A8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 4개 카테고리(필수/관리/보안/부가) 구조화 JSON 응답이 max_tokens=1024 를 넘으면 중간에 잘려 JSON 파싱이 실패하고 '추천을 가져오지 못했습니다' 로 떴음. - callClaude 에 maxTokens 파라미터 추가, suggestChecklist 는 3072 사용 - parseSuggestions 파싱 실패/빈 결과 시 원본 일부를 warn 로그로 남겨 진단 가능 런타임 재현: 동일 입력으로 필수5·관리5·보안5·부가4(총 19개) 정상 반환 확인. 백엔드 build/lint 0. Co-Authored-By: Claude Opus 4.8 (1M context) --- backend/src/modules/ai/ai.service.ts | 16 ++++++++++++++-- 1 file changed, 14 insertions(+), 2 deletions(-) diff --git a/backend/src/modules/ai/ai.service.ts b/backend/src/modules/ai/ai.service.ts index 3e75b85..f7be484 100644 --- a/backend/src/modules/ai/ai.service.ts +++ b/backend/src/modules/ai/ai.service.ts @@ -203,9 +203,12 @@ export class AiService { content, ].join('\n'); + // 4개 카테고리 × 다수 항목의 구조화 JSON 이라 토큰 여유를 넉넉히 준다 + // (1024 면 응답이 중간에 잘려 JSON 파싱이 실패할 수 있음) const raw = await this.callClaude( [{ role: 'user', content: userMsg }], SUGGEST_SYSTEM_PROMPT, + 3072, ); return { groups: this.parseSuggestions(raw) }; } @@ -225,6 +228,9 @@ export class AiService { try { parsed = JSON.parse(text); } catch { + this.logger.warn( + `추천 JSON 파싱 실패(응답 잘림 가능) — 원본 ${raw.length}자: ${raw.slice(0, 300)}`, + ); throw this.suggestFailed(); } @@ -243,7 +249,12 @@ export class AiService { .slice(0, 15); if (items.length > 0) result.push({ category, items }); } - if (result.length === 0) throw this.suggestFailed(); + if (result.length === 0) { + this.logger.warn( + `추천 결과가 비어 있음 — 원본 ${raw.length}자: ${raw.slice(0, 300)}`, + ); + throw this.suggestFailed(); + } return result; } @@ -418,6 +429,7 @@ export class AiService { private async callClaude( messages: { role: 'user' | 'assistant'; content: string }[], system: string, + maxTokens: number = MAX_TOKENS, ): Promise { let response: Response; try { @@ -430,7 +442,7 @@ export class AiService { }, body: JSON.stringify({ model: this.model, - max_tokens: MAX_TOKENS, + max_tokens: maxTokens, system, messages, }),