[02-Architect] #260 design spec + ADR-0005
- design/260-gemma-tool-calling/README.md — overall (12 AC + 7 OQ + 모듈 구조) - fn-tool_dispatcher.md — multi-tool router (validate → confirm gate → handler → envelope) - fn-add_habit_handler.md — destructive 대표 (R3/R7/R8 enforce) - fn-confirm_gate.md — 모달 AlertDialog 흐름 (OQ-3 = 모달 확정) - fn-chat_session_controller.md — multi-turn loop 상태 머신 (MAX_TURNS=4) - ADR-0005 — in-app tool runtime + R 규칙 = 핸들러 책임 + schema SoT=Dart + 모달 OQ-1/2/4 = README §11 결정. OQ-3 = 모달 (사용자 결정). 신규 OQ-5/6/7 = Developer 가 구현 중 검증. Refs #260
This commit is contained in:
67
docs/adr/0005-in-app-tool-calling-architecture.md
Normal file
67
docs/adr/0005-in-app-tool-calling-architecture.md
Normal file
@@ -0,0 +1,67 @@
|
||||
# ADR-0005: In-app tool calling architecture (MCP-equivalent)
|
||||
|
||||
> **상태**: Accepted
|
||||
> **작성**: [AI] Architect · **일자**: 2026-06-15
|
||||
> **추적성** — Redmine #260, 설계서 `docs/design/260-gemma-tool-calling/README.md`, ADR-0003 (on-device Gemma 채택)
|
||||
|
||||
## 컨텍스트
|
||||
#218 로 on-device Gemma 4 추론이 동작하고 #226 으로 카탈로그 47 항목이 노출됐다. 사용자가 "이거 내 습관으로 추가해줘" 같은 자연어 요청으로 DB mutation 까지 도달하는 경로가 필요하다.
|
||||
|
||||
선택지:
|
||||
1. **별도 MCP 서버 띄우기** — 표준 프로토콜, 외부 process. 모바일 환경에서 IPC + 추가 메모리 부담 + 모델 컨텍스트 비용 동일.
|
||||
2. **In-process Dart 함수 직접 호출** — 같은 프로세스 안에서 tool 정의 + 핸들러. flutter_gemma 0.16.5 의 `tools` 파라미터 위에 얇은 라우터.
|
||||
3. **Prompt engineering 으로 mutation 안내** — 모델이 "다음과 같이 하세요" 텍스트로만 응답, 실제 액션은 사용자가 수동 — UX 후퇴, R 규칙 enforce 불가.
|
||||
|
||||
추가로 결정해야 할 부수 항목:
|
||||
- R1~R10 운영 규칙을 **어디서** enforce 할 것인가
|
||||
- tool schema 의 source-of-truth (Dart 코드 vs yaml/json 파일)
|
||||
- destructive tool 의 사용자 확인 게이트 (모달 vs inline 카드 vs 무게이트)
|
||||
|
||||
## 결정
|
||||
|
||||
### 결정 1: in-process Dart tool runtime
|
||||
- 별도 process 띄우지 않는다. `lib/ai/tools/` 하위에 `ToolDefinition` + `ToolDispatcher` + 핸들러를 Dart 로 작성.
|
||||
- flutter_gemma 의 `createChat(tools: [...], toolChoice: ToolChoice.auto)` 가 모델 ↔ Dart 사이의 protocol layer 역할.
|
||||
- `FunctionCallResponse` 를 받으면 `ToolDispatcher.dispatch(name, args, deps)` 로 라우팅 → `chat.addToolResult(...)` 로 회신.
|
||||
|
||||
### 결정 2: R 규칙 enforce 는 tool 핸들러 책임
|
||||
- 모델 prompt 에 "R3 quota 는 build ≤ 3" 식 안내를 넣지 않는다 (학습 신뢰성 불충분).
|
||||
- 모든 mutation 핸들러는 호출 직전 도메인 함수 (`judgeActiveHabitQuota`, `detectAvoidanceKeywords`, `assertXorProtocol`, `validateTrackerValue` 등) 를 직접 호출.
|
||||
- 위반 시 `ToolErr(code: 'r3_quota' | 'r7_avoidance' | ..., reason: 한국어)` 반환 → 모델이 사용자에게 안내.
|
||||
|
||||
### 결정 3: tool schema source-of-truth = Dart 코드
|
||||
- 각 `ToolDefinition.parametersSchema` 는 Dart `Map<String, dynamic>` 리터럴 (draft-07 JSON Schema 형태).
|
||||
- yaml/json 별도 파일 두지 않는다.
|
||||
- 이유:
|
||||
1. 핸들러 시그니처와 schema 가 같은 파일에 있어 drift 방지
|
||||
2. yaml 추가 시 codegen + 버전 동기화 부담
|
||||
3. IDE 자동완성 / rename / find-usages 활용
|
||||
|
||||
### 결정 4: destructive tool = 모달 Confirm 게이트 의무
|
||||
- `add_habit`, `log_tracker_entry(value=done)` 등 mutation tool 은 `isDestructive=true` 플래그.
|
||||
- Dispatcher 가 호출 전 `ConfirmGate.show(context, tool, args)` 로 AlertDialog 표시 → 사용자 OK 시에만 핸들러 실행.
|
||||
- inline 카드 (chat 메시지 안에) 대신 모달 채택 — 시각적 안전성과 모달 API 단순성을 위해.
|
||||
- 사용자 결정 (2026-06-15).
|
||||
|
||||
## 결과
|
||||
- 새 디렉토리 `lib/ai/tools/` 와 `chat_screen.dart`.
|
||||
- `LlmService` 인터페이스에 `sendChatTurn(...)` 추가 — `MockLlmService` 도 갱신.
|
||||
- 별도 server / process 추가 없음. 의존성 증가 없음.
|
||||
- 향후 외부 서비스 (예: 클라우드 카탈로그 sync) 도입 시 핸들러 내부에서 fetch 하는 것으로 충분 — MCP 도입 부담 없음.
|
||||
|
||||
## 영향 / 후속
|
||||
- (+) tool latency in-process Dart 호출이라 < 100ms. MCP IPC 오버헤드 없음.
|
||||
- (+) R 규칙 단일 SoT — Repository 가 검증하므로 UI/CLI/Chat 모두 동일 동작.
|
||||
- (-) MCP 표준 호환 X — 외부 tool 가 MCP 서버로 연동하려면 별도 어댑터 필요. Phase 1 범위 아님.
|
||||
- (-) Dart schema 가 수십 개 넘으면 가독성 부담 — ≥ 20 tool 시 ADR 후속 (yaml/codegen 도입 재검토).
|
||||
|
||||
## 대안 (기각)
|
||||
- **A. MCP 서버 별도 띄우기**: 모바일에서 native process 띄우려면 platform channel + lifecycle 관리. 메모리 +N MB. 표준 호환 외 이득 없음 — Phase 1 부적합.
|
||||
- **B. Prompt-only 안내**: 모델이 R 규칙을 학습 못 한다는 게 이미 검증됨 (#218 OQ-1 시점). 안전한 mutation 불가.
|
||||
- **C. inline 확인 카드**: chat 메시지 흐름에 자연스럽지만 사용자가 다른 메시지에 묻혀 무심코 진행 위험. 모달이 더 안전.
|
||||
- **D. yaml schema**: codegen 부담. Dart 단일 SoT 가 단순.
|
||||
|
||||
## 참고
|
||||
- 설계서: `docs/design/260-gemma-tool-calling/README.md`
|
||||
- 관련 ADR: ADR-0003 (on-device Gemma 채택)
|
||||
- 관련 Redmine: #260
|
||||
Reference in New Issue
Block a user