feat: 약점 집중 재도전 (B-3) - #177
Merged
Merged
Conversation
`POST /api/sessions/{id}/retry` 에 `{"focusOnWeakness": true}` 를 주면, 원본 세션 피드백에서
낮았던 평가 축을 새 세션의 집중 영역으로 새긴다. 질문 생성이 그 영역을 검증하는 질문을
우선 배치한다.
## 약점 선정
- 임계값(`interview.weakness-focus.score-threshold`, 기본 70) 미만인 축을 낮은 순 최대 2개
- **전부 기준 이상이어도 가장 낮은 하나는 고른다** — 눌렀는데 아무것도 안 바뀌면 버튼이
고장난 것처럼 보인다
- 피드백이 없으면(중단 세션 등) 빈 목록 → 일반 재도전과 동일하게 동작
`@Value` 필드에 자바 초기값(`= 70`)도 함께 뒀다. Spring 밖(단위 테스트)에서는 주입이 안 돼
0.0 이 되고, 그러면 모든 축이 '기준 이상'으로 판정돼 **조용히 다른 동작**을 한다.
실제로 이 테스트를 쓰다가 걸렸다.
## 전달 경로
V25 로 `interview_sessions.focus_areas`(JSONB, `SessionFocusArea` name 배열) 추가.
`SessionQuestionsRequester` 가 세션에서 읽어 `generate.questions.focusAreas` 로 싣는다 —
이벤트 레코드를 늘리지 않고 이미 갖고 있던 `sessionRepository` 로 조회한다.
`SessionResponse.focusAreas` 로도 노출해 화면이 "무엇을 겨냥하는 면접인지" 보여줄 수 있게 했다.
## 테스트
`SessionServiceTest` +3 — 기준 미만 축 2개 선정, 전부 강할 때 최저 1개 폴백, 피드백 없을 때
일반 재도전 폴백. 기존 retry 테스트는 새 시그니처에 맞춰 갱신.
`generate.questions` 의 `focusAreas`(TECHNICAL|LOGIC|COMMUNICATION)를 받아, 그 영역을 검증하는 질문이 과반이 되도록 프롬프트에 지시한다. - `_FOCUS_GUIDE` — 축별 **질문 설계 관점**을 명시. 라벨만 넘기면 LLM 이 제각각 해석한다 (LOGIC 을 "논리 퀴즈"로 받는 식). 기술 정확도는 동작 원리·트레이드오프, 논리력은 판단 근거와 대안 폐기 이유, 전달력은 눈높이 설명으로 각각 다른 질문이 나오게 했다 - **근거 없는 질문 생성 금지는 그대로**다. 집중 영역은 *무엇을 묻는가*를 바꾸는 것이지 target_evidence 필수 조건을 면제하지 않는다 — 자료가 부족하면 집중 비중을 줄이도록 명시 - 지정이 없으면 "(지정 없음 — 치우치지 말 것)" 이 들어가 일반 면접과 동일하게 동작 테스트 `test_question_focus_areas.py` 신설 (11) — 미지정 시 쏠림 지시가 새지 않는지, 축별 관점 문구가 실리는지, 모르는 축이 와도 깨지지 않는지(Core 가 축을 늘릴 수 있다), 생성기가 프롬프트 변수를 항상 채우는지(비면 템플릿 렌더가 KeyError). pytest 362건 통과.
- 피드백 화면: "약점 집중해서 다시" 를 primary 로. 점수가 나온 직후라 여기서는 그게 기본 제안이고, "같은 설정으로 다시" 는 secondary 로 내렸다 - 종료 패널: 완료 세션에만 약점 집중을 함께 제안한다. 약점은 피드백 점수가 근거라 피드백이 없는 중단 세션에는 뜻이 없다 - 면접 화면 헤더에 "논리력·전달력 집중" 표시 + 시작 시 토스트. 질문 성격이 왜 달라졌는지 보이지 않으면 사용자는 이유를 알 수 없다 - `focusAreaLabel` — 모르는 축은 원본을 그대로 쓴다(서버가 축을 늘려도 빈칸이 되지 않게) 테스트: `SessionEndedPanel.test.tsx` +2(완료 세션의 약점 집중 호출 인자, 중단 세션엔 미노출), `focusArea.test.ts` 신설(2). vitest 90/90 · eslint 0 · tsc · build 통과.
- `docs/database.md` — interview_sessions.focus_areas 반영 - `docs/messaging.md` — generate.questions 페이로드에 focusAreas 추가 - `backend/CLAUDE.md` — 약점 선정 규칙(임계값·최저 1개 폴백)과 @value 초기값 함정 기록 - `ai/CLAUDE.md` — 축별 질문 설계 관점(_FOCUS_GUIDE)과 근거 필수 조건 유지 명시
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
변경 사항
감사 백로그 B-3. 피드백에서 낮았던 평가 축을 다음 면접의 집중 영역으로 새겨, 질문 생성이 그 영역을 검증하는 질문을 우선 배치한다.
직전 PR(#176)의 재도전 위에 얹었다 —
POST /api/sessions/{id}/retry에{"focusOnWeakness": true}를 주는 형태다.자동이 아니라 opt-in 으로 한 이유
"이전 면접이 약했으니 알아서 그쪽을 더 묻는다"를 기본값으로 넣을 수도 있었지만, 그러면 사용자 모르게 질문 성격이 바뀐다. 왜 이런 질문이 나왔는지 설명할 수 없는 AI 동작은 신뢰를 깎는다. 그래서 버튼으로 노출하고, 면접 화면 헤더에 "논리력·전달력 집중" 을 표시하고, 시작할 때 토스트로도 알린다.
약점 선정 규칙
interview.weakness-focus.score-threshold, 기본 70) 미만인 축을 낮은 순 최대 2개작업 중 걸린 함정 하나:
@Value필드는 Spring 밖(단위 테스트)에서 주입되지 않아0.0이 되고, 그러면 모든 축이 '기준 이상'으로 판정돼 조용히 다른 동작을 한다. 자바 초기값(= 70)을 함께 두는 것으로 막았다.전달 경로
이벤트 레코드를 늘리지 않고 이미 갖고 있던
sessionRepository로 조회한다.AI 프롬프트에서 주의한 것
_FOCUS_GUIDE— 축별 질문 설계 관점을 명시했다. 라벨만 넘기면 LLM 이 제각각 해석한다(LOGIC을 "논리 퀴즈"로 받는 식). 기술 정확도는 동작 원리·트레이드오프, 논리력은 판단 근거와 대안 폐기 이유, 전달력은 눈높이 설명으로 각각 다른 질문이 나오게 했다target_evidence필수 조건을 면제하지 않는다 — 자료가 부족하면 집중 비중을 줄이도록 명시했다. 이게 없으면 "논리력 집중"이 자료에 없는 가상 질문을 지어내는 구실이 된다테스트
SessionServiceTest+3test_question_focus_areas.py(11)SessionEndedPanel.test.tsx+2,focusArea.test.ts(2)backend
test통과(ArchUnit 포함) · ai flake8/black/pytest 362 · frontend vitest 90/90 · eslint 0 · tsc · build.영향 범위
interview_sessions.focus_areas컬럼 추가만. 기존 행은 NULL = 일반 면접)POST /api/sessions/{id}/retry에 optional body 추가,SessionResponse.focusAreas추가 (둘 다 하위호환)interview.weakness-focus.score-threshold는 기본값 있음)리뷰어 체크포인트
_FOCUS_GUIDE)가 실제 면접관 관점으로 맞는지. 여기가 질문 품질을 좌우한다