DEV Community

Cover image for Autonomous Tool Orchestration & Multi-Capability Agent Systems with Amazon Bedrock AgentCore & Strands SDK
Jonathan Wong
Jonathan Wong

Posted on Originally published at blog.jonanata.com on

Autonomous Tool Orchestration & Multi-Capability Agent Systems with Amazon Bedrock AgentCore & Strands SDK

Part 4 of the series: “Engineering Next-Gen AI Agents: Reflections on the Future AWS Agent Engineer Nanodegree”

In Part 3, we explored prompt-driven intent routing and automated LLM-as-a-judge evaluations within a single managed harness. While single-prompt routing works well for targeted domain tasks, enterprise AI solutions demand multi-modal tool integration, cross-session long-term memory, secure code execution, and external platform connectivity.

In Project 2 of the Future AWS Agent Engineer Nanodegree , we built an end-to-end, autonomous AI Customer Support Agent deployed on Amazon Bedrock AgentCore Runtime. Built using a single Strands SDK Agent instance and the bedrock-agentcore-starter-toolkit, this agent acts as an intelligent, multi-tool orchestration hub for an e-commerce platform—integrating REST APIs, vector databases, persistent memory, and isolated code sandboxes into a unified conversational runtime.

The complete project source code and implementation files are available on my GitHub repository: https://github.com/jonanata/ai-support-agent

Below is an architectural breakdown of how these components integrate to solve real-world customer support challenges.

Architectural Blueprint & Component Synergies

Modern customer support requires far more than basic text generation. Our agent unifies five distinct AWS and Bedrock capabilities into a single conversational entry point:

How the Components Work Together:

  • AgentCore Gateway & Model Context Protocol (MCP): Exposes backend AWS Lambda functions (order tracking, refund processing) as standardized tools via REST endpoints managed by API Gateway. The agent discovers these tools dynamically at runtime using MCP docstrings and schema definitions.
  • Bedrock Knowledge Base (RAG): Connects to an S3 bucket storing unstructured catalog data (product_catalog.txt). The search_knowledge_base tool queries Bedrock’s vector search to retrieve grounded policy and warranty facts.
  • AgentCore Memory: Provides persistent, cross-session context. Using a custom MemoryHook, user facts and preferred communication styles are automatically prepended to incoming prompts and saved back to the memory store after each turn.
  • AgentCore Code Interpreter: Runs exact numerical math—such as points redemptions, tier calculations, and tiered discounts—inside an isolated, secure Python execution sandbox to avoid LLM arithmetic errors.
  • AgentCore Browser Tool: Grants the agent real-time web access to inspect external URLs when information isn’t available in internal databases.

Deep Dive: Implementation Highlights

The application entry point is built using the Strands SDK and registered with Bedrock AgentCore Runtime. For example, in the main.py:

from strands import Agent, tool
from bedrock_agentcore.runtime import BedrockAgentCoreApp

# 1. Register the application with Bedrock AgentCore Runtime
app = BedrockAgentCoreApp()

# 2. Define the asynchronous entry point decorated with @app.entrypoint
@app.entrypoint
async def invoke(payload, context=None):
    user_input = payload.get("prompt")

    # ... setup tools, hooks, and clients ...

    # 3. Instantiate the Strands SDK Agent
    agent = Agent(
        model=model,
        tools=tools,
        hooks=[hook],
        system_prompt="You are a helpful Amazon customer support AI."
    )  

    # 4. Invoke the agent and return the response
    response = agent(user_input)  
    return response  
Enter fullscreen mode Exit fullscreen mode

Model Context Protocol (MCP) Tool Loading

Instead of manually mapping API calls, the agent connects directly to the AgentCore Gateway URL over MCP to discover available tools:

GATEWAY_URL = "https://<alias>.gateway.bedrock-agentcore.us-east-1.amazonaws.com/mcp"  
client = MCPClient(lambda: streamable_http_client(url=GATEWAY_URL))  
with client:  
    gateway_tools = client.list_tools_sync()  
    tools.extend(gateway_tools)
Enter fullscreen mode Exit fullscreen mode

Cross-Session Memory Hook

To maintain continuity without cluttering prompt state, we subclassed HookProvider to intercept conversation events:

  • MessageAddedEvent: Retrieves facts from namespaces (e.g., cs_agent/{actorId}/facts) and prepends Customer Context: to the prompt before invocation.
  • AfterInvocationEvent: Writes completed conversation pairs back to AgentCore Memory asynchronously.
