diff --git a/ai/CLAUDE.md b/ai/CLAUDE.md
index e6d6afbf..a527f714 100644
--- a/ai/CLAUDE.md
+++ b/ai/CLAUDE.md
@@ -345,6 +345,7 @@ docker run --env-file .env -p 8000:8000 stackup-ai
(직무가 무엇을 하는 자리인지 이해·지원동기). 두 축을 `panelBreakdown` 의 `evaluator="직무 적합도"`·
`evaluator="직무 이해도"` 항목으로 append(첫인상과 같은 병렬·미집계 메커니즘). 그 외 모드/빈 JD/실패는 건너뜀.
- **꼬리질문 토큰 스트리밍 본 구현**: followup 출력을 `……{json}` 구분자 포맷으로 바꾸고(`chain/prompts/followup_generation.py`), `StreamingFollowupGenerator`(`astream`)가 `` 토큰만 `SessionRealtimeNotifier`(`messaging/session_notify.py`)로 `SESSION_MESSAGE_DELTA` 발행(`stackup.realtime`/`realtime.session.notify`, Core 우회). `DONT_KNOW` 면 델타 미발행. 종료 후 `parse_followup_result` 로 검증해 기존 `callback.questions(FOLLOWUP, followupMessageId)` 발행. 와이어링은 `messaging/runner.py`(분석 진행 publisher 재사용).
+- **짧은 확인 질문/답변 커버 본 구현**: followup 프롬프트(`chain/prompts/followup_generation.py`)가 매 턴 깊이 파기 대신 "그럼 OO 하신 건가요?" 같은 **짧은 확인형 질문**도 던지도록 안내하고, 직전 답변이 확인형 단답('네 맞습니다'·'아뇨 그건 아닙니다')이면 specificity/logic/correctness=null·structure=NONE 으로 두어 짧다는 이유로 감점하지 않도록 지시. 피드백 단계에선 `feedback_consumer._is_short_confirmation`(확인/부정 표현으로 시작 + 짧은 길이) 으로 그런 단답을 **질문별 복기(코칭) 대상에서 제외** — '네 맞습니다'에 모범답안/리라이트가 붙지 않게 한다(결정론적). 출력 포맷·콜백 스키마 무변경.
- **문장 단위 TTS 본 구현 (Part B)**: followup consumer 스트림 루프가 `chain/sentence_split.next_sentences` 로 문장 경계를 잡아, 문장마다 `TtsProvider` 인라인 합성(`asyncio.create_task` 백그라운드, 텍스트 델타 비차단)→S3 `interview/tts/{sid}/{mid}/seg-{seq}.{ext}` PUT→`SessionRealtimeNotifier.emit_audio`(`SESSION_MESSAGE_AUDIO`). 콜백 전 `gather` 로 수거. 라이브 세그먼트는 휘발성(DB 미기록).
- **임베딩 본 구현** (`rag/`): `MarkdownChunker` + `GeminiEmbeddingProvider` (1536d, `gemini-embedding-001`).
운영/개발 default 는 gemini, 테스트는 `MockEmbeddingProvider`.
diff --git a/ai/src/ai_server/chain/prompts/followup_generation.py b/ai/src/ai_server/chain/prompts/followup_generation.py
index 96a34f4f..66ed35ac 100644
--- a/ai/src/ai_server/chain/prompts/followup_generation.py
+++ b/ai/src/ai_server/chain/prompts/followup_generation.py
@@ -21,6 +21,9 @@
"correctness 의심→자료와의 불일치 확인).\n"
"- 꼬리질문은 **한 문장으로 간결하게**(대략 60자 이내). 장황한 배경 설명 없이 "
"핵심만 묻고, 면접관이 입으로 말할 길이여야 합니다. CLARIFICATION 재설명도 짧고 쉽게.\n"
+ "- 매번 깊이 파고들 필요는 없습니다. 지원자가 특정 사실/선택을 말했을 때는 "
+ "'그럼 OO 하신 건가요?' 같은 **짧은 확인형(예/아니오) 질문**으로 사실관계를 짚는 것도 자연스럽습니다. "
+ "다만 매 턴 확인형만 반복하지 말고 대체로는 깊이 있는 질문을 유지하세요.\n"
"- '이미 나눈 대화'에서 다룬 내용을 그대로 반복하지 말고 새로운 각도로 파고드세요.\n"
"- answer_intent 로 답변 의도를 분류하세요:\n"
" - DONT_KNOW: '모르겠습니다/잘 모릅니다/패스' 등 사실상 답을 못 한 경우.\n"
@@ -28,6 +31,9 @@
"이때 followup_question 에는 새 꼬리질문이 아니라 **직전 질문을 더 쉽고 구체적으로 다시 설명한 문장**을 담으세요.\n"
" - NORMAL: 그 외 정상 답변. followup_question 은 평소처럼 가장 약한 축을 파는 꼬리질문.\n"
" - DONT_KNOW/CLARIFICATION 이면 채점(specificity/logic/correctness)은 보수적으로(낮게/null) 둡니다.\n"
+ "- 직전 답변이 확인형 질문에 대한 **짧은 단답·정정**(예: '네 맞습니다', '아뇨 그건 아닙니다')이라 "
+ "평가할 내용이 거의 없으면 specificity/logic/correctness 를 null, structure 를 NONE 으로 두고 "
+ "**짧다는 이유만으로 감점하지 마세요**(확인형 질문에 대한 단답은 정상입니다).\n"
"- 출력은 정확히 다음 3개 블록을 이 순서로만 작성합니다(설명·코드펜스 금지):\n"
" NORMAL 또는 DONT_KNOW 또는 CLARIFICATION\n"
" 지원자에게 보여줄 한국어 질문 1개(또는 CLARIFICATION 시 재설명 문장)\n"
diff --git a/ai/src/ai_server/messaging/consumers/feedback_consumer.py b/ai/src/ai_server/messaging/consumers/feedback_consumer.py
index b7464306..67c02972 100644
--- a/ai/src/ai_server/messaging/consumers/feedback_consumer.py
+++ b/ai/src/ai_server/messaging/consumers/feedback_consumer.py
@@ -1,6 +1,7 @@
from __future__ import annotations
import asyncio
+import re
import structlog
from aio_pika.abc import AbstractIncomingMessage
@@ -337,10 +338,27 @@ def _collect_coachable_pairs(
continue
if (question.category or "") == _SELF_INTRO_CATEGORY:
continue # 자기소개는 첫인상 평가가 커버
+ if _is_short_confirmation(m.content):
+ continue # 확인형 질문에 대한 단답('네 맞습니다' 등) — 모범답안 코칭 무의미
pairs.append((question, m))
return pairs
+# 확인형 질문에 대한 단답(예/아니오 + 짧은 정정)으로 시작하는지. 짧고(어절 수·길이) 확인/부정
+# 표현으로 시작할 때만 True — 부연이 충분히 길면 일반 답변으로 보고 코칭 대상에 남긴다.
+_CONFIRMATION_PREFIX_RE = re.compile(
+ r"^(네|예|응|맞(습니다|아요|네요)|그렇(습니다|죠|네요)|"
+ r"아(뇨|니요|닙니다|니에요)|아니(요|에요)?|모르(겠습니다|겠어요))"
+)
+
+
+def _is_short_confirmation(text: str | None) -> bool:
+ s = (text or "").strip()
+ if not s or len(s) > 25 or len(s.split()) > 6:
+ return False
+ return bool(_CONFIRMATION_PREFIX_RE.match(s))
+
+
def _coaching_target_role(req: GenerateFeedbackRequest) -> str:
"""직무 맞춤 모드일 때만 코칭 프롬프트에 실을 회사/JD 발췌. 그 외 빈 문자열."""
if (req.mode or "") != _JOB_TAILORED_MODE:
diff --git a/ai/tests/test_short_confirmation.py b/ai/tests/test_short_confirmation.py
new file mode 100644
index 00000000..07180e18
--- /dev/null
+++ b/ai/tests/test_short_confirmation.py
@@ -0,0 +1,56 @@
+from __future__ import annotations
+
+import pytest
+
+from ai_server.messaging.consumers.feedback_consumer import (
+ _collect_coachable_pairs,
+ _is_short_confirmation,
+)
+from ai_server.model.messages.feedback import FeedbackMessageItem
+
+
+@pytest.mark.parametrize(
+ "text",
+ [
+ "네 맞습니다.",
+ "네, 맞습니다",
+ "아뇨 그건 아닙니다.",
+ "아니요.",
+ "예 그렇습니다.",
+ "맞습니다.",
+ ],
+)
+def test_detects_short_confirmation(text: str) -> None:
+ assert _is_short_confirmation(text) is True
+
+
+@pytest.mark.parametrize(
+ "text",
+ [
+ # 충분히 긴 정정/부연은 일반 답변 — 코칭 대상으로 남긴다.
+ "아뇨, 그 부분은 캐시 무효화 전략이 달라서 조인 대신 임시 테이블을 썼습니다.",
+ # 짧지만 확인형 단답이 아닌 실질 답변.
+ "시간 복잡도는 O(n log n) 입니다.",
+ "인덱스를 풀스캔하지 않도록 복합 인덱스를 새로 만들었습니다.",
+ "",
+ ],
+)
+def test_keeps_substantive_answers(text: str) -> None:
+ assert _is_short_confirmation(text) is False
+
+
+def test_coaching_pairs_skip_short_confirmation() -> None:
+ msgs = [
+ FeedbackMessageItem(id=1, sequence_number=1, role="INTERVIEWER",
+ content="그럼 인덱스를 직접 만드셨나요?", category="TECH_CHOICE"),
+ FeedbackMessageItem(id=2, sequence_number=2, role="INTERVIEWEE",
+ content="네 맞습니다.", parent_message_id=1),
+ FeedbackMessageItem(id=3, sequence_number=3, role="INTERVIEWER",
+ content="어떤 컬럼으로 복합 인덱스를 구성했나요?", category="TECH_CHOICE"),
+ FeedbackMessageItem(id=4, sequence_number=4, role="INTERVIEWEE",
+ content="조회 조건인 user_id 와 created_at 으로 복합 인덱스를 잡았습니다.",
+ parent_message_id=3),
+ ]
+ pairs = _collect_coachable_pairs(msgs)
+ answer_ids = [a.id for _, a in pairs]
+ assert answer_ids == [4] # 단답(2)은 제외, 실질 답변(4)만 코칭