f512d94c74
git-subtree-dir: clients/ds-app git-subtree-mainline:a24e3e6f22git-subtree-split:5206cf3b0c
68 lines
4.0 KiB
Markdown
68 lines
4.0 KiB
Markdown
# DS App AI 라우팅 계약 (S2 인터페이스 동결) — v0.1
|
|
|
|
S1(데이터 fixture)이 앱의 **데이터 모양**을 동결했듯, 이 문서 + `Sources/AI/`는 앱의 **AI 호출 모양**을 동결한다.
|
|
S3 는 `AIProvider` 프로토콜 + `MockAIProvider` 로 AI 흐름을 끝까지 그리고, S2 가 실 provider 를 뒤에서 채운다.
|
|
|
|
- **Frozen**: 2026-06-04 · **typecheck PASS** (swift 6 strict concurrency) · **router 스모크 PASS**
|
|
- **위치**: `Sources/AI/{AIProvider,AIRouter,MockAIProvider}.swift` + `Sources/AI/Providers/*.swift`
|
|
- **파일 경계**: 이 `AI/` 디렉토리 = **S2 소유**(앱 나머지 = S3). 같은 Xcode repo 디렉토리 단위 분담 → 충돌 0.
|
|
|
|
## 1. Provider 티어 (= 디바이스 역할 표와 1:1)
|
|
|
|
| `AIProviderID` | 노드 | 용도 | 구현 경로 |
|
|
|---|---|---|---|
|
|
| `.onDevice` | 맥북·아이폰 | 즉답·오프라인·프라이버시 | Apple **FoundationModels** (`SystemLanguageModel`/`LanguageModelSession`) |
|
|
| `.localMLX` | 맥미니 허브 | 무거운 로컬 생성 | Gemma 4 26B — llm-router `:8890` / MLX `:8801` (OpenAI 호환) |
|
|
| `.remoteDS` | GPU(원격) | **코퍼스 RAG** | `GET /search/ask?backend=` (CONTRACT.md §4) |
|
|
| `.specialized` | GPU | rerank·embed·**vision**·OCR | 특화 모델 통로(온디맨드) |
|
|
|
|
## 2. 태스크 → 티어 정책 (`AIRoutingPolicy.default`)
|
|
|
|
| `AITask` | 선호 체인 | 근거 |
|
|
|---|---|---|
|
|
| `quickSummarize` | onDevice → localMLX | 빠르고 사적 |
|
|
| `memoAssist` | onDevice → localMLX | 짧은 보조 |
|
|
| `askSelection` | onDevice → localMLX → remoteDS | 로컬 컨텍스트, 부족 시 승급 |
|
|
| `corpusAsk` | **remoteDS only** | 코퍼스 필요 — 온디바이스로 폴백 불가 |
|
|
| `classify` | localMLX → remoteDS → onDevice | 분류 품질 우선 |
|
|
| `vision` | specialized → onDevice | GPU VLM 우선 |
|
|
|
|
## 3. 3단 fallback 규칙 (feedback_task_routing_hybrid + no_silent_fallback)
|
|
|
|
1. **explicit > rule > error.**
|
|
2. **명시 opt-in**(`request.explicitProvider`) → 그 provider 만. 불가 시 **에러**(`explicitProviderUnavailable`) — 자동 다른 티어 호출 **금지**.
|
|
3. **미지정** → 태스크 선호 체인 순회. 불가/실패는 다음으로 넘기되 **`routingNote`로 가시화**(silent skip 금지) + `log` 훅.
|
|
4. 전부 불가 → `noProviderAvailable`.
|
|
|
|
스모크 검증:
|
|
```
|
|
quickSummarize → onDevice corpusAsk → remoteDS (citations=1)
|
|
vision → specialized classify → localMLX
|
|
explicit onDevice 불가 → 에러(자동 fallback X)
|
|
rule fallback: onDevice 불가 → localMLX note="fallback from onDevice → localMLX"
|
|
```
|
|
|
|
## 4. S1 계약과의 다리 (`RemoteDSProvider`)
|
|
|
|
`corpusAsk` 만 `.remoteDS` 책임. 매핑(고정):
|
|
```
|
|
GET /search/ask?q=<prompt>&backend=<map(explicitProvider)> → AskResponse (CONTRACT.md §4)
|
|
AskResponse.ai_answer → AICompletionResponse.text
|
|
AskResponse.citations[] → AICitation[] (n, doc_id, title, section_title, span_text)
|
|
AskResponse.synthesis_status → AIFinishReason (completed/timeout/no_evidence/backend_unavailable/…)
|
|
AskResponse.confidence → AIConfidence
|
|
AskResponse.backend_used → routingNote (어느 LLM 이 응답했는지)
|
|
```
|
|
`backend` 매핑: `nil`→`mac-mini-default` · `.localMLX`→`gemma-macmini` · (M5 Max Qwen 경로)→`qwen-macbook` · cloud→`claude-cloud`(503, 별 PR).
|
|
|
|
## 5. S2 가 다음에 채울 것 (인터페이스는 고정)
|
|
- `OnDeviceProvider`: `import FoundationModels` 가용성 프로브 + `LanguageModelSession` 호출.
|
|
- `LocalMLXProvider`: 맥미니 OpenAI 호환 호출(messages system/user 분리 call-shape 고정).
|
|
- `RemoteDSProvider`: S3 의 DS API client 주입 → 위 §4 매핑 결선.
|
|
- `SpecializedProvider`: GPU 비전/특화 통로(필요 태스크만).
|
|
|
|
## 동시 출발선 (S1 + S2 둘 다 동결 완료)
|
|
- **S3** = `MockAIProvider` 주입 + S1 `fixtures/` 디코딩 → 실 백엔드·실 LLM 대기 0 으로 앱 전체 빌드.
|
|
- **S1** = `[S1-ADD]` 구현 + 응답 shape 유지.
|
|
- **S2** = 위 §5 provider 결선. 인터페이스(`AIProvider`/`AIRouter`) 변경 시만 합의.
|