class MemoryHook(HookProvider):
    def retrieve_customer_context(self, event: MessageAddedEvent):
        # Fetches semantic facts & preferences from AgentCore Memory
        # Prepends memories directly into the user message frame
        ...
    def save_support_interaction(self, event: AfterInvocationEvent):
        # Commits (USER, ASSISTANT) message pairs to memory after turn completion
        ...
Enter fullscreen mode Exit fullscreen mode

Exact Calculations with Code Interpreter

For complex tier structures, relying on LLM text generation risks rounding errors. The calculate_loyalty_discount tool executes pure Python arithmetic within code_session(REGION):

@tool
def calculate_loyalty_discount(loyalty_points: int, tier: str, order_total: float, product_category: str = "standard") -> str:
    code = f"""
import json
earn_rates = {{"standard": 1, "device": 2, "fresh": 5}}
tier_rates = {{"Silver": 0.00, "Gold": 0.10, "Platinum": 0.15}}
# Performs exact point conversion, tier discounting, and remaining point totals...
print(json.dumps(result))
"""
    with code_session(REGION) as code_client:
        response = code_client.invoke("executeCode", {"code": code, "language": "python", "clearContext": True})
        for event in response["stream"]:
            return json.dumps(event["result"])
Enter fullscreen mode Exit fullscreen mode

Setup Guide: Configuring the AWS Components

Before deploying the agentic runtime, you must configure and tie together the underlying AWS resources, including the API Gateway, Lambda functions, Bedrock Knowledge Base, and AgentCore Memory.

AgentCore Gateway & Model Context Protocol (MCP)

How Real-Time Dynamic Tool Discovery Works

Instead of hardcoding backend API routing or manually maintaining tool definitions in code, the AI agent relies on Model Context Protocol (MCP) capabilities built directly into the AgentCore Gateway.

When the Strands SDK Agent starts up, it connects to the GATEWAY_URL endpoint over MCP and performs a standard tools/list handshake:

  • Dynamic Schema Discovery: AgentCore Gateway queries its registered targets (both API Gateway REST stages and standalone Lambda functions).
  • Docstring & Operation Mapping: For API Gateway routes, the Gateway inspects the configured OpenAPI operation names (get_order, get_customer_orders, get_customer). For standalone Lambda functions like refund_processor, it inspects the attached MCP JSON tool schema.
  • LLM Tool Binding: The Gateway aggregates these descriptions into structured MCP tool definitions (including typed argument schemas and descriptions) and returns them to the agent runtime.
  • Real-Time Execution: When a user asks “Where is order ORD-001?”, the LLM evaluates the available tools, matches the user intent to get_order, generates the input parameters ({"order_id": "ORD-001"}), and sends a tools/call request to the Gateway. The Gateway automatically routes the payload to the underlying API Gateway REST endpoint, which triggers the corresponding AWS Lambda function behind the scenes.

Step-by-Step Setup

  • Step 1: Deploy the Lambda Functions
    • Create two separate Lambda functions using the Python 3.12 runtime in the AWS Lambda console: order-tracker and refund-processor (please refer to starter/lambda/ folder).
    • Attach an execution role with basic Lambda permissions for CloudWatch Logs and note each function’s ARN.
  • Step 2: Configure the API Operations
    • Open API Gateway -> Create API -> REST API.
    • Create the resources and GET methods configured as Lambda proxy integrations with the order-tracker Lambda function according to the mapping table below.
    • Deploy the REST API to a stage (such as prod) and copy your REST API ID and stage name.

| Method and Resource | Operation Name | Description |
| GET /orders/{order_id} | get_order | Fetch order status details |
| GET /customers/{customer_id}/orders | get_customer_orders | List all orders for a customer |
| GET /customers/{customer_id} | get_customer | Retrieve customer profile details |

  • Step 3: Set up the AgentCore Gateway
    • Navigate to Amazon Bedrock -> AgentCore -> Gateways and create a Gateway named CustomerSupportGateway using a NONE authorizer.
    • Add the targets mapping to your infrastructure according to the mapping table below.
    • Copy the resulting Gateway URL (ending with /mcp).

| Target Name | Target Type | Configuration Reference |
| order_tracker | API Gateway REST API stage | Linked to your REST API, stage (prod), and the three GET operations |
| refund_processor | Lambda function | Linked to the refund-processor function using tool schema starter/lambda/lambda schema |

Validation

To confirm that the Gateway tools are accessible via the Model Context Protocol, run the MCP Inspector CLI tool:

npx @modelcontextprotocol/inspector

Connect to your Gateway URL and confirm that all configured tools (get_order, get_customer_orders, get_customer, and refund_processor) are listed properly.

Bedrock Knowledge Base (S3 Vector Search)

To provide grounded product and policy answers, we configure a Managed Knowledge Base backed by an S3 bucket.

  • Step 1: Upload starter/product_catalog.txt to your S3 bucket.
  • Step 2: Open Amazon Bedrock AgentCore -> Built-in tools -> Knowledge Base -> Create Managed Knowledge Base named CustomerSupportKB.
  • Step 3: Configure the setup parameters according to the table below:

| Setting Field | Configuration Value |
| Knowledge Base Name | CustomerSupportKB |
| Embedding Model Type | Managed |
| Service Role | Let the console create a new service role |
| Data Source | S3 bucket containing product_catalog.txt |

  • Step 4: Sync the data source and copy your generated Knowledge Base ID.

Validation

Open the Bedrock console -> Knowledge Bases -> CustomerSupportKB -> Test tab, and query: “What is the return policy for electronics?” (Expected: a result detailing the 15-day return window).

AgentCore Memory

To ensure multi-turn contextual continuity and long-term user recall across separate sessions:

  • Step 1: In the Bedrock console, navigate to AgentCore -> Memory and create a resource named CustomerSupportMemory.
  • Step 2: Configure the memory strategies using the schema below:

| Strategy Type | Strategy Name | Namespace Template |
| Semantic extraction | customer_facts | cs_agent/{actorId}/facts |
| User preference | customer_preferences | cs_agent/{actorId}/preferences |

  • Step 3: Wait until the memory state becomes ACTIVE, then copy the Memory ID.

Putting Configs Together

Once provisioned, input your collected GATEWAY_URL, KB_ID, and MEMORY_ID directly into the configuration block of main.py:

GATEWAY_URL = "https://<alias>.gateway.bedrock-agentcore.<region>.amazonaws.com/mcp"
KB_ID = "<Your-Knowledge-Base-ID>"
REGION = "us-east-1"
MEMORY_ID = "<Your-Memory-ID>"
Enter fullscreen mode Exit fullscreen mode

Lifecycle Deployment Flow: Config -> Build -> Deploy -> Test

Deploying an autonomous agent requires a systematic sequence using the bedrock-agentcore-starter-toolkit:

1. Configure Runtime

To initialize the deployment environment and establish metadata mapping for your AWS account, execute the configuration command via the toolkit:

uv run agentcore configure \
  --entrypoint main.py \
  --name customer_support_agent \
  --deployment-type direct_code_deploy \
  --runtime PYTHON_3_13 \
  --disable-memory
Enter fullscreen mode Exit fullscreen mode

Parameter Breakdown

| Flag / Option | Purpose & Description |
| --entrypoint main.py | Specifies the root script containing the @app.entrypoint ASGI application handler (main.py). |
| --name customer_support_agent | Defines the unique identifier name for the agent within the local configuration registry. |
| --deployment-type direct_code_deploy | Tells the toolkit to bundle and push raw source code directly to Amazon S3 rather than building heavy container images, accelerating iteration cycles. |
| --runtime PYTHON_3_13 | Configures the managed serverless execution runtime environment version on AgentCore. |
| --disable-memory | Disables automated toolkit memory provisioning. This ensures the agent uses the custom CustomerSupportMemory resource explicitly configured in main.py. |

Anatomy & Impact of the Generated .bedrock_agentcore.yaml File

Running the configuration command generates a local hidden state tracking file named .bedrock_agentcore.yaml in your project root. This file acts as the central control plane for all subsequent toolkit interactions.

