From c615f1f9a3d97ff72b4516b30219df3394ec4a6e Mon Sep 17 00:00:00 2001 From: tkrmagid Date: Sat, 8 Aug 2026 22:07:12 +0900 Subject: [PATCH 1/7] Initial commit --- README.md | 2 ++ 1 file changed, 2 insertions(+) create mode 100644 README.md diff --git a/README.md b/README.md new file mode 100644 index 0000000..8c23eda --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# watch_sceen_ai + From caae56a36ac22c2f4b7ea07abd345f94f29bed09 Mon Sep 17 00:00:00 2001 From: tkrmagid Date: Sat, 8 Aug 2026 22:11:24 +0900 Subject: [PATCH 2/7] docs: update README to test token push --- README.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/README.md b/README.md index 8c23eda..d4a3d03 100644 --- a/README.md +++ b/README.md @@ -1,2 +1,4 @@ # watch_sceen_ai +저장된 Gitea 토큰으로 README 수정/푸시가 정상 동작하는지 확인하기 위한 테스트 커밋입니다. (2026-08-08) + From 8f20f5f20c502dacb82ab5651465d07861f462c9 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Sun, 9 Aug 2026 22:09:34 +0900 Subject: [PATCH 3/7] docs(readme): sync implementation blueprint from owner workspace --- README.md | 164 +++++++++++++++++++++++++++++++++++++++++++++++++++++- 1 file changed, 163 insertions(+), 1 deletion(-) diff --git a/README.md b/README.md index d4a3d03..59ddb24 100644 --- a/README.md +++ b/README.md @@ -1,4 +1,166 @@ # watch_sceen_ai -저장된 Gitea 토큰으로 README 수정/푸시가 정상 동작하는지 확인하기 위한 테스트 커밋입니다. (2026-08-08) +디스코드 화면공유를 **AI가 실시간으로 보면서, 사용자와 음성으로 대화하는** 봇. +사용자가 디스코드에서 화면공유(Go Live)를 켜면 AI가 그 화면을 실시간으로 이해하고, +사용자가 음성으로 물으면 최대한 빠르게 답한다. 화면이 크게 바뀌면 먼저 말을 걸 수도 있다(proactive). + +- 실행 위치: 이 리눅스 호스트(.9, RTX 5050 8GB / ffmpeg / node v22 있음) +- 현재 상태: 1단계(눈: 화면공유 수신) 진행 중 — E2EE 합류 관문 통과, 실제 프레임 추출 PoC가 다음 목표 + +--- + +## 1. 설계 원칙 (이 프로젝트를 관통하는 3가지) + +1. **지연이 전부다.** 음성 대화형이라 "정확하지만 느린" 답은 실패다. 전 구간을 + 스트리밍으로 잇고, 목표는 사용자가 말 끝낸 뒤 **첫 소리까지 ~1.5초 이내**. +2. **싼 것부터, 필요할 때만 비싸게.** 매 프레임을 클라우드로 보내면 비용·지연이 폭발한다. + 로컬에서 화면 변화 감지 + 1차 이해를 하고, 정말 어려운 프레임만 클라우드로 올린다. +3. **부품은 교체 가능해야 한다.** 눈/귀/두뇌/입이 전부 `wsai/interfaces.py`의 Protocol이라, + 어느 하나(예: 화면 수신 방식)가 막히거나 바뀌어도 나머지는 그대로 돌아간다. + +--- + +## 2. 전체 구조 + +두 개의 async 루프가 동시에 돈다. 둘을 잇는 건 "지금 화면이 뭔지" 하나를 담는 공유 상태다. + +``` +[인지 루프] FrameSource ──Frame──▶ 변화감지 ──▶ VisionBackend ──ScreenObservation──▶┐ + ▼ + [SharedScreenContext] + │ (최신 1건) +[대화 루프] SpeechToText ──Utterance──▶ Brain(LLM) ◀───────────────────┘ + / TextChannel │ (+대화이력) + ▼ + TextToSpeech / TextChannel +``` + +- 인지 루프: 프레임 수신 → (이전과 유사하면 버림) → 비전 이해 → 최신 화면 맥락 갱신. +- 대화 루프: 사용자 발화 → 두뇌(최신 화면 맥락 + 대화이력) → 음성/텍스트 응답. +- 두 루프는 비동기로 독립. 대화 루프는 항상 "가장 최근에 이해된 화면"을 읽을 뿐, + 프레임 하나하나를 기다리지 않는다. + +데이터 타입/인터페이스는 `wsai/interfaces.py`에 이미 정의돼 있다: +`Frame`, `ScreenObservation`, `Utterance`, `Reply` / +`FrameSource`, `VisionBackend`, `SpeechToText`, `TextToSpeech`, `Brain`, `TextChannel`. + +--- + +## 3. 눈 — 화면공유를 어떻게 받아오나 + +이게 이 프로젝트의 가장 어려운 부분이다. 두 트랙을 **병행**한다. + +### 트랙 A (주력) — 화면공유 스트림 직접 수신 +스크린샷·브라우저 캡처가 아니라, 실제 화면공유 스트림 자체를 클라이언트처럼 수신한다. + +- 공식 디스코드 봇 API는 비디오 수신을 지원하지 않으므로, **유저 토큰(셀프봇)** 으로 + 프로토콜 레벨에서 비디오 RTP를 받는다. 전용 버너 계정만 사용한다. +- 파이프: 셀프봇 음성채널 join → 상대 Go Live 스트림 "watch" 구독(비디오 SSRC 획득) + → 암호화 비디오 RTP 수신 → **DAVE(MLS E2EE) 복호** → 전송암호(aead_aes256_gcm_rtpsize) 복호 + → VP8/H264 depayload → ffmpeg 디코드 → 프레임. +- 라이브러리: `discord.js-selfbot` 계열(werift-rtp), DAVE는 `@snazzah/davey`(Rust NAPI). +- 이 트랙은 Node로 프레임까지 뽑아 로컬 소켓/파이프로 파이썬 파이프라인에 넘긴다 + (`FrameSource` 구현 하나로 감싼다). + +솔직한 리스크: (1) 완성형 "수신→프레임" 라이브러리가 없어 직접 조립해야 한다. +(2) 셀프봇은 디스코드 ToS 회색지대라 계정 밴 위험이 있다(→ 버너 전용). +(3) 셀프봇/프로토콜이 바뀌면 깨질 수 있다. + +### 트랙 B (폴백/개발용) — 로컬 렌더 화면 캡처 +디스코드 클라이언트가 화면공유를 "보고 있는" 상태의 화면을 로컬에서 캡처한다. +트랙 A가 막히거나 불안정할 때의 안전판이자, 위쪽(비전/대화) 개발을 트랙 A 완성 전에 +진행하기 위한 실용 소스다. `poc/`의 Xvfb 실험과 `wsai/backends/capture_mss.py`가 여기 해당. + +두 트랙 모두 결과물은 동일한 `Frame` 스트림이라, 눈을 바꿔도 나머지는 그대로다. + +--- + +## 4. 두뇌 — 하이브리드 비전 + LLM + +- 로컬 소형 VLM(예: moondream2 / Qwen2-VL-2B)이 RTX 5050에서 매 프레임 1차 이해. +- **변화 감지**로 이전과 거의 같은 화면은 새로 설명하지 않는다(지연·비용 절감). +- 화면이 크게 바뀌었거나, 로컬 VLM 신뢰도가 낮거나, 사용자가 화면에 대해 구체적으로 + 물으면 → Claude(OAuth, Haiku 계열)로 에스컬레이션. +- 대화 응답 생성(Brain)은 Claude가 담당하되, 항상 "최신 화면 맥락 1건 + 대화이력"만 넣어 + 토큰을 작게 유지한다. + +--- + +## 5. 귀 / 입 — 음성 + +- 귀(STT): `faster-whisper` 스트리밍/부분 전사. 마이크는 `sounddevice`. +- 입(TTS): MeloTTS 등(이 호스트에 기존 자산 있음). 문장 단위로 스트리밍 재생. +- 인터럽트: 사용자가 말하기 시작하면 재생 중인 TTS를 즉시 멈춘다(barge-in). + +### 지연 예산(목표) +사용자 발화 종료 → 첫 소리까지: + +| 구간 | 목표 | +|------|------| +| STT 종료 판정(endpointing) | ~200 ms | +| Brain 첫 토큰(화면 맥락은 이미 준비됨) | ~500 ms | +| TTS 첫 문장 합성·재생 시작 | ~400 ms | +| 여유 | ~400 ms | +| **합계** | **~1.5 s** | + +화면 이해는 대화 요청과 분리된 인지 루프에서 미리 끝나 있으므로, 응답 시점에 +비전 지연이 끼어들지 않는 게 핵심이다. + +--- + +## 6. 실행 + +이 호스트에는 `python` 별칭이 없으므로 워크스페이스 venv를 쓴다(`.venv/bin/python`). + +```bash +.venv/bin/python -m wsai # mock 데모 (의존성·키 불필요, 몇 프레임 돌고 종료) +.venv/bin/python -m wsai --env # WSAI_* 환경변수로 백엔드 조립 +.venv/bin/python -m pip install pytest && .venv/bin/python -m pytest -q # 스모크 테스트 +``` + +백엔드별 추가 설치는 `requirements.txt` 주석 참고(mss/pillow, anthropic, faster-whisper 등). + +--- + +## 7. 파일 / 디렉터리 + +| 경로 | 역할 | +|------|------| +| `wsai/interfaces.py` | 데이터 타입 + 컴포넌트 Protocol | +| `wsai/pipeline.py` | 오케스트레이터(인지/대화 루프) | +| `wsai/state.py` | 최신 화면 맥락 공유 저장소 | +| `wsai/config.py` / `factory.py` | 설정 → 백엔드 조립 | +| `wsai/backends/mock.py` | 무의존성 mock 전 계열 | +| `wsai/backends/capture_mss.py` | 로컬 화면 캡처(트랙 B) | +| `wsai/backends/claude.py` | Claude 비전 + 두뇌 | +| `dave/gate.mjs` | DAVE/MLS E2EE 합류 관문 검증(통과 증거) | +| `poc/` | Xvfb+Chromium 캡처 실험(트랙 B 참고) | +| `PLAN.md` | 단계별 착수 계획(확정본) | +| `tests/test_pipeline.py` | 파이프라인 스모크 테스트 | + +--- + +## 8. 구현 순서 (검증 가능한 체크포인트) + +- [x] 파이프라인 스켈레톤 — mock 모드로 전 흐름 동작. +- [x] E2EE 합류 관문 — 셀프봇이 DAVE/MLS 보이스 그룹에 정식 멤버로 합류 확인(`dave/gate.mjs`). +- [ ] **M1 · 진짜 프레임 뽑기(눈).** 트랙 A로 Go Live 스트림 구독 → 비디오 RTP 수신 → 복호 → + ffmpeg 디코드 → 실제 공유화면 1프레임을 PNG로 저장. + · 검증: 실제로 공유 중인 화면이 이미지로 저장된다. (Go Live 켠 테스트 통화 필요) +- [ ] **M2 · 변화 감지 + 로컬 VLM.** 프레임 스트림에서 유사 프레임 버리고, 로컬 VLM이 설명 생성. + · 검증: 화면을 바꾸면 새 설명이 나오고, 가만히 두면 새 호출이 안 나간다. +- [ ] **M3 · 화면 Q&A(텍스트).** 텍스트 입력으로 "지금 화면 뭐야?"에 최신 맥락 기반 답변. + · 검증: 실제 화면 내용을 반영한 답이 나온다. +- [ ] **M4 · 음성 왕복.** STT→Brain→TTS 스트리밍 + barge-in. 지연 예산 측정. + · 검증: 말로 묻고 말로 답을 듣는다, 첫 소리까지 ~1.5s 근처. +- [ ] **M5 · 통합·운영.** 두 루프 동시 실행, proactive, 끊김 복구, 비용 모니터링, 서비스화. + +--- + +## 9. 결정이 필요한 것 (사용자 확인 대기) + +- M1을 트랙 A로 바로 갈지, 트랙 B(로컬 캡처)로 위층(M2~M4)을 먼저 완성한 뒤 눈만 교체할지. + (트랙 B 선행이 리스크가 낮고 개발이 빠름 — 눈이 막혀도 나머지가 진행됨) +- 로컬 VLM 모델 선택(moondream2 vs Qwen2-VL-2B) — VRAM 8GB 내에서 STT/TTS와 공존 가능한지. +- Claude 예산·모델(Haiku 고정 vs 난이도별 상향). From 18dce47c1cf37947ed46752935c031f87b96a534 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 11 Aug 2026 00:07:58 +0900 Subject: [PATCH 4/7] docs(readme): refocus on voice loop first, defer screen share --- README.md | 186 +++++++++++++++++++++++++++--------------------------- 1 file changed, 92 insertions(+), 94 deletions(-) diff --git a/README.md b/README.md index 59ddb24..f28ea20 100644 --- a/README.md +++ b/README.md @@ -1,96 +1,67 @@ # watch_sceen_ai -디스코드 화면공유를 **AI가 실시간으로 보면서, 사용자와 음성으로 대화하는** 봇. - -사용자가 디스코드에서 화면공유(Go Live)를 켜면 AI가 그 화면을 실시간으로 이해하고, -사용자가 음성으로 물으면 최대한 빠르게 답한다. 화면이 크게 바뀌면 먼저 말을 걸 수도 있다(proactive). +디스코드에서 **음성으로 대화하는 AI**. 최종 목표는 화면공유(Go Live)까지 실시간으로 +보면서 대화하는 것이지만, **지금은 화면공유(눈)를 잠시 미뤄두고 음성 대화 루프 +STT → 두뇌 → TTS부터 완성**하는 단계다. - 실행 위치: 이 리눅스 호스트(.9, RTX 5050 8GB / ffmpeg / node v22 있음) -- 현재 상태: 1단계(눈: 화면공유 수신) 진행 중 — E2EE 합류 관문 통과, 실제 프레임 추출 PoC가 다음 목표 +- 현재 상태: 음성 루프 뼈대 동작 — 파이프라인이 **눈 없이(화면공유 없이)** 돌아간다 + (`python -m wsai --voice`). STT/TTS/두뇌는 아직 mock이며, 실제 엔진 연결이 다음 목표. +- 보류 중: 화면공유 수신(눈). E2EE 합류 관문은 이미 통과해 두었고(아래 8장), 음성 루프가 + 자리 잡으면 다시 붙인다. --- -## 1. 설계 원칙 (이 프로젝트를 관통하는 3가지) +## 1. 설계 원칙 1. **지연이 전부다.** 음성 대화형이라 "정확하지만 느린" 답은 실패다. 전 구간을 스트리밍으로 잇고, 목표는 사용자가 말 끝낸 뒤 **첫 소리까지 ~1.5초 이내**. -2. **싼 것부터, 필요할 때만 비싸게.** 매 프레임을 클라우드로 보내면 비용·지연이 폭발한다. - 로컬에서 화면 변화 감지 + 1차 이해를 하고, 정말 어려운 프레임만 클라우드로 올린다. -3. **부품은 교체 가능해야 한다.** 눈/귀/두뇌/입이 전부 `wsai/interfaces.py`의 Protocol이라, - 어느 하나(예: 화면 수신 방식)가 막히거나 바뀌어도 나머지는 그대로 돌아간다. +2. **싼 것부터, 필요할 때만 비싸게.** 로컬(GPU)에서 처리할 수 있는 건 로컬에서, + 정말 필요한 것만 클라우드로 올린다. +3. **부품은 교체 가능해야 한다.** 귀/두뇌/입(그리고 나중의 눈)이 전부 + `wsai/interfaces.py`의 Protocol이라, 어느 하나가 바뀌어도 나머지는 그대로 돌아간다. + 실제로 눈(source/vision)은 선택 사항이라, 없으면 파이프라인이 음성 루프만 돌린다. --- ## 2. 전체 구조 -두 개의 async 루프가 동시에 돈다. 둘을 잇는 건 "지금 화면이 뭔지" 하나를 담는 공유 상태다. +두 개의 async 루프가 있고, 지금은 **대화 루프만** 켜서 쓴다. 인지(눈) 루프는 나중에 붙인다. ``` -[인지 루프] FrameSource ──Frame──▶ 변화감지 ──▶ VisionBackend ──ScreenObservation──▶┐ - ▼ - [SharedScreenContext] - │ (최신 1건) -[대화 루프] SpeechToText ──Utterance──▶ Brain(LLM) ◀───────────────────┘ - / TextChannel │ (+대화이력) - ▼ - TextToSpeech / TextChannel +[대화 루프] SpeechToText ──Utterance──▶ Brain(LLM) ──▶ TextToSpeech + (귀) (두뇌) (입) + ▲ + │ (화면 맥락: 지금은 비어 있음) +[인지 루프] FrameSource ─▶ VisionBackend ─┘ ← 보류 (source/vision = None) + (눈) ``` -- 인지 루프: 프레임 수신 → (이전과 유사하면 버림) → 비전 이해 → 최신 화면 맥락 갱신. -- 대화 루프: 사용자 발화 → 두뇌(최신 화면 맥락 + 대화이력) → 음성/텍스트 응답. -- 두 루프는 비동기로 독립. 대화 루프는 항상 "가장 최근에 이해된 화면"을 읽을 뿐, - 프레임 하나하나를 기다리지 않는다. +- 대화 루프: 사용자 발화 → 두뇌(대화이력 + 있으면 최신 화면 맥락) → 음성 응답. +- 인지 루프: `source`/`vision`이 없으면 통째로 건너뛴다. 눈을 붙이면 최신 화면 맥락을 + 채워 넣어, 두뇌가 "지금 화면"을 참고할 수 있게 된다. +- 두 루프는 비동기로 독립. 눈이 생겨도 대화 루프는 프레임 하나하나를 기다리지 않고 + "가장 최근에 이해된 화면"만 읽는다. -데이터 타입/인터페이스는 `wsai/interfaces.py`에 이미 정의돼 있다: +데이터 타입/인터페이스는 `wsai/interfaces.py`에 정의돼 있다: `Frame`, `ScreenObservation`, `Utterance`, `Reply` / `FrameSource`, `VisionBackend`, `SpeechToText`, `TextToSpeech`, `Brain`, `TextChannel`. --- -## 3. 눈 — 화면공유를 어떻게 받아오나 +## 3. 귀 / 두뇌 / 입 — 지금 만드는 음성 루프 -이게 이 프로젝트의 가장 어려운 부분이다. 두 트랙을 **병행**한다. +### 귀 (STT) +- `faster-whisper` 스트리밍/부분 전사. 마이크는 `sounddevice`. +- 발화 종료 판정(endpointing)으로 사용자가 말을 멈춘 시점을 빠르게 잡는다. -### 트랙 A (주력) — 화면공유 스트림 직접 수신 -스크린샷·브라우저 캡처가 아니라, 실제 화면공유 스트림 자체를 클라이언트처럼 수신한다. +### 두뇌 (Brain) +- Claude(OAuth, Haiku 계열)로 대화 응답 생성. 저비용·빠른 모델을 기본으로 한다. +- 입력은 "대화이력 + (있으면) 최신 화면 맥락 1건"만 넣어 토큰을 작게 유지한다. +- 눈이 없는 지금은 화면 맥락이 비어 있고, 두뇌는 그 사실을 솔직히 말한다. -- 공식 디스코드 봇 API는 비디오 수신을 지원하지 않으므로, **유저 토큰(셀프봇)** 으로 - 프로토콜 레벨에서 비디오 RTP를 받는다. 전용 버너 계정만 사용한다. -- 파이프: 셀프봇 음성채널 join → 상대 Go Live 스트림 "watch" 구독(비디오 SSRC 획득) - → 암호화 비디오 RTP 수신 → **DAVE(MLS E2EE) 복호** → 전송암호(aead_aes256_gcm_rtpsize) 복호 - → VP8/H264 depayload → ffmpeg 디코드 → 프레임. -- 라이브러리: `discord.js-selfbot` 계열(werift-rtp), DAVE는 `@snazzah/davey`(Rust NAPI). -- 이 트랙은 Node로 프레임까지 뽑아 로컬 소켓/파이프로 파이썬 파이프라인에 넘긴다 - (`FrameSource` 구현 하나로 감싼다). - -솔직한 리스크: (1) 완성형 "수신→프레임" 라이브러리가 없어 직접 조립해야 한다. -(2) 셀프봇은 디스코드 ToS 회색지대라 계정 밴 위험이 있다(→ 버너 전용). -(3) 셀프봇/프로토콜이 바뀌면 깨질 수 있다. - -### 트랙 B (폴백/개발용) — 로컬 렌더 화면 캡처 -디스코드 클라이언트가 화면공유를 "보고 있는" 상태의 화면을 로컬에서 캡처한다. -트랙 A가 막히거나 불안정할 때의 안전판이자, 위쪽(비전/대화) 개발을 트랙 A 완성 전에 -진행하기 위한 실용 소스다. `poc/`의 Xvfb 실험과 `wsai/backends/capture_mss.py`가 여기 해당. - -두 트랙 모두 결과물은 동일한 `Frame` 스트림이라, 눈을 바꿔도 나머지는 그대로다. - ---- - -## 4. 두뇌 — 하이브리드 비전 + LLM - -- 로컬 소형 VLM(예: moondream2 / Qwen2-VL-2B)이 RTX 5050에서 매 프레임 1차 이해. -- **변화 감지**로 이전과 거의 같은 화면은 새로 설명하지 않는다(지연·비용 절감). -- 화면이 크게 바뀌었거나, 로컬 VLM 신뢰도가 낮거나, 사용자가 화면에 대해 구체적으로 - 물으면 → Claude(OAuth, Haiku 계열)로 에스컬레이션. -- 대화 응답 생성(Brain)은 Claude가 담당하되, 항상 "최신 화면 맥락 1건 + 대화이력"만 넣어 - 토큰을 작게 유지한다. - ---- - -## 5. 귀 / 입 — 음성 - -- 귀(STT): `faster-whisper` 스트리밍/부분 전사. 마이크는 `sounddevice`. -- 입(TTS): MeloTTS 등(이 호스트에 기존 자산 있음). 문장 단위로 스트리밍 재생. +### 입 (TTS) +- MeloTTS 등(이 호스트에 기존 자산 있음). 문장 단위로 스트리밍 재생. - 인터럽트: 사용자가 말하기 시작하면 재생 중인 TTS를 즉시 멈춘다(barge-in). ### 지연 예산(목표) @@ -99,68 +70,95 @@ | 구간 | 목표 | |------|------| | STT 종료 판정(endpointing) | ~200 ms | -| Brain 첫 토큰(화면 맥락은 이미 준비됨) | ~500 ms | +| Brain 첫 토큰 | ~500 ms | | TTS 첫 문장 합성·재생 시작 | ~400 ms | | 여유 | ~400 ms | | **합계** | **~1.5 s** | -화면 이해는 대화 요청과 분리된 인지 루프에서 미리 끝나 있으므로, 응답 시점에 -비전 지연이 끼어들지 않는 게 핵심이다. +--- + +## 4. 눈 — 화면공유 수신 (보류, 나중에) + +최종 목표엔 화면공유가 들어가지만, 지금은 **의도적으로 미뤄둔** 부분이다. 아래는 +나중에 다시 붙일 때를 위한 기록이다. 두 트랙을 병행할 계획이었다. + +### 트랙 A (주력) — 화면공유 스트림 직접 수신 +스크린샷·브라우저 캡처가 아니라, 실제 화면공유 스트림 자체를 클라이언트처럼 수신한다. +공식 봇 API는 비디오 수신을 막으므로 **유저 토큰(셀프봇)** 으로 프로토콜 레벨에서 +비디오 RTP를 받는다(전용 버너 계정만 사용). 파이프: join → Go Live "watch" 구독 → +암호화 RTP 수신 → **DAVE(MLS E2EE) 복호** → 전송암호 복호 → VP8/H264 depayload → +ffmpeg 디코드 → 프레임. DAVE는 `@snazzah/davey`(Rust NAPI), 수신은 `discord.js-selfbot` 계열. + +솔직한 리스크: 완성형 라이브러리가 없어 직접 조립해야 하고, 셀프봇은 ToS 회색지대라 +계정 밴 위험이 있다(→ 버너 전용). 그래서 음성 루프를 먼저 세우는 지금 순서가 안전하다. + +### 트랙 B (폴백/개발용) — 로컬 렌더 화면 캡처 +디스코드 클라이언트가 화면공유를 보고 있는 화면을 로컬에서 캡처한다. 트랙 A가 막힐 때의 +안전판이자 개발용 실용 소스. `poc/`의 Xvfb 실험과 `wsai/backends/capture_mss.py`가 여기 해당. + +두 트랙 모두 결과물은 동일한 `Frame` 스트림이라, 눈을 붙일 때 나머지는 손대지 않는다. --- -## 6. 실행 +## 5. 실행 이 호스트에는 `python` 별칭이 없으므로 워크스페이스 venv를 쓴다(`.venv/bin/python`). ```bash -.venv/bin/python -m wsai # mock 데모 (의존성·키 불필요, 몇 프레임 돌고 종료) +.venv/bin/python -m wsai --voice # 눈 없는 음성 루프 데모 (STT→두뇌→TTS, 지금 초점) +.venv/bin/python -m wsai # mock 데모 (눈 포함 전체 흐름, 몇 프레임 돌고 종료) .venv/bin/python -m wsai --env # WSAI_* 환경변수로 백엔드 조립 .venv/bin/python -m pip install pytest && .venv/bin/python -m pytest -q # 스모크 테스트 ``` -백엔드별 추가 설치는 `requirements.txt` 주석 참고(mss/pillow, anthropic, faster-whisper 등). +- `WSAI_SOURCE=none WSAI_VISION=none` 으로도 눈 없이(음성 루프만) 조립할 수 있다. +- 백엔드별 추가 설치는 `requirements.txt` 주석 참고(faster-whisper, mss/pillow, anthropic 등). --- -## 7. 파일 / 디렉터리 +## 6. 파일 / 디렉터리 | 경로 | 역할 | |------|------| | `wsai/interfaces.py` | 데이터 타입 + 컴포넌트 Protocol | -| `wsai/pipeline.py` | 오케스트레이터(인지/대화 루프) | -| `wsai/state.py` | 최신 화면 맥락 공유 저장소 | -| `wsai/config.py` / `factory.py` | 설정 → 백엔드 조립 | +| `wsai/pipeline.py` | 오케스트레이터(대화 루프 + 선택적 인지 루프) | +| `wsai/state.py` | 최신 화면 맥락 공유 저장소(눈이 생기면 사용) | +| `wsai/config.py` / `factory.py` | 설정 → 백엔드 조립. `voice()` = 눈 없는 음성 프리셋 | | `wsai/backends/mock.py` | 무의존성 mock 전 계열 | -| `wsai/backends/capture_mss.py` | 로컬 화면 캡처(트랙 B) | +| `wsai/backends/capture_mss.py` | 로컬 화면 캡처(트랙 B, 보류) | | `wsai/backends/claude.py` | Claude 비전 + 두뇌 | -| `dave/gate.mjs` | DAVE/MLS E2EE 합류 관문 검증(통과 증거) | +| `dave/gate.mjs` | DAVE/MLS E2EE 합류 관문 검증(통과 증거, 눈 재개 시 재사용) | | `poc/` | Xvfb+Chromium 캡처 실험(트랙 B 참고) | -| `PLAN.md` | 단계별 착수 계획(확정본) | +| `PLAN.md` | 단계별 착수 계획 | | `tests/test_pipeline.py` | 파이프라인 스모크 테스트 | --- -## 8. 구현 순서 (검증 가능한 체크포인트) +## 7. 구현 순서 (검증 가능한 체크포인트) + +음성 루프 먼저, 눈은 나중에. - [x] 파이프라인 스켈레톤 — mock 모드로 전 흐름 동작. -- [x] E2EE 합류 관문 — 셀프봇이 DAVE/MLS 보이스 그룹에 정식 멤버로 합류 확인(`dave/gate.mjs`). -- [ ] **M1 · 진짜 프레임 뽑기(눈).** 트랙 A로 Go Live 스트림 구독 → 비디오 RTP 수신 → 복호 → - ffmpeg 디코드 → 실제 공유화면 1프레임을 PNG로 저장. - · 검증: 실제로 공유 중인 화면이 이미지로 저장된다. (Go Live 켠 테스트 통화 필요) -- [ ] **M2 · 변화 감지 + 로컬 VLM.** 프레임 스트림에서 유사 프레임 버리고, 로컬 VLM이 설명 생성. - · 검증: 화면을 바꾸면 새 설명이 나오고, 가만히 두면 새 호출이 안 나간다. -- [ ] **M3 · 화면 Q&A(텍스트).** 텍스트 입력으로 "지금 화면 뭐야?"에 최신 맥락 기반 답변. - · 검증: 실제 화면 내용을 반영한 답이 나온다. -- [ ] **M4 · 음성 왕복.** STT→Brain→TTS 스트리밍 + barge-in. 지연 예산 측정. +- [x] **눈 없는 음성 루프.** `source`/`vision` 없이 STT→Brain→TTS만 돌아간다(`--voice`). + · 검증: `python -m wsai --voice`가 화면 없이 발화마다 응답을 낸다(테스트 포함). +- [ ] **V1 · 진짜 TTS(입).** MeloTTS로 두뇌 응답을 실제 음성으로 합성. + · 검증: 응답 텍스트가 .wav로 합성돼 들린다. +- [ ] **V2 · 진짜 STT(귀).** faster-whisper로 오디오를 텍스트로 전사. + · 검증: 오디오 파일/마이크 입력이 텍스트로 나온다. +- [ ] **V3 · 진짜 두뇌.** Claude OAuth(Haiku)로 대화 응답 생성. + · 검증: 실제 발화에 자연스러운 답이 나온다. +- [ ] **V4 · 음성 왕복 + barge-in.** STT→Brain→TTS 스트리밍, 말 끊기, 지연 예산 측정. · 검증: 말로 묻고 말로 답을 듣는다, 첫 소리까지 ~1.5s 근처. -- [ ] **M5 · 통합·운영.** 두 루프 동시 실행, proactive, 끊김 복구, 비용 모니터링, 서비스화. +- [ ] **(나중) 눈 재개.** 트랙 A/B로 화면공유 수신 → 화면 맥락을 두뇌에 연결. --- -## 9. 결정이 필요한 것 (사용자 확인 대기) +## 8. 결정이 필요한 것 (사용자 확인 대기) -- M1을 트랙 A로 바로 갈지, 트랙 B(로컬 캡처)로 위층(M2~M4)을 먼저 완성한 뒤 눈만 교체할지. - (트랙 B 선행이 리스크가 낮고 개발이 빠름 — 눈이 막혀도 나머지가 진행됨) -- 로컬 VLM 모델 선택(moondream2 vs Qwen2-VL-2B) — VRAM 8GB 내에서 STT/TTS와 공존 가능한지. -- Claude 예산·모델(Haiku 고정 vs 난이도별 상향). +실제 STT/TTS 엔진을 붙이기 전에 정해야 할 것들: + +- **GPU 사용 동의** — RTX 5050으로 STT/TTS를 돌리면 빠르다(규칙상 GPU는 먼저 확인 필요). +- **파이썬 버전** — 이 venv는 3.14인데 faster-whisper·MeloTTS는 3.11대가 안전하다. + 호스트에 이미 MeloTTS용 3.11 자산이 있어, 별도 3.11 venv + subprocess 연결을 제안. +- **엔진 선택** — STT는 faster-whisper, TTS는 MeloTTS(한국어) 기본안. +- **두뇌** — Claude OAuth(Haiku 계열)로 연결(지금 코드는 API키 기반이라 OAuth 전환 필요). From 9a6484d47aa87937b38d624cf9c9649be49e1489 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 11 Aug 2026 17:52:09 +0900 Subject: [PATCH 5/7] docs(readme): tighten latency budget to <=1s, STT via Discord voice, Korean human-like TTS --- README.md | 57 ++++++++++++++++++++++++++++++++++++------------------- 1 file changed, 38 insertions(+), 19 deletions(-) diff --git a/README.md b/README.md index f28ea20..7e64e2d 100644 --- a/README.md +++ b/README.md @@ -7,15 +7,16 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. - 실행 위치: 이 리눅스 호스트(.9, RTX 5050 8GB / ffmpeg / node v22 있음) - 현재 상태: 음성 루프 뼈대 동작 — 파이프라인이 **눈 없이(화면공유 없이)** 돌아간다 (`python -m wsai --voice`). STT/TTS/두뇌는 아직 mock이며, 실제 엔진 연결이 다음 목표. -- 보류 중: 화면공유 수신(눈). E2EE 합류 관문은 이미 통과해 두었고(아래 8장), 음성 루프가 - 자리 잡으면 다시 붙인다. +- 보류 중: 화면공유 **비디오** 수신(눈). 단, STT 입력은 **디스코드 보이스로 유저 음성을 + 수신**하므로 셀프봇의 보이스 접속 자체는 지금도 쓴다(비디오만 미룸). E2EE 합류 관문은 + 이미 통과해 두었다(아래 8장). --- ## 1. 설계 원칙 1. **지연이 전부다.** 음성 대화형이라 "정확하지만 느린" 답은 실패다. 전 구간을 - 스트리밍으로 잇고, 목표는 사용자가 말 끝낸 뒤 **첫 소리까지 ~1.5초 이내**. + 스트리밍으로 잇고, 목표는 사용자가 말 끝낸 뒤 **첫 소리까지 1초 이내**. 2. **싼 것부터, 필요할 때만 비싸게.** 로컬(GPU)에서 처리할 수 있는 건 로컬에서, 정말 필요한 것만 클라우드로 올린다. 3. **부품은 교체 가능해야 한다.** 귀/두뇌/입(그리고 나중의 눈)이 전부 @@ -51,29 +52,43 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. ## 3. 귀 / 두뇌 / 입 — 지금 만드는 음성 루프 -### 귀 (STT) -- `faster-whisper` 스트리밍/부분 전사. 마이크는 `sounddevice`. -- 발화 종료 판정(endpointing)으로 사용자가 말을 멈춘 시점을 빠르게 잡는다. +### 귀 (STT) — 디스코드 보이스 수신 +- 로컬 마이크가 아니라 **디스코드 보이스에서 유저가 말하는 음성을 수신**한다. 셀프봇이 + 보이스 채널에 접속해(이미 통과한 DAVE/MLS E2EE 경로) 유저의 Opus 오디오 RTP를 받고, + 복호 → Opus 디코드 → PCM으로 만들어 STT에 흘려보낸다. +- `faster-whisper`로 실시간 부분 전사를 계속 돌리고, VAD로 발화 종료(endpointing)를 잡는다. + 종료 판정 시점엔 전사가 거의 끝나 있게 해, 판정 후 추가 지연을 최소화한다. +- 디스코드 수신 특성상 지속 스트림 + 지터버퍼(수십 ms)가 붙는다 — 아래 지연 예산에 반영. ### 두뇌 (Brain) - Claude(OAuth, Haiku 계열)로 대화 응답 생성. 저비용·빠른 모델을 기본으로 한다. - 입력은 "대화이력 + (있으면) 최신 화면 맥락 1건"만 넣어 토큰을 작게 유지한다. - 눈이 없는 지금은 화면 맥락이 비어 있고, 두뇌는 그 사실을 솔직히 말한다. -### 입 (TTS) -- MeloTTS 등(이 호스트에 기존 자산 있음). 문장 단위로 스트리밍 재생. +### 입 (TTS) — 한국어 고정, 사람처럼 +- **한국어 전용**으로, 최대한 자연스러운(사람 같은) 음성을 목표로 한다. 문장 단위 스트리밍 재생. - 인터럽트: 사용자가 말하기 시작하면 재생 중인 TTS를 즉시 멈춘다(barge-in). +- 트레이드오프: 가장 사람 같은 합성(예: XTTS 계열)은 느려서 1초 예산과 충돌한다. 그래서 + 빠른 한국어 엔진(MeloTTS, 이 호스트에 자산 있음)으로 첫 문장 지연부터 확보하고, + 자연스러움이 부족하면 GPU + 문장 스트리밍으로 더 무거운 엔진을 검토한다(품질 vs 지연). -### 지연 예산(목표) -사용자 발화 종료 → 첫 소리까지: +### 지연 예산 (목표 ≤ 1초) +유저가 디스코드 보이스에서 말을 멈춘 순간 → 첫 소리까지: | 구간 | 목표 | |------|------| -| STT 종료 판정(endpointing) | ~200 ms | -| Brain 첫 토큰 | ~500 ms | -| TTS 첫 문장 합성·재생 시작 | ~400 ms | -| 여유 | ~400 ms | -| **합계** | **~1.5 s** | +| 디스코드 보이스 수신 + Opus 디코드(지속 스트림·지터버퍼) | ~80 ms | +| STT 종료 판정(VAD) + 마지막 청크 전사 | ~250 ms | +| Brain 첫 토큰(Claude Haiku 스트리밍) | ~350 ms | +| TTS 첫 문장 합성·재생 시작(한국어) | ~250 ms | +| 여유 | ~70 ms | +| **합계** | **~1.0 s** | + +- Brain·TTS는 문장 단위 스트리밍이라, 응답 전체 완성이 아니라 "첫 문장"만 준비되면 소리가 난다. +- 가장 빡빡한 구간은 **Brain 첫 토큰(네트워크 TTFT)**이다. Haiku로도 300ms대가 목표라, + 안 되면 로컬 소형 LLM 선응답이나 프롬프트 워밍 등을 검토한다. +- 나중에 눈(화면 이해)을 붙여도, 화면 이해는 별도 인지 루프에서 미리 끝나 있어 응답 시점에 + 비전 지연이 끼어들지 않는다. --- @@ -144,11 +159,11 @@ ffmpeg 디코드 → 프레임. DAVE는 `@snazzah/davey`(Rust NAPI), 수신은 ` - [ ] **V1 · 진짜 TTS(입).** MeloTTS로 두뇌 응답을 실제 음성으로 합성. · 검증: 응답 텍스트가 .wav로 합성돼 들린다. - [ ] **V2 · 진짜 STT(귀).** faster-whisper로 오디오를 텍스트로 전사. - · 검증: 오디오 파일/마이크 입력이 텍스트로 나온다. + · 검증: 오디오 파일 입력이 텍스트로 나온다(이후 디스코드 보이스 수신 오디오로 연결). - [ ] **V3 · 진짜 두뇌.** Claude OAuth(Haiku)로 대화 응답 생성. · 검증: 실제 발화에 자연스러운 답이 나온다. -- [ ] **V4 · 음성 왕복 + barge-in.** STT→Brain→TTS 스트리밍, 말 끊기, 지연 예산 측정. - · 검증: 말로 묻고 말로 답을 듣는다, 첫 소리까지 ~1.5s 근처. +- [ ] **V4 · 음성 왕복 + barge-in.** 디스코드 보이스 수신→STT→Brain→TTS 스트리밍, 말 끊기, 지연 측정. + · 검증: 디스코드에서 말로 묻고 말로 답을 듣는다, 첫 소리까지 **1초 이내**. - [ ] **(나중) 눈 재개.** 트랙 A/B로 화면공유 수신 → 화면 맥락을 두뇌에 연결. --- @@ -160,5 +175,9 @@ ffmpeg 디코드 → 프레임. DAVE는 `@snazzah/davey`(Rust NAPI), 수신은 ` - **GPU 사용 동의** — RTX 5050으로 STT/TTS를 돌리면 빠르다(규칙상 GPU는 먼저 확인 필요). - **파이썬 버전** — 이 venv는 3.14인데 faster-whisper·MeloTTS는 3.11대가 안전하다. 호스트에 이미 MeloTTS용 3.11 자산이 있어, 별도 3.11 venv + subprocess 연결을 제안. -- **엔진 선택** — STT는 faster-whisper, TTS는 MeloTTS(한국어) 기본안. +- **엔진 선택** — STT는 faster-whisper, TTS는 한국어 고정. 자연스러움 최우선이면 무거운 + 엔진(XTTS 계열)이 필요하지만 1초 예산과 상충 → 우선 빠른 MeloTTS로 시작할지, 처음부터 + 더 사람 같은 엔진을 GPU로 감수할지 결정 필요. - **두뇌** — Claude OAuth(Haiku 계열)로 연결(지금 코드는 API키 기반이라 OAuth 전환 필요). +- **STT 입력 경로** — 디스코드 보이스 수신(셀프봇)으로 확정. 유저 음성 Opus RTP를 받아 + 복호·디코드해 STT에 넣는다(로컬 마이크 아님). From 35b65d0d454b49ca76e0a1067e547afbbd8b51d3 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 11 Aug 2026 20:35:30 +0900 Subject: [PATCH 6/7] docs(readme): hold Brain at 350ms, trim STT/TTS budget (total ~0.85s) --- README.md | 25 ++++++++++++++++--------- 1 file changed, 16 insertions(+), 9 deletions(-) diff --git a/README.md b/README.md index 7e64e2d..1210771 100644 --- a/README.md +++ b/README.md @@ -57,7 +57,8 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. 보이스 채널에 접속해(이미 통과한 DAVE/MLS E2EE 경로) 유저의 Opus 오디오 RTP를 받고, 복호 → Opus 디코드 → PCM으로 만들어 STT에 흘려보낸다. - `faster-whisper`로 실시간 부분 전사를 계속 돌리고, VAD로 발화 종료(endpointing)를 잡는다. - 종료 판정 시점엔 전사가 거의 끝나 있게 해, 판정 후 추가 지연을 최소화한다. + 종료 판정 시점엔 전사가 사실상 끝나 있어, 판정 후 남는 건 VAD hangover + 짧은 마지막 + 디코드뿐이다. 목표 종료→텍스트 확정 ~150ms. - 디스코드 수신 특성상 지속 스트림 + 지터버퍼(수십 ms)가 붙는다 — 아래 지연 예산에 반영. ### 두뇌 (Brain) @@ -66,7 +67,9 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. - 눈이 없는 지금은 화면 맥락이 비어 있고, 두뇌는 그 사실을 솔직히 말한다. ### 입 (TTS) — 한국어 고정, 사람처럼 -- **한국어 전용**으로, 최대한 자연스러운(사람 같은) 음성을 목표로 한다. 문장 단위 스트리밍 재생. +- **한국어 전용**으로, 최대한 자연스러운(사람 같은) 음성을 목표로 한다. +- 지연을 줄이려고 첫 문장 전체를 기다리지 않고 **첫 짧은 구절부터 청크 스트리밍으로 재생**한다 + (나머지는 재생되는 동안 이어서 합성). 목표 첫 소리 ~170ms. - 인터럽트: 사용자가 말하기 시작하면 재생 중인 TTS를 즉시 멈춘다(barge-in). - 트레이드오프: 가장 사람 같은 합성(예: XTTS 계열)은 느려서 1초 예산과 충돌한다. 그래서 빠른 한국어 엔진(MeloTTS, 이 호스트에 자산 있음)으로 첫 문장 지연부터 확보하고, @@ -78,15 +81,19 @@ STT → 두뇌 → TTS부터 완성**하는 단계다. | 구간 | 목표 | |------|------| | 디스코드 보이스 수신 + Opus 디코드(지속 스트림·지터버퍼) | ~80 ms | -| STT 종료 판정(VAD) + 마지막 청크 전사 | ~250 ms | +| STT 종료 판정(VAD) + 마지막 청크 전사 | ~150 ms | | Brain 첫 토큰(Claude Haiku 스트리밍) | ~350 ms | -| TTS 첫 문장 합성·재생 시작(한국어) | ~250 ms | -| 여유 | ~70 ms | -| **합계** | **~1.0 s** | +| TTS 첫 문장 합성·재생 시작(한국어) | ~170 ms | +| 여유 | ~100 ms | +| **합계** | **~0.85 s** | -- Brain·TTS는 문장 단위 스트리밍이라, 응답 전체 완성이 아니라 "첫 문장"만 준비되면 소리가 난다. -- 가장 빡빡한 구간은 **Brain 첫 토큰(네트워크 TTFT)**이다. Haiku로도 300ms대가 목표라, - 안 되면 로컬 소형 LLM 선응답이나 프롬프트 워밍 등을 검토한다. +- **Brain(머리)은 네트워크 TTFT라 우리가 못 줄인다 → 350ms로 고정.** 대신 우리 GPU에서 + 도는 STT·TTS를 깎아 여유를 벌었다. 두 구간을 어떻게 줄이는지는 아래. + - STT ~150ms: 발화 중 부분전사를 계속 돌려 종료 판정 시점엔 전사가 사실상 끝나 있게 한다. + 남는 건 VAD hangover(~120ms) + 마지막 짧은 디코드(~30ms)뿐. + - TTS ~170ms: 첫 문장 전체가 아니라 **첫 짧은 구절만 먼저** 합성해 청크 스트리밍으로 + 바로 재생을 시작한다(나머지는 재생되는 동안 이어서 합성). +- Brain·TTS는 스트리밍이라, 응답 전체 완성이 아니라 "첫 소리"만 준비되면 재생된다. - 나중에 눈(화면 이해)을 붙여도, 화면 이해는 별도 인지 루프에서 미리 끝나 있어 응답 시점에 비전 지연이 끼어들지 않는다. From 8b9a78ae1456b0fb4f870a2fe132ff5cae7d7651 Mon Sep 17 00:00:00 2001 From: claude-bot Date: Tue, 11 Aug 2026 20:51:35 +0900 Subject: [PATCH 7/7] docs(readme): record confirmed decisions + M1..M6 deployment milestones --- README.md | 31 ++++++++++++++++++++----------- 1 file changed, 20 insertions(+), 11 deletions(-) diff --git a/README.md b/README.md index 1210771..f807250 100644 --- a/README.md +++ b/README.md @@ -175,16 +175,25 @@ ffmpeg 디코드 → 프레임. DAVE는 `@snazzah/davey`(Rust NAPI), 수신은 ` --- -## 8. 결정이 필요한 것 (사용자 확인 대기) +## 8. 확정된 결정 (2026-08-11) + 배포 목표 -실제 STT/TTS 엔진을 붙이기 전에 정해야 할 것들: +- **GPU 사용** — OK. RTX 5050(.9)으로 STT/TTS를 돌린다. .9 로컬 도커는 NVIDIA CDI로 GPU 사용 가능. +- **파이썬 버전** — STT/TTS는 3.11(faster-whisper·TTS 자산 호환), 오케스트레이터는 기존 코드 유지. +- **TTS 엔진** — 최대한 사람 같은 음성으로 하되, **전체 지연 1초 초과 금지**가 상한. 후보(XTTS 등)의 + 첫 소리 지연을 GPU에서 실측해, 1초 예산에 맞는 가장 자연스러운 엔진을 채택한다(측정 기반 선택). +- **두뇌** — Claude **OAuth**, 이 호스트에 연결된 것과 **동일 크리덴셜 공유** + (`/home/claude/EJClaw/data/claude/.credentials.json`). API키 아님. +- **STT 입력 경로** — 디스코드 보이스 수신(셀프봇). 유저 음성 Opus RTP를 받아 DAVE 복호·디코드해 STT로. -- **GPU 사용 동의** — RTX 5050으로 STT/TTS를 돌리면 빠르다(규칙상 GPU는 먼저 확인 필요). -- **파이썬 버전** — 이 venv는 3.14인데 faster-whisper·MeloTTS는 3.11대가 안전하다. - 호스트에 이미 MeloTTS용 3.11 자산이 있어, 별도 3.11 venv + subprocess 연결을 제안. -- **엔진 선택** — STT는 faster-whisper, TTS는 한국어 고정. 자연스러움 최우선이면 무거운 - 엔진(XTTS 계열)이 필요하지만 1초 예산과 상충 → 우선 빠른 MeloTTS로 시작할지, 처음부터 - 더 사람 같은 엔진을 GPU로 감수할지 결정 필요. -- **두뇌** — Claude OAuth(Haiku 계열)로 연결(지금 코드는 API키 기반이라 OAuth 전환 필요). -- **STT 입력 경로** — 디스코드 보이스 수신(셀프봇)으로 확정. 유저 음성 Opus RTP를 받아 - 복호·디코드해 STT에 넣는다(로컬 마이크 아님). +### 배포/테스트 목표 +- 대상: 디스코드 서버 `1352269198297923648`의 보이스 채널 `1352269198914621465`. +- 유저봇(셀프봇) 토큰: `.env`의 `DISCORD_SELFBOT_TOKEN`(버너 계정, 밴 리스크 수용). +- .9 로컬 GPU 도커 이미지로 올려, 그 채널에 접속한 뒤 사람이 말하면 대화하도록 한다. +- 첫 로딩 워밍업: 시작 시 모델 프리로드 + 더미 추론(CUDA 워밍) + 보이스 미리 접속 → 첫 대화도 지연 최소. + +### 구현 마일스톤 +- **M1** 셀프봇이 대상 보이스 채널에 상주 접속(DAVE 통과) + 발화자(SSRC) 감지 ← 지금 +- **M2** 유저 음성 Opus RTP 수신 → DAVE 복호 → PCM (라이브 발화자 필요, 최고 난이도) +- **M3** faster-whisper STT(부분전사+VAD) → **M4** Claude OAuth(Haiku) 두뇌 +- **M5** 한국어 TTS 첫 구절 청크를 보이스로 송신(DAVE 암호화) + barge-in +- **M6** 통합 + 워밍업 + .9 GPU 도커 이미지화, 채널 라이브 테스트