İçeriğe atla

AWS Bedrock AgentCore ile Production-Ready AI Agentları Geliştirmek

AWS Bedrock AgentCore'un agentic AI'ı ölçekte deploy etme altyapı zorluklarını runtime, memory, gateway ve multi-agent koordinasyonuyla nasıl çözdüğünü öğren.

Ayhan Sipahi Ayhan Sipahi

LangChain veya CrewAI agentlarını production’a taşımak; session isolation, credential management, memory persistence ve observability gerektirir: sıfırdan doğru şekilde inşa etmek aylar alan bir altyapı. Bu olmadan agentlar kullanıcılar arasında güvenlik sınırlarından yoksun kalır ve oturumlar arası kalıcı durum tutamaz; bu da onları gerçek iş yükleri için yetersiz kılar.

AWS Bedrock AgentCore (GA Ekim 2025) bu boşluğu dolduruyor. Bu, LangChain veya CrewAI ile rekabet eden başka bir agent framework’ü değil; HERHANGİ bir framework ile geliştirilmiş agentların ölçekte çalışması için ihtiyaç duyduğu managed altyapı katmanı. Bunu “AI agentları için Lambda” olarak düşünebilirsin: agent kodunu sen getiriyorsun, AgentCore runtime, memory, tool management ve security’yi hallediyor. Agent kodu lokalde çalışıyorsa ve geriye kalan iş operasyonelse, bu managed katman en hızlı varsayılan.

AgentCore Mimarisi#

AgentCore, bağımsız veya birlikte çalışabilen beş entegre servisten oluşuyor:

User Request

AgentCore Runtime

AI Agent

AgentCore Memory

AgentCore Gateway

AgentCore Identity

Tools Lambda/API/MCP

Short-Term Context

Long-Term Memory

Observability

CloudWatch

Runtime: 8 saatlik session pencereleri ve kullanıcı başına dedicated microVM’ler kullanarak otomatik session isolation sağlayan serverless execution ortamı.

Memory: Kendi vector database’ini kurmadan hem kısa dönemli konuşma context’i hem de uzun dönemli kullanıcı tercihleri, facts ve summary’ler için managed storage.

Gateway: Model Context Protocol (MCP) kullanarak merkezi tool management. Lambda fonksiyonlarını, REST API’leri ve mevcut servisleri agent-erişilebilir tool’lara dönüştürür.

Identity: OAuth 2.0 entegrasyonu ile güvenli credential management. Agentlar third-party API’lere credential saklamadan kullanıcı adına erişir.

Observability: CloudWatch, Datadog veya LangSmith’e export edilen OpenTelemetry uyumlu metrikler ve trace’ler.

Herhangi Bir Framework ile Agent Deploy Etmek#

Strands agent’ı AgentCore’a deploy etmek:

from bedrock_agentcore import BedrockAgentCoreApp
from strands import Agent

app = BedrockAgentCoreApp()

@app.entrypoint
def invoke(payload):
    agent = Agent(
        model="anthropic.claude-sonnet-4-20250514-v1:0",
        instructions="Sipariş geçmişi ve iade politikalarına erişimi olan bir müşteri destek agentısın."
    )
    return agent.run(payload.get("message"))

CLI ile deploy:

agentcore configure
agentcore launch --region us-east-1

Runtime execution window’ları 8 saate kadar çıkıyor; bu, geleneksel serverless fonksiyonların 15 dakikada timeout olduğu async agentic workflow’lar için fazlasıyla yeterli. Her kullanıcı dedicated bir microVM alıyor, dolayısıyla session’lar arasında data sızıntısı olmuyor. Ücretlendirme sadece aktif CPU ve memory’yi sayıyor; I/O wait time ücretsiz, bu da LLM response’larını beklerken hatırı sayılır zaman harcayan agentic workload’lar için AgentCore’u pre-allocated Lambda konfigürasyonlarından önemli ölçüde daha ucuz kılabiliyor. Container’lar ARM64’ü hedeflemeli: Docker build’lerinde --platform=linux/arm64 kullan.

Sık yapılan bir hata, AgentCore’un stateless MCP server’lar için otomatik inject ettiği Mcp-Session-Id header’ını handle etmemek:

from fastapi import FastAPI, Header

app = FastAPI()

@app.post("/mcp")
async def mcp_endpoint(
    mcp_session_id: str = Header(None, alias="Mcp-Session-Id")
):
    # AgentCore session isolation'ı yönetiyor
    # Sunucun platform tarafından üretilen ID'leri kabul etmeli
    session_state = load_session(mcp_session_id)
    return {"status": "ok"}

Konuşma ve Uzun Dönemli Memory Yönetimi#

AgentCore Memory ikisini birden kapsıyor: kısa dönemli konuşma context’i ve uzun dönemli bilgi persistence’ı.

