Prototype · AWS Bedrock · Azure Logic Apps · GCP Cloud Run

Bedrock Agent
Weather Tool Prototype

An exploration of Amazon Bedrock Agents and custom action groups, using a real-world weather data use case for Disney World parks. Three tools providing surface observations, hourly forecasts, and upper-atmosphere radiosonde data were built and wired into a Bedrock Agent via two distinct integration architectures — direct Lambda action groups and Azure Logic App action groups — resulting in a three-cloud data flow in a single agent invocation.

AWS
Amazon Bedrock
Agent + Claude foundation model
User conversation interface
action group
invocation
AWS
Lambda
Direct action group
Bedrock event envelope
— or —
Azure
Logic Apps
HTTP trigger action group
SAS-signed endpoint
HTTP GET
weather API
GCP
Cloud Run
weather-endpoint API
METAR · Forecast · Soundings
3
Action Group Tools
2
Architectures Tested
3
Clouds Used
2
Weather Stations
AWS
Bedrock Claude

Two Action Group Integration Approaches

The experiment specifically compared two ways of providing tools to a Bedrock Agent. Both were wired to the same GCP Cloud Run weather backend — the only variable was how the action group was implemented.

Direct Lambda Action Group
aws_lambda/lambda_atmospheric.py
InvocationBedrock → Lambda directly
Event formatBedrock agent envelope with actionGroup, apiPath, httpMethod, messageVersion
Response formatBedrock api_response envelope — nested action_responseresponseBody
RoutingConditional on apiPath + httpMethod inside handler
AuthIAM resource policy on Lambda
LatencyOne hop: Bedrock → Lambda → Cloud Run
Azure Logic Apps Action Group
azure_logicapps/
Cross-cloud
InvocationBedrock → Logic App HTTP trigger
Event formatStandard HTTP POST to Logic App /invoke endpoint
Response formatPlain statusCode / body — no Bedrock envelope needed (action group handles wrapping)
RoutingEach Logic App = one tool; separate OpenAPI spec per app
AuthSAS signature on Logic App trigger URL (sig query param)
LatencyTwo hops: Bedrock → Logic App → Cloud Run

Three Weather Tools — Each with an OpenAPI Spec

Bedrock Agents use OpenAPI 3.0 schemas to understand what an action group does and what parameters it accepts. Each tool was defined as a separate spec and registered as its own action group. The agent selects the right tool based on semantic understanding of the user's question.

get_weather_conditions
Current surface weather at Disney World
Endpoint
/api/v1/weather/latest_observations
Station
KISM — Kissimmee Gateway Airport (METAR)
Data
Temperature, humidity, precipitation, wind, pressure, visibility, cloud cover, wx_string
Use case
"What's the weather like right now at the parks?"
get_weather_forecast
Hourly forecast for next 5 periods
Endpoint
/api/v1/weather/forecasts?limit=5
Station
KISM — same surface station, forecast product
Data
5-period hourly forecast: temperature, precipitation probability, conditions
Use case
"Will it rain this afternoon at Magic Kingdom?"
get_atmospheric_conditions_florida
Upper-atmosphere radiosonde sounding data
Endpoint
/api/v1/weather/atmospheric_soundings
Station
72210 — NWS Tampa Bay upper-air station (twice-daily radiosonde)
Data
Pressure levels, wind shear, convective indices, thunderstorm potential at altitude
Use case
"Are atmospheric conditions favorable for afternoon thunderstorms?" (Florida-specific severe weather context)

The Bedrock Action Group Event Envelope

The most implementation-specific learning from this project: Bedrock Agents don't call Lambda functions with a plain HTTP event. They send and expect a structured envelope that includes agent metadata alongside the action payload. Getting this wrong produces silent failures.

