[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:
106
docs/design/260-gemma-tool-calling/fn-confirm_gate.md
Normal file
106
docs/design/260-gemma-tool-calling/fn-confirm_gate.md
Normal file
@@ -0,0 +1,106 @@
|
||||
# 함수 설계서: `ConfirmGate.show` (#260)
|
||||
|
||||
> **부모 설계서**: ./README.md · **상태**: Draft
|
||||
> **작성**: [AI] Architect · **구현**: `lib/ai/tools/confirm_gate.dart:show` · **테스트**: `test/ui/confirm_gate_test.dart`
|
||||
|
||||
## 1. 시그니처
|
||||
```dart
|
||||
class ConfirmGate {
|
||||
static Future<bool> show(
|
||||
BuildContext context,
|
||||
ToolDefinition tool,
|
||||
Map<String, dynamic> args,
|
||||
);
|
||||
}
|
||||
```
|
||||
|
||||
## 2. 책임 (단일 책임, 1줄)
|
||||
destructive tool 실행 직전 모달 AlertDialog 을 띄워 사용자 confirm 여부를 `Future<bool>` 로 반환한다.
|
||||
|
||||
## 3. 입력
|
||||
| 파라미터 | 타입 | 제약 | 설명 |
|
||||
|---|---|---|---|
|
||||
| `context` | BuildContext | `context.mounted == true` | chat screen 의 context |
|
||||
| `tool` | ToolDefinition | isDestructive=true | 어떤 도구인가 |
|
||||
| `args` | Map<String, dynamic> | 이미 validate 통과 | 사용자에게 보여줄 인자 |
|
||||
|
||||
## 4. 출력
|
||||
- **반환**: `Future<bool>`
|
||||
- `true` = 사용자가 "수행" 탭
|
||||
- `false` = 사용자가 "취소" 또는 outside-tap dismiss 또는 `context.mounted == false`
|
||||
- **부수효과**: 모달 표시 (UI). DB 변경 없음.
|
||||
|
||||
## 5. 동작 / 알고리즘
|
||||
```
|
||||
1. if !context.mounted:
|
||||
return false
|
||||
|
||||
2. summary = _summarize(tool.name, args)
|
||||
// tool 별 사람 친화 요약 함수 (per-tool overridable)
|
||||
|
||||
3. result = await showDialog<bool>(
|
||||
context: context,
|
||||
barrierDismissible: true, // outside-tap = 취소
|
||||
builder: (ctx) => AlertDialog(
|
||||
title: Text('이 작업을 수행할까요?'),
|
||||
content: Column(mainAxisSize: min, crossAxisAlignment: start, children: [
|
||||
Text(tool.description, style: bodyMedium),
|
||||
SizedBox(height: 12),
|
||||
Container(
|
||||
padding: EdgeInsets.all(12),
|
||||
decoration: BoxDecoration(
|
||||
color: Theme.surfaceVariant,
|
||||
borderRadius: BorderRadius.circular(8),
|
||||
),
|
||||
child: Text(summary),
|
||||
),
|
||||
]),
|
||||
actions: [
|
||||
TextButton(onPressed: () => Navigator.pop(ctx, false), child: Text('취소')),
|
||||
FilledButton(
|
||||
autofocus: true,
|
||||
onPressed: () => Navigator.pop(ctx, true),
|
||||
child: Text('수행'),
|
||||
),
|
||||
],
|
||||
),
|
||||
)
|
||||
|
||||
4. return result ?? false // dismiss 시 null → false
|
||||
```
|
||||
|
||||
### `_summarize` 규칙 (tool 별)
|
||||
- `add_habit` → "프로토콜 '$title'을 ${frame_level} 프레임으로 새 습관으로 추가합니다.\n • 문장: \"$framed_text\"\n • 앵커: ${anchor_when ?? '-'} / ${anchor_after_what ?? '-'}"
|
||||
- `log_tracker_entry` → "$habit_title 의 ${date ?? '오늘'} 기록을 '${value == 'done' ? '완료' : '공란'}' 으로 저장합니다."
|
||||
- 기타 → JSON pretty (fallback)
|
||||
|
||||
`title` 은 핸들러가 호출 직전 lookup 해서 args 에 채워줄 수 있지만, 단순화를 위해 ConfirmGate 가 직접 catalog/habit 조회는 안 함 — args 에 이미 있는 값만 사용. (안 채워졌으면 protocol_id 그대로 노출 — 트레이드오프 수용.)
|
||||
|
||||
## 6. 에러 & 실패 모드
|
||||
| 조건 | 처리 | 반환 |
|
||||
|---|---|---|
|
||||
| context dispose 후 호출 | guard 즉시 | `false` |
|
||||
| showDialog 자체 예외 (이론상 없음) | rethrow X — catch 후 false 반환 | `false` |
|
||||
| args 가 _summarize 가 기대 안 한 형태 | toString fallback | 정상 동작 (dialog 노출) |
|
||||
|
||||
## 7. 엣지케이스
|
||||
- **chat 모달 위에 chat 모달**: 동시에 호출되지 않도록 ChatSessionController 가 직렬화 (한 turn = 한 tool call 만). 다중 destructive tool 병렬 호출 시 첫 confirm 만 처리, 나머지는 `ToolCancelled` 자동 반환 (OQ-1 영향).
|
||||
- **시스템 back press**: Android 뒤로가기 → dialog dismiss → false. 의도된 cancel.
|
||||
- **autofocus + 키보드 enter**: 수행 버튼 기본 포커스. 의도치 않은 enter 누름 위험 — 사용자 결정으로 수용 (단축키 활용성 ↑).
|
||||
|
||||
## 8. 복잡도 / 성능
|
||||
- O(1). 사용자 대기 시간 = 무한 (사용자 입력 대기).
|
||||
- 호출 빈도: 사용자 대화 turn 당 0 또는 1.
|
||||
|
||||
## 9. 테스트 케이스 (필수)
|
||||
| 케이스 | 셋업 | 입력 | 기대 |
|
||||
|---|---|---|---|
|
||||
| confirm | MaterialApp + 모달 진입 → "수행" tap | add_habit args | true |
|
||||
| cancel | "취소" tap | 동일 | false |
|
||||
| outside dismiss | barrier tap | 동일 | false |
|
||||
| unmounted context | context dispose 후 호출 | — | false 즉시 |
|
||||
|
||||
## 10. 의존
|
||||
- Flutter `showDialog`, `AlertDialog`
|
||||
- `ToolDefinition` (description 출처)
|
||||
- per-tool summary 규칙은 별도 함수로 분리 (`_summarize(toolName, args)`) — 단위 테스트 용이.
|
||||
Reference in New Issue
Block a user