dain.son(손다인)‒AI 면접 피드백 기능 - 100-hours-a-week/3-team-ssammu-wiki GitHub Wiki
AI 면접 피드백 기능 구현
🎯 핵심 개요
사용자의 면접 답변에 대해 LLM 기반 자연어 피드백을 자동 생성하는 기능.
비정형 답변에 대해 정확성, 구체성, 보완점 중심의 평가 문장을 LLM이 직접 생성.
파인튜닝된 aya-expanse-8b
모델과 LangChain
, aiohttp
비동기 추론을 기반으로 설계.
🏗️ 시스템 아키텍처
FastAPI → Feedback API → LLM (aya-expanse-8b via vLLM) → 피드백 텍스트 반환
핵심 구성요소:
- FastAPI: 피드백 API 서버
- VLLM 서버: 모델 추론 처리 (
aya-expanse-8b
) - LLM 프롬프트: Few-shot 기반 질문/답변/피드백 예시 + 사용자 입력
- 비동기 처리: aiohttp 기반 비동기 API 요청 처리
🚀 주요 기능
/feedback/create
)
1. 피드백 생성 (POST /api/v1/feedback/create
→ 입력: 질문 + 사용자 답변
→ 출력: 피드백 문장 (LLM이 생성한 자연어)
예시 입력/출력
Request:
{
"question": "LSTM이 기울기 소실 문제를 해결하는 핵심 구조적 특징을 설명해주세요.",
"answer": "LSTM은 RNN보다 좋아서 기울기 소실이 안 생깁니다."
}
Response:
{
"feedback": "피드edback: LSTM이 RNN보다 개선된 점을 언급했지만, 질문의 핵심인 구조적 특징에 대한 설명이 없습니다. LSTM은 셀 상태와 게이트 메커니즘을 통해 정보 흐름을 조절하여 기울기 소실을 완화합니다. 이러한 구조에 대한 이해를 보이면 더 나은 답변이 될 것입니다."
}
🧠 LLM 프롬프트 설계 방식
[질문]
(면접 질문 예시)
[답변]
(지원자 답변 예시)
[피드백]
(LLM이 생성할 기준 피드백 예시)
...
[질문]
(사용자 질문)
[답변]
(사용자 답변)
이 답변에 대해 다음 기준을 바탕으로 간결하게 구체적인 피드백을 3~5문장으로 작성해주세요:
- 답변이 질문의 핵심을 이해하고 있는지
- 틀린 내용이나 부족한 설명이 있는지
- 어떤 내용을 보완하면 더 좋은 답변이 되는지
시스템 역할 (System Role):
당신은 컴퓨터공학 면접관입니다. 질문에 대한 지원자의 답변을 보고 어떤 점이 보완되면 좋겠는지 친절하게 피드백해주세요.
🔧 서비스 구현 코드 요약
1. 프롬프트 생성
def build_feedback_prompt(question: str, answer: str) -> str:
# few-shot 예시 + 사용자 질문/답변을 조합해 LLM 입력 프롬프트 생성
2. 비동기 추론 요청
async def generate_feedback(question: str, answer: str) -> str:
async with aiohttp.ClientSession(...) as session:
async with session.post(
url=f"{VLLM_URL}/v1/chat/completions",
json={
"model": MODEL_NAME,
"messages": [
{"role": "system", "content": SYSTEM_ROLE},
{"role": "user", "content": prompt}
],
...
}
) as response:
result = await response.json()
return result["choices"][0]["message"]["content"].strip()
🛠️ 해결한 주요 문제
문제 | 해결방법 |
---|---|
VLLM 연결 불안정 | 타임아웃 설정 + 예외 처리 (aiohttp.ClientTimeout ) |
비정형 프롬프트 구성 | Few-shot 방식으로 예시 입력 + 명확한 기준 제시 |
동기 API 응답 지연 | aiohttp 를 사용한 비동기 처리 |
LLM 응답 문장 중단 방지 | "문장 도중에 끊지 말고, 의미 단위로 마무리" 프롬프트 명시 |
📊 테스트 결과
✅ 단일 요청 평균 응답 시간: 1.5초 (L4 GPU 기준)
✅ 프롬프트에 따른 LLM 피드백 품질: 수작업 기준 90% 이상 만족
✅ 비동기 요청/응답 처리: 안정적
✅ 예외 발생 시 graceful fallback: 성공
📡 API 명세
피드백 생성
POST /api/v1/feedback/create
✅ 요청 예시
{
"question": "트랜잭션의 ACID 특성에 대해 설명해주세요.",
"answer": "ACID는 트랜잭션이 안정적으로 수행되기 위한 성질입니다."
}
✅ 응답 예시
{
"feedback": "피드백: ACID의 전반적인 목적은 언급했으나, 각 특성(원자성, 일관성, 고립성, 지속성)에 대한 설명이 생략되어 아쉽습니다. 각 특성에 대해 간단히 정의하고, 예를 들어 설명해주면 더 신뢰감 있는 답변이 될 것입니다."
}
🎉 주요 성과
- 자연어 피드백 생성 자동화 성공
- aya-expanse-8b 기반 vLLM 추론 최적화
- LLM 비동기 추론 처리 시스템 설계
- 컴퓨터공학 면접에 특화된 few-shot 템플릿 구성
- 확장 가능하고 유연한 프롬프트 기반 설계
🚀 기술 스택
- Backend: FastAPI, Python 3.11
- LLM: aya-expanse-8b (vLLM)
- Async HTTP: aiohttp
- Infra: GCP L4 GPU, Docker
- Prompt Engineering: Few-shot 예시 기반
📋 빠른 실행
# 의존성 설치
pip install -r requirements.txt
# vLLM 서버 실행 (port: 8001)
python -m vllm.entrypoints.openai.api_server --model /mnt/ssd/aya-expanse-8b --port 8001
# FastAPI 실행
uvicorn app.main:app --reload --port 8000
# API 테스트
curl -X POST http://localhost:8000/api/v1/feedback/create \\
-H "Content-Type: application/json" \\
-d '{"question": "TCP와 UDP의 차이점은?", "answer": "TCP는 느리지만 신뢰성이 높고, UDP는 빠릅니다."}'
결과: AI가 자동으로 면접 답변에 피드백을 생성하는 기능을 구현 완료.
질문 유형이 다양해져도 few-shot 방식으로 유연하게 대응 가능하며, 실제 면접 연습 또는 기업 면접관의 보조 도구로 활용 가능.
LangGraph 기반 이력서 에이전트 구현
🎯 핵심 개요
LangGraph + Redis를 활용한 대화형 이력서 생성 에이전트를 구현. 다단계 질문-답변을 통해 개인화된 이력서를 자동 생성하는 시스템.
🏗️ 시스템 아키텍처
- 고급 이력서 생성 첫번째 흐름
sequenceDiagram
participant User
participant Frontend
participant Spring
participant FastAPI
participant Redis
participant Kafka
participant SSE
User->>Frontend: 고급 이력서 생성 요청
Frontend->>Spring: POST /api/v1/advanced-resume/init
Spring->>Spring: member_id 확인 및 인증
Spring->>FastAPI: POST /resume/agent/init (member_id 포함)
FastAPI->>FastAPI: 초기 ResumeAgentState 구성
FastAPI->>FastAPI: LLM 호출 → 질문 생성
FastAPI->>Redis: SET resume:{member_id} = 상태
FastAPI-->>Spring: 생성된 question + member_id 반환
Spring->>Kafka: ResumeQuestionGenerated 이벤트 발행 (member_id + question)
Kafka-->>SSE: 메시지 소비
SSE-->>Frontend: SSE 푸시 (member_id 기반)
Note over Frontend: 사용자에게 질문 표시
- 고급 이력서 생성 두번째 흐름
sequenceDiagram
participant User
participant Frontend
participant Spring
participant FastAPI
participant Redis
participant S3
participant Kafka
participant SSE
User->>Frontend: 질문에 대한 답변 입력
Frontend->>Spring: POST /api/v1/advanced-resume/update\n+ member_id + 답변
Spring->>FastAPI: POST /resume/agent/update\n+ member_id + 답변
FastAPI->>Redis: GET resume:{member_id}
Redis-->>FastAPI: 기존 상태 반환
FastAPI->>FastAPI: 상태 업데이트 (답변 누적)
alt 조건 미충족 (질문 필요)
FastAPI->>FastAPI: 다음 질문 생성 (LLM 호출)
FastAPI->>Redis: SET resume:{member_id} = 상태
FastAPI-->>Spring: { memberId: 3, isComplete: false, question }
Spring->>Kafka: Produce ResumeQuestionGenerated(member_id, question)
else 조건 충족 (이력서 작성 완료)
FastAPI->>FastAPI: 이력서 생성 (Markdown + .docx)
FastAPI->>S3: PUT 이력서 파일
S3-->>FastAPI: object_key 반환
FastAPI->>Redis: DEL resume:{member_id}
FastAPI-->>Spring: { memberId: 3, isComplete: true, resumeObjectKey }
Spring->>Kafka: Produce ResumeCompleted(member_id, resume_url)
end
Kafka-->>SSE: 메시지 소비
SSE-->>Frontend: SSE 푸시 (질문 또는 이력서 URL)
핵심 구성요소:
- LangGraph: 대화형 워크플로우 엔진
- Redis: 상태 관리 및 동시성 제어
- LLM: 질문 생성 및 이력서 작성 (VLLM/OpenAI)
🚀 주요 기능
/resume/agent/init
)
1. 에이전트 초기화 (# 사용자 정보 → 첫 질문 생성 → Redis 저장
POST /api/v1/resume/agent/init
Response: {"member_id": 1001, "question": "..."}
/resume/agent/update
)
2. 에이전트 업데이트 (# 답변 처리 → LangGraph 실행 → 다음 질문 OR 완료
POST /api/v1/resume/agent/update
Response: {"member_id": 1001, "isComplete": true, "resumeObjectKey": "..."}
🔧 LangGraph 워크플로우
graph = StateGraph(ResumeAgentState)
graph.add_node("receive_answer", ReceiveAnswerNode) # 답변 처리
graph.add_node("check_completion", CheckCompletionNode) # 완료 판단
graph.add_node("generate_question", GenerateQuestionNode) # 질문 생성
graph.add_node("create_resume", CreateResumeNode) # 이력서 생성
💾 Redis 상태 관리
class RedisClient:
async def save_state(self, member_id, state):
# datetime 직렬화 문제 해결: default=str
state_json = json.dumps(state.to_redis_dict(), default=str)
async def acquire_lock(self, member_id):
# 동시성 제어: Redis SET NX EX
return self.redis_client.set(lock_key, value, nx=True, ex=30)
🛠️ 해결한 주요 문제
문제 | 해결방법 |
---|---|
DateTime 직렬화 오류 | json.dumps(..., default=str) |
응답 스키마 불일치 | Pydantic alias="isComplete" |
VLLM 연결 불안정 | OpenAI fallback + 에러 처리 |
동시 요청 처리 | Redis 락 메커니즘 |
📊 테스트 결과
✅ Redis 연결/CRUD: 성공
✅ init → update 플로우: 성공 (3회 질문-답변)
✅ S3 파일 업로드: 성공
✅ 동시성 제어: 성공
📄 최종 결과: resume/resume_agent_20250708_102544.docx
📡 API 명세 (핵심)
초기화
POST /api/v1/resume/agent/init
{
"member_id": 1001,
"inputs": { "email": "...", "preferred_job": "AI 엔지니어", ... }
}
→ { "member_id": 1001, "question": "첫 번째 질문" }
업데이트
POST /api/v1/resume/agent/update
{ "member_id": 1001, "answer": "사용자 답변" }
→ { "member_id": 1001, "isComplete": false, "question": "다음 질문" }
→ { "member_id": 1001, "isComplete": true, "resumeObjectKey": "s3-key" }
🎉 주요 성과
- SpringBoot → FastAPI 성공적 이전
- 대화형 AI 에이전트 구현 완료
- Redis 기반 상태 관리 시스템 구축
- 전체 플로우 테스트 통과
- S3 연동 및 파일 업로드 완료
🚀 기술 스택
- Backend: FastAPI, Python 3.11
- AI Framework: LangGraph
- State: Redis
- LLM: VLLM/OpenAI
- Storage: AWS S3
📋 빠른 실행
# 환경 설정
pip install -r requirements.txt
# Redis 시작
redis-server
# FastAPI 서버 실행
uvicorn app.main:app --reload --host 0.0.0.0 --port 8000
# 테스트 실행
python tests/test_init_update_flow.py
결과: 확장 가능하고 안정적인 AI 기반 이력서 생성 시스템 구축 완료