# ADR-0003: On-device LLM 도입 — Gemma 4 E2B Q4_0 + `flutter_gemma` > **상태**: Accepted > **날짜**: 2026-06-12 · **결정자**: [AI] Architect (사용자 Planner 채택안 수용) · **관련 이슈**: #215 > **관련 ADR**: 없음 (신규 결정) ## 맥락 (Context) life-helper Phase 1 MVP(#204) 위에 사용자 입력 자동화 기능을 얹는다. 입력 마찰점은 1) L0/L1 raw text → L2/L3 프레임 재작성, 2) 자유서술 → 구조화 변환이다. 기존 `FramePattern` 30개 정적 카탈로그는 keyword exact-match 기반이라 사용자 입력 다양성(synonym, 한국어 구어체, 도메인 변형)을 흡수하지 못한다. 선택지는: - **A. 클라우드 LLM API** (OpenAI / Claude / Gemini) — 품질 ↑, 비용·프라이버시·오프라인 X. - **B. On-device LLM** (Gemma 4 / Llama / Phi 등) — 프라이버시·오프라인 ↑, 품질·기기 부담. - **C. 도입 X, 정적 카탈로그 확장** — 안전하지만 동적 입력 흡수 한계. Planner(#215)가 5개 의사결정으로 **B 채택안 + Gemma 4 E2B + 런타임 다운로드 + opt-in** 방향을 확정했다. 본 ADR은 그 선택을 되돌리기 어려운 기술 결정으로 고정한다. ## 결정 (Decision) life-helper에 **on-device LLM**을 도입한다. 모델은 **Gemma 4 E2B (Q4_0 양자화)** 단일. 런타임 통합은 **`flutter_gemma`** 패키지(LiteRT-LM Android backend) 사용. 모델 파일은 **opt-in 토글 후 런타임에 백그라운드 다운로드**, opt-out 시 즉시 삭제. 모든 추론은 **on-device 전용**이며 어떠한 형태의 클라우드 fallback도 두지 않는다. 함수 호출(function calling) 기능을 사용해 LLM 응답을 **구조화된 JSON**으로 받는다. 프롬프트는 정적 하드코딩이 아닌 **SoT 카탈로그(`FramePattern` / `Protocol`)에서 런타임에 few-shot 예시를 동적 추출**해 조립한다. ## 근거 (Rationale) - **프라이버시 = SoT 원칙과 일치**: life-helper는 개인 습관·중독·실패 기록을 다룬다. 사용자 자유서술이 외부로 떠나면 안 된다. on-device 전용은 비타협 요구. - **오프라인 동작**: 사용자는 새벽·이동 중·산행 등 네트워크 약한 환경에서도 체크인한다. 클라우드 의존은 R8(≤ 60초 체크인) 보장 불가. - **Gemma 4 E2B Q4_0 ≈ 1.5GB RAM**: 6GB RAM mid-range Android 까지 커버. E4B(5GB)는 high-end 한정. v1은 E2B 단일로 시작해 hardware fragmentation 단순화. - **`flutter_gemma`의 function calling**: LLM 응답을 자유 텍스트가 아닌 구조화 JSON으로 강제 → 파싱 신뢰도 ↑, 도메인 모델(`FrameCandidate`) 직결. - **SoT few-shot 동적 추출**: 정적 prompt 하드코딩은 SoT(`huberman-protocols.md` / `FramePattern` 카탈로그) 변경 시 동기화 부담 발생. 런타임 조립은 SoT 1회 갱신으로 전파. - **런타임 다운로드**: APK +1.5GB는 첫 진입 장벽 과대. Play Store 제한(150MB)도 위반. WiFi 권장 + 일시정지/재개 + 진행률 UX로 흡수. - **Opt-in 기본 OFF**: AI 기능을 "추가 비용(스토리지·배터리)을 감내한 사용자만"이 켠다는 명시적 동의로, R6 사용자 통제권 원칙과 일치. ## 결과 (Consequences) - **긍정**: - 클라우드 비용·레이트리밋·약관 변경 리스크 0. - 사용자 자유서술이 절대 단말 밖으로 나가지 않음 → 신뢰 자산. - 오프라인 100% 동작. - `flutter_gemma`가 Android(LiteRT-LM) + iOS 둘 다 지원 → Phase 2 iOS 진입 시 재사용. - Function calling 응답이 구조화 → 도메인 모델 직접 매핑, 파싱 코드 최소. - SoT 카탈로그 갱신이 LLM 출력 품질에 즉시 반영 (정적 prompt 동기화 부담 X). - **부정 / 비용**: - 모델 파일 1.5GB 다운로드 — UX 부담. 일시정지/재개 + 진행률 + WiFi 권장 필수. - 첫 추론 latency 1–3초 (모델 로드 cold start). 이후 0.5–2초/응답. - Pixel 5 이하·RAM 4GB 이하 기기는 로드 실패 가능 → 사전 사양 체크 필요. - 배터리 0.5–1% / 추론 추정. 빈도 제한(throttle) 정책 필요. - `flutter_gemma` 패키지 semver 변경 위험 — v1은 latest version 고정 후 pubspec.lock 동결. - 한국어 품질 미검증 — Architect 단계 prototype 통과가 게이트 조건. - **후속 작업**: - Architect: 설계서 `docs/design/215-gemma-frame-suggest/` 작성 (본 ADR과 한 쌍). - Architect: 한국어 prompt 프로토타입 통과 확인 (Open Question). - Developer: `flutter_gemma` 의존성 추가, `LlmService` 인터페이스 + `GemmaLlmService` 구현. - QA: 한국어 출력 품질 평가 corpus 30 케이스, 저사양 기기(RAM 4/6/8GB) 회귀. - Documenter: 사용자 가이드에 "AI 도움" 섹션 추가 (오프라인·프라이버시 강조). ## 검토한 대안 (Alternatives Considered) - **A. 클라우드 LLM API (OpenAI / Claude / Gemini)** - 기각 사유: (1) 사용자 자유서술 외부 전송 → SoT 프라이버시 원칙 위반. (2) 오프라인 미지원 → R8(≤ 60초 체크인) 보장 불가. (3) API 비용·레이트리밋·약관 변경 리스크. (4) Vendor lock-in. - **C. 도입 X, 정적 카탈로그 확장 (FramePattern 30 → 100 → 300...)** - 기각 사유: (1) 동적 입력(synonym, 구어체) 흡수 한계는 카탈로그 크기로 해결 불가. (2) 한국어 변형 폭이 너무 넓어 enumeration 비현실적. (3) Architect/Developer 유지보수 부담이 LLM 도입보다 큼 (역설). - **D. Llama 3.2 1B / Phi-3.5 Mini** - 기각 사유: (1) Gemma 4 E2B가 한국어 성능 더 좋다는 Google 자체 벤치 + 한국 커뮤니티 보고. (2) `flutter_gemma`가 first-class 지원 — Llama용 동급 Flutter 패키지 부재. (3) Google이 LiteRT-LM Android 최적화에 직접 투자. - **E. E4B (4B parameter)** - 기각 사유: 5GB RAM 요구 → high-end Pixel 8+ / Galaxy S24+ 한정. Phase 2-A는 mid-range까지 커버가 우선. E4B는 v1.1 이상에서 토글 옵션으로 추가. - **F. APK 번들 모델** - 기각 사유: (1) Play Store APK 제한 150MB 위반. AAB로 회피 시도 가능하나 download size 절감 효과 미미. (2) AI 미사용자에게도 1.5GB 강제 → 옵트인 원칙 위반. ## 추적성 - 설계서: `docs/design/215-gemma-frame-suggest/README.md` + `fn-*.md`. - 의존: #204 Phase 1 MVP의 `FramePattern` 30 시드 + `validate_frame_level` 함수. - 후속 결정 가능성: ADR-0004 (E4B 토글), ADR-0005 (시나리오 #2~#6 함수 확장).