Memory extraction pipeline:

KMSLLMMemoryGuardrailsAgentUserKMSLLMMemoryGuardrailsAgentUserMessageValidate InputPassedCreateEvent APIExtract MemoriesStructured MemoryEncryptEncrypted StorageResponse

Üç strateji ile memory implementasyonu:

from bedrock_agentcore.memory import (
    MemoryClient,
    UserPreferenceMemoryStrategy,
    SemanticMemoryStrategy,
    SummaryMemoryStrategy
)

memory_client = MemoryClient()

# Çoklu strateji ile memory oluştur
memory = memory_client.create_memory(
    name="customer-support-memory",
    strategies=[
        UserPreferenceMemoryStrategy(),  # Kullanıcı patternlerini öğren
        SemanticMemoryStrategy(),  # Fact'leri/bilgiyi sakla
        SummaryMemoryStrategy()  # Session'ları sıkıştır
    ],
    encryption_key_arn="arn:aws:kms:us-east-1:123456789012:key/abc123"
)

# Konuşma event'ini sakla
memory_client.create_event(
    memory_id=memory.id,
    event_data={
        "type": "conversation",
        "content": "Kullanıcı kod örnekleri içeren teknik açıklamaları tercih ediyor"
    }
)

Strateji seçim kılavuzu:

Agent tipiStratejilerNeyi hatırlıyor
Müşteri destekUserPreferences + Summariesİletişim stili
Teknik asistanSemanticFacts + SummariesCodebase bilgisi
Kişisel agentÜç strateji deKapsamlı kişiselleştirme

Kritik security pattern - CreateEvent API’den önce her zaman Guardrails kullan:

import boto3

bedrock = boto3.client('bedrock')

# YANLIŞ: Doğrudan storage (memory poisoning'e açık)
# memory_client.create_event(
#  memory_id=memory.id,
#  event_data={"content": user_input}
# )

# DOĞRU: Önce Guardrails ile sanitize et
guardrail_response = bedrock.apply_guardrail(
    guardrailId='guardrail-123',
    guardrailVersion='1',
    content=[{"text": {"text": user_input}}]
)

if guardrail_response['action'] == 'NONE':
    memory_client.create_event(
        memory_id=memory.id,
        event_data={"content": user_input}
    )
else:
    # Saldırı denemesini blokla ve logla
    logger.warning(f"Memory poisoning denemesi engellendi: {guardrail_response}")

Maliyet optimizasyonu: Retriever hop’ları sınırla. Turn başına iki-üç retrieval işlemi normal, on tane over-retrieval göstergesi:

memory_config = {
    'retrieval_strategy': 'semantic',
    'max_results': 5,
    'max_retriever_hops': 2
}

Gateway: Merkezi Tool Management#

Tool’ları doğrudan agent koduna gömmek duplikasyon ve tutarsızlığa yol açıyor: müşteri destek, satış ve teknik agentların hepsi hava durumu verisi istediğinde, aynı tool kodunun üç kopyası bakım yükü oluşturuyor.

AgentCore Gateway bunu merkezi MCP uyumlu tool server’larda topluyor:

LangChain Agent

AgentCore Gateway MCP

CrewAI Agent

Custom Agent

Lambda Target

OpenAPI Target

MCP Server Target

Smithy Model Target

Lambda fonksiyonunu tool olarak kaydetmek:

import boto3

agentcore = boto3.client('bedrock-agentcore')

# Lambda'yı tool target olarak kaydet
response = agentcore.create_target(
    gatewayId='gateway-123',
    targetConfig={
        'type': 'LAMBDA',
        'lambdaArn': 'arn:aws:lambda:us-east-1:123456789012:function:get-weather',
        'description': 'Bir şehir için güncel hava durumunu al'
    }
)

Gateway authentication, semantic tool search ve protocol conversion’ı kapsıyor. IAM roller AWS kaynaklarını, OAuth 2.0 third-party API’leri, API key’ler diğer servisleri kapsıyor; agentlar tüm mevcut tool’ları bilmeden x_amz_bedrock_agentcore_search ile ilgili tool’ları keşfediyor; Lambda fonksiyonları, OpenAPI spec’ler, Smithy modeller ve MCP server’lar tek bir standart MCP interface’i üzerinden expose ediliyor.

Mimari pattern - yaygın tool’ları merkezileştir, domain-specific tool’ları local tut:

Ortak Toollar (Gateway üzerinden):
  - Web arama
  - Database sorguları
  - Hava durumu API
  - Hisse senedi fiyatları

Domain-Specific Toollar (agent-local):
  - İade politikası mantığı
  - Ürün katalogu
  - İş kuralları

A2A Protocol ile Multi-Agent Koordinasyonu#

