Code › tail-villain
페르소나 선택지에서 역할 분리까지
면접관 페르소나를 선택 UI가 아니라 실제 질문 방식과 학습 흐름으로 분리해 간 기록
페르소나를 구현했다.
기획했던 md 파일에는 이미 페르소나가 정리되어 있었다. 꼬빌은 꼬리질문을 던지는 기본 면접관이고, Socrates는 원리를 묻고, Deep Dive는 구현 디테일을 파고들고, Y-Bot은 근거와 trade-off를 요구하며, Angel Senior는 압박보다 코칭을 우선한다. 화면에서 고르게 만들고, 백엔드에 persona key를 넘기면 기능이 완성되는 것.
인터뷰 시작 화면에 페르소나 선택 UI를 넣고, 세션 화면에는 현재 면접관 이름과 간단한 briefing panel을 배치했다. 사용자가 지금 어떤 면접관과 대화 중인지 확인할 수 있어야 했기 때문이다.
이 과정에서 공통 persona 정의도 만들었다. 프론트와 백엔드가 서로 다른 정의를 들고 있으면 화면 설명과 실제 프롬프트가 어긋날 수 있어서, 같은 key와 설명을 기준으로 선택지와 프롬프트를 구성하게 했다.
페르소나 정보를 넣었는데도 Socrates가 평범한 기업 면접관처럼 말하는 경우가 있었다. 이름은 Socrates였지만 질문은 일반적인 회사 면접관 문장이었고, 원리를 캐묻기보다 무난하게 답변을 이어가는 쪽으로 흘렀다.
이때 확인한 것은 페르소나 데이터의 유무보다는 일반적인 면접 진행 규칙이 더 강하게 작동할 때의 문제였다. 구조화된 질문 형식과 안전한 진행 문구, 무난한 피드백 규칙이 페르소나보다 앞에 오면 모델은 페르소나를 받은 상태에서도 “일반 면접관” 쪽으로 갈 수 있었다.
결국 손봐야 할 곳은 프롬프트였는데 opening, follow-up, completion을 나누어 조정했다. 첫 질문에서 무엇을 확인할지, 답변이 얕을 때 어디를 더 물을지, 끝날 때 어떤 기준으로 피드백할지를 페르소나별로 분리했다.
Socrates라면 답변의 전제와 원리를 다시 물어야 하고, Deep Dive라면 추상적인 설명을 구현 단위로 내려오게 만들어야 한다. Y-Bot은 감상이 아니라 근거와 선택 기준을 요구해야 하며, Angel Senior는 같은 문제를 더 부드러운 코칭 흐름으로 다뤄야 한다.
단순히 “너는 Socrates다”라고 쓰는 것으로는 부족하고, 질문의 방향과 디테일한 역할을 잘 주어야 한다.
프롬프트를 손보다 보니 모든 페르소나를 같은 “면접관”으로 두는 모델 자체도 애매했다. 꼬빌은 모의 면접에서 답변을 압박하고, 약한 부분을 다시 묻고, 세션을 평가하는 역할을 맡기면 된다.
반면 Socrates, Deep Dive, Y-Bot, Angel Senior는 스터디 코치이다. Socrates는 원리를 묻는 방식으로 개념을 정리하게 만들 수 있고, Deep Dive는 내부 동작을 더 깊게 설명하게 만들 수 있다. Y-Bot은 근거를 검토하게 하고, Angel Senior는 막힌 부분을 덜 압박적인 방식으로 풀어줄 수 있다.
인터뷰 시작 화면에서는 꼬빌만 보여주고, 다른 페르소나는 study mode에서 선택하도록 바꿨다.
이 결정으로 UI도 나뉘었다. 사용자가 지금 면접을 시작하는지, 학습 코치와 대화하는지 구분할 수 있어야 했고, 같은 캐릭터 선택처럼 보여도 실제 제품 흐름은 다르기 때문이다.
실제 챗봇으로 동작해야 해서 study turn API를 따로 만들고, coach chat route를 분리했다. study mode에서 코치를 선택한 뒤 명시적으로 시작 버튼을 누르면, 별도 chat route로 들어가고 LLM이 opening과 reply를 생성하도록 했다. opening도 템플릿으로 고정하지 않고 LLM이 만들게 했다.
긴 토픽 제목이나 rubric 문장을 그대로 붙여 넣으면 대화가 아니라 안내문처럼 읽힐 수 있어서 mock interview opening과 study opening 모두 내부 rubric 표현을 그대로 노출하지 않도록 조정했고, 첫 질문은 짧게 하나만 묻도록 제한했다.
이전 인터뷰 화면은 메시지 기록과 평가 데이터를 보고 진행 상태를 추론했었다. 그런데 오래된 세션은 데이터 형식이 다르고, 일부 feedback metadata가 비어 있어서 완료된 세션도 어색하게 표시될 수 있었다.
새 세션에는 explicit session state를 저장하도록 바꿨다. 어떤 checkpoint를 지나고 있는지, follow-up이 몇 번 진행됐는지 같은 정보를 나중에 transcript에서 추측하지 않고 세션 상태로 들고 가게 했다. 기존 세션은 legacy로 표시하고, 정확한 진행 상태를 과하게 추론하지 않도록 분리했다.
이건 페르소나와 별개의 작업처럼 보일 수 있지만, 인터뷰 제품에서는 같은 화면에서 만난다. 면접관이 어떤 질문을 했는지뿐 아니라 세션이 어디까지 진행됐는지도 사용자가 믿을 수 있어야 하고, 프롬프트가 아무리 좋아도 UI가 진행 상태를 잘못 보여주면 세션 전체가 이상해진다.
오래 켜둔 세션에서 사용자가 돌아왔을 때 로그아웃된 것처럼 처리되는 문제도 있었다. 원인은 refresh 흐름의 예외 처리였다. access token이 만료된 뒤 /users/me로 세션을 복원해야 하는데, 프론트엔드 refresh interceptor가 /users/* 전체를 제외하고 있어서 복구 경로를 타지 못했다.
제외 대상을 로그인, 회원가입, refresh endpoint로 좁히자 /users/me도 refresh-token 복구를 사용할 수 있게 됐다.
이번 작업은 여러 코치의 역할을 나누고 각각을 구체화하여 동작하도록 하는 것이었다. 꼬빌은 모의 면접에서 압박과 평가를 담당하고, 코치 페르소나는 study mode에서 개념을 정리하거나 사고 과정을 보조한다. 면접관들이 가끔 엉뚱한 소리를 하거나, 이상한 답변을 하거나, 동일한 답변을 하는 경우가 있어서 프롬프트를 수정하고 강화하며 이를 손보는 과정이 꽤 힘든 작업이었다.