diff --git a/ai/src/ai_server/chain/prompts/answer_coaching.py b/ai/src/ai_server/chain/prompts/answer_coaching.py index b47fc19..08f6177 100644 --- a/ai/src/ai_server/chain/prompts/answer_coaching.py +++ b/ai/src/ai_server/chain/prompts/answer_coaching.py @@ -13,6 +13,9 @@ "없는 경험을 지어내지 말고, 있는 내용을 구조(두괄식·근거·결과)와 구체성으로 보강. 답변이 비었거나 " "'모르겠다'면 '이렇게 접근했다면' 식으로 짧게.\n" "- **coaching_comment (한 줄 코칭)**: 이 답변에서 가장 중요한 보완점 하나를 한 문장으로.\n" + "- **질문이 경험·행동·자소서 기반**('그때 무엇을 어떻게 했는가', 지원동기·성장·갈등 등)이면 " + "model_answer 와 answer_rewrite 를 **STAR(상황·과제·행동·결과) 골격**으로 구성하고, 본인의 구체적 " + "행동·정량적 결과·배운 점이 드러나게 코칭하세요. (기술 개념 질문이면 STAR 대신 정확성·깊이 우선.)\n" "- 한국어로, 기술 용어는 영문 원어 유지. 과장·미사여구 금지, 실전 조언만.\n" "- 응답은 반드시 지정된 JSON 스키마를 따릅니다." ) diff --git a/ai/src/ai_server/chain/prompts/document_analysis.py b/ai/src/ai_server/chain/prompts/document_analysis.py index 8ffe82e..306566c 100644 --- a/ai/src/ai_server/chain/prompts/document_analysis.py +++ b/ai/src/ai_server/chain/prompts/document_analysis.py @@ -33,7 +33,10 @@ "추출된 projects/experiences/skills 를 반영하되 추측은 넣지 마세요.\n" "※ 출처 유형이 COVER_LETTER(자기소개서)이면: 기술 나열보다 **지원동기·가치관·성장 경험·" "직무 적합성·핵심 강점/소재**를 우선 추출하세요. experiences 에 자소서 문항별 핵심 주장과 " - "근거 일화를 담고, tech_stack 은 자소서에 실제 언급된 기술만(없으면 빈 배열). markdown 에는 " - "면접에서 파고들 만한 주장·수치·경험을 정리해 질문의 근거가 되게 하세요.\n\n" + "근거 일화를 담고, tech_stack 은 자소서에 실제 언급된 기술만(없으면 빈 배열).\n" + " · **markdown 은 위 '개요/경험/기술/메모' 섹션 대신 문항 구조를 보존**합니다: 자소서 문항마다 " + "`## 문항: <문항 요지>` 헤딩 아래 (핵심 주장 / 근거 일화 / 지원동기·가치관)을 정리하고, 면접관이 " + "**그대로 인용해 되물을 수 있도록 원문 핵심 문장을 짧게 발췌**해 넣으세요(질문 target_evidence 근거). " + "끝에 '## 핵심 강점·소재' 요약을 덧붙여도 됩니다. 문항이 하나로 뭉쳐 오면 주제 단위로 나눕니다.\n\n" "{format_instructions}" ) diff --git a/ai/src/ai_server/chain/prompts/question_generation.py b/ai/src/ai_server/chain/prompts/question_generation.py index a140a35..a36b7dd 100644 --- a/ai/src/ai_server/chain/prompts/question_generation.py +++ b/ai/src/ai_server/chain/prompts/question_generation.py @@ -25,6 +25,21 @@ "(2) JD에 있으나 자료에서 근거가 약한 부분을 확인하는 질문, " "(3) 지원동기·해당 회사/직무 적합성(왜 이 회사·직무인지, 컬처핏) 질문을 적절히 섞습니다. " "타깃이 '(일반 면접 …)' 이면 이 항목은 무시합니다.\n" + "- **자소서(cover letter)가 자료에 포함되면**: PERSONALITY·INTEGRATED 모드(또는 " + "JOB_TAILORED 의 지원동기 파트)에서는 임원·인성 면접처럼 자소서 내용을 대부분 질문의 출발점으로 " + "삼고, 자소서에 쓴 경험·주장·지원동기를 골라 지원자가 그것을 진짜 이해·소유하는지 검증하세요 " + "— '그 상황에서 본인의 구체적 역할·행동은? 결과와 배운 점은?'처럼 한 문항을 동기·과정·기여·성찰 등 " + "여러 각도로 파고들 수 있습니다. **TECHNICAL 모드에서는 자소서를 기술 질문의 보조 근거(동기·" + "프로젝트 배경 확인)로만 쓰고 자소서 중심으로 흐르지 않습니다.**\n" + "- 경험형(BEHAVIORAL·자소서 기반) 질문은 지원자가 **STAR(상황·과제·행동·결과)**로 답하도록 " + "유도하고, expected_signal 에 답변이 드러내야 할 STAR 요소(예: '본인의 구체적 행동과 정량적 결과')를 " + "명시합니다.\n" + "- PERSONALITY·INTEGRATED(또는 자소서 있을 때)에서는 대기업 임원면접의 표준 인성 주제를 " + "지원자 자소서/자기소개에 맞춰 **개인화**해 커버합니다(그대로 베끼지 말고 지원자 맥락으로 변형). " + "표준 주제 참고: 존경하는 인물·이유 / 리더형 vs 팔로워형 / 리더십 발휘 경험 / 성공·실패 경험 / " + "장점과 단점 / 상사와의 갈등 해결 / 원하는 부서 미배치 시 대응 / 개인의 비전 / 갈등 해결 경험 / " + "가장 몰입한 경험 / 본인을 뽑아야 하는 이유·강점 / 10년 후 모습 / 마지막으로 하고 싶은 말 / " + "왜 다른 곳이 아닌 이 회사인가(지원동기).\n" "- 응답은 반드시 지정된 JSON 스키마를 따릅니다." ) @@ -64,7 +79,8 @@ "4. 각 질문에 target_evidence 와 expected_signal 을 채웁니다:\n" " - target_evidence: 질문의 근거가 된 컨텍스트 원문을 짧게 인용. " "PROJECT_DEEP_DIVE·TECH_CHOICE 는 **필수**(근거를 못 찾으면 그 질문을 만들지 마세요). " - "CS_FUNDAMENTAL·BEHAVIORAL 은 선택(없으면 빈 문자열).\n" + "CS_FUNDAMENTAL·BEHAVIORAL 은 선택(없으면 빈 문자열). " + "**단, 자소서 내용에 근거한 질문이면 자소서 원문을 target_evidence 로 인용하세요.**\n" " - expected_signal: 좋은 답변이 드러내야 할 핵심(평가 관점). 한 문장.\n" "5. 지원자 컨텍스트의 기술/프로젝트를 구체적으로 언급하는 PROJECT_DEEP_DIVE / " "TECH_CHOICE 질문을 우선합니다.\n" diff --git a/ai/src/ai_server/messaging/consumers/questions_consumer.py b/ai/src/ai_server/messaging/consumers/questions_consumer.py index 506f431..012aa25 100644 --- a/ai/src/ai_server/messaging/consumers/questions_consumer.py +++ b/ai/src/ai_server/messaging/consumers/questions_consumer.py @@ -150,10 +150,19 @@ async def _build_context(self, req: GenerateQuestionsRequest) -> str: return f"{base_context}\n\n## Retrieved document chunks\n{rag_context}" +_SOURCE_LABEL = { + "RESUME": "이력서", + "REPOSITORY": "GitHub 레포", + "COVER_LETTER": "자소서", + "WEB": "웹 문서", +} + + def _build_context(documents: list[DocumentContext]) -> str: parts: list[str] = [] for d in documents: - block = [f"## 문서 #{d.document_id} ({d.source_type})"] + label = _SOURCE_LABEL.get(d.source_type, d.source_type) + block = [f"## 문서 #{d.document_id} ({label})"] if d.summary: block.append(f"요약: {d.summary}") if d.tech_stack: diff --git a/ai/tests/test_questions_consumer.py b/ai/tests/test_questions_consumer.py index 9eb6b3b..91c02d7 100644 --- a/ai/tests/test_questions_consumer.py +++ b/ai/tests/test_questions_consumer.py @@ -431,11 +431,11 @@ def test_build_context_joins_doc_blocks(): ), ] text = _build_context(docs) - assert "문서 #1 (RESUME)" in text + assert "문서 #1 (이력서)" in text assert "요약1" in text assert "Java, Spring" in text assert "본문1" in text - assert "문서 #2 (REPOSITORY)" in text + assert "문서 #2 (GitHub 레포)" in text assert "readme" in text