AWS Builder Center
Building Natural Language Query Interfaces with AWS Bedrock

Building Natural Language Query Interfaces with AWS Bedrock

A practical guide to building NL-to-SQL systems for data warehouses. Covers architecture, costs, JSON querying, and when to build custom vs use managed solutions like Quick Suite.

Democratizing data access with AI-powered query translation

The Challenge: Bridging the Gap Between Business Users and Data

Organizations today sit on vast amounts of valuable data locked away in data warehouses like Amazon Redshift. While this data holds critical insights about cloud resources, costs, and operations, accessing it typically requires SQL expertise—a skill that most business users, operations teams, and executives don't possess.
Consider these common scenarios:
  • A finance manager needs to understand AWS spending patterns by service and region
  • An operations engineer wants to quickly find all running EC2 instances in production
  • A security analyst needs to identify S3 buckets without encryption enabled
  • A team lead wants to see Lambda function configurations for their team's resources
Each of these requests traditionally requires either SQL knowledge or a ticket to the data team, creating bottlenecks and delays in decision-making.
The question becomes: How can we enable anyone to query complex data warehouses using plain English?

Use Case: Natural Language Querying for AWS Resource and Cost Data

Our specific use case involves querying two types of data stored in Amazon Redshift:
  1. AWS Resource Configurations - EC2 instances, Lambda functions, S3 buckets with their full JSON configurations
  2. AWS Cost Data - Daily cost records by service, region, and resource with associated tags

Why Redshift SUPER Type?

AWS resource configurations are inherently heterogeneous—an EC2 instance has instanceType and state, while a Lambda function has runtime and memorySize. Rather than creating separate tables or flattening every possible field into columns, we use Redshift's SUPER data type.
SUPER provides:
  • Schema flexibility - Store varying JSON structures without predefined schemas or migrations
  • PartiQL querying - Use SQL-like dot notation to traverse JSON (e.g., resource_config_json.tags.Environment)
  • Native performance - Columnar compression and optimized storage for semi-structured data
  • Up to 16MB per value - Handles large, deeply nested configurations
This lets us store a single resource_config_json column that holds different structures per resource type, queryable with standard SQL extended by PartiQL syntax.

Example Queries

Users need to ask questions like:
  • "Count EC2 instances by region"
  • "Show Production Lambda costs"
  • "Find S3 buckets with encryption enabled"
  • "Show costs for Lambda functions with more than 512MB memory" (requires joining cost and resource data)

Key Requirements

RequirementDescription
Natural Language InputAccept plain English queries without SQL knowledge
JSON Field QueryingQuery nested JSON data in Redshift SUPER columns
Cross-Table QueriesJoin resource configurations with cost data
Tag-Based FilteringFilter by environment, team, cost center tags
AggregationsSupport COUNT, SUM, AVG with GROUP BY
Low LatencyTarget sub-3-second response for simple queries
Clear Error HandlingGuide users when queries are ambiguous

Solution Options Assessment

We evaluated several approaches for implementing natural language data warehouse querying:

Option 1: Amazon Quick Suite

Amazon Quick Suite  is AWS's generative AI-powered business intelligence platform.
Fit Assessment:
CriterionScoreNotes
Natural Language Querying✅ HighNative Q&A capability with AI
Redshift Integration✅ HighDirect connector available
JSON/SUPER Column Support⚠️ MediumRequires flattening (see below)
Custom Query Logic⚠️ MediumLimited customization
Cost Model⚠️ MediumPer-user pricing
SUPER/JSON Limitation: Quick Suite supports only primitive data types (Date, Decimal, Integer, String). To query SUPER columns, you must flatten JSON into views or custom SQL datasets—workable for known patterns but limits ad-hoc exploration.
Best For: Turnkey BI with minimal development, where standard query patterns suffice.

Option 2: RAG — Not Recommended

Some teams consider RAG for text-to-SQL, embedding schemas in a vector database. This doesn't fit:
IssueExplanation
Wrong problem typeRAG retrieves text chunks; text-to-SQL needs structured translation
Schema is smallSchemas fit in a prompt—no retrieval needed
Added complexityVector DB adds latency without clear benefit
Verdict: Skip RAG for text-to-SQL—it doesn't solve the right problem.

Option 3: GraphRAG — Not Recommended

