NovuSpark
All articles

February 6, 2026 · NovuSpark Team

Amazon Bedrock Agents: Automating Multi-Step Tasks

This is the third post in our Amazon Bedrock series, building on getting started and Knowledge Bases. It assumes the agent concepts from our LangChain and OpenAI series.

We've now built the "model decides to call a tool, your code executes it, the result feeds back in" pattern three times in this blog — directly against the OpenAI API, then with LangChain's tool-calling agents. A Bedrock Agent is AWS's managed version of the same underlying pattern, with Lambda functions as the actual tool implementations and IAM governing what each tool is permitted to do.

Defining an action group: Bedrock's equivalent of a tool

{
  "actionGroupName": "course-availability",
  "description": "Check availability and details for NovuSpark training courses",
  "actionGroupExecutor": {
    "lambda": "arn:aws:lambda:eu-west-2:008971632408:function:check-course-availability"
  },
  "apiSchema": {
    "payload": "{\"openapi\": \"3.0.0\", \"paths\": {\"/availability\": {\"get\": {\"parameters\": [{\"name\": \"course_name\", \"in\": \"query\", \"required\": true, \"schema\": {\"type\": \"string\"}}]}}}}"
  }
}

An action group is Bedrock's version of the @tool-decorated function from our LangChain series, or the JSON schema passed to tools in our OpenAI series — a description the agent uses to decide when this capability is relevant, paired here with an actual AWS Lambda function that executes when the agent decides to invoke it.

The Lambda function: where the real logic lives

def lambda_handler(event, context):
    course_name = event["parameters"][0]["value"]
 
    availability = {"Kubernetes Fundamentals": 4, "Terraform Fundamentals": 0}
    seats = availability.get(course_name, "unknown")
 
    return {
        "response": {
            "actionGroup": event["actionGroup"],
            "apiPath": event["apiPath"],
            "httpMethod": event["httpMethod"],
            "httpStatusCode": 200,
            "responseBody": {
                "application/json": {
                    "body": f'{{"course_name": "{course_name}", "seats_available": {seats}}}'
                }
            },
        }
    }

This is genuinely ordinary Lambda code — the same shape as any other Lambda function triggered by API Gateway or another AWS service — with the specific response format Bedrock Agents expect. The agent calls this function exactly the way a LangChain agent calls a Python tool function, or the OpenAI API calls back into your own code after a tool-call response — the mechanism is identical; only the specific plumbing connecting model decision to code execution differs.

Invoking the agent

client = boto3.client("bedrock-agent-runtime", region_name="eu-west-2")
 
response = client.invoke_agent(
    agentId="AGENT123",
    agentAliasId="ALIAS456",
    sessionId="session-user-789",
    inputText="Is there space in the Kubernetes course?",
)
 
for event in response["completion"]:
    if "chunk" in event:
        print(event["chunk"]["bytes"].decode("utf-8"), end="")
Yes — there are 4 seats available in the Kubernetes Fundamentals course.

sessionId is doing real work here, not just an arbitrary identifier — Bedrock Agents maintain conversation state per session automatically, the managed equivalent of the RunnableWithMessageHistory session-keyed memory covered in our LangChain series, without you managing that state storage yourself.

Combining action groups with a Knowledge Base

{
  "agentName": "novuspark-support-agent",
  "actionGroups": ["course-availability", "enrollment"],
  "knowledgeBases": [
    {
      "knowledgeBaseId": "KB123ABC",
      "description": "Company policies and course catalog documentation"
    }
  ]
}

A single Bedrock Agent can draw on both action groups (Lambda-backed tools for taking actions or fetching live data) and Knowledge Bases (retrieval over static documentation, covered in the previous post) simultaneously — deciding, per user message, whether the right response comes from calling a tool, retrieving a policy document, or both. This mirrors the multi-agent, multi-tool coordination patterns covered in our LangGraph series, provided here as a managed AWS capability rather than something you assemble and operate yourself.

Governance: the same IAM discipline, applied to what an agent can actually do

{
  "Version": "2012-10-17",
  "Statement": [
    {
      "Effect": "Allow",
      "Action": "lambda:InvokeFunction",
      "Resource": "arn:aws:lambda:eu-west-2:008971632408:function:check-course-availability"
    }
  ]
}

The agent's own execution role should be scoped to invoke only the specific Lambda functions backing its own defined action groups — not a blanket lambda:InvokeFunction on every function in the account. This is the same least-privilege discipline covered for our own GitHub Actions deploy pipeline, and flagged for LangChain agents taking consequential actions: an agent's actual permissions should match exactly what it needs, independent of trusting its judgment about when to use them.

What to actually remember from this post

  • A Bedrock action group is the managed equivalent of a tool in LangChain or the OpenAI API — a schema the agent reasons about, backed by a real Lambda function that executes.
  • The Lambda function is genuinely ordinary Lambda code, following a specific response shape Bedrock expects — the same tool-execution pattern covered throughout this blog, just running as a managed AWS function.
  • sessionId provides managed conversation memory, the equivalent of LangChain's session-keyed message history, without self-managing that storage.
  • Scope an agent's IAM execution role to exactly the Lambda functions its own action groups need — least privilege applied to what the agent can actually do, not just what it's designed to attempt.

Next in the series: Amazon Bedrock Guardrails: Responsible AI in Production, where we cover the safety and content-filtering layer that sits around everything covered so far in this series.

Ready when you are

Want training built around your team's real work?

Tell us about your team and what you're trying to solve — we'll recommend a program that fits.