_이 글은 [Hacktoberfest Open-Source AI Challenge Week 3: Slow and Steady] 제출물입니다.
제가 만든 것 (What I Built)
Mirae는 제가 설계하고 있는 작은 Raspberry Pi 5 컴패니언 로봇이며, 이번 주에는 게으름을 보상받는 하나의 임무를 부여했습니다. 바로 매일 밤 계속 이어지는 이야기 하나를 들려주는 것입니다.
대부분의 AI 이야기는 일회성입니다. 질문하면 이야기를 받고, 다음 날이면 사라집니다. 하지만 Mirae는 _연속적인 시리즈_를 들려줍니다. 매일 밤 같은 이야기의 다음 에피소드를 작성하며, 이전 내용이 끝난 지점부터 시작합니다. 첫날 소개된 선장은 30일째에도 여전히 그 선장입니다. 둘째 날 합류한 낯선 사람도 아홉째 날에는 아직 배에 타고 있습니다. 죽은 캐릭터는 계속 죽어 있습니다. 사용자는 줄거리 요약이나 누가 누구인지 상기할 필요가 전혀 없습니다. Mirae가 노트를 대신 보관해 줍니다.
설정하는 데는 약 1분밖에 걸리지 않습니다. 장르를 선택하고, 한 줄의 전제를 작성하며, 캐릭터 이름을 지정하기만 하면 됩니다. 그 후에는 할 수 있는 일은 질문하고 잠드는 것뿐입니다.
누가 가장 게으름을 피울까요? 스스로 이야기를 창조하거나, 스크롤하며 찾거나, 플롯을 추적해야 하는 사람이 아닌, 하루의 끝에 이야기가 필요한 누구든 말이죠.
데모 (Demo)
이 영상은 경험으로 시작하여 실제 대시보드로 마무리됩니다:
- 침실 및 이야기 세계 장면은 개념 시각화물입니다. 이는 Mirae의 CAD 렌더링을 Higgsfield를 이용해 생성한 것입니다. 실제 로봇은 아직 제작되지 않았으며, 디자인이 흐트러지지 않도록 모든 로봇 관련 장면을 렌더링과 대조하여 확인했습니다.
- 이야기 텍스트는 실제입니다. 영상 속 'Kael'이 화물선을 부르는 밤 2의 내용은 제가 테스트를 진행했을 때 Gemma 4가 실제로 작성한 내용이며, 그날 밤 이후의 업데이트된 'Kael 합류' 정보는 실제 대시보드에 표시됩니다.
- 솔직히 말하자면 (Honest gap): 영상에서는 그녀가 “Hey Mirae, continue our story.”라고 질문합니다. 오늘날 사용자는 대시보드에서 _오늘 밤 에피소드 들려주기(Tell tonight's episode)_를 누르거나 API를 호출하면 됩니다. 음성 인식은 이미 faster-whisper로 로컬에서 작동하고 있지만, 이것을 이야기와 연결하는 작업이 다음 단계입니다.
코드 (Code)
Mirae logo: the Mirae robot
Mirae
단순한 본체. 지능적인 행동. 라즈베리 파이 5(Raspberry Pi 5)를 기반으로 제작된 작고 개방적이며 해킹 가능한 AI 컴패니언 로봇입니다.
Build status Website: reterics.github.io/Mirae Status: early prototype Raspberry Pi 5
Mirae, a small white and charcoal robot with two softly glowing eyes, on a wooden floor in a sunlit living room
[cad/]에 있는 정확한 인쇄 가능 모델을 기반으로 렌더링되었으며, AI 이미지 생성은 아닙니다.
Open the website Download the STL files Download the shopping cart
다섯 개의 부품이 있으며, 각 부품은 220 × 220 베드에 맞습니다. 쇼핑 카트: HESTORE 카트 내보내기(XML), 약 22,600 피트 순(net)이며, 전체 부품 목록을 참조하세요.
미라에를 만나다 (Meet Mirae)
미라에는 바닥에서 생활하는 두 바퀴의 컴패니언입니다. 당신을 보고, 듣고, 응답하지만, 인간처럼 보이려고 노력하지 않습니다. 입도, 귀도, 만화 같은 얼굴도 없습니다. 부드럽게 둥근 본체와 눈 역할을 하는 두 개의 확산된 빛점, 그리고 나중에 스크린이 될 수 있는 전면 패널만 있습니다.
그의 개성은 기계 장치보다는 소프트웨어에서 나옵니다. 컴퓨터 비전(Computer vision), 음성 인식(speech recognition), 그리고 플러그인 가능한 AI 레이어는 Raspberry Pi 5 위에서 작동합니다. 주변 몸체는 일반적인 220 ×… 크기로 완전히 3D 프린팅이 가능합니다.
View on GitHub
Repository: github.com/Reterics/Mirae
스토리 기능은 src/mirae/story/ (상태, 프롬프트, 연속성 규칙)에, 음성은 src/mirae/speech/에, 그리고 대시보드 페이지는 src/mirae/admin/story-ui.js에 있습니다.
제작 과정 (How I Built It)
오픈 웨이트 모델(Open-weight model), 로컬 추론(local inference). 모든 에피소드는 Ollama를 통해 로컬로 구동되는 **Gemma 4 (12B)**에 의해 작성됩니다. Mirae는 모든 OpenAI 호환 엔드포인트와 통신할 수 있으므로, 모델을 로봇, 가정용 서버 또는 데스크톱에서 실행할 수 있습니다. 약 350단어 분량의 에피소드는 대략 1분이 소요됩니다.
가장 어려운 부분은 첫날 밤이 아니라 서른 번째 밤입니다. 모델에게 전체 역사를 모두 주면 속도가 느려지고 흐트러지며, 아무것도 주지 않으면 선장의 이름을 잊어버립니다. 그래서 Mirae는 자체적인 작은 _스토리 바이블(story bible)_을 유지하며, 오늘 밤에 필요한 정보만 모델에게 전달합니다:
- 참고 자료 (Reference): 장르와 전제, 그리고 지금까지의 모든 내용을 요약한 짧은 진행 요약본.
- 등장인물 (Cast): 이야기에 등장하는 인물들과 그들의 역할, 누가 떠났거나 사망했는지, 그리고 언제였는지.
- 상황 (Situation): 지난밤 에피소드가 끝난 장소.
장르는 프롬프트 내의 엄격한 규칙이므로, 4일 밤에 공상과학(science-fiction) 이야기가 동화로 방황할 수는 없습니다.
모델은 제안하고, 코드가 결정합니다. 각 에피소드는 Gemma를 두 번 통과합니다. 첫 번째는 일반 산문으로 에피소드를 작성합니다. 두 번째는 이를 다시 읽고 연속성 노트(continuity notes)를 JSON 형식으로 반환합니다: 누가 합류했는지, 누가 떠났는지, 누가 사망했는지, 새로운 요약본, 그리고 현재 상황이 어떠한지입니다. 그런 다음 결정론적 Python 코드가 모델이 아닌 이 노트를 적용합니다:
for removal in update.removed:
key = removal.name.casefold()
if key == state.protagonist.casefold():
...
실제 출연진만 떠날 수 있고, 주인공은 절대 떠나지 않으며, 죽은 캐릭터는 다시 돌아오지 않습니다. 노트가 사용할 수 없더라도 에피소드는 보존되며 출연진 구성도 그대로 유지됩니다. 에피소드와 스토리 바이블(story bible)은 하나의 SQLite 트랜잭션으로 저장되므로 이야기가 분기될 수 없습니다.
음성은 ElevenLabs를 사용합니다. Mirae는 로봇의 스토리 페이지에서 선택된 ElevenLabs eleven_v4로 말하며, API 키는 로봇에 암호화되어 저장됩니다. 에피소드는 단락별로 스트리밍되므로, 전체 챕터가 렌더링 된 후가 아니라 약 2초 만에 첫 단어가 도착합니다. Piper는 설정 하나만으로 사용할 수 있는 오프라인 음성으로 내장되어 있습니다.
일관성을 유지할까요? 연속성 규칙(continuity rules), 저장소, API 및 음성은 167개의 테스트를 통과했습니다. 실제 실행에서 Gemma 4는 존 대위와 그의 엔지니어 클레이에 대한 두 편의 SF 밤을 작성했으며, 둘째 날 밤에는 두 사람 모두 원래 모습 그대로 유지되었고, 새로운 캐릭터인 Kael이 출연진에 자동으로 추가되었습니다.
오픈 이노베이션(Open Innovation)은 왜 중요할까요?
- 취침 전 이야기는 개인적입니다. 캐릭터는 종종 당신의 자녀, 파트너, 혹은 내부적인 농담거리일 것입니다. 로컬에서 실행되는 오픈 모델을 사용하면, 설정(premise), 출연진, 그리고 모든 에피소드가 사용자 자신의 하드웨어에 머무릅니다.
- 매일 밤 이야기가 토큰당 비용을 발생시켜서는 안 됩니다. 로컬 환경에서는 1년 치 에피소드가 전기 요금만 발생시킵니다.
- 기억은 판매자가 아닌 로봇에게 속합니다. 스토리 바이블은 누군가의 채팅 기록이 아니라 Mirae에 존재하므로, Gemma를 더 작거나 다른 오픈 모델로 교체해도 존과 클레이는 여전히 존과 클레이입니다.
- 저는 소형 하드웨어를 위해 설계할 수 있습니다. 제한된 요약(capped summary)은 컨텍스트를 적당한 장치에 충분히 작게 유지합니다. 다른 사람들은 Raspberry Pi 5에서 작은 Gemma 4 모델이 실행되는 것을 보여주었습니다 (Deploying Gemma 4 2B on a Raspberry Pi 5). 저는 아직 Pi에서 Mirae의 스토리텔러를 측정하지 않았습니다. 오늘 Gemma 4 12B는 제 데스크톱에서 실행되며 로봇은 홈 네트워크를 통해 그것과 대화합니다.
— 일관성 유지를 위해서는 폐쇄형 API가 제공할 수 없는 제어가 필요했습니다. AI 캐릭터는 드리프트(drift) 현상이 발생하는 것으로 알려져 있습니다 (Why AI characters drift when you switch models). 오픈 모델을 사용하면 프롬프트, 메모리, 그리고 그 주변의 규칙을 제가 소유하게 되므로, 바로 이 부분에 수정이 들어가야 했습니다.
게으른 보상 (The Lazy Payoff)
- 처음에는: 장르를 선택하고, 전제를 작성하며, 캐릭터 이름을 지정하는 데 약 1분이 걸립니다.
- 매일 밤: 한 번의 요청만 합니다. 약 1분 후 Mirae가 2~3분 분량의 챕터를 읽기 시작한 다음, 스스로 꺼집니다.
- 다시는: 플롯을 창조하거나, 등장인물을 기억하거나, 어디서 멈췄는지 알아낼 필요가 없습니다.
지금까지 이 도전 주간 동안 몇 밤에 걸쳐 실행해 보았습니다.
수상 부문 (Prize Categories)
- Gemma 활용 최우수: Ollama를 통해 Gemma 4 (12B)가 모든 에피소드와 연속성 노트를 작성합니다.
- ElevenLabs 활용 최우수:
eleven_v4 목소리가 Mirae(스토리 내레이션 및 음성 출력) 역할을 하며, 영화의 스코어는 ElevenLabs Music을 사용하고 청취자를 위한 두 번째 ElevenLabs 목소리를 사용했습니다.