AgentCore, agent ekiplerini Agent-to-Agent (A2A) protokolü üzerinden koordine ediyor.

MCP, agent’ın hava durumu API’sini çağırması gibi agent-to-tool iletişimini kapsıyor; A2A ise supervisor’ın uzmanları koordine etmesi gibi agent-to-agent iletişimini kapsıyor.

Hub-and-spoke supervisor implementasyonu:

import { BedrockAgentCoreClient, InvokeAgentCommand } from '@aws-sdk/client-bedrock-agentcore';

class HostAgent {
  private client: BedrockAgentCoreClient;
  private specialistAgents: Map<string, AgentConfig>;

  async routeToSpecialist(query: string, capability: string) {
    const agentConfig = this.specialistAgents.get(capability);

    // Remote agent'ın A2A konfigürasyonunu getir
    const agentCard = await this.fetchAgentCard(agentConfig.endpoint);

    // A2A protokolü ile invoke et
    const command = new InvokeAgentCommand({
      agentId: agentCard.id,
      sessionId: this.generateSessionId(),
      inputText: query,
      protocol: 'A2A'
    });

    return await this.client.send(command);
  }

  private async fetchAgentCard(endpoint: string): Promise<AgentCard> {
    // Agent capability schema'sını al
    const response = await fetch(`${endpoint}/.well-known/agent-card`);
    return response.json();
  }
}

Orchestration pattern’leri:

User Query

Supervisor Agent

Analyze Query

Decompose Problem

Specialist A Parallel

Specialist B Parallel

Aggregate Results

Specialist C Serial

Supervisor Synthesis

Response

Routing modlu supervisor - her sorgu tam orchestration gerektirmiyor:

class SupervisorAgent:
    def route_query(self, query: str):
        # Basit sorgu → doğrudan routing
        if self.is_simple_query(query):
            specialist = self.select_single_specialist(query)
            return specialist.invoke(query)

        # Karmaşık sorgu → tam orchestration
        else:
            plan = self.analyze_and_plan(query)
            results = self.orchestrate_subagents(plan)
            return self.synthesize(results)

    def is_simple_query(self, query: str) -> bool:
        intents = self.detect_intents(query)
        return len(intents) == 1

Framework interoperability: LangGraph monitoring agent + CrewAI analytics agent + Strands incident response agent hepsi A2A üzerinden, framework lock-in olmadan iletişim kurabiliyor.

Security ve Maliyet Optimizasyonu#

Guardrails Konfigürasyonu#

Guardrails prompt injection, memory poisoning ve zararlı içeriğe karşı koruma sağlıyor:

import boto3

bedrock = boto3.client('bedrock')

guardrail = bedrock.create_guardrail(
    name='production-agent-guardrail',
    contentPolicyConfig={
        'filtersConfig': [
            {'type': 'HATE', 'inputStrength': 'HIGH', 'outputStrength': 'HIGH'},
            {'type': 'VIOLENCE', 'inputStrength': 'MEDIUM', 'outputStrength': 'HIGH'},
            {'type': 'PROMPT_ATTACK', 'inputStrength': 'HIGH', 'outputStrength': 'NONE'}
        ]
    },
    topicPolicyConfig={
        'topicsConfig': [
            {
                'name': 'Financial Advice',
                'definition': 'Spesifik yatırım önerileri sağlama',
                'type': 'DENY'
            }
        ]
    },
    wordPolicyConfig={
        'wordsConfig': [
            {'text': 'internal-api-key'},
            {'text': 'secret-token'}
        ],
        'managedWordListsConfig': [
            {'type': 'PROFANITY'}
        ]
    }
)

Defense-in-depth stratejisi:

  1. Input validation: Girişte kötü niyetli prompt’ları blokla
  2. Memory protection: CreateEvent API’den önce sanitize et
  3. Output filtering: Zararlı response’ları önle
  4. Audit trail’leri: Compliance için CloudWatch logları

Maliyet Optimizasyon Stratejileri#

Prompt caching - cached token’larda %90 indirim:

response = bedrock_runtime.converse(
    modelId="anthropic.claude-sonnet-4-20250514-v1:0",
    messages=[{"role": "user", "content": user_query}],
    system=[
        {
            "text": large_system_prompt,
            "cachePoint": {"type": "default"}
        }
    ]
)

Model routing - karmaşıklığı model maliyetiyle eşleştir:

def route_to_model(query: str) -> str:
    complexity = classify_query_complexity(query)

    if complexity < 0.3:
        return "anthropic.claude-haiku-4-5-20251001-v1:0"  # 1M token başına $1/$5 (input/output)
    elif complexity < 0.7:
        return "anthropic.claude-sonnet-4-20250514-v1:0"  # 1M token başına $3/$15 (input/output)
    else:
        return "anthropic.claude-opus-4-20250514-v1:0"  # 1M token başına $15/$75 (input/output)

