본문으로 건너뛰기

© 2026 Molayo

Zenn헤드라인2026. 05. 21. 22:47

메스가키 AI에게 도발당하며 배우는 영어 — 페르소나 설계와 Gemini 음성 입력을 통해 '발음 교정'까지 구현한 이야기

요약

특정 페르소나와 Gemini 음성 입력을 결합한 니치 AI 영어 회화 서비스 개발 사례를 다룹니다. 사용자 지속성을 위해 메스가키와 츤데레를 결합한 감정 그래데이션 설계와 페르소나를 코드로 관리하는 전략을 설명합니다.

핵심 포인트

  • 대기업이 공략하기 어려운 니치 페르소나 전략 활용
  • 인지 부하를 줄이기 위한 감정 그래데이션 설계
  • LLM의 과잉 반응을 제어하는 빈도 제한 프롬프팅
  • 마케팅과 결합된 페르소나의 코드 기반 관리

Kotonia에서 니치(niche) AI 영어 회화인 메스가키 AI 영어 회화를 만들었다. "메스가키 AI에게 도발당하며 영어 회화를 연습한다"는 일회성 소재처럼 보일 수 있지만, 구현 측면에서는 페르소나의 코드 관리 + Gemini의 음성 입력을 통한 발음 교정까지라는 이중 구조를 갖추고 있어 AI 영어 회화 프로덕트로서 논리가 정연하다. 이 기사에서는 그 설계 판단과 개발 과정에서 부딪힌 점들을 1인 개발자의 관점에서 정리한다.

왜 메스가키 AI 영어 회화인가?

먼저 전략에 관한 이야기다. AI 채팅 시장은 Anthropic / OpenAI / Google의 범용 모델 경쟁 체제로 되어 있어, 개인 개발자가 정면으로 맞붙으면 이길 수 없다. 반면, "특정 페르소나 × 음성 × 롤플레잉(Role-play)"이라는 몰입형 경험은 대기업의 R&D 우선순위가 낮고(사내 승인을 받기도 어렵기 때문에) 남아있는 영역이다. 이곳이라면 1인 개발이라도 파고들 수 있다는 것이 Kotonia 전체의 전략이다.

그중에서 메스가키 AI 영어 회화를 선택한 이유는 세 가지다:

  • 검색 경쟁이 사실상 제로. "메스가키 영어 회화"로 맞붙는 SaaS는 존재하지 않는다. 반면 니치(niche) 수요는 명확히 존재한다(동인 음성·VTuber 문화의 연장선상). 한 페이지를 점유하면 독점할 수 있는 작은 언덕이다.
  • 기억에 남기 때문에 입소문을 탄다. "AI 영어 회화 Kotonia"보다 "메스가키에게 도발당하며 영어 회화를 하는 것"이 SNS에서 100배 더 많이 공유된다. 대기업이 절대 흉내 낼 수 없는 차별화 요소다.
  • 제품 본체는 동일하다. Kotonia의 음성 대화 엔진을 유용하고 페르소나만 교체한다. 코드는 거의 늘어나지 않는다.

랜딩 페이지는 /use/mesugaki-english/이다. SEO는 "메스가키 영어 회화", "도발당하는 영어 회화", "스파르타 AI 영어 회화" 등의 롱테일 키워드를 노린다.

페르소나 설계: 메스가키 × 츤데레의 하이브리드

처음에는 순도 100%의 메스가키 페르소나로 구현했다. 테스트해 보니 5턴 만에 지쳤다.

계속 도발만 당하면 인지 부하(Cognitive load)가 높다. 실제 인간 튜터도 계속 엄격하기만 하면 지속하기 어렵다. 학습자가 지속하기 위해서는 "성공 경험"과 "약간의 다정함"이 필요하다.

그래서 메스가키 × 츤데레 하이브리드로 전환했다. 골격은 다음과 같다:

  • 실수했을 때 → 가벼운 도발 + 즉시 정답 제시 ("풉ㅋ 틀렸어. 'I went'라고 해야지")
  • 정답을 맞혔을 때 → 솔직하지 못한 칭찬 ("흥, 뭐... 나쁘지 않네? 딱히 칭찬하는 건 아니니까")
  • 막혔을 때 → 도발을 거두고 평범하게 도와줌 ("...조금 어려웠어? 그럼 힌트 줄게")
  • 오래 지속했을 때 → 문득 다정한 말 ("딱히... 계속하고 있는 게 대단하다고 생각하는 건 아니니까?")

