"Go로 LLM 서비스를 붙이다 보면 응답이 한꺼번에 오는 방식은 금방 답답해져요. 글자가 흘러나오는 화면을 만들려면 스트리밍을 쓰고, 모델이 함수를 부르게 하려면 도구 호출 결과를 되돌려 주는 반복문이 필요해요. Anthropic의 공식 Go SDK는 2026년 10월 9일에 v1.80.0이 나왔고, 최근 2주 사이 스트림 처리 쪽 변경이 몇 개 들어갔어요. 공식 문서와 저장소 예제를 기준으로 기본 흐름을 정리해 볼게요.

요즘 릴리스에서 바뀐 부분
릴리스 노트를 날짜 순으로 보면 스트리밍과 관계있는 항목이 두 개 눈에 띄어요. v1.76.0(9월 28일)에는 응답을 받는 중간에 연결이 끊기면 다시 시도하도록 고친 내용이 들어 있고, SSE 스트림을 이벤트마다 메모리를 새로 잡지 않고 해석하도록 바꾼 성능 항목도 있어요. v1.79.0(10월 7일)은 SSE 스트림과 자동 페이지 넘김 객체에 All() 반복자를 더했어요.
다만 All()의 사용 예는 이번에 열어 본 공식 문서 어디에도 나오지 않았어요. 아래 코드는 문서에 실려 있는 Next(), Current(), Err() 방식으로 썼어요. 새 반복자를 쓰고 싶다면 사용하는 버전의 소스에서 시그니처를 먼저 보고 옮기는 쪽이 안전해요.
설치 조건은 Go 1.24 이상이고, 명령은 go get github.com/anthropics/anthropic-sdk-go예요. 요청 구조체가 Go 1.24의 omitzero 규칙을 따르기 때문이에요.
스트림을 열고 한 줄씩 읽어요
공식 문서의 예제는 client.Messages.NewStreaming으로 스트림을 열어요. 매개변수는 일반 Messages.New와 같아서 모델, MaxTokens, 메시지 목록을 그대로 넘기면 돼요.
stream := client.Messages.NewStreaming(ctx, params)
message := anthropic.Message{}
for stream.Next() {
event := stream.Current()
if err := message.Accumulate(event); err != nil {
return err
}
switch ev := event.AsAny().(type) {
case anthropic.ContentBlockDeltaEvent:
if d, ok := ev.Delta.AsAny().(anthropic.TextDelta); ok {
print(d.Text)
}
}
}
if err := stream.Err(); err != nil {
return err
}
여기서 눈여겨볼 곳은 Accumulate예요. 이벤트를 화면에 뿌리면서 같은 이벤트를 message에 계속 합쳐 두면, 스트림이 끝난 뒤 완성된 응답 객체를 그대로 쓸 수 있어요.
루프가 끝났다고 성공이라고 보면 안 돼요. Next()가 false를 돌려준 이유가 정상 종료인지 오류인지는 stream.Err()로만 알 수 있어요. 이 검사를 빼 먹은 코드는 중간에 끊긴 응답을 아무 일 없이 지나가요.
공식 스트리밍 문서에는 이벤트 흐름에 ping이 끼어들 수 있고, 과부하일 때는 스트림 도중에 overloaded_error가 올 수 있다고 적혀 있어요. 새 이벤트 종류가 추가될 수 있으니 모르는 타입은 조용히 넘기라는 안내도 있고요. 위 코드의 switch에 default로 오류를 내지 않는 이유가 이거예요.
도구 호출은 반복문 하나로 끝나요
도구를 쓰면 응답 한 번으로 끝나지 않아요. 모델이 ToolUseBlock을 담아 답하면, 우리 코드가 그 도구를 실행하고 결과를 NewToolResultBlock(block.ID, 결과, false)로 포장해서 사용자 메시지로 다시 보내요. 그 전에 방금 받은 응답을 message.ToParam()으로 대화 기록에 붙여 둬야 하고요.
문서의 예제 구조는 이래요. 응답을 받는다. 기록에 붙인다. 내용 블록을 돌면서 ToolUseBlock이면 variant.JSON.Input.Raw()를 json.Unmarshal로 풀어 함수를 실행한다. 결과가 하나도 없으면 반복을 끝낸다.
저장소의 examples/tools-streaming도 같은 모양이에요. 한 가지 짚을 점은 이 예제가 stop_reason을 보지 않고 도구 결과가 비었는지로 끝낸다는 거예요. 출력이 max_tokens에서 잘리는 경우까지 다루려면 종료 이유를 한 번 더 확인하는 코드를 직접 넣어야 해요.
스트리밍으로 도구 입력을 받을 때는 이벤트에 조각난 JSON이 와요. 공식 문서는 이 조각을 input_json_delta의 partial_json으로 설명하고, content_block_stop이 올 때까지 이어 붙인 다음에 해석하라고 안내해요. 조각 하나를 바로 Unmarshal하면 거의 확실히 실패해요. 예제처럼 Accumulate가 모아 준 결과에서 입력을 꺼내는 편이 간단해요.

재시도와 시간 제한은 따로 정해요
SDK는 연결 오류, 408, 409, 429, 그리고 500번대 응답을 기본 두 번 다시 시도해요. 횟수는 option.WithMaxRetries로 바꿔요. 도구 호출 루프 안에서 부르는 API가 한 번 실패해도 같은 요청이 반복될 수 있으니, 도구의 부작용은 요청 단위가 아니라 도구 실행 단위에서 따로 지켜야 해요.
시간 제한도 구분이 필요해요. 스트리밍이 아닌 Messages 요청은 기본 10분이 지나면 시간 초과가 나고, MaxTokens를 크게 잡았는데 스트리밍을 쓰지 않으면 SDK가 오류를 낼 수 있다고 문서가 알려 줘요. 컨텍스트에 건 제한 시간은 재시도 때문에 다시 시작되지 않고, 시도마다 제한을 두려면 option.WithRequestTimeout을 써요.
직접 반복문을 짜는 게 부담스러우면 저장소 예제에 tool-runner와 tool-runner-streaming도 있어요. 이름만 확인했고 동작은 열어 보지 않았으니, 쓰려면 예제 소스를 먼저 읽어 보세요.
참고한 자료
- anthropic-sdk-go 릴리스 목록 https://github.com/anthropics/anthropic-sdk-go/releases (확인일 2026-10-10)
- 릴리스 v1.79.0 노트 https://github.com/anthropics/anthropic-sdk-go/releases/tag/v1.79.0 (확인일 2026-10-10)
- 릴리스 v1.76.0 노트 https://github.com/anthropics/anthropic-sdk-go/releases/tag/v1.76.0 (확인일 2026-10-10)
- Anthropic 공식 문서 Go SDK https://platform.claude.com/docs/en/cli-sdks-libraries/sdks/go (확인일 2026-10-10)
- Anthropic 공식 문서 Streaming Messages https://platform.claude.com/docs/en/build-with-claude/streaming (확인일 2026-10-10)
- anthropic-sdk-go 예제 tools-streaming https://github.com/anthropics/anthropic-sdk-go/tree/main/examples/tools-streaming (확인일 2026-10-10)"