Skip to content

Commit 6881abf

Browse files
committed
Merge branch 'feature/optimize-agent-analytics-metadata' into 'develop'
Feature/optimize agent analytics metadata See merge request genaiic-reusable-assets/engagement-artifacts/genaiic-idp-accelerator!322
2 parents cba7f11 + a663beb commit 6881abf

File tree

10 files changed

+1446
-217
lines changed

10 files changed

+1446
-217
lines changed

CHANGELOG.md

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,15 @@ SPDX-License-Identifier: MIT-0
66
## [Unreleased]
77

88
### Added
9+
- **Analytics Agent 2-Phase Schema Optimization for Improved Performance**
10+
- Implemented progressive schema disclosure system with efficient 2-phase approach
11+
- Phase 1: `get_database_overview()` provides fast table listing and guidance (~500 tokens vs 3000+ tokens - 6x faster)
12+
- Phase 2: `get_table_info(['specific_tables'])` loads detailed schemas only for tables actually needed by the query
13+
- Enhanced SQL guidance with comprehensive Athena/Trino function reference and PostgreSQL operator warnings to prevent common query failures
14+
15+
### Fixed
16+
- Fix missing data in Glue tables when using a document class that contains a dash (-).
17+
918

1019
## [0.3.16]
1120

lib/idp_common_pkg/idp_common/agents/analytics/agent.py

Lines changed: 178 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -15,7 +15,12 @@
1515
from ..common.config import load_result_format_description
1616
from ..common.strands_bedrock_model import create_strands_bedrock_model
1717
from .config import load_python_plot_generation_examples
18-
from .tools import CodeInterpreterTools, get_database_info, run_athena_query
18+
from .tools import (
19+
CodeInterpreterTools,
20+
get_database_overview,
21+
get_table_info,
22+
run_athena_query,
23+
)
1924
from .utils import register_code_interpreter_tools
2025

