⚡ 중급2026-05-077분
긴 응답이 뚝뚝 끊길 때: Streaming + 토큰 관리로 UX 살리기
max_tokens 초과나 느린 첫 응답으로 사용자가 이탈하기 전에, Streaming API와 토큰 예산 전략을 함께 적용해 체감 속도와 비용을 동시에 잡는 방법을 소개합니다.
streamingtoken-managementapi-options
문제: 왜 응답이 늦거나 잘리는가
max_tokens를 크게 잡으면 전체 생성이 끝날 때까지 사용자는 빈 화면을 봅니다. 반대로 작게 잡으면 문장 중간에 응답이 잘립니다. 두 문제를 동시에 해결하는 열쇠가 Streaming과 토큰 예산 계획입니다.
Streaming으로 첫 글자를 빠르게
import anthropic
client = anthropic.Anthropic()
def stream_response(user_query: str, context: str = "") -> str:
"""
Streaming으로 응답을 출력하면서 전체 텍스트도 누적 반환.
context: RAG 등으로 주입할 배경 문서 (토큰 절약을 위해 미리 요약 권장)
"""
system_prompt = (
"당신은 간결한 기술 문서 작성 전문가입니다. "
"답변은 핵심만 담아 500자 이내로 작성하세요."
)
if context:
system_prompt += f"\n\n[참고 문서]\n{context[:1500]}" # 컨텍스트 길이 캡
collected_text = []
# stream() 컨텍스트 매니저 사용
with client.messages.stream(
model="claude-sonnet-4-6",
max_tokens=700, # 예상 출력 + 20% 여유
temperature=0.3, # 사실 기반 작업엔 낮은 temperature
system=system_prompt,
messages=[{"role": "user", "content": user_query}],
) as stream:
for text_chunk in stream.text_stream:
print(text_chunk, end="", flush=True) # 실시간 출력
collected_text.append(text_chunk)
print() # 줄바꿈
full_response = "".join(collected_text)
# 사용 토큰 확인 (과금 모니터링)
final_message = stream.get_final_message()
usage = final_message.usage
print(f"\n[토큰 사용] 입력: {usage.input_tokens} / 출력: {usage.output_tokens}")
return full_response
if __name__ == "__main__":
query = "Transformer 아키텍처의 Attention 메커니즘을 초보자에게 설명해줘"
result = stream_response(query)
코드 핵심 포인트
| 옵션 | 설정값 | 이유 |
|---|---|---|
| temperature | 0.3 | 사실 기반 답변의 일관성 확보 |
| max_tokens | 700 | 목표 길이(500자) × 약 1.4 (한국어 토큰 비율 고려) |
| context 캡 | 1500자 | 불필요한 입력 토큰 낭비 방지 |
| stream.get_final_message() | 스트림 종료 후 | 실제 사용량 로깅·알림에 활용 |
토큰 예산 3원칙
1. 입력 토큰 줄이기
- system 프롬프트는 재사용 가능한 구조로 고정하고, 변하는 부분만 user 메시지에 넣습니다.
- 긴 문서를 통째로 붙이지 말고, 관련 청크만 추출해 주입하세요 (RAG의 핵심).
2. 출력 토큰 예측하기
- 한국어 기준 1토큰 ≈ 0.5~0.7글자입니다. 500자 답변이면
max_tokens는 700~800이 적당합니다. stop_sequences를 활용해 특정 구분자에서 생성을 조기 종료할 수도 있습니다.
3. stop_reason 반드시 확인하기
if final_message.stop_reason == "max_tokens":
# 응답이 잘렸음 → 사용자에게 안내하거나 재요청
print("⚠️ 응답이 max_tokens 한도로 잘렸습니다.")
적용 체크리스트
- [ ]
stream()컨텍스트 매니저를 사용해 첫 토큰 도달 시간(TTFT)을 단축했는가? - [ ]
max_tokens를 목표 출력 길이 기준으로 계산해 설정했는가? - [ ]
stop_reason == "max_tokens"케이스를 에러 핸들링에 포함했는가? - [ ]
usage.input_tokens+usage.output_tokens를 로그로 기록하고 있는가? - [ ] 사실 기반 작업에
temperature를 0.1~0.4로 낮춰 일관성을 확보했는가? - [ ] 컨텍스트 문서를 주입할 때 길이 상한(예: 1500자)을 두어 토큰 폭증을 방지했는가?