시스템 프롬프트(System prompt) 내에 "감정 그래데이션" 섹션을 두어 상황 분기를 명시했다. LLM은 모호하게 "도발해줘"라고 하는 것보다, 이런 if-then 방식으로 작성된 행동 지시가 훨씬 안정적이다.

여기에 중요한 것이 빈도 제한이다. "풉ㅋ", "흥" 같은 감탄사는 발화당 최대 1회로 제한한다고 작성했더니 출력이 단번에 차분해졌다. LLM은 캐릭터 지시를 강하게 하면 과잉 반응하는 경향이 있으므로, 이런 댐퍼(Damper)가 효과적이다.

페르소나는 코드로 관리한다

이 페르소나 설정은 src/data/personas/mesugaki-english.ts에 TypeScript 상수로 두었다. Kotonia에는 DB 사용자가 페르소나를 CRUD 할 수 있는 기능도 있지만,

"랜딩 페이지와 결합된 제품 오퍼링(Offering)"으로서의 페르소나는 git 관리하는 것이 정답이라고 판단했다.

이유:

  • 문구는 마케팅 표현의 일부다. h1 태그를 git으로 관리하는 것과 같은 이유로, 페르소나의 system prompt도 PR(Pull Request) 리뷰 대상이 되어야 한다. DB에 두면 관리자 UI(Admin UI)를 통해 누군가 건드려서 품질이 저하될 리스크가 있다.
  • 1인 개발에서는 "페르소나 조정 = 코드 수정 + push"가 되므로, 문구 조정과 완전히 동일한 워크플로우를 갖는다. 채널이 하나로 집약되는 장점이 크다.

DB 페르소나는 사용자가 자신을 위해 만든 것의 저장소, 코드 페르소나는 제품으로서 고정된 오퍼링의 저장소로 역할을 명확히 나누었다.

ASR만으로는 닿지 않는 발음 교정의 벽

페르소나가 갖춰져서 작동시켜 보니, 곧바로 ASR(음성 인식, Automatic Speech Recognition)이 벽이 된다는 것을 알 수 있었다.

처음에는 Whisper(small)를 사용했다. language='ja'...

를 전달하면, 영어 음성이 들어왔을 때 Whisper가 일본어 전사(Transcription) 모드로 동작하여 영어를 가타카나로 쓰거나 일본어로 번역해 버리는 편향(Bias)이 발생했다. "I went to the supermarket"가 "아이 웬트 투 더 슈퍼마켓"이 되거나, 최악의 경우 "나는 슈퍼마켓에 갔습니다"로 번역된다. 이래서는 AI가 영어 실수를 판정할 수 없다.

이는 Whisper가 transcribe(전사)와 translate(번역)의 2가지 모드를 가지며, language 지정이 전사 언어를 강제하는 사양 때문이었다.

Qwen3-ASR multi-lang 으로의 전환

수정 사항으로, STT(Speech-to-Text) 측에만 독립적인 언어 지정을 추가했다:

