← Semua pembelajaran / Python untuk AI Engineer
Fase 4 · Bicara dengan LLM

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.
  • content selalu berupa list of block, sama seperti API Anthropic.
  • Tool use memakai toolConfig, toolUse, dan toolResult — alurnya identik.
  • ConverseStream untuk 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.

InvokeModelConverse
Format bodyBerbeda tiap penyediaSeragam
Ganti modelPerlu ubah kodeCukup ganti modelId
Tool useFormat asli penyediaSeragam
Akses fitur khusus modelPenuhLewat 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)
modelmodelId
system (string)system (list of block)
max_tokensinferenceConfig.maxTokens
messages[].content (string atau list)messages[].content (selalu list)
response.contentresponse["output"]["message"]["content"]
stop_reasonstopReason
usage.input_tokensusage["inputTokens"]
toolstoolConfig.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?

PilihKalau
AnthropicBedrockMantleKamu 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)
KeduanyaUntuk 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.