[Fix] 자기소개서 소제목을 첨삭 평가 대상에서 제외 - #241
Conversation
- 단일 패스: 소제목을 questionAnalyses, keyStrengths, keyWeaknesses, 점수 근거에서 제외 - 1차 후보: 소제목에 행동·성과가 없다는 이유로 후보 생성 금지 - 2차 검토: 잘못 유입된 소제목 후보는 NOT_ACTIONABLE로 거절 - 소제목은 바로 뒤 문단의 주제와 흐름을 이해하는 보조 맥락으로만 사용 - 소제목의 요약 품질 자체도 별도 첨삭 대상으로 만들지 않음
📝 WalkthroughWalkthrough
ChangesAnalysisAiClient 프롬프트 변경
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant 입력
participant AnalysisAiClient
participant FewShotSearchService
participant AI모델
입력->>AnalysisAiClient: 분석 입력 전달
AnalysisAiClient->>FewShotSearchService: FewShotSearchQuery로 관련 사례 검색
FewShotSearchService-->>AnalysisAiClient: 선택된 few-shot 사례 반환
AnalysisAiClient->>AI모델: 동적 사례와 소제목 제외 규칙을 포함한 프롬프트 전달
AI모델-->>AnalysisAiClient: 분석 결과와 후보 반환
Possibly related PRs
🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✨ Finishing Touches📝 Generate docstrings
🧪 Generate unit tests (beta)
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
There was a problem hiding this comment.
Actionable comments posted: 3
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClient.java`:
- Around line 223-230: 한 줄 전체가 대괄호로 감싸진 소제목을 서버에서 식별하고 모든 후보 경로에서 제외하도록 수정하세요.
analyzeSinglePass의 결과와 sanitizeStrengthCandidates, sanitizeAnalysisCandidates의
원문 부분 문자열 검증에 해당 판별을 적용하고, buildRecheckPrompt 및 recheckValidationFailure를 포함한
recheckWhenAllCandidatesRejected 복원 경로에서도 소제목이 다시 추가되지 않게 하세요.
- Line 1209: Update the few-shot prompt construction in the relevant analysis
flow around selectedFewShots so it uses an API actually exposed by
FewShotPromptProvider. Prefer the existing getPrompt() method, or add
buildPromptBlock(List<SelectedFewShotCase>) to FewShotPromptProvider if that
behavior is required, ensuring the code compiles and preserves the intended
prompt output.
In
`@src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClientTest.java`:
- Around line 309-343: Expand
buildPromptUsesSelectedFewShotsWhenDynamicSelectionEnabled around
resolveFewShotPromptBlock to cover both an empty searchRelevantFewShots result
and an exception from that service, asserting each fallback prompt contains the
fixed few-shot block. Also verify searchRelevantFewShots is called with
fewShotProperties.getSearch().getTopK() as its top-k argument, while preserving
the existing successful-selection assertion.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Path: .coderabbit.yaml
Review profile: ASSERTIVE
Plan: Pro Plus
Run ID: d49b004d-92df-4eea-8441-34f1091ae523
📒 Files selected for processing (2)
src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClient.javasrc/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClientTest.java
| [소제목 처리 규칙] | ||
| - 답변에서 한 줄 전체가 대괄호로 감싸진 형식(예: [문제를 기회로 바꾼 경험])은 본문 문장이 아니라 소제목이다. | ||
| - 소제목 자체를 EXPERIENCE, PLAN, MOTIVATION, COMPETENCY 문장으로 분류하거나 questionAnalyses의 sentence로 반환하지 않는다. | ||
| - 소제목 자체를 keyStrengths의 quote 또는 keyWeaknesses의 근거로 반환하지 않는다. | ||
| - 소제목에 행동, 역할, 방법, 성과가 없다는 이유로 구체성이 부족하다고 판단하거나 점수를 감점하지 않는다. | ||
| - 소제목은 바로 뒤 문단의 주제와 흐름을 이해하는 보조 맥락으로만 사용하고, 실제 평가는 본문 문장을 기준으로 한다. | ||
| - 소제목이 문단 내용을 요약하는 표현인지 여부는 문단 이해에만 참고하며, 소제목 문구 자체를 별도 첨삭 대상으로 만들지 않는다. | ||
There was a problem hiding this comment.
🎯 Functional Correctness | 🟠 Major | 🏗️ Heavy lift
소제목 제외를 서버 검증으로 강제하세요.
현재 규칙은 프롬프트에만 있습니다. analyzeSinglePass는 모델 응답을 바로 반환합니다. sanitizeStrengthCandidates와 sanitizeAnalysisCandidates도 대괄호 소제목을 원문 부분 문자열로 인정합니다.
또한 2차 검토가 소제목 후보를 거절해도 recheckWhenAllCandidatesRejected가 이를 재검증 단계에서 다시 복원할 수 있습니다. buildRecheckPrompt와 recheckValidationFailure에는 소제목 차단 규칙이 없습니다.
한 줄 전체가 대괄호로 감싸진 항목을 식별하는 서버 측 판별을 추가하세요. 이 판별을 single-pass 결과, 1차 strength/analysis 후보, 재검증 복원 경로에 모두 적용하세요. 그렇지 않으면 모델 응답에 따라 PR 목표와 달리 소제목이 첨삭 또는 강점으로 노출됩니다.
Also applies to: 688-695, 867-867
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClient.java`
around lines 223 - 230, 한 줄 전체가 대괄호로 감싸진 소제목을 서버에서 식별하고 모든 후보 경로에서 제외하도록 수정하세요.
analyzeSinglePass의 결과와 sanitizeStrengthCandidates, sanitizeAnalysisCandidates의
원문 부분 문자열 검증에 해당 판별을 적용하고, buildRecheckPrompt 및 recheckValidationFailure를 포함한
recheckWhenAllCandidatesRejected 복원 경로에서도 소제목이 다시 추가되지 않게 하세요.
| selectedFewShots.stream().map(item -> "%.4f".formatted(item.score())).toList(), | ||
| fewShotProperties.getDatasetVersion() | ||
| ); | ||
| return fewShotPromptProvider.buildPromptBlock(selectedFewShots); |
There was a problem hiding this comment.
🎯 Functional Correctness | 🔴 Critical | ⚡ Quick win
FewShotPromptProvider에 없는 메서드를 호출합니다.
제공된 FewShotPromptProvider.java:13-43에는 getPrompt()만 있고 buildPromptBlock(List<SelectedFewShotCase>)가 없습니다. 이 호출은 컴파일되지 않습니다. FewShotPromptProvider에 해당 메서드를 구현하거나 기존 공개 API를 사용하세요.
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/main/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClient.java`
at line 1209, Update the few-shot prompt construction in the relevant analysis
flow around selectedFewShots so it uses an API actually exposed by
FewShotPromptProvider. Prefer the existing getPrompt() method, or add
buildPromptBlock(List<SelectedFewShotCase>) to FewShotPromptProvider if that
behavior is required, ensuring the code compiles and preserves the intended
prompt output.
| @Test | ||
| @DisplayName("dynamic few-shot이 활성화되면 선택된 예시만 single-pass 프롬프트에 포함한다") | ||
| void buildPromptUsesSelectedFewShotsWhenDynamicSelectionEnabled() { | ||
| fewShotProperties.setDynamicSelectionEnabled(true); | ||
| FewShotCase selectedCase = new FewShotCase( | ||
| "FS-DYNAMIC-1", | ||
| FewShotSource.CURATED, | ||
| FewShotReviewStatus.APPROVED, | ||
| true, | ||
| 0, | ||
| "백엔드 개발", | ||
| "Backend Engineer", | ||
| List.of("API 개발"), | ||
| List.of("Spring Boot"), | ||
| "직무 경험", | ||
| "API를 개발했습니다.", | ||
| "{\"questionAnalyses\":[]}", | ||
| List.of("api"), | ||
| "fewshot-test-v1", | ||
| "## 예시 Z: 동적 선택 예시\n출력 중 문장/누락 관련 필드:\n{}" | ||
| ); | ||
| when(fewShotSearchService.searchRelevantFewShots(org.mockito.ArgumentMatchers.any(), org.mockito.ArgumentMatchers.anyInt())) | ||
| .thenReturn(List.of(new SelectedFewShotCase(selectedCase, 0.91, "test"))); | ||
|
|
||
| String prompt = analysisAiClient.buildPrompt( | ||
| mockJobPosting(), | ||
| List.of(mockQuestion()), | ||
| new RetrievalContext(List.of(), List.of()), | ||
| null | ||
| ); | ||
|
|
||
| assertThat(prompt) | ||
| .contains("## 예시 Z: 동적 선택 예시") | ||
| .doesNotContain("## 예시 A: 좋은 근거가 있으면 보완 문장이 0개일 수 있음"); | ||
| } |
There was a problem hiding this comment.
🩺 Stability & Availability | 🟡 Minor | ⚡ Quick win
빈 결과와 검색 실패 fallback을 테스트하세요.
이 테스트는 선택 성공 경로만 검증합니다. resolveFewShotPromptBlock의 빈 결과 및 예외 fallback은 프롬프트 생성 가능성을 보장하는 동작입니다.
searchRelevantFewShots가 빈 목록을 반환하는 경우와 예외를 던지는 경우를 추가하세요. 두 경우 모두 고정 few-shot 블록이 포함되는지 검증하세요. 또한 호출 인자가 fewShotProperties.getSearch().getTopK()와 같은지 검증하세요.
As per path instructions: "해피패스만 있는 경우 경계 조건과 회귀 위험을 지적하세요."
🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
In
`@src/test/java/com/jobdri/jobdri_api/domain/analysis/service/ai/AnalysisAiClientTest.java`
around lines 309 - 343, Expand
buildPromptUsesSelectedFewShotsWhenDynamicSelectionEnabled around
resolveFewShotPromptBlock to cover both an empty searchRelevantFewShots result
and an exception from that service, asserting each fallback prompt contains the
fixed few-shot block. Also verify searchRelevantFewShots is called with
fewShotProperties.getSearch().getTopK() as its top-k argument, while preserving
the existing successful-selection assertion.
Source: Path instructions
✨ 어떤 이유로 PR를 하셨나요?
📋 세부 내용 - 왜 해당 PR이 필요한지 작업 내용을 자세하게 설명해주세요
📸 작업 화면 스크린샷
🚨 관련 이슈 번호 [ ]
Summary by CodeRabbit