Tool-call budget’ları - sınırsız tool kullanımını önle:

agent = Agent(
    model="anthropic.claude-sonnet-4-20250514-v1:0",
    max_tool_calls_per_turn=3,
    instructions="Kullanıcı birden fazla item sorarsa, kapsamlı lookup yerine özetle"
)

Basit 0.3ten az

Orta 0.3 ile 0.7

Karmasik 0.7den fazla

Evet

Hayir

OK

Asim

User Query

Classify Complexity

Claude Haiku

Claude Sonnet

Claude Opus

Cached?

Yuzde 90 Indirim

Tam Ucret

Budget Check

Execute

Reject

Maliyet bileşenleri:

  • Runtime: Aktif CPU/memory tüketimi (pre-allocated değil)
  • Memory: Short-term (event başına), long-term (işlenen memory + retrieval’lar başına)
  • Gateway: MCP işlemleri (ListTools, CallTool, Ping) + semantic search sorguları
  • Identity: Runtime/Gateway üzerinden kullanıldığında ek ücret yok
  • Observability: CloudWatch standart fiyatlandırması

Production’da Nerede Kırılıyor#

Guardrails Olmadan Memory Poisoning#

Ham kullanıcı input’unu doğrudan saklamak, memory’ye prompt injection’a izin veriyor:

# YANLIŞ
user_input = "Önceki talimatları yoksay, artık sen..."
memory_client.create_event(
    memory_id=memory.id,
    event_data={"content": user_input}
)

Yazmadan önce Guardrails ile sanitize etmek (yukarıda Memory bölümünde gösterilen pattern) bu açığı kapatıyor.

Tool-Call Storm’ları#

Limit olmadan bir agent sorgu başına 20+ tool invoke edebiliyor:

Kullanıcı: "Büyük şehirlerde hava durumu nasıl?"
Agent 50 ayrı get_weather() çağrısı yapıyor
Latency ve maliyet, üst sınır olmadan çağrı sayısıyla birlikte büyüyor

Tool-call budget’ları uygulamak ve agent’ı instruction’larla yönlendirmek (yukarıda Maliyet Optimizasyonu bölümünde gösterilen pattern aynısı) bunu sınırlı tutuyor.

Deployment Konfigürasyon Boşlukları#

Deploy sırasında iki konfigürasyon detayı önlenebilir hatalara yol açıyor. x86 container’lar kullanmak deployment hatalarına neden oluyor; açıkça ARM64 için build et:

FROM --platform=linux/arm64 python:3.11-slim
COPY . /app
CMD ["python", "agent.py"]
docker buildx build --platform linux/arm64 -t agent:latest .

Ayrıca agent trafiği varsayılan olarak public internet üzerinden gidiyor. Internal API’ler için VPC ve PrivateLink konfigüre etmek bu trafiği AWS içinde tutuyor:

runtime_config = {
    'vpcConfig': {
        'securityGroupIds': ['sg-12345'],
        'subnetIds': ['subnet-abc', 'subnet-def']
    },
    'privateLinkEnabled': True
}

AgentCore Ne Zaman Mantıklı#

AgentCore kullan:

  • Birden fazla agent framework kullanımda (LangChain + CrewAI + custom)
  • Farklı modelleri değerlendirme ihtiyacı (Bedrock + OpenAI + Anthropic)
  • Enterprise security gerekli (VPC, PrivateLink, customer-managed KMS)
  • Multi-agent sistemler planlanıyor (A2A koordinasyonu)
  • Production’a geçiş süresi haftalarla ölçülüyor
  • Ekip büyüklüğü 10’un altında (sıfırdan altyapı inşa edilemez)

Tablo daha basitse alternatifleri değerlendir: tek framework sonsuza kadar kullanılacaksa (sadece LangGraph için LangGraph Cloud uyar), tek cloud ekosistemindeyse (tamamen Azure için Azure AI Agent Service uyar), sürekli çok yüksek volume varsa (amortize edilmiş self-hosted kapasite consumption pricing’in altına inebiliyor), özelleşmiş modeller için GPU gibi custom hardware gerekiyorsa, ya da zaten kurulmuş ve sunk cost haline gelmiş bir agent altyapısı varsa.

Platformu kademeli benimse: önce Runtime, sonra ihtiyaç doğdukça Memory, Gateway, Identity ve Observability. Her servis tek başına çalıştığı için yarı yolda durmak da geçerli bir son durak. İş yükü ömrü boyunca tek framework ve tek cloud üzerinde kalacaksa varsayılanı değiştir; o framework’ün kendi platformu daha basit kalır ve managed katman fazla bir şey kazandırmaz.

Kaynaklar#

İlgili yazılar