에이전틱스
도메인 2. 모델 선택과 프롬프트·컨텍스트 엔지니어링

2.1모델 선택의 트레이드오프

예상 학습 시간 40

아키텍트에게 모델 선택은 "가장 똑똑한 모델을 고른다"가 아니라 품질·지연시간·비용이라는 세 축 사이에서 요구사항에 맞는 지점을 고르고 그 근거를 설명하는 일입니다. 이 수업에서는 세 축의 실제 관계, 사고 강도(effort)라는 네 번째 손잡이, 계층형 라우팅 구조, 그리고 선택을 평가로 검증하는 방법을 다룹니다.

세 축은 독립적이지 않다

모델 선택 논의는 대개 "품질이 높은 모델은 비싸고 느리다"는 단순한 직선으로 시작합니다. 실무에서는 이 직선이 자주 뒤집힙니다.

상위 모델이 오히려 총비용을 낮추는 경우가 있습니다. 경량 모델이 3회 재시도한 뒤 사람에게 에스컬레이션되는 작업을, 상위 모델이 1회에 끝낸다면 토큰 단가가 몇 배 높아도 전체 비용은 내려갑니다. 에이전틱 워크플로에서는 이 역전이 흔합니다. 한 턴의 판단 오류가 다음 턴의 잘못된 툴 호출로 이어지고, 그 결과가 다시 컨텍스트에 쌓여 오류를 증폭시키기 때문입니다.

그래서 비교 단위를 토큰당 단가가 아니라 완료된 작업당 비용(cost per completed task) 으로 잡아야 합니다.

# 단가만 보면 경량 모델이 유리해 보이지만
cheap_unit_cost = 0.25          # 상위 모델 대비 상대 단가
cheap_success_rate = 0.62       # 1회 시도 성공률
cheap_attempts = 1 / cheap_success_rate   # 약 1.6회

strong_unit_cost = 1.0
strong_success_rate = 0.94
strong_attempts = 1 / strong_success_rate  # 약 1.06회

# 작업당 비용으로 환산하면 격차가 크게 좁혀진다
print(cheap_unit_cost * cheap_attempts)    # 0.40
print(strong_unit_cost * strong_attempts)  # 1.06

위 예에서는 여전히 경량 모델이 저렴하지만, 실패한 시도가 사람 검토 시간을 소모한다면 그 비용까지 더해야 합니다. 인건비를 포함하는 순간 결론이 뒤집히는 일이 자주 있습니다. 시험에서는 "단가가 낮은 모델이 항상 총비용도 낮다"는 전제를 깔아둔 선택지가 오답으로 나옵니다.

지연시간도 마찬가지입니다. 사용자가 체감하는 것은 모델의 초당 토큰 수가 아니라 작업 완료까지의 시간입니다. 툴을 여러 번 호출하는 에이전트에서는 판단이 정확해 왕복 횟수가 줄어드는 쪽이, 개별 응답이 빠른 쪽보다 먼저 끝나는 경우가 많습니다.

사고 강도라는 네 번째 손잡이

모델 등급만이 조절 가능한 값은 아닙니다. 최신 Claude 모델은 적응형 사고(adaptive thinking) 를 지원합니다. 고정된 사고 토큰 예산을 개발자가 정하는 대신, 모델이 문제의 난이도에 따라 얼마나 생각할지 스스로 조절합니다.

response = client.messages.create(
    model=MODEL_ID,
    max_tokens=16000,
    thinking={"type": "adaptive"},
    output_config={"effort": "medium"},   # low | medium | high
    messages=[{"role": "user", "content": task}],
)

여기서 effort는 사고 깊이와 전체 토큰 지출을 함께 조절합니다. 아키텍처 관점에서 중요한 점은 이것이 모델 등급과 부분적으로 대체 가능한 손잡이라는 것입니다. 상위 모델을 낮은 강도로 쓰는 것과 중간 모델을 높은 강도로 쓰는 것은 비용·품질 지점이 겹칠 수 있고, 어느 쪽이 나은지는 작업 성격에 달려 있습니다.

두 손잡이를 혼동하지 않는 것이 핵심입니다.

  • max_tokens출력 상한입니다. 모델은 이 값을 알지 못하며, 초과하면 응답이 잘립니다. 사고가 켜져 있으면 사고 토큰도 이 예산을 함께 씁니다.
  • effort얼마나 깊이 파고들지에 대한 지침입니다. 상한이 아니라 성향 조절입니다.