GraphRAG is sometimes considered for nested JSON. Also doesn't fit:
GraphRAG StrengthOur Use CaseFit?
Multi-hop reasoning across documentsWe're querying structured data❌
Entity relationship discoveryRelationships are already defined❌
Verdict: For text-to-SQL, the schema is your graph. No discovery needed.

Option 4: Direct LLM Translation (Recommended)

Use an LLM to translate natural language to SQL with schema context in the prompt.
CriterionScoreNotes
Natural Language Querying✅ HighLLM handles complex queries
Latency✅ HighSingle API call, <3 seconds
JSON/SUPER Support✅ HighFull PartiQL support
Customization✅ HighComplete control
Error Handling✅ HighClear feedback for ambiguous queries
Best For: Full control over translation, complex JSON querying, production-grade AI capabilities.

Recommendation: Direct LLM Translation

For querying AWS resource configurations and cost data with complex JSON fields, we recommend Direct LLM Translation using AWS Bedrock with Claude.

Why Not Quick Suite?

  1. Dynamic JSON Querying - SUPER columns need ad-hoc exploration; Quick Suite requires pre-flattening
  2. Custom JOIN Logic - Service-to-resource-type mapping is easier in code
  3. Tag Alignment - Different casing between tables handled seamlessly
  4. Better Error Handling - Ambiguous queries get clarification prompts instead of incorrect results
  5. Cost Control - Per-query vs per-user pricing

Cost Considerations

Bedrock (Claude Opus 4.5) Pricing:
  • Input: ~$15 per 1M tokens
  • Output: ~$75 per 1M tokens
Typical Query:
  • Input: ~500 tokens (schema + query)
  • Output: ~150 tokens (SQL + reasoning)
  • Cost per query: ~$0.012
Monthly estimates:
  • 100 queries/day: ~$36/month
  • 1,000 queries/day: ~$360/month
Compare to Quick Suite pricing (varies by edition) - break-even depends on query patterns and user count.

When Quick Suite Makes Sense

  • Rapid deployment without development resources
  • Standard BI dashboards are the primary need
  • Data doesn't require complex JSON querying
  • Per-user licensing fits your organization

Architecture

Our solution uses a layered architecture:
1. Streamlit Web UI
  • Query input with natural language
  • Interactive results grid with generated SQL display
  • Visualization charts
  • CSV/JSON export
2. NL Query Engine
  • Intent detection (SELECT, COUNT, AGGREGATE)
  • Table selection (resource_table, cost_data)
  • Result formatting
3. AWS Bedrock (Claude Opus 4.5)
  • Natural language understanding
  • JSON field extraction
  • Ambiguity detection
  • ~2-3 second latency
4. Query Translator
  • Converts ParsedQuery to SQL
  • Handles PartiQL for JSON fields
  • Generates parameterized queries
  • Maps service names for JOINs (e.g., 'AWS Lambda' → 'Lambda')
5. Amazon Redshift
  • resource_table with resource_config_json (SUPER)
  • cost_data with cost_json (SUPER)
  • Supports complex JOINs between tables

Error Handling and User Guidance

A key design principle is providing clear, actionable feedback when queries can't be processed.
Ambiguous Query Detection:
When Claude detects an ambiguous query, the system:
  1. Returns a specific clarification message from the AI
  2. Provides contextual suggestions (specify resource type, add conditions, etc.)
  3. Guides users toward more precise queries
Example:
  • User: "Show me resources"
  • System: "🤔 Your query needs clarification. Please specify which resource type (EC2, Lambda, S3) and any filters."
Other Error Scenarios:
Error TypeUser Experience
SQL Validation FailureShows security/safety issue with suggestions
Bedrock UnavailableClear setup instructions for AWS configuration
Database ConnectionTroubleshooting steps for connectivity
Parse ErrorsTips for simpler phrasing with examples
This approach ensures users learn how to write better queries rather than receiving incorrect results.

Scaling to Large Schemas

The approach above works for small schemas (2-10 tables). Enterprise warehouses with hundreds of tables need a different strategy.

The Challenge

Schema SizeTablesFits in Prompt?
Small2-10✅ Yes
Medium20-50⚠️ Tight
Large100-500❌ No

Solution: Two-Stage Table Selection

