
GPT-5.6과 Claude 5 세대가 나오면서 모델 선택이 오히려 더 복잡해졌다.
예전에는 대략 이런 식이었다.
빠른 모델
vs
똑똑한 모델
지금은 그렇지 않다.
OpenAI에는 GPT-5.6 Sol, Terra, Luna가 있고 Claude에는 Fable 5, Opus 5, Sonnet 5, Haiku 4.5가 있다.
여기에 reasoning effort까지 붙는다.
low
medium
high
xhigh
max
장시간 Agent인지, 실시간 응답인지, Tool을 얼마나 많이 호출하는지, Context가 얼마나 큰지도 모델 선택에 영향을 준다.
그래서 앞으로 Agent Runtime에서 모델을 이렇게 고정하는 방식은 좋지 않다.
const MODEL = "gpt-5.6-sol";
모든 작업을 가장 강한 모델에게 보내는 것도 답이 아니다.
간단한 JSON 정리까지 최고급 모델이 처리하면 비용과 지연 시간이 커진다.
반대로 대규모 리팩터링이나 장시간 Agent 작업을 지나치게 작은 모델에게 맡기면 재시도와 Tool 호출이 늘어 결국 전체 비용이 더 커질 수도 있다.
이제 필요한 것은 Model Router다.
Task
↓
Task 분석
↓
Complexity / Latency / Risk / Budget / Horizon
↓
Model Router
↓
적절한 모델 + Reasoning Effort
↓
Agent 실행
이번 글에서는 GPT-5.6과 Claude 5 세대를 기준으로 실제 Agent Runtime에서 사용할 수 있는 Model Router를 만들어본다.
2026년 7월 기준 OpenAI의 GPT-5.6 계열은 세 단계로 나뉜다.
GPT-5.6 Sol
→ 복잡한 전문 작업과 고난도 추론
GPT-5.6 Terra
→ 성능과 비용의 균형
GPT-5.6 Luna
→ 저비용·대량 처리
API 모델 ID는 다음과 같다.
gpt-5.6-sol
gpt-5.6-terra
gpt-5.6-luna
gpt-5.6 alias는 현재 Sol을 가리킨다.
세 모델 모두 최대 약 105만 토큰 Context Window와 128K 출력 한도를 제공한다.
Claude도 역할이 나뉜다.
Claude Fable 5
→ 가장 어려운 장시간 Agent 작업
Claude Opus 5
→ 복잡한 Agent Coding과 Enterprise 작업
Claude Sonnet 5
→ 속도와 성능의 균형
Claude Haiku 4.5
→ 빠르고 대량 처리하기 좋은 경량 모델
API ID는 다음과 같다.
claude-fable-5
claude-opus-5
claude-sonnet-5
claude-haiku-4-5
Fable 5, Opus 5, Sonnet 5는 100만 토큰 Context Window와 최대 128K 출력을 지원한다.
이 구조만 봐도 한 가지가 보인다.
최고 모델 하나를 정해서 쓰는 시대
↓
작업별 모델을 선택하는 시대
2026년 7월 현재 GPT-5.6 API 가격은 100만 토큰 기준 다음과 같다.
GPT-5.6 Sol
Input $5
Output $30
GPT-5.6 Terra
Input $2.50
Output $15
GPT-5.6 Luna
Input $1
Output $6
Sol과 Luna의 출력 가격은 5배 차이다.
Claude 역시 모델별 차이가 있다.
Claude Fable 5
Input $10
Output $50
Claude Opus 5
Input $5
Output $25
Claude Sonnet 5
Input $2
Output $10
Sonnet 5의 $2 / $10은 2026년 8월 31일까지 적용되는 출시 가격이고, 9월 1일부터는 $3 / $15가 예정되어 있다.
Haiku 4.5는
Input $1
Output $5
수준이다.
단순히 가격만 보면 작은 모델이 무조건 좋아 보인다.
하지만 Agent에서는 그렇게 계산하면 안 된다.
예를 들어 어려운 버그 수정 작업이 있다고 하자.
작은 모델을 사용했을 때 다음처럼 움직일 수 있다.
Repository 탐색
→ 잘못된 파일 수정
→ 테스트 실패
→ 다시 탐색
→ 수정
→ 테스트 실패
→ 다시 수정
→ Reviewer 재검토
큰 모델은 한 번에 다음처럼 끝낼 수도 있다.
Repository 탐색
→ 핵심 파일 판단
→ 수정
→ 테스트
→ 완료
그래서 Agent 비용은 이런 식으로 봐야 한다.
실제 작업 비용
=
모델 토큰 비용
+
Tool 호출 비용
+
재시도 비용
+
Subagent 비용
+
실행 시간
+
실패 후 복구 비용
Model Router가 필요한 이유가 바로 이것이다.
가장 싼 모델을 선택하는 것이 아니라
작업을 성공시키는 데 필요한 총비용이 가장 낮은 모델을 선택해야 한다.
복잡한 AI Router부터 만들 필요 없다.
Task를 네 단계 정도로 나누는 것으로 시작할 수 있다.
SIMPLE
STANDARD
COMPLEX
LONG_HORIZON
파일 하나 확인
짧은 설명
형식 변환
간단한 코드 변경
문서 정리
반복 작업
일반 기능 구현
Bug Fix
테스트 추가
몇 개 파일 수정
일반적인 PR 리뷰
여러 Module을 건드리는 작업
Architecture 변경
복잡한 Debugging
대규모 Refactoring
보안 검토
고난도 코드 분석
수십 분 이상 실행
큰 Repository 분석
대규모 Migration
여러 단계의 Research
다수 Tool 사용
Subagent 협업
장시간 Coding Agent
이것만 있어도 기본 Model Router를 만들 수 있다.
OpenAI만 사용한다면 이렇게 시작할 수 있다.
type TaskComplexity =
| "simple"
| "standard"
| "complex"
| "long_horizon";
type OpenAIModel =
| "gpt-5.6-luna"
| "gpt-5.6-terra"
| "gpt-5.6-sol";
function selectOpenAIModel(
complexity: TaskComplexity
): OpenAIModel {
switch (complexity) {
case "simple":
return "gpt-5.6-luna";
case "standard":
return "gpt-5.6-terra";
case "complex":
case "long_horizon":
return "gpt-5.6-sol";
}
}
Claude만 사용한다면 비슷하다.
type ClaudeModel =
| "claude-haiku-4-5"
| "claude-sonnet-5"
| "claude-opus-5"
| "claude-fable-5";
function selectClaudeModel(
complexity: TaskComplexity
): ClaudeModel {
switch (complexity) {
case "simple":
return "claude-haiku-4-5";
case "standard":
return "claude-sonnet-5";
case "complex":
return "claude-opus-5";
case "long_horizon":
return "claude-fable-5";
}
}
실제 운영에서는 이것보다 조건이 더 필요하지만 출발점으로는 충분하다.
다음 두 작업을 생각해보자.
Swift 파일 100개를 읽고
deprecated API 사용 여부를 찾아 목록으로 만들어라.
파일은 많지만 판단 자체는 단순하다.
AuthManager.swift에서 가끔 발생하는
Race Condition 원인을 찾아 수정해라.
파일은 몇 개 안 되지만 추론 난이도가 높다.
둘 다 단순히 파일 개수로 판단하면 안 된다.
그래서 Router 입력을 조금 더 구조화한다.
type TaskProfile = {
complexity: number;
latencySensitivity: number;
risk: number;
toolDepth: number;
contextSize: number;
expectedDuration: number;
volume: number;
};
각 값은 1~5 정도로 두면 된다.
예를 들어 간단한 반복 작업은 다음과 같다.
const simpleTask: TaskProfile = {
complexity: 1,
latencySensitivity: 5,
risk: 1,
toolDepth: 1,
contextSize: 1,
expectedDuration: 1,
volume: 5
};
대규모 리팩터링은 다르다.
const refactoringTask: TaskProfile = {
complexity: 5,
latencySensitivity: 1,
risk: 4,
toolDepth: 5,
contextSize: 5,
expectedDuration: 5,
volume: 1
};
Router가 이 정보를 기반으로 판단한다.
GPT-5.6에서는 모델 선택만큼 reasoning.effort도 중요하다.
현재 GPT-5.6은 다음 수준을 지원한다.
none
low
medium
high
xhigh
max
그래서
Terra냐 Sol이냐
만 판단할 것이 아니라
Terra + low
Terra + medium
Sol + high
Sol + max
까지 같이 판단해야 한다.
예를 들어 일반적인 기능 구현이라면
GPT-5.6 Terra
+
medium
으로 시작할 수 있다.
복잡한 Architecture 문제라면
GPT-5.6 Sol
+
high
가 적합한 후보가 된다.
아주 어려운 분석이라면
GPT-5.6 Sol
+
max
를 검토한다.
중요한 것은 처음부터 무조건 max를 사용하지 않는 것이다.
Claude Opus 5 역시 최신 API에서 effort를 주요 제어 수단으로 사용한다.
지원 단계는 다음과 같다.
low
medium
high
xhigh
max
Anthropic은 기본적으로 high에서 시작하고, 실제 Eval 결과에 따라 낮추거나 높이는 방식을 권장한다.
예를 들어
일반 코드 분석
→ Sonnet 5
복잡한 Agent Coding
→ Opus 5 high
매우 어려운 추론
→ Opus 5 xhigh / max
장시간 고난도 Agent
→ Fable 5
같은 식으로 나눌 수 있다.
이제 Router 결과를 다음처럼 만든다.
type ModelSelection = {
provider: "openai" | "anthropic";
model: string;
effort:
| "none"
| "low"
| "medium"
| "high"
| "xhigh"
| "max";
};
예를 들어
{
"provider": "openai",
"model": "gpt-5.6-terra",
"effort": "medium"
}
또는
{
"provider": "anthropic",
"model": "claude-opus-5",
"effort": "high"
}
처럼 반환한다.
Router가 모델 이름만 선택하는 구조보다 훨씬 유연하다.
사용자가 기다리고 있는 작업과 백그라운드 Agent 작업은 요구사항이 다르다.
예를 들어 IDE 안에서 코드 한 줄을 설명하는 기능은 빠른 응답이 중요하다.
사용자 요청
→ 1~2초 내 반응 기대
이런 작업에서 최고급 long-horizon 모델을 사용할 이유는 적다.
후보는 다음처럼 잡을 수 있다.
GPT-5.6 Luna
Claude Haiku 4.5
조금 더 판단이 필요하다면
GPT-5.6 Terra
Claude Sonnet 5
정도가 된다.
반대로 사용자가
이 Repository 전체를 분석해서
Concurrency 구조를 개선하고
테스트까지 수정해줘.
라고 했다면 latency보다 성공률이 중요하다.
이때는
GPT-5.6 Sol
Claude Opus 5
Claude Fable 5
같은 모델이 후보가 된다.
같은 난이도의 작업이라도 위험도가 다르다.
예를 들어
README 수정
과
결제 로직 수정
은 Complexity가 비슷할 수 있다.
하지만 Risk는 전혀 다르다.
그래서 Router에 Risk를 넣는다.
type RiskLevel =
| "low"
| "medium"
| "high"
| "critical";
높은 Risk에서는 모델을 한 단계 올릴 수 있다.
Standard + Low Risk
→ Terra
Standard + High Risk
→ Sol
또는
Sonnet 5
→ Opus 5
로 승격한다.
하지만 여기서 중요한 것이 하나 있다.
고위험 작업을 강한 모델에게 보낸다고 안전해지는 것은 아니다.
사람 승인과 테스트가 별도로 필요하다.
High Risk
→ Strong Model
→ Test
→ Review
→ Human Approval
모델 선택은 안전 정책을 대체하지 않는다.
Task Complexity와 Task Duration은 다르다.
예를 들어
1만 개 파일에서 특정 패턴을 찾아라.
는 판단은 단순하지만 오래 걸릴 수 있다.
반대로
작은 Race Condition 하나를 분석해라.
는 짧지만 매우 어려울 수 있다.
그래서 Router에는 horizon을 별도로 둔다.
type TaskHorizon =
| "short"
| "medium"
| "long";
장시간 Agent라면 단순 모델 성능뿐 아니라 다음 항목도 본다.
Tool 사용 안정성
Context 유지
Checkpoint
Recovery
Subagent
Budget
Timeout
Artifact 관리
Claude Fable 5가 공식적으로 long-running agent를 주요 용도로 두는 이유도 여기 있다.
GPT-5.6 Sol 역시 long-horizon 전문 작업과 Agent workflow를 중요한 사용처로 두고 있다.
Tool 호출이 한두 번인 작업과 수십 번인 작업은 다르다.
Task A
read_file
→ answer
Task B
read_file
→ git_diff
→ test
→ read_log
→ edit_file
→ test
→ shell
→ git_diff
→ reviewer
→ fix
→ test
두 번째 작업에서는 모델의 Tool 사용 능력이 전체 성공률에 큰 영향을 준다.
그래서 다음 값을 둔다.
type ToolDepth =
| "shallow"
| "medium"
| "deep";
deep이면 한 단계 높은 모델로 올릴 수 있다.
GPT-5.6 계열은 약 105만 토큰, Claude 5 주요 모델들은 100만 토큰 Context를 지원한다.
그렇다고 Repository 전체를 항상 Context에 넣는 것은 좋은 설계가 아니다.
예를 들어
800K Context
+
간단한 파일 분류
라면 가장 강한 모델이 필요한 것은 아니다.
Model Router와 Context Router를 분리해야 한다.
Task Router
→ 어떤 모델?
Context Router
→ 어떤 정보를 넣을까?
큰 Context Window는
다 넣어도 된다.
는 뜻이 아니라
필요할 때 더 많이 넣을 수 있다.
는 의미로 보는 편이 좋다.
간단한 점수 기반 Router를 만들 수 있다.
function calculateTaskScore(
task: TaskProfile
): number {
return (
task.complexity * 3 +
task.risk * 2 +
task.toolDepth * 2 +
task.contextSize +
task.expectedDuration * 2 -
task.latencySensitivity
);
}
점수에 따라 모델을 선택한다.
function routeOpenAI(
task: TaskProfile
): ModelSelection {
const score = calculateTaskScore(task);
if (score <= 10) {
return {
provider: "openai",
model: "gpt-5.6-luna",
effort: "low"
};
}
if (score <= 24) {
return {
provider: "openai",
model: "gpt-5.6-terra",
effort: "medium"
};
}
if (score <= 34) {
return {
provider: "openai",
model: "gpt-5.6-sol",
effort: "high"
};
}
return {
provider: "openai",
model: "gpt-5.6-sol",
effort: "max"
};
}
이 숫자가 정답이라는 뜻은 아니다.
실제 프로젝트에서는 Eval 결과로 조정해야 한다.
function routeClaude(
task: TaskProfile
): ModelSelection {
const score = calculateTaskScore(task);
if (score <= 10) {
return {
provider: "anthropic",
model: "claude-haiku-4-5",
effort: "low"
};
}
if (score <= 24) {
return {
provider: "anthropic",
model: "claude-sonnet-5",
effort: "medium"
};
}
if (score <= 34) {
return {
provider: "anthropic",
model: "claude-opus-5",
effort: "high"
};
}
return {
provider: "anthropic",
model: "claude-fable-5",
effort: "max"
};
}
실제 Claude API에서 모델별 effort 지원 방식이 다르므로 Adapter에서 최종 요청 파라미터를 변환하는 편이 좋다.
Router가 Provider API 세부 구현까지 알게 만들 필요는 없다.
구조는 이렇게 만든다.
Task
↓️
Task Classifier
↓️
Model Router
↓️
ModelSelection
↓️
Provider Adapter
├── OpenAI Adapter
└── Anthropic Adapter
폴더 구조도 분리한다.
src/
├── routing/
│ ├── TaskClassifier.ts
│ ├── ModelRouter.ts
│ ├── RoutingPolicy.ts
│ └── ModelCatalog.ts
│
├── providers/
│ ├── openai/
│ │ └── OpenAIAdapter.ts
│ │
│ └── anthropic/
│ └── AnthropicAdapter.ts
│
└── runtime/
└── AgentRuntime.ts
이렇게 해야 나중에 모델이 바뀌어도 Runtime 전체를 수정하지 않는다.
Model Catalog를 따로 관리한다.
type ModelCapability = {
provider: "openai" | "anthropic";
model: string;
tier:
| "fast"
| "balanced"
| "advanced"
| "frontier";
longHorizon: boolean;
toolUse: boolean;
maxContextTokens: number;
maxOutputTokens: number;
inputCostPerMTok: number;
outputCostPerMTok: number;
};
예를 들어
const models: ModelCapability[] = [
{
provider: "openai",
model: "gpt-5.6-luna",
tier: "fast",
longHorizon: false,
toolUse: true,
maxContextTokens: 1_050_000,
maxOutputTokens: 128_000,
inputCostPerMTok: 1,
outputCostPerMTok: 6
},
{
provider: "openai",
model: "gpt-5.6-terra",
tier: "balanced",
longHorizon: true,
toolUse: true,
maxContextTokens: 1_050_000,
maxOutputTokens: 128_000,
inputCostPerMTok: 2.5,
outputCostPerMTok: 15
},
{
provider: "openai",
model: "gpt-5.6-sol",
tier: "frontier",
longHorizon: true,
toolUse: true,
maxContextTokens: 1_050_000,
maxOutputTokens: 128_000,
inputCostPerMTok: 5,
outputCostPerMTok: 30
}
];
Claude 모델도 같은 Catalog에 추가한다.
가격은 바뀐다.
특히 현재 Claude Sonnet 5처럼 기간 한정 가격이 적용되는 경우가 있다.
그래서 Application 코드 안에 가격을 박아두지 않는다.
{
"pricingVersion": "2026-07-28",
"models": {
"gpt-5.6-sol": {
"input": 5,
"output": 30
},
"gpt-5.6-terra": {
"input": 2.5,
"output": 15
},
"gpt-5.6-luna": {
"input": 1,
"output": 6
},
"claude-fable-5": {
"input": 10,
"output": 50
},
"claude-opus-5": {
"input": 5,
"output": 25
},
"claude-sonnet-5": {
"input": 2,
"output": 10
}
}
}
파일 이름도 날짜를 포함할 수 있다.
model-pricing-2026-07-28.json
가격 변경 시 Router 코드를 수정할 필요가 없다.
사용자가 다음 작업을 요청했다고 하자.
Repository 전체를 분석해서
Architecture 개선안을 만들어줘.
Router는 Sol 또는 Fable을 선택할 수 있다.
하지만 Task Budget이 아주 낮다면 문제가 된다.
type TaskBudget = {
maxCostUsd: number;
maxDurationSeconds: number;
};
예를 들어
{
"maxCostUsd": 0.5,
"maxDurationSeconds": 120
}
이라면 Router는 모델을 낮추거나 작업을 분할해야 한다.
Requested
Sol + max
Budget Check
↓️
Budget 초과 예상
↓️
Terra + medium
또는
Task 분할
Model Routing과 Budget Engineering이 연결되는 지점이다.
많이 사용하는 방식 중 하나가
작은 모델
→ 실패
→ 큰 모델
이다.
항상 좋은 것은 아니다.
복잡한 작업인데 처음부터 Luna를 보내면
Luna 실패
→ Terra 재시도
→ Sol 재시도
로 Context와 Tool 비용을 세 번 쓸 수 있다.
Task Classifier가 복잡한 작업이라고 확신한다면 처음부터 적절한 모델로 보내는 것이 낫다.
High Confidence Complex
→ Sol
또는
→ Opus
Escalation은 불확실한 작업에서만 사용한다.
Model Router는 위로만 올라갈 필요가 없다.
예를 들어 Sol에게 작업 계획을 맡겼다고 하자.
Sol
→ Task 분석
→ 47개의 단순 파일 수정으로 분해
실제 Subtask는 Terra나 Luna가 수행할 수 있다.
Sol
Planner
│
├── Luna
├── Luna
├── Terra
└── Luna
Claude에서도 같은 구조가 가능하다.
Opus / Fable
Planner
│
├── Sonnet
├── Sonnet
└── Haiku
큰 모델은 어려운 판단에 쓰고 반복 실행은 작은 모델에게 맡긴다.
이게 Multi-Agent에서 Model Router가 특히 중요한 이유다.
다음 구조를 생각해보자.
Planner
→ 작업 분석
→ Subtask 생성
Worker
→ 실제 실행
Planner는 높은 reasoning이 필요할 수 있다.
Sol high
또는
Opus high
Worker는 단순할 수 있다.
Terra medium
Sonnet 5
대량 Worker라면
Luna
Haiku
까지 내려갈 수 있다.
예를 들어
Architecture Migration
Sol
↓
50개 파일 작업 계획
Terra Worker × 5
↓
수정
Sol Reviewer
↓
최종 검증
처럼 구성할 수 있다.
Review 유형도 나눈다.
Style Review
→ 작은 모델
Scope Review
→ Balanced 모델
Architecture Review
→ Advanced 모델
Security Critical Review
→ Frontier 모델 + 사람
예를 들어
Formatting
→ Luna
일반 PR Review
→ Terra / Sonnet
대규모 Architecture 변경
→ Sol / Opus
장시간 전체 Repository Review
→ Fable
정도로 시작할 수 있다.
OpenAI와 Claude를 모두 사용한다면 Router가 Provider까지 고를 수도 있다.
다만
GPT가 더 좋다.
Claude가 더 좋다.
같은 고정 규칙을 만들면 안 된다.
자신의 실제 작업 Eval을 사용한다.
예를 들어 내부 결과가 이런 식이라고 해보자.
{
"ios_bug_fix": {
"openai_success_rate": 0.93,
"anthropic_success_rate": 0.91
},
"architecture_review": {
"openai_success_rate": 0.88,
"anthropic_success_rate": 0.95
}
}
Router는 이 데이터를 사용할 수 있다.
type TaskType =
| "coding"
| "debugging"
| "review"
| "planning"
| "documentation"
| "data"
| "computer_use";
그리고 Routing Policy를 만든다.
routing:
coding:
default:
provider: openai
model: gpt-5.6-terra
debugging:
complex:
provider: openai
model: gpt-5.6-sol
review:
default:
provider: anthropic
model: claude-sonnet-5
planning:
complex:
provider: anthropic
model: claude-opus-5
long_horizon:
provider: anthropic
model: claude-fable-5
이건 어디까지나 예시다.
실제 정책은 팀의 Eval 결과에 맞춰야 한다.
여기서 흔히 생기는 설계가 있다.
LLM에게 물어본다.
"어떤 모델을 사용할까요?"
그리고 LLM이
이건 어려우니까 최고 모델을 사용하겠습니다.
라고 판단한다.
문제가 있다.
모델은 비용 정책을 정확히 알지 못할 수 있고 자기 자신을 과도하게 선택할 수 있다.
Model Routing의 핵심 정책은 Runtime이 가져야 한다.
Agent
→ Task Metadata 생성
Runtime
→ Router Policy 평가
Runtime
→ 모델 선택
모델은 신호를 제공할 수 있지만 최종 선택은 Runtime이 한다.
왜 해당 모델을 선택했는지 기록한다.
{
"taskId": "PAP-142",
"selection": {
"provider": "openai",
"model": "gpt-5.6-sol",
"effort": "high"
},
"reason": {
"complexity": 5,
"risk": 4,
"toolDepth": 4,
"longHorizon": false
}
}
나중에 분석할 수 있다.
왜 Sol이 선택됐는가?
Terra로도 가능했는가?
비용 대비 성공률은 어땠는가?
Model Router도 Observability가 필요하다.
실행이 끝난 뒤 결과를 저장한다.
{
"taskId": "PAP-142",
"route": {
"model": "gpt-5.6-sol",
"effort": "high"
},
"result": {
"status": "completed",
"durationMs": 48121,
"toolCalls": 12,
"retries": 0,
"testsPassed": true
},
"usage": {
"inputTokens": 42000,
"outputTokens": 8100
}
}
이 데이터가 쌓이면 Router를 개선할 수 있다.
처음에는 사람이 규칙을 만든다.
Simple
→ Luna
Standard
→ Terra
Complex
→ Sol
하지만 운영 데이터가 쌓이면 더 정확해진다.
예를 들어 1,000개 Task를 분석했더니
일반 Bug Fix
Sol
성공률 96%
평균 비용 $1.10
Terra
성공률 94%
평균 비용 $0.31
이라면 대부분의 Bug Fix는 Terra로 보내는 것이 합리적이다.
반면
Concurrency Bug
Sol
성공률 91%
Terra
성공률 63%
라면 Sol로 보내는 편이 낫다.
Router는 벤치마크보다 우리 Repository에서의 실제 성공률을 봐야 한다.
Router 평가용 작업을 저장한다.
.ai/
└── routing-evals/
├── simple/
│ ├── rename-property.json
│ └── document-update.json
│
├── standard/
│ ├── login-error-fix.json
│ └── add-unit-test.json
│
├── complex/
│ ├── concurrency-bug.json
│ └── architecture-refactor.json
│
└── long-horizon/
└── repository-migration.json
각 Task에 기대 결과를 둔다.
{
"id": "concurrency-bug",
"expected": {
"testsMustPass": true,
"maxUnexpectedFiles": 0,
"requiredFiles": [
"FramePublisher.swift"
]
}
}
모델을 바꿀 때 같은 Fixture를 실행한다.
결과는 이런 형태로 저장할 수 있다.
{
"task": "concurrency-bug",
"results": [
{
"model": "gpt-5.6-terra",
"success": false,
"durationMs": 82103,
"retries": 2
},
{
"model": "gpt-5.6-sol",
"success": true,
"durationMs": 51442,
"retries": 0
},
{
"model": "claude-opus-5",
"success": true,
"durationMs": 59304,
"retries": 0
}
]
}
이런 데이터가 Model Router의 실제 근거가 된다.
모델 호출은 실패할 수 있다.
Rate Limit
Provider 장애
Timeout
Refusal
Context 초과
Tool 오류
그래서 Fallback Chain을 만든다.
fallbacks:
gpt-5.6-sol:
- claude-opus-5
- gpt-5.6-terra
claude-opus-5:
- gpt-5.6-sol
- claude-sonnet-5
gpt-5.6-terra:
- claude-sonnet-5
claude-sonnet-5:
- gpt-5.6-terra
하지만 모든 오류에서 Fallback하면 안 된다.
예를 들어
503 Provider Error
라면 다른 Provider로 Fallback할 수 있다.
하지만
Policy Violation
은 모델을 바꿔 우회해서는 안 된다.
function canFallback(
errorCode: string
): boolean {
return [
"PROVIDER_UNAVAILABLE",
"RATE_LIMITED",
"TIMEOUT"
].includes(errorCode);
}
보안·정책 오류는 중단한다.
POLICY_DENIED
→ stop
GPT-5.6에는 ultra라는 높은 Capability 설정도 등장했다.
하지만 이것을
model = "gpt-5.6-ultra"
같은 일반 API 모델로 생각하면 안 된다.
현재 OpenAI는 ChatGPT Work와 Codex의 ultra를 복잡한 작업을 여러 Agent가 병렬로 처리하는 최고 Capability 설정으로 설명하고 있다.
API에서는 Multi-Agent 기능이 별도의 beta 기능으로 제공된다.
따라서 Runtime Model Catalog에서는
Sol
Terra
Luna
를 모델 Tier로 보고,
effort
multi-agent
같은 실행 전략을 별도로 관리하는 편이 좋다.
Fable 5는 Anthropic이 가장 강한 공개 모델이자 장시간 Agent 작업을 주요 용도로 설명하는 모델이다.
그러나 일반 기능 개발까지 모두 Fable로 보내는 것은 비효율적이다.
UI 텍스트 수정
→ Fable
간단한 Unit Test
→ Fable
README 수정
→ Fable
같은 구조는 좋지 않다.
Sonnet이나 Haiku로 충분한 작업은 작은 모델로 보낸다.
Fable은
대규모 Repository 작업
오래 실행되는 Agent
복잡한 연구
중요한 장기 Planning
여러 단계의 자율 작업
같은 곳에 남겨두는 편이 낫다.
처음부터 8개 모델을 모두 사용하면 관리하기 어렵다.
OpenAI 중심 팀이라면 세 개면 충분하다.
Luna
→ Fast
Terra
→ Default
Sol
→ Escalation
Claude 중심 팀이라면
Haiku
→ Fast
Sonnet
→ Default
Opus
→ Complex
Fable
→ Long Horizon
정도로 시작한다.
두 Provider를 모두 쓰더라도 처음에는 네 개 정도면 충분하다.
Fast
→ Luna
Default
→ Terra
Complex
→ Opus 5
Long Horizon
→ Fable 5
또는 팀 Eval 결과가 다르면 당연히 다른 조합을 선택하면 된다.
처음 버전은 다음 구조로 시작할 것 같다.
Task
↓️
Task Profiler
complexity
risk
latency
toolDepth
horizon
↓️
Policy Router
↓️
Fast
Balanced
Advanced
Frontier
↓️
Model Catalog
↓️
Provider Adapter
↓️
Agent Run
↓️
Execution Result
↓️
Routing Eval Store
폴더 구조는 다음 정도다.
src/
├── routing/
│ ├── TaskProfiler.ts
│ ├── ModelRouter.ts
│ ├── RoutingPolicy.ts
│ ├── ModelCatalog.ts
│ └── BudgetGuard.ts
│
├── providers/
│ ├── OpenAIAdapter.ts
│ └── AnthropicAdapter.ts
│
├── eval/
│ ├── RoutingEval.ts
│ └── TaskFixtures.ts
│
└── runtime/
└── AgentRuntime.ts
Router는 Agent Prompt와 분리한다.
이게 중요하다.
Model Router를 만들 때 처음부터 AI가 알아서 완벽한 모델을 선택하게 만들 필요는 없다.
오히려 작은 규칙부터 시작한다.
1.
Default 모델을 정한다.
2.
빠른 작업은 아래 모델로 내린다.
3.
어려운 작업만 위 모델로 올린다.
4.
장시간 작업은 별도로 분류한다.
5.
실제 성공률과 비용을 기록한다.
6.
Eval 데이터를 기준으로 Router를 수정한다.
예를 들어 처음에는
OpenAI
Default
→ Terra
Fast
→ Luna
Complex
→ Sol
만으로 충분하다.
Claude라면
Default
→ Sonnet
Fast
→ Haiku
Complex
→ Opus
Long Horizon
→ Fable
정도로 시작한다.
앞으로 모델 성능은 계속 올라갈 것이다.
동시에 모델 종류도 계속 늘어난다.
Fast Model
Balanced Model
Reasoning Model
Long-Horizon Model
Computer Use Model
Multi-Agent Mode
개발자가 매번 직접 모델 이름을 선택하는 방식은 오래가기 어렵다.
애플리케이션은 사용자의 요청을 알아야 하고,
Agent Runtime은 그 작업을 수행하기에 적절한 계산량을 선택해야 한다.
결국 구조는 다음처럼 바뀐다.
사용자
"이 작업을 해줘"
↓
Agent Runtime
"이 작업은 어느 정도 어려운가?"
↓
Model Router
"이 정도 모델과 Effort면 충분하다."
↓
Model
작업 실행
Cloud에서 모든 요청을 가장 큰 서버에 보내지 않는 것과 비슷하다.
AI Agent에서도 작업에 필요한 만큼의 계산 자원을 배정해야 한다.
GPT-5.6과 Claude 5 세대에서 중요한 변화는 단순히 모델이 더 똑똑해졌다는 것이 아니다.
모델 제품군 자체가 작업 성격에 따라 나뉘고 있다.
빠른 작업
균형 잡힌 일반 작업
복잡한 추론
장시간 Agent
최대 Capability
그래서 Agent Runtime도 바뀌어야 한다.
Task
→ Task Profile
→ Model Router
→ Model + Effort
→ Agent Execution
→ Result
→ Eval
→ Router 개선
모든 작업을 최고 모델에 보내는 것은 가장 쉬운 구현이지만 좋은 운영 방식은 아니다.
반대로 모든 작업을 작은 모델에 보내고 실패할 때마다 올리는 것도 비용 효율적이지 않을 수 있다.
좋은 Router는
난이도
위험도
지연 시간
Tool 깊이
Context 크기
장시간 실행 여부
Budget
실제 성공률
을 함께 본다.
그리고 가장 중요한 데이터는 제조사의 Benchmark가 아니다.
우리 서비스의 실제 Task를 어떤 모델이 얼마의 비용과 시간으로 성공시켰는가다.
한 줄로 정리하면 이렇다.
GPT-5.6와 Claude 5 시대에는
최고 모델을 고르는 능력보다
작업마다 충분한 모델을 자동으로 고르는 능력이 더 중요하다.
모델은 계속 바뀐다.
Sol 다음 버전이 나오고, Terra가 업데이트되고, Claude의 새로운 모델이 추가돼도 Router 구조가 잘 만들어져 있다면 Agent Runtime 전체를 다시 설계할 필요가 없다.
앞으로 중요한 것은 특정 모델 하나에 애플리케이션을 묶는 것이 아니라,
Task와 Model 사이에 교체 가능한 Routing Layer를 만드는 것이다.
OpenAI — GPT-5.6: Frontier intelligence that scales with your ambition
GPT-5.6 Sol, Terra, Luna의 역할과 가격, reasoning effort, Multi-Agent 및 Agent workflow 관련 변화가 정리된 공식 발표 자료.
OpenAI API — Model guidance / Models
GPT-5.6 Sol·Terra·Luna의 API Model ID, Context Window, 최대 출력, 가격과 모델 선택 가이드를 확인할 수 있는 공식 개발 문서.
Anthropic — Claude Models Overview
Claude Fable 5, Opus 5, Sonnet 5, Haiku 4.5의 용도와 Context Window, 지연 특성, 가격을 비교할 수 있는 공식 문서.
Anthropic — What's new in Claude Opus 5
Claude Opus 5의 effort 조절, 장시간 Agent 작업, Adaptive Thinking, Fast Mode 등 최신 Agent Runtime 관련 변경을 확인할 수 있는 자료.
GPT-5.6은 Sol·Terra·Luna 세 Tier를 통해 고난도 작업부터 대량 저비용 작업까지 명확하게 역할을 나누고 있으며, reasoning effort를 none부터 max까지 별도로 조절할 수 있다.
Claude 역시 Fable·Opus·Sonnet·Haiku를 작업 특성에 따라 구분하고 있다. 특히 Fable 5는 long-running agent, Opus 5는 복잡한 agentic coding, Sonnet 5는 대부분의 production workload에서 속도와 지능의 균형을 목표로 한다.
따라서 최신 Agent Runtime에서는 Model ID 하나를 고정하기보다 Task Complexity + Effort + Budget + Horizon을 함께 판단하는 Routing Layer를 두는 것이 현실적인 방향이다.