17f8830d37
S1 = contract/CONTRACT.md + 14 fixtures + README + AI-ROUTING.
S2 = Sources/AI/{AIProvider,AIRouter,MockAIProvider} + Providers skeletons.
Baseline before S3 (device app) scaffold work begins.
Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
4.0 KiB
4.0 KiB
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)
- explicit > rule > error.
- 명시 opt-in(
request.explicitProvider) → 그 provider 만. 불가 시 에러(explicitProviderUnavailable) — 자동 다른 티어 호출 금지. - 미지정 → 태스크 선호 체인 순회. 불가/실패는 다음으로 넘기되
routingNote로 가시화(silent skip 금지) +log훅. - 전부 불가 →
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주입 + S1fixtures/디코딩 → 실 백엔드·실 LLM 대기 0 으로 앱 전체 빌드. - S1 =
[S1-ADD]구현 + 응답 shape 유지. - S2 = 위 §5 provider 결선. 인터페이스(
AIProvider/AIRouter) 변경 시만 합의.