Rather than sending all table schemas to the LLM, use a lightweight selection layer:
Stage 1: Identify relevant tables using keyword matching or embeddings
Stage 2: Generate SQL using only selected table schemas
This approach:
  • Keeps prompts under token limits
  • Reduces latency and cost
  • Scales to 1000+ tables
Trade-offs:
ApproachLatencyAccuracyBest For
Single-stageFastHigh< 10 tables
Two-stage keywordFastMedium10-100 tables
Two-stage with LLMSlowerHigh100+ tables
Why Not Fine-Tuning?
Fine-tuning is sometimes suggested for text-to-SQL. For schema scaling, it's the wrong tool:
ConcernFine-TuningTable Selection
Schema changesRetrain modelUpdate catalog
Time to implementDays/weeksHours
CostHigh (training + hosting)Near zero
MaintenanceOngoing retrainingSimple config
Fine-tuning helps when the LLM struggles with query patterns (complex joins, domain-specific syntax). Schema scaling is a context window problem—table selection solves it directly.

Key Implementation Insights

1. JSON Field Inconsistencies Are the Real Challenge

The hardest part isn't the AI—it's handling data inconsistencies. Tags exist in both tables with different casing:
1
2
# cost_data: cost_json.tags.environment (lowercase)
# resource_table: resource_config_json.tags.Environment (CamelCase)
Solution: When joining tables, filter both with their respective conventions. This requires understanding your data sources deeply.

2. Redshift SUPER Needs Special Configuration

Enable case-sensitive JSON access:
1
SET enable_case_sensitive_super_attribute TO true;
Without this, tags.Environment and tags.environment are treated as the same field, causing silent failures.

3. Service Name Mapping Requires Business Logic

Cost data uses Amazon EC2, resource data uses EC2. JOIN conditions need CASE statements to map service names to resource types—this is domain knowledge the LLM can't infer.

4. Tag Structure Variations

In our implementation, EC2 and S3 tags are stored as arrays ([{"key": "Environment", "value": "Production"}]), while Lambda uses objects ({"Environment": "Production"}). This matches how we modeled the data from AWS Config.
Our current implementation supports Lambda tags directly. For EC2/S3 array tags, you'd need to either:
  • Flatten tags into separate columns via views
  • Implement array iteration in the query translator
  • Focus on Lambda resources for tag-based queries
Note: Actual AWS Config format may vary—verify your specific data structure.

What We Learned

1. The AI Isn't the Hard Part

Modern LLMs like Claude handle text-to-SQL remarkably well. The real challenges are:
  • Data inconsistencies (tag casing, service name variations)
  • Domain-specific business logic (how services map to resources)
  • Edge cases in your schema (nullable fields, nested arrays)

2. Error Handling Is a Product Decision

Silent fallbacks that return incorrect results are worse than clear error messages. When queries are ambiguous, guide users toward better questions rather than guessing intent.

3. Start Small, Validate Early

Don't build for 100 tables on day one. Start with 2-3 tables, test against real user queries, and iterate. You'll discover data quality issues and edge cases that no amount of planning reveals.

4. Bedrock as a Requirement Simplifies Everything

For prototypes, requiring AI availability keeps scope focused. You're demonstrating AI capabilities—own that dependency rather than building elaborate fallback systems.

Conclusion

Natural language interfaces for data warehouses are no longer experimental—they're practical with the right architecture choices.
Key Takeaways:
  1. Skip RAG and GraphRAG for text-to-SQL—they solve different problems
  2. Evaluate managed solutions first (Quick Suite) before building custom
  3. Build custom when you need dynamic JSON querying, complex business logic, or full control
  4. Plan for scale early if you have 20+ tables—two-stage selection is straightforward
  5. Prioritize error handling over silent fallbacks—guide users, don't guess
The technology is ready. The question is whether your data is ready—schema consistency, clear relationships, and well-defined business logic matter more than the AI model you choose.
What's Next?
If you're building something similar:
  • Start with your top 10 query patterns
  • Test against real data early (you'll find surprises)
  • Show the generated SQL for transparency
  • Iterate on prompts based on what works and what doesn't
The future of data access isn't about teaching everyone SQL—it's about making data as accessible as asking a question.
Any opinions in this article are those of the individual author and may not reflect the opinions of AWS.
Enjoyed reading this content? Let the author know!

Your likes, comments, shares, and saves help creators reach more builders.

Loading recommendations

Loading article