AI¶
D-SKET Canvas는 AI 다이어그램/시각화 캔버스입니다. 그 핵심은 자체 LLM 추상화 레이어로, 여러 벤더의 모델을 하나의 일관된 인터페이스로 묶는 데 있습니다. 여러 벤더(Claude, OpenAI, Gemini)의 모델을 교체 가능한 어댑터로 관리합니다.
왜 자체 추상화 레이어인가?¶
- 벤더 종속 제거: 모델/가격/정책 변경 시 애플리케이션 코드 수정 최소화
- 응답 정규화: 벤더마다 다른 응답 형식을 우리 표준 스키마로 통일
- 에러 분류: 토큰 초과·속도 제한 등 벤더별 에러를 공통 타입으로 매핑
- 버퍼링/스트리밍: 스트리밍 응답을 일관되게 처리
flowchart LR
APP["D-SKET Canvas 애플리케이션"]
LAYER["LLM 추상화 레이어"]
AD1["Adapter: Claude"]
AD2["Adapter: OpenAI"]
AD3["Adapter: Gemini"]
APP --> LAYER
LAYER --> AD1
LAYER --> AD2
LAYER --> AD3
어댑터 인터페이스 (JS 스케치)¶
표준 응답 스키마
모든 어댑터는 아래 인터페이스를 구현하여 동일한 LLMResponse를 반환합니다.
벤더별 차이는 어댑터 내부에서 정규화됩니다.
llm-adapter.ts
export interface LLMMessage {
role: "system" | "user" | "assistant";
content: string;
}
export interface LLMResponse {
text: string; // 벤더와 무관한 정규화된 텍스트
usage?: {
inputTokens: number;
outputTokens: number;
};
model: string; // 실제 사용된 모델명
}
export interface LLMAdapter {
readonly vendor: "claude" | "openai" | "gemini";
stream(messages: LLMMessage[], opts?: StreamOptions):
AsyncIterable<LLMResponse>;
complete(messages: LLMMessage[], opts?: CompleteOptions):
Promise<LLMResponse>;
}
에러 분류¶
벤더별 에러를 공통 유형으로 매핑해 상위 레이어에서 일관되게 처리합니다.
| 벤더 | 예시 에러 | 공통 분류 |
|---|---|---|
| Claude | overloaded_error |
rate_limited |
| OpenAI | 429 |
rate_limited |
| Gemini | RESOURCE_EXHAUSTED |
rate_limited |
| 공통 | 토큰 한도 초과 | context_too_long |
절대 벤더 에러를 그대로 노출하지 말 것
사용자에게 벤더 이름이 드러나는 원시 에러를 전달하지 마세요.
항상 공통 분류(예: rate_limited, context_too_long)로 변환 후
사용자 친화적 메시지를 제공합니다.
버퍼링¶
스트리밍 응답은 버퍼링을 거쳐 UI에 전달됩니다. 청크 단위로 도착하는 토큰을 모아 부드러운 타이핑 효과와 안정적인 렌더링을 만듭니다.
- 도착한 청크를 내부 버퍼에 누적
- 일정 간격/크기로 UI에 flush
- 마지막에
usage집계 후 비용 로깅
BYOK (Bring Your Own Key)¶
고객이 자신의 API 키를 제공해 사용량/비용을 직접 관리하는 방식입니다.
BYOK 설계 고려사항
- 키는 절대 클라이언트에 노출하지 말 것 (서버 측 프록시 경유)
- 키를 저장하는 경우 반드시 암호화(KMS 등) 후 DB에 보관
- 벤더별 키 유효성 검증을 어댑터 레이어에서 수행
온프레미스 계획¶
엔터프라이즈 고객을 위해 온프레미스 배포를 계획 중입니다.
- 자체 모델(or 오픈소스 모델)을 고객 인프라에 배포
- 추상화 레이어 덕분에 어댑터만 추가하면 온프레미스 모델로 교체 가능
- 네트워크 단절 환경에서도 캔버스 기능 유지
로드맵
온프레미스는 현재 계획 단계입니다. 추상화 레이어가 확장성을 보장하므로 향후 어댑터를 추가하는 방식으로 전환할 예정입니다.