실무에서 자주 나오는 실수는 사고를 켜면서 max_tokens를 그대로 두는 것입니다. 사고가 예산의 상당 부분을 쓰고 나면 정작 답변이 중간에 잘립니다. 사고를 켤 때는 출력 예산에 여유를 함께 줘야 합니다.

또 하나. 사고를 끄는 것이 항상 절약은 아닙니다. 사고를 끄면 모델이 그 추론을 눈에 보이는 응답 본문에 풀어쓰는 경향이 생겨, 사용자에게 보이는 출력이 오히려 길어지고 읽기 나빠질 수 있습니다. 비용을 줄이려면 사고를 끄기보다 강도를 낮추는 쪽을 먼저 시도하는 편이 안전합니다.

단일 모델과 계층형 라우팅

모든 요청을 한 모델로 처리하는 구조는 운영이 단순하고 캐시 효율이 좋습니다. 반면 계층형 라우팅은 쉬운 요청을 저렴하게 처리해 평균 비용을 낮춥니다. 아키텍트는 이 둘 사이에서 선택해야 하며, 계층화가 공짜가 아니라는 점을 알아야 합니다.

계층화의 숨은 비용은 세 가지입니다.

  1. 분류 자체의 비용과 오류. 어떤 요청이 쉬운지 판단하는 단계가 추가됩니다. 이 분류기가 틀리면 어려운 요청이 경량 모델로 가서 조용히 나쁜 답을 냅니다.
  2. 캐시 분할. 프롬프트 캐시는 모델 단위로 유지됩니다. 트래픽을 두 모델로 나누면 각각의 캐시 적중률이 떨어집니다.
  3. 두 배의 평가 부담. 프롬프트를 고칠 때마다 두 경로 모두 검증해야 합니다.

그래서 계층화는 트래픽 분포가 뚜렷하게 갈릴 때 값어치를 합니다. 요청의 80%가 정형적인 조회이고 20%가 복잡한 분석이라면 효과가 크지만, 난이도가 고르게 퍼져 있으면 분류 비용만 늘고 이득이 작습니다.

계층화의 대안으로 자주 쓰이는 두 가지 구조가 있습니다.

  • 에스컬레이션: 먼저 경량 모델로 시도하고, 신뢰도가 낮거나 검증에 실패하면 상위 모델로 다시 보냅니다. 사전 분류가 필요 없다는 장점이 있지만, 실패한 시도의 비용이 낭비됩니다.
  • 역할 분리: 오케스트레이터는 상위 모델, 읽기 위주의 서브에이전트는 경량 모델로 둡니다. 요청을 난이도로 나누는 대신 작업의 성격으로 나누는 방식이라 분류기가 필요 없습니다.

지연시간이 요구사항의 중심이라면 배치 처리도 검토 대상입니다. 즉시성이 필요 없는 작업을 Message Batches API로 넘기면 비용이 크게 줄어듭니다. 대신 결과가 도착하는 시점을 보장받지 못하므로, 야간 집계나 대량 분류처럼 마감이 느슨한 작업에만 맞습니다.

선택을 근거로 만들기

요강이 요구하는 것은 "좋은 모델을 고르는 능력"이 아니라 선택을 정당화하는 능력입니다. 정당화의 재료는 평가 결과입니다.

모델 선택을 위한 최소한의 평가 설계는 다음과 같습니다.

  1. 실제 트래픽에서 뽑은 대표 사례 집합을 만듭니다. 합성 예제만으로는 실패 양상이 드러나지 않습니다. 어려운 사례와 흔한 사례를 함께 담고, 각 사례에 기대 결과를 붙입니다.
  2. 후보를 동일 조건에서 돌립니다. 프롬프트, 툴 정의, 사고 설정을 고정해야 비교가 성립합니다. 한쪽만 프롬프트를 다듬은 비교는 모델 비교가 아니라 프롬프트 비교입니다.
  3. 세 축을 함께 기록합니다. 정확도만 기록하면 지연시간 회귀를 놓칩니다.
import time

