Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
6 changes: 3 additions & 3 deletions ai/src/ai_server/chain/feedback_generation_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -28,7 +28,7 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
total_question_count: int | None,
end_reason: str | None,
transcript: str,
Expand All @@ -44,7 +44,7 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
total_question_count: int | None,
end_reason: str | None,
transcript: str,
Expand All @@ -53,7 +53,7 @@ async def generate(
result = await self._chain.ainvoke(
{
"job_category": job_category,
"interview_type": interview_type,
"mode": mode,
"total_question_count": total_question_count or 0,
"end_reason": end_reason or "USER_REQUEST",
"transcript": transcript,
Expand Down
6 changes: 3 additions & 3 deletions ai/src/ai_server/chain/followup_generation_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -24,7 +24,7 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
previous_question: str,
answer_text: str,
) -> FollowupResult: ...
Expand All @@ -38,14 +38,14 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
previous_question: str,
answer_text: str,
) -> FollowupResult:
result = await self._chain.ainvoke(
{
"job_category": job_category,
"interview_type": interview_type,
"mode": mode,
"previous_question": previous_question,
"answer_text": answer_text,
}
Expand Down
2 changes: 1 addition & 1 deletion ai/src/ai_server/chain/prompts/feedback_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -22,7 +22,7 @@

HUMAN_PROMPT = (
"직군: {job_category}\n"
"면접 유형: {interview_type}\n"
"면접 모드: {mode}\n"
"총 질문 수: {total_question_count}\n"
"종료 사유: {end_reason}\n\n"
"=== 메시지 시퀀스 ===\n"
Expand Down
2 changes: 1 addition & 1 deletion ai/src/ai_server/chain/prompts/followup_generation.py
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@

HUMAN_PROMPT = (
"직군: {job_category}\n"
"면접 유형: {interview_type}\n\n"
"면접 모드: {mode}\n\n"
"직전 질문:\n{previous_question}\n\n"
"지원자 답변:\n{answer_text}\n\n"
"{format_instructions}"
Expand Down
8 changes: 4 additions & 4 deletions ai/src/ai_server/chain/prompts/question_generation.py
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
# 질문 풀 생성 프롬프트 (US-18)
# 분석된 이력서/레포 컨텍스트 + 직군 + 면접 유형 → 질문 N개.
# 분석된 이력서/레포 컨텍스트 + 직군 + 면접 모드 → 질문 N개.

SYSTEM_PROMPT = (
"당신은 IT 직군 채용을 진행하는 시니어 면접관입니다. "
"지원자 컨텍스트(이력서, GitHub 레포 분석)와 면접 유형에 맞춰 면접 질문 풀을 "
"지원자 컨텍스트(이력서, GitHub 레포 분석)와 면접 모드에 맞춰 면접 질문 풀을 "
"생성하세요.\n"
"- 사실에 근거한 질문만. 자료에 없는 내용은 추측하지 않습니다.\n"
"- 자료에 명시된 기술 스택과 프로젝트 경험을 적극 인용하세요.\n"
Expand All @@ -15,7 +15,7 @@

HUMAN_PROMPT = (
"직군: {job_category}\n"
"면접 유형: {interview_type}\n"
"면접 모드: {mode}\n"
"최대 질문 수: {max_questions}\n\n"
"지원자 컨텍스트 (이력서/레포 분석):\n"
"---\n"
Expand All @@ -24,7 +24,7 @@
"요구 사항:\n"
"1. 정확히 {max_questions}개의 질문을 생성합니다.\n"
"2. 각 질문에 적절한 category 를 부여합니다.\n"
"3. 직군({job_category})·유형({interview_type}) 에 맞는 비중으로 카테고리 분배:\n"
"3. 직군({job_category})·면접 모드({mode}) 에 맞는 비중으로 카테고리 분배:\n"
" - TECHNICAL: CS_FUNDAMENTAL + TECH_CHOICE + PROJECT_DEEP_DIVE 중심.\n"
" - PERSONALITY: BEHAVIORAL 중심.\n"
" - INTEGRATED: 모두 균형.\n"
Expand Down
6 changes: 3 additions & 3 deletions ai/src/ai_server/chain/question_generation_chain.py
Original file line number Diff line number Diff line change
Expand Up @@ -23,7 +23,7 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
max_questions: int,
context: str,
) -> GeneratedQuestionPool: ...
Expand All @@ -37,14 +37,14 @@ async def generate(
self,
*,
job_category: str,
interview_type: str,
mode: str,
max_questions: int,
context: str,
) -> GeneratedQuestionPool:
result = await self._chain.ainvoke(
{
"job_category": job_category,
"interview_type": interview_type,
"mode": mode,
"max_questions": max_questions,
"context": context,
}
Expand Down
2 changes: 1 addition & 1 deletion ai/src/ai_server/messaging/consumers/feedback_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -87,7 +87,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None:

result = await self._generator.generate(
job_category=req.job_category,
interview_type=req.interview_type,
mode=req.mode,
total_question_count=req.total_question_count,
end_reason=req.end_reason,
transcript=transcript,
Expand Down
2 changes: 1 addition & 1 deletion ai/src/ai_server/messaging/consumers/followup_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -62,7 +62,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None:

result = await self._generator.generate(
job_category=req.job_category,
interview_type=req.interview_type,
mode=req.mode,
previous_question=req.previous_question,
answer_text=req.answer_text,
)
Expand Down
2 changes: 1 addition & 1 deletion ai/src/ai_server/messaging/consumers/questions_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -71,7 +71,7 @@ async def handle(self, message: AbstractIncomingMessage) -> None:
context_text = _build_context(req.documents)
pool = await self._generator.generate(
job_category=req.job_category,
interview_type=req.interview_type,
mode=req.mode,
max_questions=effective_pool_size,
context=context_text,
)
Expand Down
4 changes: 3 additions & 1 deletion ai/src/ai_server/model/messages/feedback.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from ai_server.model._config import camel_config

InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"]


class FeedbackMessageItem(BaseModel):
"""세션 시퀀스 한 줄 (Core 가 통째로 동봉)."""
Expand All @@ -21,7 +23,7 @@ class GenerateFeedbackRequest(BaseModel):
model_config = camel_config()

session_id: int
interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"]
mode: InterviewMode
job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"]
total_question_count: int | None = None
end_reason: Literal["USER_REQUEST", "MAX_QUESTIONS_REACHED"] | None = None
Expand Down
4 changes: 3 additions & 1 deletion ai/src/ai_server/model/messages/followup.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from ai_server.model._config import camel_config

InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"]


class GenerateFollowupRequest(BaseModel):
"""Core 가 답변 commit 후 발행."""
Expand All @@ -14,7 +16,7 @@ class GenerateFollowupRequest(BaseModel):
answer_message_id: int # 답변 메시지 ID
previous_question: str
answer_text: str
interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"]
mode: InterviewMode
job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"]


Expand Down
4 changes: 2 additions & 2 deletions ai/src/ai_server/model/messages/questions.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@

from ai_server.model._config import camel_config

InterviewType = Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"]
InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"]
JobCategory = Literal["FRONTEND", "BACKEND", "INFRA", "DBA"]
QuestionCategory = Literal[
"CS_FUNDAMENTAL",
Expand All @@ -30,7 +30,7 @@ class GenerateQuestionsRequest(BaseModel):
model_config = camel_config()

session_id: int
interview_type: InterviewType
mode: InterviewMode
job_category: JobCategory
documents: list[DocumentContext] = []
max_questions: int = 10
Expand Down
4 changes: 3 additions & 1 deletion ai/src/ai_server/model/messages/voice.py
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,8 @@

from ai_server.model._config import camel_config

InterviewMode = Literal["PERSONALITY", "TECHNICAL", "INTEGRATED"]


class AnalyzeVoiceRequest(BaseModel):
"""Core 가 음성 답변 업로드 commit 후 발행."""
Expand All @@ -15,7 +17,7 @@ class AnalyzeVoiceRequest(BaseModel):
audio_s3_key: str
content_type: str
previous_question_text: str | None = None
interview_type: Literal["PERSONALITY", "TECHNICAL", "LIVE_CODING", "INTEGRATED"]
mode: InterviewMode
job_category: Literal["FRONTEND", "BACKEND", "INFRA", "DBA"]


Expand Down
2 changes: 1 addition & 1 deletion ai/tests/test_feedback_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ def _envelope(*, context_documents: list[int] | None = None) -> bytes:
"publisher": "core-server",
"payload": {
"sessionId": 50,
"interviewType": "TECHNICAL",
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
"totalQuestionCount": 2,
"endReason": "MAX_QUESTIONS_REACHED",
Expand Down
2 changes: 1 addition & 1 deletion ai/tests/test_followup_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ def _envelope() -> bytes:
"answerMessageId": 502,
"previousQuestion": "결제 outbox 어떻게 구현?",
"answerText": "RabbitMQ로 보냈습니다.",
"interviewType": "TECHNICAL",
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
},
"context": {"userId": 42, "sessionId": 99},
Expand Down
6 changes: 3 additions & 3 deletions ai/tests/test_questions_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -73,7 +73,7 @@ async def test_consumer_generates_questions_and_publishes_callback():
body = _envelope(
{
"sessionId": 99,
"interviewType": "TECHNICAL",
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
"documents": [
{
Expand All @@ -92,7 +92,7 @@ async def test_consumer_generates_questions_and_publishes_callback():
generator.generate.assert_awaited_once()
call = generator.generate.await_args
assert call.kwargs["job_category"] == "BACKEND"
assert call.kwargs["interview_type"] == "TECHNICAL"
assert call.kwargs["mode"] == "TECHNICAL"
# envelope.max_questions(=5) 무시하고 initial_pool_size(default 1) 로 강제. Core 가 첫 질문만 사용.
assert call.kwargs["max_questions"] == 1
assert "Java" in call.kwargs["context"]
Expand Down Expand Up @@ -126,7 +126,7 @@ async def test_consumer_skips_when_message_id_already_seen():
body = _envelope(
{
"sessionId": 99,
"interviewType": "TECHNICAL",
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
"documents": [],
"maxQuestions": 3,
Expand Down
2 changes: 1 addition & 1 deletion ai/tests/test_voice_consumer.py
Original file line number Diff line number Diff line change
Expand Up @@ -47,7 +47,7 @@ def _envelope() -> bytes:
"audioS3Key": "interview/voice/raw/99/501.webm",
"contentType": "audio/webm",
"previousQuestionText": "ACID 설명해주세요",
"interviewType": "TECHNICAL",
"mode": "TECHNICAL",
"jobCategory": "BACKEND",
},
"context": {"userId": 42, "sessionId": 99},
Expand Down
3 changes: 1 addition & 2 deletions backend/CLAUDE.md
Original file line number Diff line number Diff line change
Expand Up @@ -137,9 +137,8 @@ QueryDSL Q-class 생성 위치: `build/generated/sources/annotationProcessor/...
public record SessionCreateRequest(
String title,
@NotNull SessionMode mode,
@NotNull InterviewType interviewType,
@NotNull JobCategory jobCategory,
@Min(1) @Max(30) Integer maxQuestions,
@Min(2) @Max(30) Integer maxQuestions,
@Min(5) @Max(180) Integer maxDurationMinutes,
List<Long> contextDocumentIds
) {}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,7 @@ public void onSessionEnded(SessionEndedEvent event) {

GenerateFeedbackPayload payload = new GenerateFeedbackPayload(
session.getId(),
session.getInterviewType().name(),
session.getMode().name(),
session.getJobCategory().name(),
session.getTotalQuestionCount(),
event.reason(),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -45,7 +45,7 @@ public void onAnswerSubmitted(AnswerSubmittedEvent event) {
answer.getId(),
parent.getContent(),
answer.getContent(),
session.getInterviewType(),
session.getMode(),
session.getJobCategory()
);
publisher.publishToAi(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -50,7 +50,7 @@ public void onSessionCreated(SessionCreatedEvent event) {
List<DocumentContext> documents = buildDocumentContexts(event.contextDocumentIds());
GenerateQuestionsPayload payload = new GenerateQuestionsPayload(
event.sessionId(),
event.interviewType(),
event.mode(),
event.jobCategory(),
documents,
event.maxQuestions()
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -44,7 +44,6 @@ public SessionResult create(Long userId, SessionCreateCommand command) {
command.title(),
command.memo(),
command.mode(),
command.interviewType(),
command.jobCategory(),
command.maxQuestions(),
command.maxDurationMinutes()
Expand All @@ -56,7 +55,7 @@ public SessionResult create(Long userId, SessionCreateCommand command) {
events.publishEvent(new SessionCreatedEvent(
userId,
session.getId(),
session.getInterviewType(),
session.getMode(),
session.getJobCategory(),
session.getMaxQuestions(),
linkedIds
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,7 @@ public MessageResult submit(Long userId, Long sessionId, VoiceAnswerUploadComman
key,
cmd.contentType(),
latest.getContent(),
session.getInterviewType().name(),
session.getMode().name(),
session.getJobCategory().name()
);
publisher.publishToAi(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@ public record AnalyzeVoicePayload(
String audioS3Key,
String contentType,
String previousQuestionText, // STT 정확도 향상용 hint (optional)
String interviewType,
String mode,
String jobCategory
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@
// AI 가 세션 메시지/평가/분석문서 컨텍스트 기반으로 종합 피드백을 생성.
public record GenerateFeedbackPayload(
Long sessionId,
String interviewType,
String mode,
String jobCategory,
Integer totalQuestionCount,
String endReason, // USER_REQUEST | MAX_QUESTIONS_REACHED
Expand Down
Original file line number Diff line number Diff line change
@@ -1,15 +1,15 @@
package com.stackup.stackup.session.application.dto;

import com.stackup.stackup.session.domain.InterviewType;
import com.stackup.stackup.session.domain.JobCategory;
import com.stackup.stackup.session.domain.SessionMode;

public record GenerateFollowupPayload(
Long sessionId,
Long parentMessageId,
Long answerMessageId,
String previousQuestion,
String answerText,
InterviewType interviewType,
SessionMode mode,
JobCategory jobCategory
) {
}
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.stackup.stackup.session.application.dto;

import com.stackup.stackup.session.domain.InterviewType;
import com.stackup.stackup.session.domain.JobCategory;
import com.stackup.stackup.session.domain.SessionMode;
import java.util.List;

public record GenerateQuestionsPayload(
Long sessionId,
InterviewType interviewType,
SessionMode mode,
JobCategory jobCategory,
List<DocumentContext> documents,
Integer maxQuestions
Expand Down
Loading