Model Providers
모델 프로바이더 시스템은 여러 런타임에 걸쳐 LLM 모델을 관리합니다. DynamicModelRegistry는 LlmRuntimeCatalog의 chat 바인딩과 구성된 TieredLLMProperties 계층에서 모델을 등록하며, DynamicModelSelectionStrategy는 각 요청에 대해 명시적 모델 힌트·티어 매핑·기본 ChatModel 폴백 순서로 런타임 모델을 해석합니다.
개요
Contexa는 여러 LLM 런타임을 동시에 지원합니다. 애플리케이션 시작 시 DynamicModelRegistry는 두 가지 경로로 디스크립터 맵을 채웁니다:
- LlmRuntimeCatalog chat 바인딩 —
LlmRuntimeCatalog.getChatBindings()가 반환하는 모든LlmRuntimeBinding이 등록됩니다. 카탈로그는 Contexa 런타임(예:contexa.llm.chat.ollama.*)과 Spring AI 자동 구성(Anthropic, OpenAI 등)으로 생성된 Spring AIChatModel빈으로부터 구성됩니다. - TieredLLMProperties 계층 —
spring.ai.security.layer1과spring.ai.security.layer2에 구성된 기본·백업 모델이 해당 티어로 등록됩니다. 레지스트리는resolveCanonicalModelId(...)를 사용하므로 동일 모델을 runtime ID, bean name, alias 어느 쪽으로도 조회할 수 있습니다.
바인딩이 없을 때 레지스트리는 모델 ID에서 provider 이름을 추론합니다. 인식되는 부분 문자열은 llama, qwen, gemma, mistral, phi, exaone, codellama, deepseek (→ ollama), claude (→ anthropic), gpt·o1·davinci (→ openai), gemini·vertex (→ gemini), bedrock (→ bedrock)입니다. 나머지는 spring으로 분류됩니다.
LlmRuntimeCatalog
탐색된 chat·embedding 런타임을 노출하는 인터페이스입니다. 레지스트리가 바인딩을 찾고 필요할 때 실제 ChatModel 인스턴스를 해석하는 데 사용합니다.
public interface LlmRuntimeCatalog
ChatModel을 반환합니다.EmbeddingModel을 반환합니다.ChatModel 빈을 반환합니다.EmbeddingModel 빈을 반환합니다.LlmRuntimeBinding
단일 런타임 바인딩에 대한 불변 레코드입니다. 바인딩은 Contexa LLM 자동 구성이 생성하며 카탈로그와 레지스트리 모두가 소비합니다.
public final class LlmRuntimeBinding
| 속성 | 타입 | 설명 |
|---|---|---|
runtimeId | String | 이 바인딩에 할당된 Contexa 런타임 ID. |
beanName | String | 실제 모델 빈의 Spring bean 이름. |
provider | String | 프로바이더 식별자(예: ollama, anthropic, openai). |
modelId | String | 런타임이 보고한 정규 모델 ID(예: llama3.1:8b). |
aliases | Set<String> | 같은 바인딩으로 해석되는 추가 선택자들. |
type | LlmRuntimeType | Chat 또는 embedding 런타임 유형. |
primary | boolean | 이 바인딩이 해당 유형의 Spring primary로 표시되어 있는지 여부. |
source | String | 바인딩의 출처(예: 자동 구성 클래스 또는 설정 키). |
헬퍼 메서드
| 메서드 | 반환 | 설명 |
|---|---|---|
canonicalId() | String | 바인딩의 선호 식별자를 반환합니다: modelId → runtimeId → beanName 순. |
matches(String selector) | boolean | 선택자가 runtimeId, beanName, modelId, 또는 alias 중 하나와 일치하는지 반환합니다. |
ModelDescriptor
모델의 정체성·기능·기본 옵션·현재 상태를 기술합니다.
@Data @Builder
public class ModelDescriptor
| 속성 | 타입 | 설명 |
|---|---|---|
modelId | String | 고유 모델 식별자(예: llama3.1:8b, claude-3-opus). |
displayName | String | 사람이 읽기 쉬운 모델 이름. |
provider | String | 프로바이더 이름(ollama, anthropic, openai). |
tier | Integer | 모델에 할당된 구성 런타임 티어(1 또는 2). 할당되지 않으면 null. |
version | String | 모델 버전 문자열. |
capabilities | ModelCapabilities | Streaming·Tool 호출·멀티모달·컨텍스트 윈도우·출력 예산을 기술하는 nested class. |
options | ModelOptions | 기본 샘플링 옵션(temperature, topP, topK, repetitionPenalty)을 기술하는 nested class. |
status | ModelStatus | AVAILABLE 또는 UNAVAILABLE. |
ModelCapabilities (nested class)
| 필드 | 타입 | 기본값 |
|---|---|---|
streaming | boolean | true |
toolCalling | boolean | false |
functionCalling | boolean | false |
multiModal | boolean | false |
maxTokens | int | 4096 |
contextWindow | int | 4096 |
maxOutputTokens | int | 4096 |
ModelOptions (nested class)
| 필드 | 타입 | 기본값 |
|---|---|---|
temperature | Double | 0.7 |
topP | Double | 0.9 |
topK | Integer | null |
repetitionPenalty | Double | 1.0 |
헬퍼 메서드
true를 반환합니다.DynamicModelRegistry
디스크립터를 관리하고 ChatModel 인스턴스를 캐시하는 중앙 레지스트리입니다. @PostConstruct 초기화 동안 카탈로그와 티어 설정에서 모델을 등록합니다.
public class DynamicModelRegistry
생성자 의존성
| 의존성 | 설명 |
|---|---|
ApplicationContext | 초기화 시 사용되는 Spring application context 참조. |
TieredLLMProperties | 구성된 티어 계층 구조(layer 1·layer 2, primary·backup 모델). |
LlmRuntimeCatalog | Spring AI 런타임 바인딩 카탈로그. Optional: 없을 경우 getModel(...)이 ModelSelectionException을 던집니다. |
Public API
ChatModel을 반환합니다. 정규 모델 ID를 해석하고 바인딩을 조회하여 카탈로그가 실제 모델을 해석하도록 요청하며 결과를 캐시합니다. ID나 카탈로그가 없으면 ModelSelectionException을 던집니다.ChatModel을 인스턴스화하지 않고 모델 ID에 대한 등록된 디스크립터를 반환합니다.AVAILABLE 디스크립터를 반환합니다(대소문자 무시).@PreDestroy 훅.ModelSelectionStrategy
모델 선택 로직을 위한 인터페이스입니다. DynamicModelSelectionStrategy가 기본 구현체입니다.
public interface ModelSelectionStrategy
DynamicModelSelectionStrategy
기본 선택 전략은 명시적 모델 우선 폴백 체인을 사용합니다. DynamicModelRegistry, TieredLLMProperties, 기본 ChatModel 빈으로 생성됩니다.
public class DynamicModelSelectionStrategy implements ModelSelectionStrategy
- 명시적 모델 요청 — 먼저
ExecutionContext.preferredModel을 시도합니다. 없으면 다음 순서로 메타데이터 키를 찾습니다:requestedModelId,preferredModel,runtimeModelId,modelId. - 티어 해석 — 명시적 모델이 해석되지 않으면 다음 순서로 티어를 선택합니다:
analysisLevel.getDefaultTier()→securityTaskType.getDefaultTier()→ExecutionContext.tier. 1을 초과하는 값은 구성된 layer 2로 정규화됩니다. - Primary/backup 조회 — 전략은
TieredLLMProperties.getModelNameForTier(...)로 기본 모델을,getBackupModelNameForTier(...)로 백업 모델을 조회합니다. - Primary ChatModel 폴백 — 모든 티어 후보가 실패하면 자동 구성된 기본
ChatModel빈을 마지막 폴백으로 반환합니다.
해석 메타데이터
전략은 결과를 ExecutionContext.metadata에 기록합니다. 하위 코드와 어드바이저는 다음 키를 확인할 수 있습니다:
| 키 | 설명 |
|---|---|
requestedModelId | 요청된 명시적 모델 ID(제공된 경우). |
requestedModelSourceKey | 요청 ID의 출처(예: executionContext.preferredModel 또는 executionContext.metadata.runtimeModelId). |
selectedModelId | 최종적으로 선택된 모델 ID. |
selectedModelProvider | 일치하는 ModelDescriptor에서 가져온 provider 이름(가능할 때). |
runtimeModelId | 해당 키를 찾는 하위 소비자를 위한 selectedModelId의 복제본. |
modelSelectionSource | 모델이 선택된 이유(예: explicit_model, analysis_level:NORMAL, security_task_type:FORENSIC_ANALYSIS, tier:2, primary_chat_model). |
modelSelectionFallbackUsed | 백업 또는 기본 폴백이 사용되었을 때 true. |
modelSelectionCandidates | 전략이 시도한 모델 ID의 정렬된 목록. |
modelSelectionFailure | 모델을 선택할 수 없을 때의 실패 이유. |
추가 메서드
설정
계층형 모델 계층 구조
spring:
ai:
security:
layer1:
model: qwen2.5:7b
backup:
model: qwen2.5:7b
layer2:
model: gpt-4o-mini
backup:
model: llama3.1:8b
tiered:
layer1:
timeout-ms: 30000
layer2:
timeout-ms: 60000
contexa:
llm:
chat:
ollama:
base-url: http://localhost:11434
model: qwen2.5:7b
선택 순서: 명시적 모델 요청 → analysisLevel → securityTaskType → 명시적 tier → 기본 ChatModel 폴백. 런타임은 구성된 layer 1과 2만 가지므로, 더 높은 시맨틱 티어는 layer 2로 정규화됩니다.