Skip to content

fix(ai): 비개발 직군 면접이 IT 면접관 페르소나로 진행되던 문제 - #276

Merged
i3months merged 2 commits into
devfrom
fix/ai-non-engineering-interview
Sep 29, 2026
Merged

i3months merged 2 commits into
devfrom
fix/ai-non-engineering-interview

Conversation

@i3months

Copy link
Copy Markdown
Member

문제

면접 직군을 비개발 12종까지 넓혔는데(PR #266/#267), 확장된 건 평가 패널뿐이었습니다. 질문 생성·꼬리질문·첫인상·문서분석 프롬프트는 여전히 IT 고정입니다.

question_generation.py:5   "당신은 IT 직군 채용을 진행하는 시니어 면접관입니다."
document_analysis.py:6     "당신은 IT 직군 채용을 진행하는 시니어 면접관입니다."
followup_generation.py:5   "당신은 IT 직군 면접관입니다."
self_intro_evaluation.py:6 "당신은 IT 직군 면접의 **첫인상 평가위원**입니다."

그래서 영업·인사 지원자의 면접은 이렇게 흐릅니다:

IT 렌즈로 이력서가 요약되고 → IT 채용 담당자가 → 'CS 기초'·'기술 선택' 카테고리로 질문을 만들고 → IT 면접관이 꼬리질문을 답니다. 직군에 맞는 건 평가뿐입니다.

카테고리 자체가 개발 전용인 것이 핵심입니다. 선택지가 CS_FUNDAMENTAL / PROJECT_DEEP_DIVE / TECH_CHOICE / BEHAVIORAL 네 개라, 문구만 중립화하면 LLM 은 여전히 CS_FUNDAMENTAL 을 고릅니다.

그리고 followup·self_intro 프롬프트는 예전부터 {job_category} 를 받고 있었습니다 — 받아 놓고 안 썼고, 페르소나만 IT 로 박혀 있어 서로 모순이었습니다.

수정

DOMAIN_KNOWLEDGE 카테고리 추가 (직무 지식·업무 도구·업계 이해). Core 는 category 를 문자열로만 저장하므로(V9, CHECK 없음) 마이그레이션이 없고, 프론트는 categoryLabel 에 라벨만 더합니다.

_format_job_guide 가 직군 지침을 주입 — 개발/비개발/혼합 3분기. 패널의 _DOMAIN_TECH_GUIDE 와 같은 이유로 라벨이 아니라 무엇을 묻고 무엇을 묻지 말지를 문장으로 줍니다.

  • 개발: 현행 유지(CS_FUNDAMENTAL·TECH_CHOICE 적극, DOMAIN_KNOWLEDGE 미사용)
  • 비개발: CS_FUNDAMENTAL·TECH_CHOICE 금지, 실무 역량·도구를 직군별로 예시(마케팅 GA4·재무 결산·생산 불량률·법무 계약검토…), 성과는 숫자로, "GitHub 레포가 없는 것이 정상"
  • 혼합(복수 선택): 질문마다 그 질문이 겨냥하는 직군의 규칙을 따르도록

직군이 비면 개발을 가정하지 않습니다 — 가정이 틀렸을 때 비개발 지원자에게 기술 질문이 나가는 쪽이 반대보다 나쁩니다.

ENGINEERING_CATEGORIES(8개)를 직군 목록과 같은 파일에 뒀습니다. 그 파일의 존재 이유와 같습니다 — 목록만 늘리고 집합을 빠뜨리면 그 직군이 조용히 반대 취급을 받고, 검증에서 튕기지 않아 아무도 모릅니다.

document_analysis 는 업로드 시점이라 직군을 모릅니다. 페르소나를 중립화하고 tech_stack 의 의미를 "개발이면 기술 스택, 아니면 그 직무의 실무 역량·도구·자격"으로 넓혔습니다(필드명 때문에 없는 기술을 지어내지 말라고 명시). 이게 가장 상류입니다 — 분석 마크다운이 RAG·질문·피드백 전부의 근거입니다.

영향 범위

운영 사용 현황은 BACKEND 64 / FRONTEND 39 로 비개발 세션이 0건입니다. 라이브 회귀를 고친 게 아니라 미검증 경로를 연 것입니다. 개발 직군 동작은 지침이 현행과 같아 변하지 않습니다(테스트로 고정).

테스트

tests/test_non_engineering_interview.py 16건:

  • 개발 집합 ⊆ 직군 목록, 개수 고정(8/20) — 두 목록이 어긋나는 사고를 막습니다.
  • 비개발 5직군에서 지침이 CS_FUNDAMENTAL 금지·DOMAIN_KNOWLEDGE 사용·"레포 없음이 정상"을 포함
  • 개발 3직군에서 현행 동작 유지
  • 혼합 선택에 두 규칙이 모두 들어감
  • 빈 직군이 개발로 떨어지지 않음
  • DOMAIN_KNOWLEDGE 가 QuestionCategory Literal 에 있음 — 없으면 LLM 이 지침대로 골라도 검증에서 튕겨 생성이 통째로 실패합니다
  • 4개 프롬프트에 "IT 직군" 문자열이 없음

프롬프트가 새 변수로 실제 조립되는지도 확인했습니다(format_messages 로 전 변수 주입). uv run pytest 479건 · flake8 · black --check · 프론트 type-check/lint/테스트 통과.

🤖 Generated with Claude Code

plainto_tsquery 는 토큰을 전부 AND 로 묶는다. 그런데 이 검색의 질의는
꼬리질문이면 "직전질문 + 답변", 질문 풀이면 "모드 + 직군 + 자기소개 600자"라
토큰이 10~90개다. 그 전부를 담은 청크는 존재하지 않으므로 full-text 브랜치가
항상 0건이었고, RRF 가 조용히 벡터 단독 검색으로 퇴화했다.

운영 실측: 꼬리질문 질의 47/47, 질문 풀 질의 30/30 이 0건. 구현돼 있고
테스트도 통과하지만 실제 질의 형태에서는 무효인 상태였다.

tsquery 를 OR 로 결합한다. 운영과 같은 헤딩 청킹 인덱스에서 질의 47개,
top_k=5 기준:

  MRR    0.791 → 0.885  (paired bootstrap 95% CI +0.029~+0.162, 12승/3패)
  Top-1  0.681 → 0.830  (CI +0.043~+0.277, 8승/1패)
  Recall 0.936 → 0.979

후보 풀 :cand 은 손대지 않았다 — 측정한 구성이 cand = top_k 였고, 바꾸면
측정하지 않은 것을 배포하는 셈이 된다.

회귀 테스트는 벡터만으로는 top_k 밖인 청크를 키워드로만 끌어올리게 만들어
두 절반을 구분한다. 미끼를 top_k 만큼 채우지 않으면 벡터가 어차피 전부
반환해 AND 에서도 통과한다(처음에 그렇게 써서 변별력이 없었다). 기존 헬퍼
vector(head) 는 0번 차원의 크기만 바꿔 모든 벡터가 평행이라 코사인 거리가
언제나 0이었다 — 직교 단위벡터 헬퍼 axis(dim) 를 추가했다.

AND 로 되돌리면 이 테스트가 실패하는 것을 확인했다.
서비스 대상을 취준생 전반(개발 8 + 비개발 12 직군)으로 넓히면서 평가
패널은 직군별로 확장했는데, 질문 생성·꼬리질문·첫인상·문서분석 프롬프트는
"IT 직군" 페르소나로 고정돼 있었다.

그 상태의 영업·인사 지원자 면접은 이렇게 흐른다: IT 렌즈로 이력서가
요약되고 → IT 채용 담당자 페르소나가 → 'CS 기초'·'기술 선택' 카테고리로
질문을 만들고 → IT 면접관이 꼬리질문을 단다. 직군에 맞는 것은 평가뿐이다.

카테고리 자체가 개발 전용인 것이 핵심이다. 문구만 중립화하면 LLM 은 여전히
CS_FUNDAMENTAL 을 고른다. DOMAIN_KNOWLEDGE(직무 지식·업무 도구·업계 이해)를
추가하고, 비개발이면 CS_FUNDAMENTAL·TECH_CHOICE 를 쓰지 말라고 명시한다.
Core 는 category 를 문자열로만 저장하므로(V9, CHECK 없음) 마이그레이션이
필요 없고 프론트는 라벨만 더한다.

- job_category.py 에 ENGINEERING_CATEGORIES + is_engineering 을 직군 목록과
  같은 파일에 둔다. 목록만 늘리고 집합을 빠뜨리면 그 직군이 조용히 반대
  취급을 받는다 — 검증에서 튕기지 않아 아무도 모른다.
- _format_job_guide 가 개발/비개발/혼합 3분기 지침을 {job_guide} 로 주입한다.
  패널의 _DOMAIN_TECH_GUIDE 와 같은 이유로 라벨이 아니라 무엇을 묻고 무엇을
  묻지 말지를 문장으로 준다. 직군이 비면 개발을 가정하지 않는다 — 틀렸을 때
  비개발 지원자에게 기술 질문이 나가는 쪽이 반대보다 나쁘다.
- followup·self_intro 프롬프트는 예전부터 {job_category} 를 받고 있었는데
  쓰지 않았다. 받은 값을 페르소나에 넣었다.
- document_analysis 는 업로드 시점이라 직군을 모른다. 페르소나를 중립화하고
  tech_stack 의 의미를 "개발이면 기술 스택, 아니면 그 직무의 실무 역량·도구·
  자격"으로 넓혔다. 이게 가장 상류다 — 분석 마크다운이 RAG·질문·피드백
  전부의 근거다.

운영 사용 현황은 BACKEND 64 / FRONTEND 39 로 비개발 세션이 0건이다.
라이브 회귀를 고친 것이 아니라 미검증 경로를 연 것이다.
@i3months
i3months merged commit ba4adef into dev Sep 29, 2026
6 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant