Home / Amazon Bedrock Runtime API / Bedrock Runtime
Note

Bedrock Runtime

boto3 클라이언트 구조, Runtime API 2종(InvokeModel vs Converse), 인증, VPC, 에러 핸들링

⏱ 30분 20 / 189

Bedrock Runtime

boto3 클라이언트 구조, Runtime API 2종(InvokeModel vs Converse), 인증, VPC, 에러 핸들링

모든 호출은 Runtime을 지난다

InvokeModel부터 Converse · 함수 호출 · 멀티모달까지

InvokeModel API

모델별 네이티브 포맷 · 저수준 호출

PART 2 · InvokeModel API

InvokeModel — 동기 vs 스트리밍

InvokeModel도 두 가지 — 한 번에 받는 invoke_model · 흘려받는 invoke_model_with_response_stream




📦 invoke_model() — 동기


애플리
케이션



0s · 요청 전송


⏳ 0.5s ~ 6s — 빈 화면 · 생성 중


6s · 전체 응답 한 번에



Bedrock
Runtime




🌊 invoke_model_with_response_stream()


애플리
케이션



0s · 같은 요청



0.5s · chunk — 첫 델타 "1."



2s · 델타 연속 수신 — 화면 갱신



6s · 마지막 chunk — stop_reason



Bedrock
Runtime




takeaway

총 생성 시간은 같고 첫 글자 체감(6s vs 0.5s, 예시)이 다릅니다 — chunk 델타 JSON까지 모델별 포맷이라 모델을 바꾸면 파서도 다시 씁니다.

PART 2 · InvokeModel API

실물 요청·응답 — 네이티브 포맷

같은 회의록 요약 요청 — 필드 이름부터 응답 구조까지 전부 모델별 포맷

takeaway

같은 요청, 같은 정보인데 필드 이름과 구조가 전부 모델별입니다 — 이 차이를 흡수하는 것이 Converse입니다.

요청 — 네이티브 JSON (json.dumps 필수)

python
body = json.dumps({
    # Anthropic 전용 필수
    'anthropic_version': 'bedrock-2023-05-31',
    # 네이티브에선 필수 (Converse는 옵션)
    'max_tokens': 512,
    'messages': [{'role': 'user',
        'content': '이 회의록을 3줄로 요약해 줘'}],
})

response = bedrock_runtime.invoke_model(
    modelId='us.anthropic.claude-sonnet-4-6',
    body=body)

응답 (예시 수치) — 이것도 모델별 포맷