def bench(model_id, cases):
    hits, latency, tokens = 0, 0.0, 0
    for case in cases:
        t0 = time.perf_counter()
        r = client.messages.create(
            model=model_id,
            max_tokens=4096,
            messages=[{"role": "user", "content": case["input"]}],
        )
        latency += time.perf_counter() - t0
        tokens += r.usage.input_tokens + r.usage.output_tokens
        if grade(r, case["expected"]):
            hits += 1
    n = len(cases)
    return {"accuracy": hits / n, "p50_latency": latency / n, "tokens": tokens / n}

모델을 바꾸는 일은 프롬프트를 바꾸는 일이기도 합니다. 이전 모델의 약점을 보완하려고 넣어둔 지시가 새 모델에서는 과잉으로 작동해 오히려 품질을 떨어뜨리는 경우가 흔합니다. "반드시", "절대" 같은 강조가 붙은 지시일수록 그렇습니다. 모델 교체 시에는 이런 잔여 지시를 제거한 상태로도 함께 평가해야 합니다.

마지막으로 문서화입니다. 선택 근거에는 최소한 대상 작업, 비교한 후보, 측정한 세 축의 수치, 그리고 어떤 조건이 바뀌면 재검토할지가 들어가야 합니다. 마지막 항목이 빠지면 그 결정은 시간이 지나면서 아무도 손대지 못하는 고정값이 됩니다.

시험 함정

  • "토큰 단가가 낮은 모델이 총비용도 낮다"는 선택지 — 재시도와 사람 개입까지 포함한 작업당 비용으로 봐야 합니다.
  • max_tokens와 effort를 같은 것으로 취급하는 선택지 — 전자는 출력 상한이고 후자는 사고 깊이 지침입니다.
  • 사고를 켜면서 출력 예산을 그대로 두는 구성 — 사고 토큰이 예산을 나눠 쓰므로 응답이 잘릴 수 있습니다.
  • 비용 절감책으로 사고를 끄는 선택지 — 추론이 응답 본문으로 흘러나와 출력이 길어질 수 있어, 강도를 낮추는 쪽이 먼저입니다.
  • 계층형 라우팅이 언제나 비용을 줄인다는 서술 — 분류 비용, 캐시 분할, 이중 평가 부담이 함께 늘어납니다.
  • 프롬프트를 각각 따로 다듬은 상태로 두 모델을 비교하는 설계 — 모델이 아니라 프롬프트를 비교한 결과가 됩니다.

실습 시나리오

실제 시험과 같은 형식의 시나리오 문제입니다.

실시간 고객 응대 에이전트를 상위 모델로 교체했더니 응답당 지연시간이 늘어 SLA를 위협하고 있습니다. 정확도는 개선되어 되돌리고 싶지는 않습니다. 아키텍트가 가장 먼저 검토할 조치는 무엇입니까?

빌드 연습 · 작업당 비용 기준으로 모델 후보 비교하기

50
  1. 1.대표 사례 집합 구성

    실제 요청 로그에서 30건을 뽑아 흔한 사례 20건과 어려운 사례 10건으로 나누고 기대 결과를 붙입니다.

    기대 결과 · 각 사례에 입력, 기대 결과, 난이도 라벨이 붙은 파일이 만들어집니다.

  2. 2.동일 조건 벤치마크

    프롬프트와 툴 정의를 고정한 채 후보 두 개를 같은 사례로 돌리고 정확도·지연시간·토큰을 기록합니다.

    기대 결과 · 후보별로 세 축의 수치가 담긴 표가 나옵니다.

  3. 3.사고 강도 스윕

    상위 후보에 대해 강도를 낮음·중간·높음으로 바꿔가며 같은 측정을 반복합니다.

    기대 결과 · 강도별 품질과 지연시간의 곡선이 확보되고, 품질이 꺾이기 시작하는 지점이 보입니다.

  4. 4.작업당 비용 환산

    정확도를 반영해 재시도 횟수를 추정하고, 실패 시 사람 검토 시간을 비용으로 환산해 더합니다.

    기대 결과 · 토큰 단가가 아니라 완료된 작업 1건당 비용으로 후보 순위가 매겨집니다.

  5. 5.결정 문서 작성

    선택한 조합과 근거 수치, 그리고 재검토를 촉발할 조건을 한 쪽 분량으로 정리합니다.

    기대 결과 · 재검토 조건이 명시되어 있어, 6개월 뒤에도 결정을 다시 열어볼 수 있습니다.

출처 및 더 읽기