정확도를 올리는 거의 모든 수단은 지연시간이나 비용을 늘립니다. 이 수업에서는 어떤 손잡이가 어떤 축을 움직이는지 정리하고, 구성 결정을 감각이 아니라 근거로 정당화하는 방법을 다룹니다.
손잡이마다 움직이는 축이 다르다
정확도-지연시간 논의가 흐려지는 이유는 서로 다른 손잡이를 한 덩어리로 다루기 때문입니다. 각 손잡이가 무엇을 대가로 무엇을 얻는지 분리해야 합니다.
| 손잡이 | 정확도 | 지연시간 | 비용 |
|---|---|---|---|
| 모델 등급 상향 | ↑ | ↑ | ↑ |
| 사고 강도(effort) 상향 | ↑ | ↑ | ↑ |
| 프롬프트 캐싱 | – | ↓ | ↓ |
| 스트리밍 | – | 체감 ↓ | – |
| 병렬 툴 호출 | – | ↓ | – |
| 검증 루프 추가 | ↑ | ↑ | ↑ |
| 컨텍스트 축소 | 상황에 따라 | ↓ | ↓ |
이 표에서 가장 중요한 줄은 캐싱, 스트리밍, 병렬 호출입니다. 이 세 가지는 정확도를 깎지 않고 지연시간을 줄입니다. 트레이드오프가 아니라 그냥 이득이라, 다른 손잡이를 건드리기 전에 먼저 확보해야 할 것들입니다.
시험에서 자주 나오는 함정이 여기 있습니다. "지연시간이 문제다"라는 상황에서 모델을 무조건 작은 것으로 바꾸는 선택지는 대개 오답입니다. 캐싱이 안 걸려 있거나 순차 호출을 병렬로 바꾸지 않은 상태라면, 품질을 깎지 않고 얻을 수 있는 개선이 남아 있기 때문입니다.
반대로 캐싱이 정확도를 올린다는 서술도 틀립니다. 캐싱은 같은 프리픽스를 다시 계산하지 않을 뿐이고 모델이 보는 내용은 동일합니다.
무료 구간을 먼저 소진한다
프롬프트 캐싱은 접두부 일치(prefix match)로 동작합니다. 렌더링 순서는 tools → system → messages이고, 프리픽스의 어느 한 바이트라도 달라지면 그 뒤 전부가 무효화됩니다.
따라서 캐싱을 얻으려면 안정적인 내용이 물리적으로 앞에 와야 합니다. 여기서 흔한 실수가 시스템 프롬프트 머리말에 현재 시각이나 세션 ID를 끼워 넣는 것입니다. 그 한 줄 때문에 뒤의 모든 것이 매번 새로 계산됩니다.
# 안티패턴: 앞부분이 매 요청 달라져 뒤 전부가 무효화된다
system = f"현재 시각: {now()}\n\n{LARGE_STABLE_PROMPT}"
# 개선: 안정적인 부분을 앞에 두고 캐시 경계를 건다
system = [
{"type": "text", "text": LARGE_STABLE_PROMPT,
"cache_control": {"type": "ephemeral"}},
]
# 변하는 값은 messages 뒤쪽에 넣는다
캐시가 걸렸는지는 추측하지 말고 응답의 usage.cache_read_input_tokens로 확인합니다. 같은 프리픽스로 반복 요청했는데 이 값이 계속 0이면 어딘가에 조용한 무효화 요인이 있는 것입니다. 정렬되지 않은 JSON 직렬화, 요청마다 달라지는 툴 목록, 사용자별로 다른 시스템 프롬프트가 대표적입니다.
병렬 툴 호출은 기본 동작입니다. 하나의 응답에 여러 툴 호출이 담겨 올 수 있으므로 동시에 실행한 뒤 모든 결과를 하나의 메시지에 담아 돌려줘야 합니다. 결과를 여러 메시지로 쪼개 보내면 모델이 병렬 호출을 그만두는 방향으로 학습되어, 이후 턴이 조용히 순차적으로 바뀝니다.
스트리밍은 총 소요 시간을 줄이지는 않지만 첫 토큰까지의 시간을 앞당깁니다. 사용자가 기다리는 화면이라면 체감 개선이 크고, 배치 작업이라면 의미가 없습니다. 다만 출력이 큰 요청에서는 스트리밍이 사실상 필수입니다 — 긴 출력을 비스트리밍으로 받으면 HTTP 타임아웃에 걸립니다.
진짜 트레이드오프: 사고 강도와 검증
무료 구간을 다 쓴 다음에야 진짜 트레이드오프가 남습니다.
사고 강도(effort) 는 모델이 얼마나 깊게 생각하고 얼마나 많이 행동할지를 조절합니다. 낮추면 응답이 빨라지고 토큰이 줄지만, 복잡한 문제에서 얕게 생각할 위험이 생깁니다. 높이면 반대입니다.
여기서 중요한 실무 감각은 강도가 높다고 항상 좋지 않다는 것입니다. 높은 강도는 단순한 작업에서 과하게 탐색하고 요청하지 않은 정리까지 하는 경향이 있습니다. 그래서 실무 권고는 작업 유형별로 다르게 두고 평가 세트로 확인하는 것입니다.
검증 루프는 정확도를 올리는 가장 확실한 수단이지만 왕복이 늘어납니다. 여기서 설계 판단이 갈립니다.
| 검증 방식 | 지연시간 영향 | 언제 쓰는가 |
|---|---|---|
| 스키마 강제(구조화된 출력) | 거의 없음 | 출력 형식이 정해진 경우 항상 |
| 코드로 하는 규칙 검증 | 작음 | 결정적으로 판정 가능한 규칙 |
| 모델을 다시 부르는 재검토 | 큼 | 판단이 필요하고 실패 비용이 큰 경우 |
위에서부터 소진하는 것이 원칙입니다. 형식 오류를 잡으려고 모델을 한 번 더 부르는 것은 스키마로 해결되는 문제에 왕복을 낭비하는 것입니다.
여러 번의 왕복을 하나로 접는 방법도 있습니다. 툴 호출이 사슬처럼 이어질 때, 각 호출마다 결과가 모델의 컨텍스트로 돌아왔다가 다음 호출이 나가면 왕복이 호출 수만큼 쌓입니다. 이 사슬을 코드로 묶어 한 번에 실행하면 중간 결과가 컨텍스트를 거치지 않고 최종 결과만 돌아옵니다. 왕복과 토큰이 함께 줄어드는 구조적 개선입니다.
구성 결정을 근거로 정당화하기
요강이 요구하는 것은 최적화 자체가 아니라 결정을 정당화하는 것입니다. 정당화에는 세 가지가 필요합니다.
1. 목표가 분포로 정의되어 있을 것. "빨라야 한다"는 목표가 아닙니다. 지연시간은 분포이므로 평균이 아니라 백분위로 말해야 합니다. p50이 좋아도 p95가 나쁘면 사용자 스무 명 중 한 명은 매번 나쁜 경험을 합니다. SLA는 p95나 p99로 잡습니다.
2. 측정이 같은 조건에서 이뤄질 것. 캐시가 더워진 상태와 차가운 상태의 지연시간은 다릅니다. 첫 요청만 재보고 "느리다"고 결론 내리면 잘못된 처방이 나옵니다. 반대로 캐시가 더워진 상태만 재면 실제 사용자의 첫 경험을 놓칩니다. 둘 다 기록해야 합니다.
3. 변경이 한 번에 하나일 것. 모델 등급과 사고 강도를 동시에 바꾸면 어느 쪽이 효과를 냈는지 알 수 없습니다.
정당화 문서의 뼈대는 다음과 같습니다.
결정: 요약 경로의 사고 강도를 high에서 medium으로 낮춘다
근거
- 목표: p95 지연시간 3초 이하 (현재 4.8초)
- 무료 구간 확인: 캐시 적중률 92%, 툴 호출 병렬화 완료 → 남은 여지 없음
- 실험: 평가 세트 200건에서 강도만 변경
· 정확도 94.1% → 93.6% (허용 기준 93% 이상)
· p95 4.8초 → 2.9초
- 판단: 정확도 0.5%p를 내주고 목표를 충족
되돌리는 조건
- 정확도가 93% 미만으로 떨어지면 강도를 되돌리고 다른 경로를 찾는다
마지막 항목이 중요합니다. 되돌리는 조건을 함께 적어두면 그 결정이 영구적인 것이 아니라 가정 위에 선 것임이 분명해지고, 나중에 조건이 바뀌었을 때 재검토할 근거가 남습니다.
시험 함정
- 지연시간 문제에 곧바로 더 작은 모델을 제시하는 선택지 — 캐싱과 병렬화 같은 품질 손실 없는 수단이 남아 있으면 그쪽이 먼저입니다.
- 프롬프트 캐싱이 정확도를 개선한다는 서술 — 캐싱은 같은 내용을 다시 계산하지 않을 뿐 모델이 보는 것은 동일합니다.
- 스트리밍이 총 처리 시간을 줄인다는 서술 — 줄어드는 것은 첫 토큰까지의 체감 시간이고 총 소요 시간은 그대로입니다.
- 병렬 툴 결과를 여러 메시지로 나눠 보내도 무방하다는 서술 — 하나의 메시지에 모아 보내지 않으면 이후 병렬 호출이 줄어듭니다.
- 지연시간 목표를 평균으로 정의하는 선택지 — 분포의 꼬리가 사용자 경험을 결정하므로 p95·p99로 잡아야 합니다.
- 형식 오류를 잡으려고 모델 재호출 검증을 추가하는 방안 — 구조화된 출력으로 왕복 없이 해결할 수 있는 문제입니다.
실습 시나리오
실제 시험과 같은 형식의 시나리오 문제입니다.
문서 질의응답 서비스의 p95 지연시간이 목표를 초과했습니다. 요청마다 동일한 8,000토큰 규격 문서가 시스템 프롬프트로 들어가고, 그 앞줄에 요청 처리 시각이 문자열로 삽입되어 있습니다. 툴 호출은 평균 3회이며 순차 실행됩니다. 가장 먼저 시도할 조치는 무엇입니까?
빌드 연습 · 지연시간 예산 배분과 구성 결정 정당화
약 50분1.구간별 시간 계측
한 요청을 프롬프트 조립·모델 추론·툴 실행·후처리 구간으로 나눠 각각의 소요 시간을 기록합니다.
기대 결과 · 구간별 시간이 합쳐져 전체와 맞고, 가장 큰 구간이 특정됩니다.
2.캐시 적중 확인
동일 프리픽스로 반복 요청해 cache_read_input_tokens를 확인합니다.
기대 결과 · 적중률이 수치로 나오고, 0이면 프리픽스를 바이트 단위로 비교해 원인이 특정됩니다.
3.무료 구간 소진
캐시 경계 배치와 툴 호출 병렬화를 적용하고 다시 측정합니다.
기대 결과 · 정확도 변화 없이 p95가 개선된 수치로 기록됩니다.
4.단일 변수 실험
사고 강도만 한 단계 낮춰 평가 세트를 돌립니다.
기대 결과 · 정확도와 p95가 함께 기록되고, 다른 설정은 변경되지 않았음이 확인됩니다.
5.정당화 문서 작성
목표·근거·실험 결과·되돌리는 조건을 담은 결정 문서를 씁니다.
기대 결과 · 지연시간 목표가 백분위로 적혀 있고 되돌리는 조건이 명시되어 있습니다.