Project 4 · Generative AI & Agents

Bedrock AgentCore Data Assistant

An AI agent that answers natural-language questions about a company's financial data (“What was July's revenue?”). It uses Amazon Bedrock AgentCore to run the model-orchestration loop and route tool calls into a Lambda that executes parameterized SQL against PostgreSQL — no custom orchestration code.

This page is the visual overview. Full source code, deploy guide, and cost breakdown live on GitHub.

01 The big picture

Five Terraform modules stack into a single request path: a static frontend, an authenticated API, a mediator Lambda, the AgentCore harness + gateway (the AI brain), a SQL-executor Lambda (the hands), and an RDS database inside a VPC.

flowchart TB
  user([User browser])
  cf["CloudFront + S3
static chat UI"] cog["Cognito
User Pool + App Client"] api["API Gateway (HTTP)
/chat + JWT authorizer"] med["Mediator Lambda
HTTP ⇄ AgentCore streaming"] harness["AgentCore Harness
model + system prompt + tools"] gw["AgentCore Gateway
MCP tool hub"] kpi["KPI Tools Lambda
SQL query executor"] rds[("RDS PostgreSQL
company database")] user --> cf --> api cog -.->|"issues / validates JWT"| api api --> med --> harness harness --> gw --> kpi --> rds classDef edge fill:#22303f,stroke:#4493f8,color:#e6edf3; classDef ai fill:#3a2d12,stroke:#ff9900,color:#e6edf3; classDef data fill:#12351f,stroke:#3fb950,color:#e6edf3; class cf,cog,api,med edge; class harness,gw ai; class kpi,rds data;
Blue = edge/auth/entry. Orange = the AgentCore AI layer. Green = the data/tool layer. Cognito authenticates users and the API Gateway authorizer validates every request's JWT.

02 What happens on a single question

The interesting part is the agent loop: the model decides which tool to call, AgentCore invokes it through the Gateway, and the result flows back to the model to be turned into a natural-language answer.

sequenceDiagram
  actor U as User
  participant FE as CloudFront UI
  participant CG as Cognito
  participant API as API Gateway
  participant MED as Mediator Lambda
  participant H as AgentCore Harness
  participant M as Foundation model
  participant GW as AgentCore Gateway
  participant L as KPI Lambda
  participant DB as RDS Postgres

  U->>FE: Log in
  FE->>CG: Authenticate
  CG-->>FE: JWT token
  U->>FE: "What is total revenue for July 2026?"
  FE->>API: POST /chat (JWT)
  API->>API: Validate JWT (Cognito authorizer)
  API->>MED: {message, sessionId}
  MED->>H: InvokeHarness (Converse format)
  H->>M: system prompt + question
  M-->>H: tool call: getMonthlyRevenue(month=2026-07)
  H->>GW: route tool call
  GW->>L: invoke with tool name + params
  L->>DB: parameterized SQL
  DB-->>L: result rows
  L-->>GW: result
  GW-->>H: result
  H->>M: tool result
  M-->>H: "Total revenue for July 2026 is 4,822.50..."
  H-->>MED: streamed tokens
  MED-->>API: {reply, sessionId}
  API-->>FE: {reply, sessionId}
  FE-->>U: Answer
        
AgentCore owns the orchestration loop (model → tool → result → model → answer), session isolation (each session in its own microVM), and observability. The application supplies only the model choice, the system prompt, and the tools.

03 Module wiring & trust relationships

The root Terraform composes five modules, threading each module's outputs into the next (database IP → KPI Lambda → harness → API → frontend). Two IAM roles establish the trust chain that lets the AI layer reach the tool Lambda.

flowchart TB
  subgraph root["Root Terraform — module wiring"]
    db["module.database
VPC · subnets · RDS · Secrets Manager · seeder"] kpi["module.kpi_tools_lambda
SQL executor + SG"] ac["module.agentcore_harness
Gateway + Harness + IAM"] apim["module.api_mediator
Cognito · API GW · mediator Lambda"] fe["module.frontend
S3 + CloudFront"] db -->|"db host / creds"| kpi kpi -->|"lambda ARN + name"| ac ac -->|"harness ARN"| apim apim -->|"api endpoint + client id"| fe end subgraph iam["Trust chain"] gwrole["IAM role: Gateway
→ invoke KPI Lambda"] hrole["IAM role: Harness
→ call Bedrock model + Gateway"] end ac -.defines.-> gwrole ac -.defines.-> hrole gwrole -.allows.-> kpi classDef m fill:#1c2330,stroke:#ff9900,color:#e6edf3; classDef r fill:#22303f,stroke:#4493f8,color:#e6edf3; class db,kpi,ac,apim,fe m; class gwrole,hrole r;
Terraform output-to-input flow across modules (solid) and the IAM trust relationships the harness module defines (dashed). The Gateway role is what actually authorizes invoking the KPI Lambda.

The six tools the agent can call

ToolAnswers
getMonthlyRevenue(month)Total sales for a month
getMonthlyProfit(month)Revenue minus costs
getOverdueInvoices(customerId?)Unpaid invoices past due
getTopProducts(month, limit?)Best sellers by revenue
getCustomerOrderHistory(customerId)All orders for a customer
getSalesByRegion(month)Geographic sales breakdown

04 Why AgentCore over Bedrock Agents Classic

This project uses the newer AgentCore primitives instead of classic Bedrock Agents. The practical differences show up in how tools are declared and how changes ship.

Bedrock Agents ClassicAgentCore equivalent
Action Group (OpenAPI schema + Lambda)Gateway Target (inline tool schema + Lambda)
Agent AliasHarness Endpoint (DEFAULT)
InvokeAgent APIInvokeHarness API
Agent preparation stepChanges apply immediately
Built-in orchestration onlySame loop, extensible + MCP-standard tools
No OpenAPI files, no prep step, model flexibility. Tools are defined inline in the Gateway Target, the harness updates in place, and the model can be swapped per invocation. Tools are exposed over the MCP protocol, so they interoperate with other agent frameworks.

05 Design decisions & tradeoffs

Auth at the edge. Every /chat request carries a Cognito JWT validated by the API Gateway JWT authorizer before the Mediator Lambda is invoked. The RDS instance is not publicly accessible (publicly_accessible = false) and is reached only from inside the VPC. A randomly generated DB password is also stored in Secrets Manager, though in this demo the Lambdas read their DB credentials from environment variables rather than fetching the secret at runtime — a pragmatic simplification worth hardening for production.
Managed orchestration over custom code. Letting AgentCore own the agent loop, session isolation, and observability removes a large amount of custom orchestration code — at the cost of depending on a newer, evolving AWS service surface.
Parameterized SQL tools over a free-form query tool. Six fixed, parameterized tools (rather than letting the model emit arbitrary SQL) keep the data layer safe and predictable, trading some flexibility for a much smaller attack surface.

Running cost for a light demo is roughly $15/month, dominated by the db.t4g.micro RDS instance; model tokens on Nova 2 Lite are about $0.001 per query.

← Back to all projects