Here is how its key sections affect your deployment:

  • Source & Entrypoint Mapping (agents block): Specifies the exact absolute path to entrypoint and source_path. When you run agentcore deploy, the toolkit references these paths to bundle your code files and upload them to the designated S3 bucket (s3://bedrock-agentcore-codebuild-sources-...).
  • AWS Infrastructure Binding (aws block): Automatically records your target AWS account, region (us-east-1), and the execution_role ARN (e.g., arn:aws:iam::783469453888:role/AmazonBedrockAgentCoreSDKRuntime-...). This ensures that any command you execute targets the correct AWS environment without requiring manual environment variable exports every time.
  • Runtime State Tracking (bedrock_agentcore block): Once deployed, this section stores the generated remote resource identifiers, including the agent_id (e.g., customer_support_agent-nUpbKY5Ff2) and the full agent_arn. Subsequent commands like agentcore invoke read these identifiers to route payloads directly to your active remote agent runtime.

2. Deployment

uv run agentcore deploy

Enter fullscreen mode Exit fullscreen mode

Deployment Mechanics & Runtime Initialization

When you execute agentcore deploy, the toolkit and Amazon Bedrock AgentCore execute a structured provisioning workflow behind the scenes:

  • Source Bundling & S3 Upload: The toolkit bundles your local project directory (including main.py and local dependencies) and pushes the archive up to the designated S3 source bucket specified in your .bedrock_agentcore.yaml configuration (s3://bedrock-agentcore-codebuild-sources-...).
  • Container Provisioning & Runtime Activation: Amazon Bedrock AgentCore reads the deployment specification (PYTHON_3_13, direct_code_deploy, platform linux/arm64) and provisions a managed serverless execution container instance.
  • Agent Warm-Up & ASGI Bootstrapping: Inside the remote container, AgentCore pulls your uploaded source files from S3 and mounts the execution environment. It spins up the ASGI server registered by your BedrockAgentCoreApp instance (app = BedrockAgentCoreApp()) from main.py. Once the ASGI application binds to the runtime port and completes its internal initialization, the deployed agent (referenced by its unique agent_id) goes into an Active state, ready to receive incoming invocations and handle asynchronous event loops.

3. Configure IAM Permissions

uv run setup_permissions.py

Enter fullscreen mode Exit fullscreen mode

This script reads .bedrock_agentcore.yaml to locate your execution role name and runtime ID. It uses this mapping to safely attach inline IAM policies granting your deployed agent access to Bedrock Knowledge Bases, AgentCore Memory namespaces, and the Browser session pool.

4. Integration Testing & Real-World Validation

To verify autonomous execution across subsystems, we executed structured test invocations against the live runtime endpoint:

  • Test 1: Backend Integration (MCP & Lambda)
    • Command: agentcore invoke '{"prompt": "Can you track order ORD-001?", "customer_id": "CUST-123", "session_id": "t1"}'
    • Validation: Agent routes request via AgentCore Gateway to order-tracker Lambda, returning status, carrier (UPS), tracking number (TRK987654321), and estimated delivery.
  • Test 2: Cross-Session Long-Term Memory Recall
    • Session A: agentcore invoke '{"prompt": "Hi, I am Jane. I prefer concise responses.", "customer_id": "CUST-123", "session_id": "s-A"}'
    • (Wait 30s for background memory extraction)
    • Session B (New Session): agentcore invoke '{"prompt": "Do you remember my name and communication preference?", "customer_id": "CUST-123", "session_id": "s-B"}'
    • Validation: Agent successfully recalls “Jane” and formats its response concisely without needing prior prompt context in session s-B.
  • Test 3: Knowledge Base RAG Retrieval
    • Command: agentcore invoke '{"prompt": "What are the benefits of the Platinum loyalty tier?", "customer_id": "CUST-123", "session_id": "t3"}'
    • Validation: Retrieves grounded facts from S3 vector index (free same-day shipping, 15% discount, priority support).
  • Test 4: Sandbox Code Execution
    • Command: agentcore invoke '{"prompt": "I am a Gold member with 4250 points. Calculate my discount on a $150 standard order.", "customer_id": "CUST-123", "session_id": "t5"}'
    • Validation: Executes sandbox Python script, correctly applying 4,000 redeemed points ($40) + 10% Gold tier discount, yielding the exact mathematical total and remaining point balance.

Business Value & Engineering Takeaways

Integrating multi-agent tools directly into enterprise workflows generates distinct operational metrics:

| Technical Component | Business Problem Solved | Business ROI Impact |
| AgentCore Gateway (MCP) | Siloed legacy APIs requiring custom integration code | Standardizes backend integration; reduces API connector dev time by ~60%. |
| AgentCore Memory | Repetitive user context switching & disjointed support tickets | Increases First Contact Resolution (FCR) by remembering customer facts across channels. |
| Code Interpreter | Mathematical hallucinations in financial/discount calculations | Eliminates billing errors with 100% deterministic calculation precision. |
| Bedrock Knowledge Base | Hallucinated support policies & outdated documentation | Grounded RAG responses ensure zero compliance/policy drift. |


Advanced Architecture Extension: Production Integration & Runtime Trade-Offs

Note: The following integration and architectural analysis go beyond the baseline requirements of the original project spec, exploring production-ready deployment patterns for enterprise environments.

Finding Your AGENT_RUNTIME_ARN

Once your agent is deployed via the starter toolkit, you can retrieve its unique Amazon Resource Name (ARN) either through the AWS CLI or the management console:

  • Via AWS CLI: aws bedrock-agentcore list-agent-runtimes --region us-east-1
  • Via AWS Console: Navigate to Amazon Bedrock -> AgentCore -> Runtimes , select your deployed agent (customer_support_agent), and copy the Agent Runtime ARN from the overview panel. It follows the format: arn:aws:bedrock-agentcore:<region>:<account-id>:runtime/<agent-name>-<hash>. You can also find it locally in your project’s .bedrock_agentcore.yaml configuration file.

Production Integration: Invoking AgentCore from a FastAPI Backend

Instead of executing your agent solely through local CLI commands, you can wrap it inside a production microservice using FastAPI and the AWS SDK (boto3). Your backend acts as an API gateway that securely proxies requests to the managed AgentCore container runtime:

import json
import boto3
from fastapi import FastAPI, HTTPException
from pydantic import BaseModel

app = FastAPI(title="Customer Support API Gateway")

# Initialize the Bedrock AgentCore boto3 client
bedrock_agentcore_client = boto3.client("bedrock-agentcore", region_name="us-east-1")
AGENT_RUNTIME_ARN = "arn:aws:bedrock-agentcore:us-east-1:123456789012:runtime/customer_support_agent-nUpbKY5Ff2"

class ChatRequest(BaseModel):
    prompt: str
    customer_id: str
    session_id: str # Requires valid session tracking format

@app.post("/api/v1/support/chat")
async def invoke_agent(request: ChatRequest):
    try:
        payload = {
            "prompt": request.prompt,
            "customer_id": request.customer_id,
            "session_id": request.session_id
        }

        # Programmatically invoke the remote AgentCore runtime
        response = bedrock_agentcore_client.invoke_agent_runtime(
            agentRuntimeArn=AGENT_RUNTIME_ARN,
            runtimeSessionId=request.session_id,
            payload=json.dumps(payload)
        )

        response_body = response["body"].read().decode("utf-8")
        return {"status": "success", "data": json.loads(response_body)}

    except Exception as e:
        raise HTTPException(status_code=500, detail=str(e))
Enter fullscreen mode Exit fullscreen mode

Architectural Decision: Why Bedrock AgentCore Runtime over AWS Lambda?

While an AWS Lambda function (even utilizing 10GB container images) can technically host agent logic and the Strands SDK, it introduces severe architectural friction for autonomous agentic workflows.

| Evaluation Dimension | AWS Lambda (Container / Zip) | Amazon Bedrock AgentCore Runtime | Architectural Impact |
| Execution Timeout | Hard-capped at 15 minutes (29 seconds for synchronous API Gateway). | Built for long-running, persistent agent sessions. | Agentic loops requiring multiple reasoning steps or human-in-the-loop pauses can time out prematurely on Lambda. |
| State & Memory | Fully stateless; requires manual boilerplate code to persist data across turns (e.g., DynamoDB). | Native, built-in support for memory hooks, semantic fact extraction, and cross-session management. | AgentCore eliminates custom CRUD boilerplate for conversation state and user preference tracking. |
| Cost & Billing Model | Billed for the entire duration the function execution runs. | Optimized for agent workloads; billed based on active execution. | AI agents spend significant time idling while waiting for LLM tokens or external API tools. Lambda bills you for that wait time; AgentCore does not. |
| MicroVM Isolation | Shared multi-tenant execution environment per container instance. | Isolated microVM execution environment per runtime container. | Enhanced security segregation when executing dynamic tool calls or untrusted code snippets. |


What’s Next

In Part 5, we will build a production-grade multi-agent customer support system using Amazon Bedrock AgentCore and the Strands Agents SDK.


About the Author

Jonathan Wong is an IT and AI consultant with 20+ years of experience leading engineering teams across Vancouver and Hong Kong. He specializes in modernizing legacy platforms, cloud security, and building AI-ready systems for startups and large enterprises while advising leadership on using strategic technology to drive business growth.

Connect with me on LinkedIn

The post Autonomous Tool Orchestration & Multi-Capability Agent Systems with Amazon Bedrock AgentCore & Strands SDK appeared first on Behind the Build.

Top comments (0)