2126
logger = logging.getLogger(__name__)
@@ -50,29 +55,179 @@ def create_analytics_agent(
5055
# Task
5156
Your task is to:
5257
1. Understand the user's question
53-
2. Use get_database_info tool to understand initial information about the database schema
54-
3. Generate a valid Athena query that answers the question OR that will provide you information to write a second Athena query which answers the question (e.g. listing tables first, if not enough information was provided by the get_database_info tool)
55-
4. Before executing the Athena query, re-read it and make sure _all_ column names mentioned _anywhere inside of the query_ are enclosed in double quotes.
56-
5. Execute your revised query using the run_athena_query tool. If you receive an error message, correct your Athena query and try again a maximum of 5 times, then STOP. Do not ever make up fake data. For exploratory queries you can return the athena results directly. For larger or final queries, the results should need to be returned because downstream tools will download them separately.
57-
6. Use the write_query_results_to_code_sandbox to convert the athena response into a file called "query_results.csv" in the same environment future python scripts will be executed.
58-
7. If the query is best answered with a plot or a table, write python code to analyze the query results to create a plot or table. If the final response to the user's question is answerable with a human readable string, return it as described in the result format description section below.
59-
8. To execute your plot generation code, use the execute_python tool and directly return its output without doing any more analysis.
58+
2. **EFFICIENT APPROACH**: Use get_database_overview() to get a fast overview of available tables and their purposes
59+
3. Apply the Question-to-Table mapping rules below to select the correct tables for your query
60+
4. Use get_table_info(['table1', 'table2']) to get detailed schemas ONLY for the tables you need
61+
5. Generate a valid Athena query based on the targeted schema information
62+
6. **VALIDATE YOUR SQL**: Before executing, check for these common mistakes:
63+
- All column names enclosed in double quotes: `"column_name"`
64+
- No PostgreSQL operators: Replace `~` with `REGEXP_LIKE()`
65+
- No invalid functions: Replace `CONTAINS()` with `LIKE`, `ILIKE` with `LOWER() + LIKE`
66+
- Only valid Trino functions used
67+
- Proper date formatting and casting
68+
7. Execute your validated query using the run_athena_query tool. If you receive an error message, correct your Athena query and try again a maximum of 5 times, then STOP. Do not ever make up fake data. For exploratory queries you can return the athena results directly. For larger or final queries, the results should need to be returned because downstream tools will download them separately.
69+
8. Use the write_query_results_to_code_sandbox to convert the athena response into a file called "query_results.csv" in the same environment future python scripts will be executed.
70+
9. If the query is best answered with a plot or a table, write python code to analyze the query results to create a plot or table. If the final response to the user's question is answerable with a human readable string, return it as described in the result format description section below.
71+
10. To execute your plot generation code, use the execute_python tool and directly return its output without doing any more analysis.
72+
73+
# CRITICAL: Two-Step Database Information Approach
74+
**For optimal performance and accuracy:**
75+
76+
## Step 1: Overview (Fast)
77+
- Always start with `get_database_overview()` to see available tables
78+
- This gives you table names, purposes, and question-to-table mapping guidance
79+
- **~500 tokens vs 3000+ tokens** - much faster for simple questions
80+
81+
## Step 2: Detailed Schemas (On-Demand)
82+
- Use `get_table_info(['table1', 'table2'])` for specific tables you need
83+
- Only request detailed info for tables relevant to your query
84+
- Get complete column listings, sample queries, and aggregation rules
85+
86+
# CRITICAL: Question-to-Table Mapping Rules
87+
**ALWAYS follow these rules to select the correct table:**
88+
89+
## For Classification/Document Type Questions:
90+
- "How many X documents?" → Use `document_sections_x` table
91+
- "Documents classified as Y" → Use `document_sections_y` table
92+
- "What document types processed?" → Query document_sections_* tables
93+
- **NEVER use metering table for classification info - it only has usage/cost data**
94+
95+
Examples:
96+
```sql
97+
-- ✅ CORRECT: Count W2 documents
98+
SELECT COUNT(DISTINCT "document_id") FROM document_sections_w2 WHERE "date" = CAST(CURRENT_DATE AS VARCHAR)
99+
100+
-- ❌ WRONG: Don't use metering for classification
101+
SELECT COUNT(*) FROM metering WHERE "service_api" LIKE '%w2%'
102+
```
103+
104+
## For Volume/Cost/Consumption Questions:
105+
- "How much did processing cost?" → Use `metering` table
106+
- "Token usage by model" → Use `metering` table
107+
- "Pages processed" → Use `metering` table (with proper MAX aggregation)
108+
109+
## For Accuracy Questions:
110+
- "Document accuracy" → Use `evaluation` tables (may be empty)
111+
- "Precision/recall metrics" → Use `evaluation` tables
112+
113+
## For Content/Extraction Questions:
114+
- "What was extracted from documents?" → Use appropriate `document_sections_*` table
115+
- "Show invoice amounts" → Use `document_sections_invoice` table
60116
61117
DO NOT attempt to execute multiple tools in parallel. The input of some tools depend on the output of others. Only ever execute one tool at a time.
62118
63-
When generating Athena:
64-
- ALWAYS put ALL column names in double quotes when including ANYHWERE inside of a query.
65-
- Use standard Athena syntax compatible with Amazon Athena, for example use standard date arithmetic that's compatible with Athena.
66-
- Do not guess at table or column names. Execute exploratory queries first with the `return_full_query_results` flag set to True in the run_athena_query_with_config tool. Your final query should use `return_full_query_results` set to False. The query results still get saved where downstream processes can pick them up when `return_full_query_results` is False, which is the desired method.
67-
- Use a "SHOW TABLES" query to list all dynamic tables available to you.
68-
- Use a "DESCRIBE" query to see the precise names of columns and their associated data types, before writing any of your own queries.
69-
- Include appropriate table joins when needed
70-
- Use column names exactly as they appear in the schema, ALWAYS in double quotes within your query.
71-
- When querying strings, be aware that tables may contain ALL CAPS strings (or they may not). So, make your queries agnostic to case whenever possible.
72-
- If you cannot get your query to work successfully, stop. DO NOT EVER generate fake or synthetic data. Instead, return a text response indicating that you were unable to answer the question based on the data available to you.
73-
- The Athena query does not have to answer the question directly, it just needs to return the data required to answer the question. Python code will read the results and further analyze the data as necessary. If the Athena query is too complicated, you can simplify it to rely on post processing logic later.
74-
- If your query returns 0 rows, it may be that the query needs to be changed and tried again. If you try a few variations and keep getting 0 rows, then perhaps that tells you the answer to the user's question and you can stop trying.
75-
- If you get an error related to the column not existing or not having permissions to access the column, this is likely fixed by putting the column name in double quotes within your Athena query.
119+
# CRITICAL: Athena SQL Function Reference (Trino-based)
120+
**Athena engine version 3 uses Trino functions. DO NOT use PostgreSQL-style operators or invalid functions.**
121+
122+
## CRITICAL: Regular Expression Operators
123+
**Athena does NOT support PostgreSQL-style regex operators:**
124+
- ❌ NEVER use `~`, `~*`, `!~`, or `!~*` operators (these will cause query failures)
125+
- ✅ ALWAYS use `REGEXP_LIKE(column, 'pattern')` for regex matching
126+
- ✅ Use `NOT REGEXP_LIKE(column, 'pattern')` for negative matching
127+
128+
### Common Regex Examples:
129+
```sql
130+
-- ❌ WRONG: PostgreSQL-style (will fail with operator error)
131+
WHERE "inference_result.wages" ~ '^[0-9.]+$'
132+
WHERE "service_api" ~* 'classification'
133+
WHERE "document_type" !~ 'invalid'
134+
135+
-- ✅ CORRECT: Athena/Trino style
136+
WHERE REGEXP_LIKE("inference_result.wages", '^[0-9.]+$')
137+
WHERE REGEXP_LIKE(LOWER("service_api"), 'classification')
138+
WHERE NOT REGEXP_LIKE("document_type", 'invalid')
139+
```
140+
141+
## Valid String Functions (Trino-based):
142+
- `LIKE '%pattern%'` - Pattern matching (NOT CONTAINS function)
143+
- `REGEXP_LIKE(string, pattern)` - Regular expression matching (NOT ~ operator)
144+
- `LOWER()`, `UPPER()` - Case conversion
145+
- `POSITION(substring IN string)` - Find substring position (NOT STRPOS)
146+
- `SUBSTRING(string, start, length)` - String extraction
147+
- `CONCAT(string1, string2)` - String concatenation
148+
- `LENGTH(string)` - String length
149+
- `TRIM(string)` - Remove whitespace
150+
151+
## ❌ COMMON MISTAKES - Functions/Operators that DON'T exist in Athena:
152+
- `CONTAINS(string, substring)` → Use `string LIKE '%substring%'`
153+
- `ILIKE` operator → Use `LOWER(column) LIKE LOWER('pattern')`
154+
- `STRPOS(string, substring)` → Use `POSITION(substring IN string)`
155+
- `~` regex operator → Use `REGEXP_LIKE(column, 'pattern')`
156+
157+
## Valid Date/Time Functions:
158+
- `CURRENT_DATE` - Current date
159+
- `DATE_ADD(unit, value, date)` - Date arithmetic (e.g., `DATE_ADD('day', 1, CURRENT_DATE)`)
160+
- `CAST(expression AS type)` - Type conversion
161+
- `FORMAT_DATETIME(timestamp, format)` - Date formatting
162+
163+
## Critical Query Patterns:
164+
```sql
165+
-- ✅ CORRECT: String matching
166+
WHERE LOWER("service_api") LIKE '%classification%'
167+
168+
-- ❌ WRONG: Invalid function
169+
WHERE CONTAINS("service_api", 'classification')
170+
171+
-- ✅ CORRECT: Numeric validation with regex
172+
WHERE REGEXP_LIKE("inference_result.amount", '^[0-9]+\.?[0-9]*$')
173+
174+
-- ❌ WRONG: PostgreSQL regex operator
175+
WHERE "inference_result.amount" ~ '^[0-9.]+$'
176+
177+
-- ✅ CORRECT: Case-insensitive pattern matching
178+
WHERE LOWER("document_type") LIKE LOWER('%invoice%')
179+
180+
-- ❌ WRONG: ILIKE operator
181+
WHERE "document_type" ILIKE '%invoice%'
182+
183+
-- ✅ CORRECT: Today's data
184+
WHERE "date" = CAST(CURRENT_DATE AS VARCHAR)
185+
186+
-- ✅ CORRECT: Date range
187+
WHERE "date" >= '2024-01-01' AND "date" <= '2024-12-31'
188+
```
189+
190+
**TRUST THIS INFORMATION - Do not run discovery queries like SHOW TABLES or DESCRIBE unless genuinely needed.**
191+
192+
When generating Athena queries:
193+
- **ALWAYS put ALL column names in double quotes** - this includes dot-notation columns like `"document_class.type"`
194+
- **Use only valid Trino functions** listed above - Athena engine v3 is Trino-based
195+
- **Leverage comprehensive schema first** - it contains complete table/column information
196+
- **Follow aggregation patterns**: MAX for page counts per document (not SUM), SUM for costs
197+
- **Use case-insensitive matching**: `WHERE LOWER("column") LIKE LOWER('%pattern%')`
198+
- **Handle dot-notation carefully**: `"document_class.type"` is a SINGLE column name with dots
199+
- **Prefer simple queries**: Complex logic can be handled in Python post-processing
200+
201+
## Error Recovery Patterns:
202+
- **`~ operator not found`** → Replace with `REGEXP_LIKE(column, 'pattern')`
203+
- **`ILIKE operator not found`** → Use `LOWER(column) LIKE LOWER('pattern')`
204+
- **`Function CONTAINS not found`** → Use `column LIKE '%substring%'`
205+
- **`Function STRPOS not found`** → Use `POSITION(substring IN column)`
206+
- **Column not found** → Check double quotes: `"column_name"`
207+
- **Function not found** → Use valid Trino functions only
208+
- **0 rows returned** → Check table names, date filters, and case sensitivity
209+
- **Case sensitivity** → Use `LOWER()` for string comparisons
210+
211+
## Standard Query Templates:
212+
```sql
213+
-- Document classification count
214+
SELECT COUNT(DISTINCT "document_id")
215+
FROM document_sections_{type}
216+
WHERE "date" = CAST(CURRENT_DATE AS VARCHAR)
217+
218+
-- Cost analysis
219+
SELECT "context", SUM("estimated_cost") as total_cost
220+
FROM metering
221+
WHERE "date" >= '2024-01-01'
222+
GROUP BY "context"
223+
224+
-- Joined analysis
225+
SELECT ds."document_class.type", AVG(CAST(m."estimated_cost" AS DOUBLE)) as avg_cost
226+
FROM document_sections_w2 ds
227+
JOIN metering m ON ds."document_id" = m."document_id"
228+
WHERE ds."date" = CAST(CURRENT_DATE AS VARCHAR)
229+
GROUP BY ds."document_class.type"
230+
```
76231
77232
When writing python:
78233
- Only write python code to generate plots or tables. Do not use python for any other purpose.
@@ -132,7 +287,8 @@ def run_athena_query_with_config(
132287
run_athena_query_with_config,
133288
code_interpreter_tools.write_query_results_to_code_sandbox,
134289
code_interpreter_tools.execute_python,
135-
get_database_info,
290+
get_database_overview, # Fast, lightweight table overview
291+
get_table_info, # Detailed schema for specific tables
136292
]
137293

138294
# Get model ID from environment variable

0 commit comments

Comments
 (0)