json
{"content": [{"type": "text",
    "text": "1. 3분기 예산 확정
2. 출시 일정 9월 합의
3. QA 담당 지정"}],

 // 스네이크 케이스 — Converse는 stopReason
 "stop_reason": "end_turn",

 // 이름까지 다름 — Converse는 inputTokens
 "usage": {"input_tokens": 1873,
           "output_tokens": 96}}
PART 2 · InvokeModel API

InvokeModel 호출

모델별 JSON 포맷으로 요청 — 응답도 모델별 파싱 필요

python
import json

# Anthropic Claude - InvokeModel
body = json.dumps({
    "anthropic_version": "bedrock-2023-05-31",
    "max_tokens": 1024,
    "messages": [
        {"role": "user", "content": "Python으로 피보나치 함수 작성"}
    ]
})

response = bedrock_runtime.invoke_model(
    modelId='us.anthropic.claude-sonnet-4-6',
    body=body
)

# 응답 파싱 (Anthropic 포맷)
output = json.loads(response['body'].read())
code = output['content'][0]['text']
print(code)
  • body는 모델별 네이티브 JSON 문자열 — anthropic_version 같은 모델 고유 필드 포함
  • 응답도 모델별 포맷 — response['body'].read()를 직접 파싱
  • 모델을 바꾸면 요청 본문과 파싱 코드를 함께 재작성
PART 2 · InvokeModel API

InvokeModel 스트리밍 호출

같은 body, 메서드만 교체 — chunk를 순회하며 모델별 델타를 파싱

python
response = bedrock_runtime.invoke_model_with_response_stream(
    modelId='us.anthropic.claude-sonnet-4-6',
    body=body   # 동기 호출과 같은 네이티브 JSON
)

# response['body']는 EventStream — chunk 단위로 순회
for event in response['body']:
    chunk = json.loads(event['chunk']['bytes'])

    # Anthropic 스트리밍 포맷 — 타입별 분기
    if chunk['type'] == 'content_block_delta':
        print(chunk['delta']['text'], end='', flush=True)
    elif chunk['type'] == 'message_delta':
        stop_reason = chunk['delta']['stop_reason']
  • 요청 body는 동기 호출과 동일 — 메서드만 _with_response_stream
  • chunk['bytes'] 안의 델타 JSON도 모델별 포맷 — 타입 이름부터 Anthropic 전용
  • content_block_delta에서 텍스트, message_delta에서 stop_reason
PART 2 · InvokeModel API

모델별 네이티브 포맷

같은 요청, 같은 512 토큰 상한 — JSON 모양은 네 가지






Anthropic Claude

{'anthropic_version': 'bedrock-2023-05-31',
 'max_tokens': 512,
 'messages': [{'role': 'user',
    'content': '회의록을 요약해 줘'}]}

응답 경로 — content[0].text · stop_reason




Amazon Nova

{'messages': [{'role': 'user',
    'content': [{'text': '회의록을 요약해 줘'}]}],
 'inferenceConfig': {'maxTokens': 512}}

응답 경로 — output.message.content[0].text




Meta Llama

{'prompt': '회의록을 요약해 줘',
 'max_gen_len': 512}
// messages 배열 없이 단일 문자열

응답 경로 — generation




Google Gemma 3 (4B/12B/27B)

{'messages': [{'role': 'user',
    'content': '회의록을 요약해 줘'}],
 'max_tokens': 512}
// OpenAI Chat Completions 스키마

응답 경로 — choices[0].message.content




takeaway

같은 InvokeModel이라도 요청·응답 스키마가 프로바이더마다 다릅니다 — 모델을 바꿀 때마다 본문과 파싱 코드를 다시 쓰는 이 번거로움을 하나로 통합한 것이 Converse API입니다.

추론 파라미터

다음 토큰 선택 · Temperature · Top-P · Max Tokens

PART 3 · 추론 파라미터

다음 토큰 선택 — 파라미터의 개입 지점

모델은 확률 분포에서 다음 토큰을 고른다 — Temperature·Top-P가 조절하는 대상

takeaway

파라미터는 세 지점에 개입합니다 — ① Temperature 분포의 뾰족함(Softmax 전 로짓 나누기) · ② Top-P 후보 컷라인 · ③ Max Tokens · stopSequences 반복을 멈추는 조건. 컷라인 통과 후보 중 추첨(샘플링)은 모델의 몫입니다.

PART 3 · 추론 파라미터

① Temperature — 분포의 뾰족함

Softmax 전에 로짓을 T로 나눕니다 — 낮으면 확정적, 높으면 다양 (0~1, 예시 수치)





T = 0.2 — 뾰족한 분포


서울
0.88

부산
0.08

대전
0.03

인천
0.01


사실상 "서울" 고정 — 코드 생성 · 정보 추출 · 분류



T = 1.0 — 평평한 분포


서울
0.52

부산
0.18

대전
0.09

인천
0.06


후보가 살아남음 — 대화 0.7 · 브레인스토밍 0.9~1




같은 로짓, 다른 T — 분포 자체를 바꾸는 첫 개입 · 기본값은 모델별 상이라 명시 지정 권장


PART 3 · 추론 파라미터

② Top-P — 후보 컷라인

확률 내림차순으로 누적 P에 도달할 때까지만 후보로 — 나머지는 제외




TOP-P = 0.7 — T=1.0 분포에서 (예시 수치)

서울0.52누적 0.52 — 포함

부산0.18누적 0.70 — P 도달, 포함

✂ Top-P 0.7 컷라인

대전0.09제외 — 확률 재분배 후 서울·부산 중 추첨

인천0.06제외



분포가 뾰족하면 후보가 줄고 평평하면 늘어나는 가변 컷 — 그래서 Temperature와 둘 중 하나만 조정하는 것이 예측 가능한 기본


PART 3 · 추론 파라미터

③ Max Tokens · Stop Sequences — 멈추는 조건

생성이 끝나는 세 가지 길 — stopReason으로 어느 길이었는지 확인




end_turn

모델이 스스로 답을 끝냄 — "서울입니다." [EOS] · 정상 종료



max_tokens

maxTokens 상한 도달 — "서울은 대한민국의 수도로서 인구는 약" ✂ · 문장이 잘린 채 반환 — 여유 있게 설정



stop_sequence

stopSequences 문자열을 만나면 즉시 중단 — stopSequences=['###'] → "서울입니다 ###" 직전까지 · 구조화 출력 절단에 활용



maxTokens는 생성분만 제한합니다 — 입력 + 생성이 컨텍스트 윈도우를 넘을 수는 없음 · 어느 길로 끝났는지는 stopReason이 알려줌


포맷은 모델마다 달라도, 코드는 하나면 됩니다.

InvokeModel · 추론 파라미터 · Converse · 함수 호출 · 멀티모달 — 운영 패턴은 추론 인프라 모듈에서