
AmiVoice × Claude로 'AI가 설치한 지뢰 단어'를 피하는 게임 '말하게 하면 승리' 만들기
요약
AmiVoice의 실시간 음성 인식과 Claude API를 결합하여, 특정 NG 단어를 말하면 패배하는 2인 대전 음성 게임을 구현한 사례입니다. LLM을 게임 준비 단계에서만 활용하여 비용과 속도를 최적화하고, 대전 중에는 자체 로직으로 판정하는 효율적인 구조를 제안합니다.
핵심 포인트
- AmiVoice API와 Claude를 결합한 실시간 음성 대전 게임 구현
- LLM을 게임 준비 단계에만 사용하여 비용 및 응답 속도 최적화
- 음성 인식을 단순 받아쓰기가 아닌 게임 규칙 판정 근거로 활용
- 온도 바(Temperature Bar)를 통한 심리전 요소 도입
서론
지난 기사에서 고민했던 "'〇〇하면 승리'하는 무언가를 만들고 싶다"라는 생각에서, 인랑(Werewolf)이나 워드 울프(Word Wolf)와 같이 "말로 서로를 속이는" 게임과 결합하여 "'특정 단어'를 말하면 패배(상대에게 말하게 하면 승리)하는 게임을 음성으로 할 수 없을까"라고 생각했습니다. 텍스트 채팅이라면 위험한 단어는 의식적으로 피할 수 있지만, 자연스러운 대화에서는 무심코 입 밖으로 나오기 마련입니다. 구두의 자연스러움 × NG 단어 판정의 조합은 AmiVoice의 실시간 음성 인식(Speech Recognition) 강점을 살릴 수 있을 것 같았습니다.
그래서 만든 것이 **AmiVoice API와 Claude (Anthropic API)를 결합한 2인 대전 음성 게임 「말하게 하면 승리(言わせたもん勝ち)」**입니다. AI가 주제에 설치한 "비밀 NG 단어"를 두 플레이어는 모르는 상태로 대화를 나눕니다. 대화가 지뢰에 가까워지면 온도 바(Temperature Bar)가 올라가고, NG 단어를 입에 담은 쪽이 패배합니다. 온도의 움직임을 통해 지뢰의 방향을 추리하며, 자신은 피하면서 상대를 그쪽으로 유도하는 게임입니다.
이 앱은 음성 인식을 "받아쓰기(Transcription)를 하기 위해서"가 아니라 "게임 규칙 판정의 근거"로 사용하는 것이 특징입니다.
만든 것
흐름
-
주제: AI가 주제를 생성 (마음에 들지 않으면 3회까지 리롤 가능)
-
대전: 쇼기(일본 장기) 타이머 방식으로 교대로 대화. 온도 바가 실시간으로 움직임
-
결판: NG 단어를 말한 쪽이 패배. NG 단어가 공개됨
"자연스러운 잡담이 그대로 게임이 된다"는 점이 포인트입니다. 온도 바는 양쪽 모두에게 보이므로, "지금 온도가 올라갔다는 것은 지뢰가 이 화제 근처에 있다"라는 추리가 작동합니다. 자신은 피하면서 상대를 그 화제로 유인하는 심리전이 발생합니다.
기술 스택
서버: Node.js + Express + ws (WebSocket)
음성 입력: 브라우저의 Web Audio API (AudioWorklet)
음성 인식: AmiVoice API (WebSocket 인터페이스)
주제·NG 단어 생성: Claude (claude-haiku-4-5)
온도 판정: 자체 로직 (음운 + 관련어 매칭. API는 사용하지 않음)
1. Claude로 "절묘한 지뢰"를 설치하기
이 게임의 재미는 NG 단어의 질에 크게 의존합니다. 주제가 "최근 빠져 있는 것"일 때, NG가 "취미"라면 첫마디에 나와버리고, "냉장고"라면 아무도 밟지 않습니다. "대화 흐름상 자연스럽게 나올 것 같지만, 의식하면 피할 수 있는" 절묘한 라인의 단어가 필요합니다.
이 선정을 Claude에게 맡겼습니다. 동시에, 딱 맞는 단어뿐만 아니라 비슷한 단어에서도 온도가 올라가게 하고 싶으므로, NG 단어를 다차원(카테고리·속성·연상·음운)으로 분해하는 작업도 동일한 1회의 호출로 수행합니다.
// ai.js (발췌) 모델은 claude-haiku-4-5 (고속·저비용)
const user = `주제 「${topic}」로 두 사람이 잡담할 때, 「대화 흐름상 자연스럽게
나올 것 같지만, 의식하면 피할 수 있는」 단어를 하나 고르고, 관련 정보를 JSON으로
...
주제 「최근 빠져 있는 것」에 대한 출력 예시:
{
"word": "동영상",
"category": ["미디어", "콘텐츠", "엔터테인먼트"],
...
이를 게임 시작 시점에 1회만 생성하여 서버 측에 유지합니다. 대전 중에는 LLM을 호출하지 않습니다. "LLM은 게임 전 준비에 1회, 본 게임 중에는 자체 로직으로 싸운다"라는 구분으로 템포와 비용을 중시하고 있습니다.
2. 온도 판정: 대전 중에는 API를 호출하지 않음
대전 중 판정에 Claude나 Embedding API를 매번 호출하면 1~2초의 지연이 발생하여 템포가 좋지 않습니다. 그래서 시작 시 생성한 다차원 데이터와의 문자열 매칭 + 음운 유사도만으로 온도를 산출하기로 했습니다.
판정은 다음 우선순위로 진행합니다.
- NG 단어 본체 또는 읽기가 발화에 포함되어 있으면 즉시 NG 확정 (온도 5)
- 각 차원(카테고리/속성/연상/동작)의 관련어와 대조. 포함되면 강한 히트, 소리가 비슷하면 약한 히트
- NG 단어의 읽기와의 음운적 유사성 (말을 하려다 멈추는 경우 대비)
음운의 유사성은 가타카나를 히라가나로 정규화한 후, 포함·전방 일치·편집 거리(Edit Distance)를 조합하여 산출합니다.
function phoneticSim(aRaw, bRaw) {
const a = normalize(aRaw), b = normalize(bRaw);
if (a.includes(b) || b.includes(a)) {
...
고전한 부분: 음운 스코어가 너무 잘 듣는다 / 잘 듣지 않는다
이 부분의 조정에 가장 많은 시간을 사용했습니다. 단순한 편집 거리(Edit Distance)만 사용하면,
- "도..."와 같이 말을 꺼내려다 만 경우를 포착할 수 없음 (너무 짧아서 거리가 멀어짐)
- 그렇다고 전방 일치(Prefix Match)를 너무 강하게 설정하면, "도우시요(어떡해)", "도우모(안녕하세요)"까지 온도가 급상승함
이라는 양극단의 문제가 발생합니다. 최종적으로는,
- 전방 일치 스코어에는 **상한(0.9)**을 두어, 짧은 일치만으로는 확정(온도 5)에 이르지 않도록 함
- NG 단어의 읽기(phonetic_variants)를 통째로 포함하고 있을 때만 확정을 허용함
- 한자 NG 워드는 정규화를 해도 히라가나로 변하지 않으므로, 읽기(phonetic_variants)와 비교함
이라는 조정을 통해, "말을 끝맺으면 확정, 말을 하다 말면 아슬아슬하게(온도 4), 관계없거나 혼동되는 단어는 0"이라는 밸런스를 잡았습니다. 실제 테스트 결과는 다음과 같습니다.
온도 5 [ngword] ← "최근에 유튜브만 보고 있어"
온도 4 [음운] ← "도..." (말을 하다 만 경우: 아슬아슬함)
온도 0 [-] ← "도우시요(어떡해)" (오작동하지 않음)
...
3. partial과 final의 역할 분담
AmiVoice는 인식 중인 partial과 확정된 final을 반환합니다. 이 구분 방식이 게임의 체감 성능에 직결되었습니다.
- partial을 매번 판정에 사용 → 힌트 표시가 생동감 있게 움직이지만, 인식의 흔들림으로 인해 오판정이 발생함
- final로만 판정 → 확실하지만 반응이 느리고, 화면이 너무 정적임
채택한 방침은 다음과 같습니다.
- 온도 바의 표시 업데이트는 partial로 수행 (체감 템포를 확보)
- 승패를 결정하는 NG 확정은 final로만 수행 (공정성 담보)
- partial에서 온도 5가 나오더라도 확정하지 않고, 온도 4로 내림 (아슬아슬한 상태에서 멈춤)
4. NG 워드를 누가 정하는가: 인간에서 AI로
사실 처음에는 "서로 상대방에게 줄 NG 워드를 입력하는" 규칙으로 설계했습니다. 하지만 구체화하다 보니 정보 설계상의 약점이 보였습니다. NG를 설정한 본인은 해당 단어를 알고 있기 때문에, 힌트를 누구에게 어떻게 보여줄지가 복잡해지고 규칙 설명도 길어집니다.
검토 결과, NG 워드 설정을 AI에게 맡기는 방식으로 변경했습니다. 이를 통해 게임은 "양측 모두 NG를 모르는 페어(Fair)한 상태"에서 시작하며, 온도 바 하나만 보고 있으면 됩니다. 규칙 설명이 한 문장("AI가 설치한 지뢰 단어를 말하면 패배")으로 끝나고, NG 입력 단계도 사라져 게임 시작이 순식간에 이루어집니다.
부수적인 효과로 추리하는 재미도 생겼습니다. 온도가 올라가는 순간, 양측 모두 "방금 발언의 어느 부분이 지뢰에 가까웠지?"라고 생각하기 시작합니다. 먼저 지뢰의 방향을 파악한 쪽이 상대방을 그쪽으로 유도하는, "누가 먼저 추리할 수 있는가"라는 심리전으로 변하여 결과적으로 더 즐겁게 플레이할 수 있게 되었습니다.
쇼기 타이머 방식이라는 타협
"마이크 하나로 화자 분리(Speaker Diarization)를 하여 교체가 필요 없게 만든다"는 안도 검토했지만, AmiVoice의 화자 분리는 비동기 API용이라 짧은 발화의 실시간 판정에는 적합하지 않았습니다. 템포를 최우선으로 하여, 버튼으로 교체하는 쇼기 타이머 방식을 채택하였고, 화자는 "버튼을 누른 사람"으로 물리적으로 확정했습니다.
요약 및 향후 하고 싶은 것
- 대전 중에는 LLM을 호출하지 않고, 준비 단계에서 한 번만 호출하는 설계가 템포와 비용의 양립에 효과적임
- partial/final을 "보여주기용"과 "결정용"으로 분리
향후 해보고 싶은 것:
- 리플레이 기능: 대전 녹음본을 비동기 API(화자 분리 포함)에 던져, "아슬아슬했던 포인트", "결정타"를 되돌아보기
- 실시간 화자 분리에 대한 도전: 쇼기 타이머를 없애고 마이크 하나로 플레이할 수 있을지 확인
- 효과음·연출, 결판 장면의 공유 이미지, 스마트폰 최적화
- 스트리머를 위해 "시청자가 투표로 NG 워드를 결정하는" 모드
참고 링크
Discussion

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