Bedrock → Lambda inbound event
# What Bedrock sends to your Lambda function { "actionGroup": "get_atmospheric_conditions", # which action group was matched "apiPath": "/atmospheric_conditions", # the OpenAPI path Bedrock resolved to "httpMethod": "GET", # method from the OpenAPI spec "messageVersion": "1.0", # must echo back in the response "parameters": [...], # any params Bedrock extracted from user input "requestBody": {...} # POST body if applicable }
Lambda → Bedrock outbound response envelope
# What your Lambda MUST return — plain statusCode/body is NOT enough { "response": { "actionGroup": "get_atmospheric_conditions", # echo back from event "apiPath": "/atmospheric_conditions", "httpMethod": "GET", "httpStatusCode": 200, "responseBody": { "application/json": { "body": "{ ...JSON-stringified weather data... }" # must be a string, not object } } }, "messageVersion": "1.0" # echo event['messageVersion'] }
Event Parse
Extract actionGroup, apiPath, httpMethod, messageVersion Route on apiPath == "/atmospheric_conditions" and httpMethod == "GET" Unknown paths return error body with HTTP 200 — Bedrock always sees 200 at transport layer
Tool Execution
Station ID hardcoded: 72210 (atmospheric) or KISM (surface) GET weather-endpoint-936515207074.us-east1.run.app — GCP Cloud Run response.raise_for_status() — propagates HTTP errors as exceptions Response body: json.dumps(weather_data) — must be stringified JSON, not dict
Response Build
Wrap result in responseBody → application/json → body structure Echo actionGroup, apiPath, httpMethod from inbound event Top-level key must be "response" — not "actionResponse" or any other variant messageVersion echoed from event['messageVersion']

Key Findings from the Prototype

Why test Logic Apps as action group backends at all?+

The Logic Apps experiment was motivated by a specific question: can a Bedrock Agent's action groups be served from non-AWS infrastructure? If the answer is yes, it means the agent reasoning layer (Bedrock) can be decoupled from the tool execution layer, and tools can run wherever the data lives — without routing everything through Lambda first. The Logic Apps backends already existed for another Azure workflow, so reusing them as Bedrock action groups was a low-effort test of the cross-cloud pattern.

The finding was mixed: technically it works — Bedrock calls the Logic App's HTTP trigger URL as specified in the OpenAPI server block, and the Logic App's response is passed back to the agent. But the SAS URL authentication is awkward to manage (the sig token expires), and the two-hop latency (Bedrock → Azure → GCP) adds observable delay compared to the direct Lambda path.

Why three tools instead of one?+

Testing tool selection behavior was a core goal. A Bedrock Agent with one weather tool can't demonstrate anything interesting about agent reasoning. With three tools serving different data types at different stations, the agent must decide which tool to invoke based on the user's question. "Is it raining?" → observations. "Will it storm this afternoon?" → forecast. "Are conditions favorable for afternoon thunderstorms developing?" → atmospheric soundings.

The upper-atmosphere sounding tool (station 72210) was the most interesting to test because its value proposition is not obvious from a consumer perspective — NWS radiosonde data isn't something most people know exists. Whether the agent would correctly identify when to use it (when asked about convective instability, CAPE index, or storm development potential) vs defaulting to the surface forecast was a useful signal for evaluating the agent's tool reasoning quality.

The response envelope — the sharpest implementation constraint+

The single largest friction point in getting the Lambda action group working correctly was the response format. A standard Lambda function returns {"statusCode": 200, "body": "..."}. Bedrock Agents require a completely different structure: the responseBody must be nested inside an action_response object inside a top-level response key, and the body itself must be a JSON string (not a parsed object). An early version returned the weather data as a Python dict inside the body — Bedrock silently discarded it.

The messageVersion echo requirement was also non-obvious: the top-level response must include "messageVersion": event['messageVersion'] or the agent invocation fails with an opaque validation error. None of these requirements are enforced at Lambda test time — they only surface when the actual Bedrock agent invokes the function.

How this prototype informed the production Park Agent architecture+

Three decisions from this prototype carried forward into the production Park Agent Chat (Azure Functions-based):

  • Tool definitions as OpenAPI specs — the discipline of writing a proper OpenAPI spec for each tool, rather than ad-hoc function definitions, was validated here and applied to the Azure Functions action group equivalents
  • Avoid cross-cloud hops — the Logic App experiment confirmed that cross-cloud action group backends work but add latency and authentication complexity with no benefit when all compute can live in one cloud. The production system keeps Bedrock/Azure agent reasoning and tool execution in the same cloud.
  • Separate weather tool from wait-time tool — the prototype confirmed that weather context and ride-specific context should be separate tools, not a single monolithic "park conditions" tool, because the agent selects more accurately when tools have narrow, well-named purposes

The production system migrated from AWS Bedrock to Azure AI (Azure Functions + Azure OpenAI), but the action group design principles — narrow tools, OpenAPI specs, one purpose per tool — were directly informed by what worked and didn't work in this prototype.

Production Successor: Park Agent Chat → Knowledge RAG ↗ All Projects ↗