Fine-grained tool streaming은 왜 빠른 도구 호출이 아니라 검증 지점 이동일까
도구 인자를 생성되는 즉시 스트리밍하면 사용자 경험은 좋아지지만, 우리 런타임은 무엇을 새로 책임져야 할까?
Fine-grained tool streaming은 왜 빠른 도구 호출이 아니라 검증 지점 이동일까
- 카테고리: ai_dev
- 예상 읽기 시간: 10분
- 오늘의 질문: 도구 인자를 생성되는 즉시 스트리밍하면 사용자 경험은 좋아지지만, 우리 런타임은 무엇을 새로 책임져야 할까?
- 핵심 출처:
- Fine-grained tool streaming - 게시일 미표기, 확인일 2026-07-24
- Tool use with Claude - 게시일 미표기, 확인일 2026-07-24
- Streaming Messages - 게시일 미표기, 확인일 2026-07-24
- Anthropic Claude tool use - Amazon Bedrock - 게시일 미표기, 확인일 2026-07-24
1. 왜 지금 봐야 하나
에이전트 제품을 만들다 보면 “모델 답변을 스트리밍하자”는 요구는 금방 나온다. 사용자는 빈 화면을 오래 기다리는 것보다, 조금씩 진행되는 화면을 더 신뢰한다. 그런데 도구 호출이 들어가면 이야기가 달라진다. 모델이 긴 SQL, 큰 문서, 코드 파일, 여러 줄의 패치, 대량 검색 쿼리 같은 도구 인자를 만들 때는 일반 텍스트처럼 조금씩 보여주기 어렵다. 기존 방식에서는 API가 도구 인자 JSON을 어느 정도 버퍼링하고 검증한 뒤에야 클라이언트로 흘려보낼 수 있기 때문이다.
Anthropic의 Fine-grained tool streaming 문서는 이 병목을 직접 다룬다. eager_input_streaming을 켠 사용자 정의 도구에서는 Claude가 도구 입력을 생성하는 즉시 조각으로 스트리밍할 수 있고, 서버 쪽 JSON 버퍼링과 검증을 기다리지 않는다. AWS Bedrock 문서도 같은 기능을 Claude Sonnet/Haiku/Opus 계열에서 “도구 사용 파라미터를 버퍼링 또는 JSON 검증 없이 스트리밍해 지연을 줄이는 기능”으로 설명한다.
좋아 보인다. 하지만 개발자에게 중요한 문장은 “빠르다”가 아니라 그 뒤의 경고다. 부분 JSON이나 잘못된 JSON을 받을 수 있다. max_tokens로 중간에 끊긴 도구 인자도 받을 수 있다. 즉 fine-grained tool streaming은 검증을 없애는 기능이 아니다. 검증 지점을 모델 API 서버 앞단에서 우리 애플리케이션 런타임으로 옮기는 기능이다.
오늘의 핵심은 이것이다. 도구 인자 스트리밍은 UX와 지연에는 이득을 주지만, 파서·상태 머신·취소·재시도·감사 로그를 제대로 설계하지 않으면 “빠르게 실패하는 에이전트”를 만들 수 있다.
2. 핵심 개념
일반적인 client tool 흐름은 단순하다. 애플리케이션이 도구 이름, 설명, input_schema를 모델에게 알려준다. 모델이 도구가 필요하다고 판단하면 응답은 stop_reason: "tool_use"와 tool_use 블록을 포함한다. 애플리케이션은 그 블록의 name과 input을 읽어 실제 함수를 실행하고, 결과를 tool_result로 다시 보낸다. Claude 문서의 도구 사용 개요는 이 왕복을 “도구 정의 → 모델의 tool_use → 애플리케이션 실행 → tool_result 반환 → 최종 답변”으로 설명한다.
여기서 스트리밍을 켜면 응답은 Server-Sent Events 형태로 온다. Claude Streaming Messages 문서는 표준 스트림이 message_start, content_block_start, 여러 content_block_delta, content_block_stop, message_delta, message_stop 순서로 흐른다고 설명한다. 텍스트뿐 아니라 tool use도 content block 안에서 델타로 도착한다.
fine-grained tool streaming이 바꾸는 지점은 tool_use 블록의 입력 인자 도착 시점이다. 표준 buffered streaming에서는 API가 각 파라미터 값을 버퍼링하고 검증한 뒤 흘려보내므로 큰 파라미터는 늦게 나타날 수 있다. fine-grained 방식에서는 input_json_delta의 partial_json 문자열 조각을 누적해 나중에 파싱한다. 문서가 제시하는 누적 계약은 간단하다.
content_block_start에서type: "tool_use"를 만나면 빈 문자열 버퍼를 만든다.- 각
content_block_delta의input_json_delta.partial_json을 버퍼에 이어 붙인다. content_block_stop에서 누적 문자열을 JSON으로 파싱한다.
차이는 작아 보이지만 운영 의미는 크다. 표준 방식에서 “파싱 가능한 JSON”은 플랫폼이 어느 정도 보장해 주는 계약에 가깝다. fine-grained 방식에서 “파싱 가능한 JSON”은 우리 코드가 스트림 끝, 중단, 재시도, 오류 도구 결과까지 포함해 직접 만들어야 하는 계약이다.
3. 최신 이슈와 연결
최근 LLM 제품의 방향은 한 가지로 수렴한다. 모델이 단순히 글을 쓰는 것이 아니라, 도구를 고르고, 도구 인자를 만들고, 실행 결과를 다시 읽고, 여러 단계 작업을 이어간다. 이때 병목은 모델의 최종 답변 토큰만이 아니다. 도구 인자 생성도 병목이 된다.
예를 들어 “리포지토리 전체에서 보안 위험을 찾아 패치 파일을 만들어줘”라는 기능을 생각해 보자. 모델은 write_patch 같은 도구에 수백 줄의 diff를 인자로 넣을 수 있다. 또는 create_document 도구에 긴 Markdown 문서를 넣을 수 있다. 표준 방식에서는 이 긴 인자가 완성되고 검증될 때까지 클라이언트가 아무것도 못 볼 수 있다. 사용자는 멈춘 것처럼 느끼고, 서버는 장시간 연결을 유지해야 한다.
fine-grained streaming은 이 UX를 개선한다. 도구 인자 조각이 빨리 도착하면 애플리케이션은 “파일 내용을 생성 중”, “패치 30% 수신”, “JSON 조립 중” 같은 진행 상태를 보여줄 수 있다. 일부 도구는 안전한 범위에서 미리 준비 작업을 시작할 수도 있다. 예를 들어 최종 실행은 하지 않더라도 임시 파일을 열거나, 크기 제한을 계산하거나, 사용자가 취소할 UI를 빠르게 띄울 수 있다.
하지만 공식 문서들이 반복해서 경고하는 실패 모드도 같이 봐야 한다. Anthropic 문서는 서버가 도구 입력을 버퍼링하거나 검증하지 않기 때문에 부분 또는 invalid JSON을 받을 수 있고, stop_reason: "max_tokens"가 파라미터 중간을 잘라낼 수 있다고 설명한다. Bedrock 문서도 incomplete JSON 입력을 코드에서 처리해야 한다고 강조한다. 따라서 이 기능은 “도구 호출을 빨리 실행하라”가 아니라 “도구 호출 후보를 빨리 관측하되, 실행 가능 상태가 되었는지 별도 판정하라”에 가깝다.
4. 개발자 관점 해석
개발자 관점에서 fine-grained tool streaming은 세 가지 경계를 다시 그리게 만든다.
첫째, 표시와 실행의 경계다. 스트림 조각을 받는다고 바로 도구를 실행하면 안 된다. 아직 JSON이 닫히지 않았고, 필수 필드가 없을 수 있고, 마지막 조각에서 의미가 바뀔 수 있다. UI에는 “생성 중”으로 보여줄 수 있지만, 실제 side effect는 content_block_stop 이후 파싱과 검증이 끝난 뒤에만 실행하는 것이 기본값이다. 특히 파일 쓰기, 이메일 발송, 결제, 배포, DB 변경 같은 도구는 부분 인자 단계에서 절대 실행하면 안 된다.
둘째, 스키마 검증의 위치다. input_schema는 여전히 중요하지만, fine-grained 방식에서는 스트리밍 중간에 schema-valid 객체가 존재하지 않을 수 있다. 그래서 런타임에는 두 층의 검증이 필요하다. 하나는 “문자열 버퍼를 안전하게 누적하는 파서 레이어”이고, 다른 하나는 “완성된 객체가 도구 실행 계약을 만족하는지 확인하는 validator 레이어”다. 파싱 실패와 스키마 실패를 구분해야 한다. 파싱 실패는 모델 출력이 아직 불완전하거나 깨졌다는 뜻이고, 스키마 실패는 JSON은 맞지만 도구 계약에 맞지 않는다는 뜻이다.
셋째, 오류를 모델에게 돌려주는 방식이다. Anthropic 문서는 파싱할 수 없는 입력을 받았을 때 raw string을 안전하게 escape한 JSON wrapper로 감싸고, tool_result에 is_error: true를 붙여 돌려주는 패턴을 제시한다. 이 패턴이 중요한 이유는 모델에게 “도구가 실패했다”가 아니라 “네가 만든 도구 입력이 파싱되지 않았다”는 진단 정보를 주기 때문이다. 그래야 모델이 같은 호출을 수정해서 재시도할 수 있다.
이 지점에서 흔한 실수가 나온다. 개발자가 invalid JSON을 조용히 버리거나, 임의로 고쳐서 실행하거나, 실패를 일반 예외로 처리해 사용자에게만 보여주는 것이다. 에이전트 루프에서는 오류도 대화의 일부다. 모델이 수정할 수 있는 오류라면 구조화해서 되돌려줘야 한다. 반대로 보안상 민감한 오류라면 모델에게 상세 내용을 주지 않고 런타임 로그에만 남겨야 한다.
5. 내 프로젝트에 적용할 체크포인트
fine-grained tool streaming을 켜기 전에 아래 질문을 통과해야 한다.
- 이 도구의 인자는 정말 커서 buffered streaming 지연이 문제인가? 짧은 검색 쿼리나 간단한 ID 조회라면 복잡도만 늘 수 있다.
- 도구가 side effect를 만드는가? 만든다면 스트림 완료 전에는 절대 실행하지 않는 게이트가 있는가?
content_block_start부터content_block_stop까지 block index별 버퍼를 분리해서 누적하는가?message_stop없이 연결이 끊기거나,stop_reason이max_tokens일 때 해당 도구 호출을 실패로 닫는가?- 파싱 실패, 스키마 실패, 정책 실패, 실제 도구 실행 실패를 서로 다른 오류 타입으로 기록하는가?
- invalid JSON을 모델에게 되돌려줄 때 raw 문자열을 안전하게 escape하는가?
- UI에는 “생성 중”, “검증 중”, “실행 승인 대기”, “실행 완료” 상태가 분리되어 있는가?
- 로그에는 최종 실행된 인자만 남기는가, 아니면 부분 조각도 남기는가? 부분 조각에 개인정보나 비밀이 들어갈 수 있다면 보관 정책은 무엇인가?
- 재시도 시 같은 도구가 중복 실행되지 않도록 idempotency key를 쓰는가?
작은 팀이라면 모든 도구에 켜기보다 “큰 텍스트를 생성하지만 side effect가 낮은 도구”부터 시작하는 편이 안전하다. 예를 들어 draft_file, generate_patch_preview, compose_report는 후보가 될 수 있다. 반대로 send_email, charge_payment, deploy_production, delete_records는 fine-grained streaming보다 승인·검증·감사 로그가 먼저다.
6. 오늘 10분 액션
오늘 바로 할 수 있는 실습은 “내 도구 하나를 스트리밍 상태 머신으로 그려보기”다.
- 현재 프로젝트의 LLM 도구 중 인자가 가장 큰 도구 하나를 고른다. 예:
write_file,create_report,apply_patch,run_query. - 종이에 상태를 5개로 나눈다.
receiving_partial_json→json_parse_pending→schema_validation_pending→approval_or_policy_check→executed_or_error_returned. - 각 상태에서 “사용자에게 보여줄 수 있는 것”과 “절대 실행하면 안 되는 것”을 한 줄씩 적는다.
max_tokens로 중간에 끊겼을 때 어떤 error tool_result를 모델에게 돌려줄지 예시 JSON을 만든다.- 마지막으로 이 도구에 fine-grained streaming을 켤지 말지 결정한다. 이유가 “빠를 것 같아서”뿐이면 아직 보류한다. “큰 인자 때문에 TTFT가 나쁘고, 실행 전 검증 게이트가 있다”면 실험 후보가 된다.
핵심은 기능을 켜는 것이 아니다. 스트림 조각을 실행 명령이 아니라 아직 검증 전인 관측 데이터로 다루는 습관을 만드는 것이다.
7. 더 볼 자료
- Fine-grained tool streaming:
eager_input_streaming, partial JSON, invalid JSON 처리 패턴을 확인할 수 있다. - Tool use with Claude: client tool/server tool,
tool_use,tool_result,tool_choice의 기본 흐름을 복습하기 좋다. - Streaming Messages: SSE 이벤트 순서와 content block delta 누적 방식을 확인할 수 있다.
- Anthropic Claude tool use - Amazon Bedrock: Bedrock에서의 tool use와 fine-grained streaming 동작, beta header, partial JSON 주의사항을 확인할 수 있다.
중복 회피 메모
로컬 content/generated와 Supabase 최근 ai_dev 글을 확인했다. 최근 글은 Hosted File Search 인덱스 수명주기, LLM-as-a-judge 보정, MCP Sampling 권한 요청, 임베딩 차원/pgvector 스키마, 멀티모달 토큰 예산, LLM 서비스 티어, 프롬프트 인젝션 신뢰 경계, Programmatic Tool Calling 실행 경계를 다뤘다. 이번 글은 도구 선택, 권한 승인, sandbox 실행, eval이 아니라 도구 인자 스트리밍에서 JSON 검증 지점이 플랫폼에서 애플리케이션 런타임으로 이동할 때 생기는 파서·상태 머신·오류 반환 계약에 집중한다.
핵심 출처
로그인하면 이 글을 북마크하고, 나만 보는 한 줄 메모를 남길 수 있어요.
댓글 0
최신순 ▾혹시 이 글을 읽는 동료 개발자가 있다면, GitHub으로 로그인하고 한 줄 흔적을 남겨줘요. (스팸 방지용 로그인이에요)