airi는 "LLM으로 움직이는 가상 캐릭터를, 서버·구독 없이 내 컴퓨터에서 완전히 소유하며 키우는 오픈소스 프로젝트"다. 화면에는 2D(Live2D) 혹은 3D(VRM) 아바타가 떠 있고, 당신이 마이크로 말을 걸면 캐릭터가 귀로 듣고(음성 인식) → 머리로 생각하고(LLM) → 입으로 답하고(음성 합성) → 몸으로 반응한다(입 모양·표정·시선). 여기에 대화를 기억하는 장기 기억과, 디스코드·텔레그램에서 함께 떠들거나 마인크래프트를 직접 플레이하는 능력까지 붙는다.
핵심 지향점은 딱 한 문장이다. "Neuro-sama 같은 AI 버튜버를, 특정 회사의 클라우드에 묶이지 않고 누구나 자기 손으로 굴릴 수 있게 하자." 그래서 airi는 브라우저 웹앱(PWA), 데스크톱 앱(Electron), 모바일 앱(Capacitor), 그리고 네이티브 게임 엔진(Godot)까지 같은 두뇌를 여러 몸에 이식하는 형태로 만들어졌다.
옛 다마고치는 껍데기(액정 캐릭터) 하나에 정해진 반응만 있었다. airi는 그 껍데기(아바타)는 그대로 두되, 속에 들어가는 '영혼'을 마음대로 바꿀 수 있게 했다 — 오늘은 GPT, 내일은 로컬 Llama, 인터넷이 끊기면 브라우저 내 소형 모델로. 데스크톱 앱 이름이 실제로 stage-tamagotchi인 이유다.
그리고 이 다마고치는 귀(마이크)·입(스피커)·눈(웹캠 모션캡처)·손(게임 봇)·기억(벡터 DB)을 갖췄다. 이 다섯 감각을 하나의 두뇌에 연결하는 배선도가 airi의 전부이고(§4), 그 배선을 47개 패키지로 깔끔히 나눈 것이 이 저장소의 진짜 볼거리다.
airi가 눈길을 끄는 이유는 "요즘 뜨는 기술 주제를 거의 다 한 저장소에서 실제로 동작하게 묶었다"는 데 있다. 브라우저 내 AI 추론, provider 무관 LLM 계층, 실시간 음성 대화, 3D 아바타, 임베디드 에이전트(게임 플레이), 로컬 벡터 메모리, 크로스플랫폼 배포 — 각각만 해도 인기 주제인데, airi는 이 전부를 하나의 완성도 높은 제품으로 합쳤다. "에이전트형 멀티모달 캐릭터 앱을 어떻게 짓는가"를 통째로 보여 주는 드문 레퍼런스다.
| 포인트 | 내용 |
|---|---|
| 정체 | 프레임워크·라이브러리가 아니라 "실제로 돌아가는 제품". 브라우저 웹앱 + Electron 데스크톱 + 모바일 + Godot 네이티브까지 한 모노레포에. |
| 가장 큰 매력 | 음성 파이프라인(듣기·감지·말하기)과 선택적 LLM까지 전부 브라우저 안에서 WebGPU로. 서버 GPU 없이도 로컬 캐릭터가 성립. |
| 차별점 | 32개 LLM provider를 xsai 한 겹으로 추상화 + 실무 상호운용 처리(뒤에 상세). "어느 모델이든 꽂아 쓴다". |
| 덩치 | TypeScript 2,049개 · Vue 605개 파일, 47 packages. pnpm 카탈로그 + Turborepo로 관리하는 대형 모노레포의 모범. |
airi에서 가장 인상적인 건 음성 인식(Whisper)·음성 활동 감지(Silero VAD)·음성 합성(Kokoro), 그리고 원한다면 LLM까지 클라우드 없이 브라우저 탭 안에서 돌린다는 점이다. 이를 가능케 한 것이 @huggingface/transformers(transformers.js)와 onnxruntime-web, 그리고 WebGPU다. GPU가 없거나 브라우저가 WebGPU를 못 쓰면 자동으로 WASM으로 내려앉는(fallback) 안전장치까지 있다. "AI는 무조건 서버에서 돌린다"는 통념을 정면으로 흔드는, 요즘 가장 뜨거운 흐름의 실물 교보재다.
airi는 아바타 UI(무대, stage)와 실제 능력(음성·게임·기억)을 WebSocket "신경망 버스"로 느슨하게 분리했다. 덕분에 같은 두뇌가 브라우저에서는 말동무가 되고, 데스크톱에서는 화면 위를 떠다니는 오버레이가 되고, 마인크래프트 서버에 접속하면 게임 캐릭터가 된다. 이 "능력을 모듈로 꽂았다 뺐다" 하는 이벤트 기반 구조(§4)가 airi를 단순한 챗봇 UI가 아니라 확장 가능한 플랫폼으로 만든다.
airi는 배포된 데스크톱 바이너리(v0.10.2, Windows/macOS/Linux)가 있어 받아서 바로 써 볼 수는 있다. 다만 저장소 루트 버전은 0.11.0의 개발 중 상태이고, 최고 경험을 내려면 LLM API 키(또는 로컬 모델 셋업)·마이크 권한·(Live2D를 쓰려면) 프로퍼티어리 Cubism SDK 등 여러 조각을 맞춰야 한다.
또한 이름의 "waifu/soul" 같은 표현이 시사하듯 컴패니언(정서적 동반자) 성격이 강한 프로젝트다. 기술 학습 대상으로는 최고의 교보재지만, 실제 사용 시에는 "AI 캐릭터에 대한 정서적 의존"을 스스로 건강하게 관리하는 관점도 함께 필요하다. 이 문서는 어디까지나 기술 구조 학습에 초점을 둔다.
airi의 스택은 방대하지만, 캐릭터의 신체 은유로 묶으면 한눈에 들어온다 — 눈에 보이는 무대(Vue 프론트엔드), 듣고 말하는 감각(음성 파이프라인), 판단하는 두뇌(LLM 계층), 기억(로컬 벡터 DB), 그 모두를 잇는 신경(WebSocket 버스), 그리고 이 두뇌가 깃드는 여러 몸(데스크톱·모바일·게임)이다. 아래 버전은 클론한 소스의 실제 값 기준이다.
| 기술 | 버전 · 역할 |
|---|---|
| Vue | ^3.5.32 — 아바타 무대와 설정 UI 전체. 상태관리 pinia ^3.0.4, 헤드리스 UI reka-ui ^2.9.6, 다국어 vue-i18n(한국어 README.ko-KR.md 포함). |
| Vite | ^8.0.8 — 빌드/개발 서버. vite-plugin-pwa로 모바일 설치형 PWA 지원. |
| UnoCSS | ^66.6.8 — 원자적(atomic) CSS 엔진. Tailwind류지만 더 빠르고 커스터마이즈 자유도가 높음. |
| TypeScript | ^5.9.3 — 코드 절대다수(추적 파일 기준 TS 2,049 · Vue 605). |
| 기술 | 버전 · 역할 |
|---|---|
| VRM (3D) | @pixiv/three-vrm ^3.5.2(+ -core/-animation) 위에서 Three.js ^0.184.0로 렌더. 이를 Vue에서 선언적으로 다루려고 @tresjs/core ^5.8.0(+ cientos)를 씀 — "Three.js를 Vue 컴포넌트처럼". |
| Live2D (2D) | pixi-live2d-display ^0.4.0(패치본) + @proj-airi/unplugin-live2d-sdk ^0.1.7. 후자가 프로퍼티어리 Cubism SDK를 끌어와 붙인다(라이선스 주의). |
| Spine | @esotericsoftware/spine-webgl ~4.2.0 — 또 다른 2D 스켈레탈 애니메이션 포맷 지원. |
| 모션 캡처 | @mediapipe/tasks-vision ^0.10.34 — 웹캠으로 사람 포즈를 추적해 VRM 아바타에 반영(packages/model-driver-mediapipe). 내 움직임이 곧 캐릭터 움직임. |
| 단계 | 기술 · 세부 |
|---|---|
| STT (듣기) | Whisper via @huggingface/transformers ^3.8.1(transformers.js)를 Web Worker에서. WebGPU 우선, 실패 시 WASM 자동 폴백. |
| VAD (말 시작·끝 감지) | Silero VAD를 @huggingface/transformers AutoModel로 로드해 AudioWorklet 안에서 실행(packages/stage-ui/src/workers/vad/). "지금 사람이 말하는 중인가?"를 실시간 판정. |
| TTS (말하기) | 로컬 Kokoro(kokoro-js ^1.2.1) + ElevenLabs·Azure·OpenAI 호환·Alibaba. 이 여러 백엔드를 unspeech(음성판 LiteLLM 프록시)로 하나의 인터페이스로 통일. |
| 립싱크 | wlipsync — 소리에서 음소(A/E/I/O/U/S)를 추출해 아바타 입 모양(blendshape)에 매핑. |
| 런타임 | onnxruntime-web ^1.24.3, @webgpu/types, gpuu(WebGPU 헬퍼). |
airi의 LLM 추상화 핵심은 xsai ^0.5.0-beta.2 — OpenAI 스타일의 경량 SDK다. @xsai/stream-text·generate-text·generate-speech·stream-transcription·tool 등으로 잘게 쪼개져 있고, @xsai-ext/providers가 provider들을, @xsai-transformers가 브라우저 내 LLM(transformers.js)을 담당한다. 문서상 32개 provider(OpenAI·Anthropic Claude·Ollama·vLLM·SGLang·Gemini·DeepSeek·Qwen·Groq·Mistral·Cloudflare…)를 지원한다.
| 기술 | 버전 · 역할 |
|---|---|
| pglite | @electric-sql/pglite ^0.4.4 — Postgres + pgvector를 WASM으로 브라우저·로컬에. 서버 없이 벡터 검색(기억). |
| DuckDB WASM | @proj-airi/drizzle-duckdb-wasm ^0.5.0 — 브라우저 내 분석용 컬럼형 DB. drizzle-orm ^0.45.2로 타입 세이프하게. |
| 서버 | apps/server = Hono 4.11.3 + better-auth ^1.6.5(+ drizzle 어댑터·OAuth) + @hono/node-ws + pglite. |
| WS 버스 | @proj-airi/server-runtime(서버) ↔ @proj-airi/server-sdk 클라이언트(better-ws + crossws). 타입 있는 announce/heartbeat/event 프로토콜로 모듈을 연결. |
| 타깃 | 기술 · 세부 |
|---|---|
| 데스크톱 | Electron ^41.2.1(electron-vite + electron-builder + electron-updater, appId ai.moeru.airi, Flatpak 매니페스트). Tauri가 아니다(src-tauri 없음 확인). uiohook-napi로 전역 입력 후킹, 투명·클릭 통과 오버레이 창. |
| 네이티브 엔진 | engines/stage-tamagotchi-godot — Godot 4 + C#(.NET 8, Android은 net9). 웹 스택과 별개의 두 번째 네이티브 무대. |
| 모바일 | Capacitor(@capacitor/ios·/android) 기반 apps/stage-pocket. |
| 게임 에이전트 | services/minecraft = mineflayer ^4.33.0 + pathfinder/pvp/tool/collectblock/auto-eat, minecraft-data, prismarine-*. 여기에 isolated-vm(LLM이 생성한 코드 격리 실행) + awilix(DI) + @modelcontextprotocol/sdk(MCP). 팩토리오는 형제 저장소 moeru-ai/airi-factorio. |
pnpm-workspace.yaml 한 곳에 버전을 몰아 적어 두고 각 패키지가 "카탈로그의 그 버전"을 참조하게 해, 버전을 한 줄만 고치면 전체가 일관되게 바뀌도록 한다. Turborepo는 이 많은 패키지의 빌드·테스트를 의존성 순서대로, 캐시를 활용해 빠르게 돌리는 태스크 러너다. 대형 프로젝트 관리의 정석을 그대로 보여 준다. (도구: pnpm 10.33.0, Node 24.13.0.)airi를 이해하는 가장 빠른 길은 "당신이 말 한마디를 걸었을 때, 그 신호가 어떤 순서로 캐릭터의 반응이 되는가"를 따라가는 것이다. 귀에서 시작해 몸으로 끝나는 이 루프가 airi의 심장이다.
핵심을 지탱하는 발상은 가운데 무대(아바타 UI)와 바깥의 능력들(게임·기억·챗 플랫폼)이 WebSocket 버스로 분리돼 있다는 것이다. 그래서 같은 두뇌가 게임을 하든, 채팅을 하든, 음성 통화를 하든 무대 코드는 그대로다.
airi에서 가장 구조적으로 영리한 부분은 server-runtime(서버) ↔ server-sdk(클라이언트)로 이뤄진 타입 있는 WebSocket 프로토콜이다. 어떤 모듈(마인크래프트 봇, 기억 저장소, 디스코드 브리지)이든 서버에 자신을 announce(등록)하고 이벤트를 주고받는다. 그 결과 능력을 코드 수정 없이 런타임에 붙였다 뗐다 할 수 있다.
// packages/memory-pgvector/src/index.ts:11 — 기억 모듈이 버스에 등록
const client = new Client<{ connectionString: string }>({ name: 'memory-pgvector' })
client.onEvent('module:configure', (_event) => {
/* 서버로부터 DB 설정을 수신 */
})
runUntilSignal() // 종료 신호가 올 때까지 이벤트 루프 유지
idle → connecting → authenticating → announcing → ready. 즉 "연결하고 → 인증하고 → 나는 이런 능력이라고 알리고 → 준비 완료". 이렇게 계약(무엇을 보내고 받는지)을 타입으로 못 박아 두면, 서로 다른 팀이 만든 봇도 같은 규칙으로 두뇌에 꽂힌다. 마이크로서비스에서 쓰는 서비스 디스커버리를, 캐릭터의 감각·능력 단위로 축소해 놓은 셈이다.airi의 LLM은 단발 응답이 아니라 도구를 여러 번 호출하며 스스로 걸음을 이어가는 에이전트로 동작한다. 아래는 실제 런타임 코드다.
// packages/core-agent/src/runtime/llm-service.ts:231
const streamResult = streamText({
...chatConfig,
abortSignal: options?.abortSignal,
messages: sanitized, // ↓ 4-4에서 설명
streamOptions: { includeUsage: true },
stopWhen: stepCountAtLeast(10), // 최대 10걸음까지 도구 호출 반복
tools: streamTools,
onEvent,
})
stopWhen: stepCountAtLeast(10)이 핵심이다. LLM이 "도구를 부르고 → 결과를 보고 → 또 도구를 부르고"를 최대 10번까지 반복하도록 허용한다. 예컨대 마인크래프트에서 "나무를 모아 집을 지어"라고 하면, 두뇌가 여러 걸음에 걸쳐 이동·채집·배치 도구를 순차 호출한다.
"32개 provider 지원"은 말은 쉽지만, OpenAI 호환을 자처하는 게이트웨이들이 실제로는 미묘하게 다른 스펙을 요구한다. airi의 sanitizeMessages()는 그 상처를 꿰맨 흔적이다.
content를 거부한다OpenAI 최신 스펙은 메시지 content를 텍스트·이미지 파트의 배열로 받을 수 있다. 그런데 serde(직렬화)에 엄격한 일부 OpenAI 호환 백엔드는 이 배열을 거부하고 순수 문자열만 받는다. airi는 core-agent의 sanitizeMessages()에서 이런 provider를 만나면 content 파트를 문자열로 평탄화(flatten)해 넘긴다(저장소 이슈 #1500 참조). "추상화 계층은 결국 이런 예외 처리로 먹고산다"는 걸 보여 주는 훌륭한 산 교재다.
WebGPU는 강력하지만 불안정하다 — 탭이 백그라운드로 가거나 드라이버가 GPU 컨텍스트를 회수(device loss)하면 추론이 실패한다. airi는 이를 감지해 자동으로 WASM으로 강등한다.
// packages/stage-ui/src/libs/inference/adapters/whisper.ts:279
// device-loss가 임계값 넘게 반복되면 선제적으로 WASM으로 승격(강등)
const requestedDevice =
deviceLossCount >= DEVICE_LOSS_WASM_THRESHOLD ? 'wasm' : 'webgpu'
w.postMessage({ type: 'load-model', requestId, modelId: MODEL_NAMES.WHISPER, device: requestedDevice })
① 실시간 루프: 마이크→VAD→STT→LLM→TTS→입모양이 끊김 없이 순환. ② 모듈 버스: 게임·기억·챗 능력을 타입 있는 WebSocket으로 느슨하게 결합. ③ 여러 걸음: stepCountAtLeast(10)로 도구를 반복 호출하는 임베디드 에이전트. ④ 로컬 우선·폴백: 브라우저 내 추론을 기본으로 하되 WebGPU→WASM, 로컬→클라우드로 안전하게 물러선다. 네 원칙 모두 "내 기기에서 사는 캐릭터"라는 목표에 직결된다.
airi는 크다. 그래서 오히려 대형 모노레포가 어떻게 관심사를 나누는지를 통째로 볼 수 있는 교보재다. 크게 다섯 갈래다 — 실제 실행되는 앱(apps), 앱들이 공유하는 부품(packages), 외부 세계에 붙는 서비스(services), 네이티브 엔진(engines), 그리고 플러그인(plugins).
| 갈래 | 역할 |
|---|---|
| apps/ | 몸. 같은 두뇌가 깃드는 여러 실행체 — 웹(stage-web)·데스크톱(stage-tamagotchi)·모바일(stage-pocket)·백엔드(server). 각각 package.json이 프레임워크(Vite/Electron/Capacitor)를 드러낸다. |
| packages/ | 장기·부품. 앱들이 공유하는 47개 라이브러리. 무대 UI(stage-ui), 렌더러(three/live2d/spine), 두뇌(core-agent), 캐릭터(core-character), 기억(memory-pgvector), 신경(server-*)으로 관심사가 칼같이 분리. |
| services/ | 외부 능력. 마인크래프트·디스코드·텔레그램·트위터·컴퓨터 사용(MCP). 두뇌에 WS로 붙는 별도 프로세스들. |
| engines/ | 웹과 완전히 다른 네이티브 무대 — Godot 4 + C#. 같은 캐릭터 개념을 게임 엔진으로 다시 구현. |
| plugins/ | 확장점. 빌리빌리·Claude Code·체스 게임·홈어시스턴트·브라우저 확장 등, 두뇌에 붙일 수 있는 곁가지 능력. |
packages/ccc는 SillyTavern 등에서 쓰는 캐릭터 카드를 다룬다. 캐릭터의 이름·성격·말투·초기 상황 같은 설정을 PNG 이미지 파일 안에(meta-png로 메타데이터 청크에) 심어 두는 방식이다. 그림 한 장을 주고받는 것만으로 캐릭터의 '영혼 설정'까지 함께 전달되는 것 — airi가 "영혼을 담는 그릇"을 자처하는 이유가 이런 데서 드러난다.이 구조는 하나의 극단에 가깝다. packages/는 배우·소품·조명 같은 공용 자원이고, apps/는 그 극단이 오르는 서로 다른 무대(브라우저·데스크톱·모바일)다. services/는 무대 밖에서 협연하는 객원(마인크래프트·디스코드), engines/는 아예 다른 극장(Godot)에서의 재연이다. 배우(핵심 로직)를 한 번 잘 키워 두면, 무대만 바꿔 여러 곳에 세울 수 있다.
xsai를 얇은 OpenAI 호환 계층으로 두고, 그 위에서 32개 백엔드의 미묘한 차이를 런타임에 흡수하는 법(§4-4의 sanitizeMessages). "추상화는 예외 처리로 완성된다"는 실무 감각을, 실제 이슈 번호가 달린 코드로 배운다. LLM 앱을 특정 벤더에 묶지 않으려는 모든 개발자에게 직접적인 교보재다.
Whisper(STT)·Silero(VAD)·Kokoro(TTS), 그리고 옵션 LLM까지 transformers.js + onnxruntime-web + WebGPU로 브라우저에서 돌리고, device-loss 시 WASM으로 우아하게 폴백하는 실물. "AI = 서버"라는 고정관념을 깨는, 지금 가장 앞선 프론트엔드 AI 패턴을 코드로 익힐 수 있다.
Three.js 명령형 코드 대신 @tresjs/core로 VRM 아바타를 Vue 컴포넌트처럼 마운트·애니메이션하는 법. 자동 눈 깜빡임·시선 추적·idle 모션, 그리고 소리→음소→blendshape(wlipsync) 립싱크까지 — 웹 3D 캐릭터를 다루는 실전 레시피다.
// packages/stage-ui-three/src/composables/vrm/lip-sync.ts:7
import { createWLipSyncNode } from 'wlipsync'
// 음소 → VRM ARKit 비짐(viseme) 블렌드셰이프 이름 매핑
const BLENDSHAPE_MAP: Record<LipKey, string> = {
A: 'aa', E: 'ee', I: 'ih', O: 'oh', U: 'ou',
}
능력(게임·기억·챗 봇)을 코어에 하드코딩하지 않고, 타입 있는 WebSocket 프로토콜로 announce·이벤트 교환하며 붙였다 뗐다 하는 설계(§4-2). 마이크로서비스의 서비스 디스커버리를 앱 내부 규모로 축소한 좋은 예로, 확장 가능한 클라이언트 앱을 짜려는 사람에게 유용하다.
services/minecraft/src/cognitive/가 지각(perception)·의식(conscious)·행동(action)·반사(reflex)로 나뉜 교과서적 구조라는 점, 그리고 isolated-vm으로 LLM이 생성한 코드를 격리 실행하고 @modelcontextprotocol/sdk(MCP)로 도구를 노출하는 방식. "게임하는 AI"를 만들려는 사람에게 직접적인 청사진이다.
// packages/stage-ui/src/workers/vad/vad.ts — Silero VAD 상태 텐서(발췌)
import { AutoModel, Tensor } from '@huggingface/transformers'
// 기본값: sampleRate 16000, speechThreshold 0.3, minSilenceDurationMs 400
this.sampleRateTensor = new Tensor('int64', [this.config.sampleRate], [])
this.state = new Tensor('float32', new Float32Array(2 * 1 * 128), [2, 1, 128]) // LSTM 상태
웹·데스크톱·모바일·네이티브까지 한 저장소에서 버전을 한 곳(catalog)에 몰고 Turborepo로 태스크를 조율하는 법. 47개 패키지가 어떻게 관심사별로 쪼개지고 서로를 참조하는지 — 규모 있는 프론트엔드 프로젝트를 지속 가능하게 유지하는 실전 구조를 통째로 관찰할 수 있다.
이 저장소가 남기는 큰 교훈은 분명하다. 현대 AI 클라이언트 앱은 '모델 하나 붙이기'가 아니라, 감각(음성·비전)·두뇌(provider 무관 LLM)·기억(로컬 벡터 DB)·능력(도구·게임)·표현(3D 아바타)을 느슨하게 연결하는 오케스트레이션이다. 와이푸에 관심이 없어도, 여기서 배우는 조각들(브라우저 AI·xsai·@tresjs·WS 모듈 버스·모노레포)은 어떤 멀티모달 앱에도 그대로 옮겨 쓸 수 있다.
airi의 요구사항은 두 축으로 정리된다 — 어떤 실행체(브라우저·데스크톱·모바일·Godot)를 쓰느냐, 그리고 LLM·음성 추론을 로컬(내 GPU)에서 하느냐 클라우드에서 하느냐다. 클라우드 LLM을 쓰면 GPU 없이도 충분히 돌아간다.
| 실행 환경 | 요구/수치 |
|---|---|
| 브라우저(stage-web) | 모던 Chromium 계열. 로컬 추론 가속을 원하면 WebGPU 필요(안 되면 자동 WASM 폴백). 모바일에서는 PWA로 설치 가능. 클라우드 LLM/TTS만 쓰면 GPU 불필요. |
| 데스크톱(Electron) | Windows x64 · macOS arm64 · Linux(AppImage/Flatpak). 배포 바이너리 v0.10.2 제공. 로컬 모델에 네이티브 NVIDIA CUDA / Apple Metal(HuggingFace candle 경유) 활용 가능. NixOS는 nix develop .#fhs 필요. |
| 모바일(stage-pocket) | iOS / Android — Capacitor 빌드. |
| Godot 엔진 | .NET 8 SDK(engines/stage-tamagotchi-godot 빌드용). |
| Live2D 사용 시 | 프로퍼티어리 Cubism SDK 필요(unplugin-live2d-sdk가 끌어옴). VRM(3D)만 쓰면 불필요. |
| 개발 환경 | Node 24.13.0 · pnpm 10.33.0. 웹은 pnpm i && pnpm dev, 데스크톱은 pnpm dev:tamagotchi. |
| LLM · 음성 백엔드 | 클라우드(OpenAI·Claude 등) API 키 또는 로컬(Ollama·vLLM) 또는 브라우저 내(transformers.js). 마이크 권한 필수(음성 대화 시). |
가장 쉽게 시작하려면: 데스크톱 바이너리(v0.10.2)를 받고 클라우드 LLM API 키만 넣으면 된다 — GPU도 셋업도 거의 없이 대화가 된다. 가장 사적으로(내 데이터만으로) 쓰려면: 로컬/브라우저 내 모델 + 로컬 벡터 DB로 인터넷 없이 굴린다 — 대신 어느 정도의 GPU/메모리와 셋업이 필요하다. airi의 장점은 이 스펙트럼을 한 앱 안에서 선택할 수 있다는 것이다.
먼저 클론 후 웹 무대부터. 모노레포라 루트에서 한 번 설치하면 된다.
# 클론 & 웹 무대 실행 (Node 24 · pnpm 10 권장)
git clone --depth 1 https://github.com/moeru-ai/airi.git
cd airi && pnpm i
pnpm dev # stage-web (브라우저 PWA) 개발 서버
# 데스크톱 오버레이를 원하면:
pnpm dev:tamagotchi # Electron 무대
pnpm dev로 stage-web을 띄우고, 설정에서 OpenAI 호환 provider 하나(API 키)를 넣어 텍스트로 대화해 본다. 먼저 GPU·음성 없이 두뇌(LLM)만 붙여 캐릭터가 응답하는지 확인 — airi의 데이터 흐름을 감 잡는 가장 빠른 길이다.
무료 VRM 모델(VRoid 등)을 하나 구해 아바타를 바꾸고, TTS를 켠 뒤 소리에 맞춰 입 모양이 움직이는지 본다. packages/stage-ui-three의 lip-sync.ts에서 음소→blendshape 매핑을 따라가며, 표정·시선 자동화가 어디서 오는지 코드로 확인.
마이크를 연결하고 Whisper(STT) + Silero(VAD)를 브라우저 내에서 돌려 음성으로 말을 건다. 개발자도구로 워커 로딩을 관찰하고, WebGPU를 끈 채(또는 지원 안 되는 브라우저에서) WASM으로 폴백되는지 확인 — whisper.ts의 device-loss 로직을 눈으로 검증.
클라우드 대신 Ollama 로컬 모델로 두뇌를 교체해 본다. 설정만 바꿔도 대화가 이어지는 걸 확인하고, 만약 응답이 깨지면 core-agent의 sanitizeMessages가 왜 필요한지(§4-4) 로그로 추적한다. "영혼 갈아끼우기"를 실제로 해 보는 실습.
services/minecraft를 로컬 서버에 접속시켜, 두뇌가 WebSocket으로 announce되고 채집·이동 도구를 여러 걸음(step) 호출하는 걸 관찰한다. cognitive/의 지각·행동 구조를 읽고, 간단한 새 도구(예: "이 블록을 캐라")를 하나 추가해 stepCountAtLeast 루프에 태워 본다.
| 주차 | 주제 · 목표 |
|---|---|
| 1주차 | 모노레포 & 도구체인. pnpm workspace·catalog, Turborepo 태스크 그래프, Vite/Vue 3 기본. 목표: airi의 apps↔packages 의존 관계를 읽고 하나를 빌드하기. |
| 2주차 | 브라우저 내 AI. transformers.js·onnxruntime-web·WebGPU vs WASM, Web Worker/AudioWorklet. Whisper·Silero VAD를 직접 워커에서 돌려 보고 device-loss 폴백을 이해. |
| 3주차 | 웹 3D 캐릭터. Three.js 기초 → @tresjs 선언적 렌더 → @pixiv/three-vrm, 그리고 음소→blendshape 립싱크. 아바타 하나를 마운트해 표정·시선·립싱크를 붙인다. |
| 4주차 | provider 무관 LLM & 도구 루프. xsai의 streamText·tool, stopWhen/step 개념, OpenAI 호환 게이트웨이의 상호운용 함정. 여러 걸음 에이전트를 직접 짜 본다. |
| 5주차 | 이벤트 기반 아키텍처 & 임베디드 에이전트. WebSocket 프로토콜 설계(announce·heartbeat), isolated-vm 샌드박싱, MCP 도구 노출, mineflayer 인지 루프. "능력을 모듈로 꽂는" 구조를 스스로 설계. |
| airi | moeru-ai가 만든 오픈소스 AI 버튜버·컴패니언. 음성 대화·아바타·기억·게임 플레이를 갖춘 self-hosted 캐릭터. |
| VTuber / Digital Human | 가상 아바타로 소통하는 존재. airi는 그 아바타를 AI가 실시간 조종하는 Neuro-sama식 개념을 오픈소스로. |
| self-hosted / you-owned | 코드·모델·데이터를 회사 서버가 아닌 내 기기에 두는 것. airi의 핵심 철학. |
| xsai | OpenAI 스타일의 경량 LLM SDK. airi가 32개 provider를 한 겹으로 추상화하는 데 사용(stream-text·tool 등). |
| transformers.js | @huggingface/transformers. Whisper·Silero·(옵션)LLM을 브라우저 안에서 실행하게 하는 라이브러리. |
| WebGPU / WASM 폴백 | 브라우저 GPU 가속 API. 불가 시 WASM(CPU)로 자동 강등해 어디서나 추론이 성립하게. |
| Whisper / Silero VAD / Kokoro | 각각 음성 인식(STT) / 발화 시작·끝 감지(VAD) / 음성 합성(TTS) 모델. airi의 음성 파이프라인 3요소. |
| VRM / Live2D / @tresjs | VRM=3D 아바타 표준, Live2D=2D 아바타, @tresjs=Three.js를 Vue 컴포넌트처럼 쓰는 렌더러. |
| wlipsync | 소리에서 음소를 뽑아 아바타 입 모양(blendshape)으로 바꾸는 립싱크 라이브러리. |
| server-runtime / server-sdk | airi의 "신경망 버스" — 모듈이 announce·이벤트 교환하는 타입 있는 WebSocket 프로토콜(서버/클라이언트). |
| pglite / DuckDB-WASM | 브라우저·로컬에서 도는 Postgres(pgvector)/DuckDB. 서버 없이 기억(벡터 검색)·분석을 가능하게. |
| stopWhen / stepCountAtLeast | LLM이 도구를 몇 걸음까지 반복 호출할지 정하는 조건. airi는 최대 10걸음 에이전트 루프. |
| mineflayer / isolated-vm / MCP | 마인크래프트 봇 라이브러리 / LLM 생성 코드 격리 실행 / 도구를 표준 방식으로 노출하는 프로토콜. |
| ccc (캐릭터 카드) | 캐릭터 설정을 PNG 이미지 메타데이터에 심는 방식(SillyTavern 호환). 그림 한 장으로 '영혼'을 전달. |
| stage-tamagotchi | Electron 데스크톱 앱(투명·클릭 통과 오버레이). 이름처럼 "영혼 갈아끼우는 전자 다마고치". |
공식
· GitHub 저장소: github.com/moeru-ai/airi (모노레포 · MIT · © 2024-PRESENT Neko Ayaka)
· TrendShift 카드: trendshift.io/repositories/14636 (Daily #15 · #AI agent #Digital human)
· 한국어 README: 저장소 내 README.ko-KR.md · 데스크톱 바이너리 릴리스 v0.10.2(Win/macOS/Linux)
저장소 안에서 꼭 읽을 것
· packages/core-agent/src/runtime/llm-service.ts — 도구 루프(stepCountAtLeast)와 sanitizeMessages(두뇌)
· packages/stage-ui/src/workers/vad/ · libs/inference/adapters/whisper.ts — 브라우저 내 음성·폴백(감각)
· packages/stage-ui-three/src/composables/vrm/ — VRM 렌더·립싱크(몸)
· packages/server-runtime · server-sdk — WebSocket 모듈 버스(신경)
· services/minecraft/src/cognitive/ — 계층형 임베디드 에이전트(능력)
곁들여 학습
· transformers.js · onnxruntime-web · WebGPU 기초(MDN·HuggingFace 문서)
· Three.js → @tresjs/core → @pixiv/three-vrm · VRM/Live2D 아바타
· xsai · OpenAI 호환 API 상호운용 · Model Context Protocol(MCP)
· pnpm workspace/catalog · Turborepo 모노레포 관리
· mineflayer · isolated-vm 샌드박싱 · 임베디드 게임 에이전트
① 저장소 루트 버전은 0.11.0의 개발 중 상태이며, 사용자용 배포 바이너리는 v0.10.2(README 다운로드 링크)다. ② 데스크톱 셸은 Electron이다 — src-tauri가 없어 Tauri가 아님을 확인했다. ③ 팩토리오 연동은 이 저장소가 아니라 형제 저장소 moeru-ai/airi-factorio에 있다. ④ Live2D는 프로퍼티어리 Cubism SDK에 의존하니 라이선스를 확인할 것(VRM만 쓰면 무관). ⑤ 모노레포·트렌딩 특성상 패키지 수·버전·구조는 계속 바뀐다 — 최신은 공식 저장소에서 확인.