Bedrock — Converse API
Converse API memberi satu bentuk request yang sama untuk semua model di Bedrock — dan inilah yang paling sering muncul di ujian.
Intisari
- Satu bentuk request untuk semua model — ganti
modelId, kode lain tetap. - Struktur intinya sama dengan Messages API, hanya berbeda nama field.
contentselalu berupa list of block, sama seperti API Anthropic.- Tool use memakai
toolConfig,toolUse, dantoolResult— alurnya identik. ConverseStreamuntuk streaming; strukturnya paralel dengan Converse.
Kenapa Converse ada
Sebelum Converse, tiap penyedia model punya format request sendiri lewat InvokeModel.
Mengganti model berarti menulis ulang kode parsing. Converse memberi satu bentuk seragam;
Bedrock yang menerjemahkannya ke format asli tiap model.
InvokeModel | Converse | |
|---|---|---|
| Format body | Berbeda tiap penyedia | Seragam |
| Ganti model | Perlu ubah kode | Cukup ganti modelId |
| Tool use | Format asli penyedia | Seragam |
| Akses fitur khusus model | Penuh | Lewat additionalModelRequestFields |
Panggilan dasar
import boto3
bedrock = boto3.client("bedrock-runtime", region_name="us-east-1")
response = bedrock.converse(
modelId="anthropic.claude-opus-5",
system=[{"text": "Kamu asisten yang menjawab ringkas dalam bahasa Indonesia."}],
messages=[
{"role": "user", "content": [{"text": "Apa itu RAG?"}]},
],
inferenceConfig={
"maxTokens": 4096,
"temperature": 0.7,
"stopSequences": [],
},
)
print(response["output"]["message"]["content"][0]["text"])
print(response["usage"]) # inputTokens, outputTokens, totalTokens
print(response["stopReason"]) # end_turn | max_tokens | tool_use | ...
Pemetaan istilah
| Messages API (Anthropic) | Converse API (Bedrock) |
|---|---|
model | modelId |
system (string) | system (list of block) |
max_tokens | inferenceConfig.maxTokens |
messages[].content (string atau list) | messages[].content (selalu list) |
response.content | response["output"]["message"]["content"] |
stop_reason | stopReason |
usage.input_tokens | usage["inputTokens"] |
tools | toolConfig.tools |
Perhatikan pola yang berulang: content selalu berupa list of block,
stopReason selalu diperiksa dulu, dan hasil tool dikirim kembali sebagai pesan
user. Struktur konseptualnya sama — yang berbeda hanya nama dan bentuk
penulisannya (camelCase vs snake_case).
Tool use
tool_config = {
"tools": [{
"toolSpec": {
"name": "cari_tiket",
"description": "Cari tiket support berdasarkan status.",
"inputSchema": {
"json": {
"type": "object",
"properties": {
"status": {"type": "string", "enum": ["open", "closed"]},
},
"required": ["status"],
}
},
}
}],
"toolChoice": {"auto": {}}, # atau {"any": {}} / {"tool": {"name": "..."}}
}
response = bedrock.converse(
modelId="anthropic.claude-opus-5",
messages=messages,
toolConfig=tool_config,
inferenceConfig={"maxTokens": 4096},
)
Loop tool
while True:
r = bedrock.converse(
modelId=MODEL, messages=messages,
toolConfig=tool_config, inferenceConfig={"maxTokens": 4096},
)
if r["stopReason"] != "tool_use":
break
pesan_model = r["output"]["message"]
messages.append(pesan_model)
hasil = []
for blok in pesan_model["content"]:
if "toolUse" not in blok:
continue
tu = blok["toolUse"]
keluaran = jalankan(tu["name"], tu["input"])
hasil.append({
"toolResult": {
"toolUseId": tu["toolUseId"],
"content": [{"text": str(keluaran)}],
}
})
messages.append({"role": "user", "content": hasil})
Alurnya persis sama seperti loop tool_use di API Anthropic — hanya nama field-nya yang berbeda.
Streaming
response = bedrock.converse_stream(
modelId="anthropic.claude-opus-5",
messages=messages,
inferenceConfig={"maxTokens": 4096},
)
for event in response["stream"]:
if "contentBlockDelta" in event:
print(event["contentBlockDelta"]["delta"].get("text", ""), end="", flush=True)
elif "messageStop" in event:
print(f"\nstopReason: {event['messageStop']['stopReason']}")
elif "metadata" in event:
print(event["metadata"]["usage"])
Gambar dan dokumen
messages = [{
"role": "user",
"content": [
{"image": {"format": "png", "source": {"bytes": data_gambar}}},
{"text": "Apa isi gambar ini?"},
],
}]
messages = [{
"role": "user",
"content": [
{"document": {"format": "pdf", "name": "laporan",
"source": {"bytes": data_pdf}}},
{"text": "Ringkas temuan utamanya."},
],
}]
Parameter khusus model
response = bedrock.converse(
modelId="anthropic.claude-opus-5",
messages=messages,
inferenceConfig={"maxTokens": 16000},
additionalModelRequestFields={
"thinking": {"type": "adaptive"},
},
)
Field ini adalah pintu keluar untuk fitur yang belum punya padanan seragam di Converse. Isinya diteruskan apa adanya ke model, jadi bentuknya mengikuti API asli penyedia.
Prompt caching
system=[
{"text": dokumen_besar},
{"cachePoint": {"type": "default"}}, # titik cache
]
Guardrails
response = bedrock.converse(
modelId=MODEL,
messages=messages,
guardrailConfig={
"guardrailIdentifier": "gr-abc123",
"guardrailVersion": "1",
"trace": "enabled",
},
inferenceConfig={"maxTokens": 4096},
)
if response["stopReason"] == "guardrail_intervened":
print(response["trace"]["guardrail"])
Menangani error
from botocore.exceptions import ClientError
try:
r = bedrock.converse(...)
except ClientError as e:
kode = e.response["Error"]["Code"]
if kode == "ThrottlingException":
... # layak diulang dengan backoff
elif kode == "ValidationException":
... # request salah — jangan diulang
elif kode == "AccessDeniedException":
... # IAM atau model access belum diberikan
elif kode == "ModelTimeoutException":
... # layak diulang
Converse atau SDK Anthropic?
| Pilih | Kalau |
|---|---|
AnthropicBedrockMantle | Kamu hanya memakai Claude dan ingin permukaan API yang sama dengan API utama |
| Converse (boto3) | Kamu ingin bisa berganti penyedia model, atau butuh integrasi Bedrock (Guardrails, KB) |
| Keduanya | Untuk persiapan ujian — soal-soalnya memakai Converse |
Rangkuman ini sengaja dipangkas ke bagian yang dipakai di roadmap. Buka sumber aslinya saat kamu butuh detail lengkap atau referensi parameter.