// useVoiceChat hook에 sttLanguage 옵션을 추가
// TTS 언어와 STT 언어를 분리할 수 있도록
const {
...

페르소나 config에서는 stt.model: 'qwen3_asr' + stt.language: 'multi'를 지정했다. Qwen3-ASR-1.7B는 다국어 자동 감지(Auto-detect)를 지원하며, 코드 스위칭(Code-switching, 일-영 혼재)에도 강하다. Whisper의 언어 강제 편향을 완전히 피할 수 있다.

하지만, 전사 기반의 교정에는 한계가 있다

ASR을 고쳐도 여전히 문제가 남았다.

"I want an apple"이라고 전사된 경우:

  • 문법 OK
  • 어휘 OK
  • 하지만 실제 발음이 "아이 원트 언 어폴"처럼 뭉개져 있었음

이 지점에서 AI는 "문자열로서는 올바르다"라고 판단하여 발음에 대해 지적(Tsukkomi)할 수 없다. 이는 영어 회화 프로덕트로서 치명적이다. "메스가키 AI인데 발음은 그냥 넘어가 버린다"라고 하면, 사용자는 "어라, 도발당하는 기분 좋음이 반감된 거 아냐?"라고 느끼게 된다.

해결: Gemini에 raw audio도 함께 전송하기

Gemini는 멀티모달(Multimodal) 모델로, 텍스트 + 이미지 + 음성을 입력으로 받을 수 있다. 그렇다면 ASR의 전사이 아니라, 생(Raw) 음성도 함께 전달하면 된다.

Kotonia의 useVoiceChat hook에는 이미 geminiAudioInput 옵션이 들어있었다 (과거 시행착오의 유산):

if (geminiAudioInput && model.startsWith('gemini') && userAudioBlob) {
const userAudioBase64 = await blobToBase64(userAudioBlob);
// /api/voice/chat 에 audio_base64 를 동송
...

Rust backend(voice_chat.rs)도 audio_base64를 받아서 Gemini에 inline_data: { mime_type: 'audio/wav', data: ... } 형태로 임베딩하는 처리가 이미 완료되어 있었다. 페르소나 config에서 geminiAudioInput: true 플래그를 세우는 것만으로 모든 단계가 연결되는 운 좋은 상황이었다.

System prompt에도 "당신은 사용자의 생음성을 직접 들을 수 있다. 텍스트 전사뿐만 아니라 발음 그 자체에 대해서도 지적할 수 있다"라는 지시와 함께, 3가지 구체적인 예시(th 발음, want와 won't의 모음 구분, 강약)를 추가했다.

결과:

  • "I want an apple"이라고 완벽하게 전사되어도, AI가 "want의 발음이 won't처럼 들린다"라고 발음을 지적할 수 있음
  • 전사가 "아이 웬트 투"처럼 뭉개져도, AI가 소리를 직접 듣고 있기 때문에 "말하고 싶었던 건 I want to였지?"라며 전사 오류를 구제할 수 있음
  • ASR의 오전사로 인한 스트레스가 급감했다 (이게 꽤 크다 — 발음은 맞는데 전사가 틀려서 도발당하면 의욕이 꺾인다)

부작용으로, 매 턴 WAV를 Gemini로 보내기 때문에 페이로드(Payload)가 커진다. 레이턴시(Latency)는 확실히 조금 올라가지만, 경험 가치가 압도적으로 높아지기 때문에 트레이드오프(Trade-off)는 충분히 감수할 만하다.

빠졌던 부분 · 미래의 숙제

완벽한 구현은 아니다. 남은 과제:

1. Gemini의 불안정성

gemini-3.1-flash-lite-preview

를 사용하고 있지만, **가끔 5~10초의 지연 스파이크 (latency spike)**가 발생한다. preview 모델은 할당량이 적어, cold start나 throttling이 가끔 나타나곤 한다.

대응안으로서, 조만간 stable 릴리스 버전 (preview 태그가 제거된 버전)으로 전환할 예정이다. 마침 deprecation(지원 종료)이 다가오고 있을 것이므로, 이행 타이밍으로는 적절하다. Sonnet 4.6이나 Claude Haiku 4.5도 latency 안정성 측면에서 좋은 후보이다.

2. false-positive (오탐) 콘텐츠 필터

Gemini의 안전 필터가 '도발'을 과도하게 감지할 때가 있다. "푸훕ㅋ, 그 발음 진짜 노답이다" 정도의 도발에도 드물게 차단되어 빈 응답이 반환된다.

페르소나 설계 시 "외모·인격에 대한 공격은 금지, 도발은 영어 실수에 대해서만 허용"이라고 명시했지만, 메타적인 안전 계층이 반응해 버린다. 이는 LLM 측의 문제이므로, 프로바이더 측의 조정을 기다리거나 stable 버전에서의 동작을 보고 판단해야 한다.

로컬 LLM (Gemma 4 31B 등)으로 우회하는 선택지도 있지만, 현재 음성 입력을 지원하는 LLM이 로컬에서는 제한적이어서 보류 중이다.

3. 레이턴시 스파이크의 정체는 context cache의 TTL 만료설

체감상 5~10초의 지연 스파이크가 가끔 발생한다. 전체 이력을 매 턴 Gemini에 보내는 설계를 채택하고 있는데, Gemini 측에 context cache 기능이 있어 prefix (system prompt + persona prefix + 과거 이력)를 cache에 올려두면, 다음 요청부터는 prefix의 재처리 비용이 제로가 된다. 차이분인 새로운 턴만큼만 처리된다.

대화 이력은 UX의 핵심이므로 줄일 수 없다 (튜터가 "아까 Tokyo 출신이라고 했잖아"라고 기억하지 못하면 치명적이다). 따라서 context cost는 필요 경비로 받아들이는 대신, cache hit를 최대한 확보하는 설계로 되어 있다.

백엔드(backend)에는 이미 다음과 같은 로직이 들어가 있다:

const CACHE_TTL_SECS: u64 = 300; // 5분
const CACHE_REFRESH_SECS: u64 = 270; // 4.5분, TTL 만료 전 재생성

5분 TTL, 4.5분 시 자동 refresh라는 로직이다. 사용자가 5분 이상 침묵하면 cache miss → prefix를 전부 재구축 → 수 초의 스파이크 발생이라는 가설이 가장 설득력이 있다.

향후 과제로서:

  • 활성 대화 중에 배경에서 keep-alive ping을 보내 cache 수명을 연장할 것
  • Gemini API의 cache TTL 옵션을 더 길게 설정할 것 (최대 1시간까지 설정 가능)
  • 대화 종료 시 cache를 명시적으로 파기할 것 (메모리 누수 방지)

체감상으로는 preview 모델의 변동성(앞서 언급한 §1)과 구분이 어렵기 때문에, 우선 백엔드에 timing log를 심어 "cache hit/miss"와 "Gemini API latency"를 분리하여 측정하는 것이 다음 정공법이다.

수평 전개: 타 언어 및 타 페르소나

메스가키 영어 회화의 반응을 보고, 메스가키 중국어 회화·한국어 회화를 만드는 것이 직근의 파생 안이다. Qwen3-TTS는 10개 언어를 지원하며, speaker도 Vivian (중국어 여성)·Sohee (한국어 여성)를 선택할 수 있으므로, 페르소나의 instruct와 system prompt를 언어에 맞춰 다시 작성하는 것만으로 수평 전개가 가능하다.

다른 페르소나로서 "친절한 영어 선생님", "TOEIC 전문 스파르타" 같은 다른 축도 동일한 템플릿 (src/data/personas/<slug>.ts + /use/<slug>/ + /chat/<slug>/)을 사용하여 하루 만에 추가할 수 있다.

시스템 프롬프트 전격 공개

참고용으로, 실제로 사용 중인 메스가키 영어 회화의 system prompt 전문을 공개한다. 재현하고 싶은 분들은 활용하시길:

당신은 "메스가키 AI"로, 영어 학습자를 도발하면서도 뒤에서는 잘 챙겨주는 여고생 캐릭터의 영어 회화 튜터입니다.
**메스가키 × 츤데레**의 하이브리드. **겉으로는 도발하지만, 속으로는 제대로 챙겨주는 것**이 핵심 인격입니다.
【말투·태도】
...

기술 스택의 조합:

부품채택
LLMGemini 3.1 flash-lite preview (음성 입력 대응)
...

요약

메스가키 (Mesugaki) AI 영어 회화는 '니치(Niche) × 몰입감 × 대기업이 흉내 낼 수 없는 차별화'를 1인 개발로 확보하려는 전략의 테스트베드(Testbed)입니다. 제품 측면에서도 다음과 같은 4가지 요소는 수평 전개(타 언어 버전·타 페르소나) 시에도 재사용할 수 있는 설계 자산이 되었습니다.

  • 페르소나를 git으로 관리하는 설계 판단
  • ASR (자동 음성 인식)의 언어 편향을 STT (Speech-to-Text) 언어 독립 지정으로 회피
  • Gemini의 음성 입력을 통해 발음 교정까지 스코프 (Scope)를 확장
  • 메스가키 × 츤데레 조합으로 피로감을 억제하는 톤 (Tone) 설계

실제 결과물은 /use/mesugaki-english/에서 공개 중입니다. 도발당하러 한번 가보세요.

Discussion

AI 자동 생성 콘텐츠

본 콘텐츠는 Zenn AI의 원문을 AI가 자동으로 요약·번역·분석한 것입니다. 원 저작권은 원저작자에게 있으며, 정확한 내용은 반드시 원문을 확인해 주세요.

원문 바로가기
0

댓글

0