9.4 KiB
AI Usage Subagent Guide
The ai-usage subagent specializes in LLM APIs (Gemini, Claude), prompt engineering, and AI-powered features in the Flyer Crawler project.
Quick Reference
| Aspect | Details |
|---|---|
| Primary Use | Gemini API integration, prompt engineering, AI extraction |
| Key Files | src/services/aiService.server.ts, src/services/flyerProcessingService.server.ts |
| Key ADRs | ADR-041 (AI Integration), ADR-046 (Image Processing) |
| API Key Env | VITE_GOOGLE_GENAI_API_KEY (prod), VITE_GOOGLE_GENAI_API_KEY_TEST (test) |
| Error Handling | Rate limits (429), JSON parse errors, timeout handling |
| Delegate To | coder (implementation), testwriter (tests), integrations-specialist |
When to Use
Use the ai-usage subagent when you need to:
- Integrate with the Gemini API for flyer extraction
- Debug AI extraction failures
- Optimize prompts for better accuracy
- Handle rate limiting and API errors
- Implement new AI-powered features
- Fine-tune extraction schemas
What ai-usage Knows
The ai-usage subagent understands:
- Google Generative AI (Gemini) API
- Flyer extraction prompts and schemas
- Error handling for AI services
- Rate limiting strategies
- Token optimization
- AI service architecture (ADR-041)
AI Architecture Overview
┌─────────────────┐ ┌─────────────────┐ ┌─────────────────┐
│ Flyer Upload │───►│ AI Service │───►│ Gemini API │
│ │ │ │ │ │
└─────────────────┘ │ - Preprocessing │ │ - Vision model │
│ - Prompt build │ │ - JSON output │
│ - Response parse│ │ │
└─────────────────┘ └─────────────────┘
│
▼
┌─────────────────┐
│ Validation & │
│ Normalization │
└─────────────────┘
Key Files
| File | Purpose |
|---|---|
src/services/aiService.server.ts |
Gemini API integration |
src/services/flyerProcessingService.server.ts |
Flyer extraction pipeline |
src/schemas/flyer.schemas.ts |
Zod schemas for AI output validation |
src/types/ai.types.ts |
TypeScript types for AI responses |
Example Requests
Debugging Extraction Failures
"Use ai-usage to debug why flyer extractions are failing for
multi-page PDFs. The error logs show 'Invalid JSON response'
but only for certain stores."
Optimizing Prompts
"Use ai-usage to improve the item extraction prompt. Currently
it's missing unit prices when items show 'X for $Y' pricing
(e.g., '3 for $5')."
Handling Rate Limits
"Use ai-usage to implement exponential backoff for Gemini API
rate limits. We're seeing 429 errors during high-volume uploads."
Adding New AI Features
"Use ai-usage to add a feature that uses Gemini to categorize
extracted items into grocery categories (produce, dairy, meat, etc.)."
Extraction Pipeline
1. Image Preprocessing
// Convert PDF to images, resize large images
const processedImages = await imageProcessor.prepareForAI(uploadedFile);
2. Prompt Construction
The extraction prompt includes:
- System instructions for the AI model
- Expected output schema (JSON)
- Examples of correct extraction
- Handling instructions for edge cases
3. API Call
const response = await aiService.extractFlyerData(processedImages, storeContext, extractionOptions);
4. Response Validation
// Validate against Zod schema
const validatedItems = flyerItemsSchema.parse(response.items);
5. Normalization
// Normalize prices, units, quantities
const normalizedItems = normalizeExtractedItems(validatedItems);
Common Issues and Solutions
Issue: Inconsistent Price Extraction
Symptoms: Same item priced differently on different extractions.
Solution: Improve prompt with explicit price format examples:
"Price formats to recognize:
- $X.XX (regular price)
- X for $Y.YY (multi-buy)
- $X.XX/lb or $X.XX/kg (unit price)
- $X.XX each (per item)
- SAVE $X.XX (discount amount, not item price)"
Issue: Missing Items from Dense Flyers
Symptoms: Flyers with many items on one page have missing extractions.
Solution:
- Split page into quadrants for separate extraction
- Increase token limit for response
- Use structured grid-based prompting
Issue: Rate Limit Errors (429)
Symptoms: 429 Too Many Requests errors during bulk uploads.
Solution: Implement request queuing:
// Add to job queue instead of direct call
await flyerQueue.add(
'extract',
{
flyerId,
images,
},
{
attempts: 3,
backoff: {
type: 'exponential',
delay: 2000,
},
},
);
Issue: Hallucinated Items
Symptoms: Items extracted that don't exist in the flyer.
Solution:
- Add confidence scoring to extraction
- Request bounding box coordinates for verification
- Add post-extraction validation against image
Prompt Engineering Best Practices
1. Be Specific About Output Format
Output MUST be valid JSON matching this schema:
{
"items": [
{
"name": "string (product name as shown)",
"brand": "string or null",
"price": number (in dollars),
"unit": "string (each, lb, kg, etc.)",
"quantity": number (default 1)
}
]
}
2. Provide Examples
Example extractions:
- "Chicken Breast $4.99/lb" -> {"name": "Chicken Breast", "price": 4.99, "unit": "lb"}
- "Coca-Cola 12pk $5.99" -> {"name": "Coca-Cola", "quantity": 12, "price": 5.99, "unit": "each"}
3. Handle Edge Cases Explicitly
Special cases:
- If "LIMIT X" shown, add to notes, don't affect price
- If "SAVE $X" shown without base price, mark price as null
- If item is "FREE with purchase", set price to 0
4. Request Structured Thinking
For each item:
1. Identify the product name and brand
2. Find the associated price
3. Determine if price is per-unit or total
4. Extract any quantity information
Monitoring AI Performance
Metrics to Track
| Metric | Description | Target |
|---|---|---|
| Extraction success rate | % of flyers processed without error | >95% |
| Items per flyer | Average items extracted | Varies by store |
| Price accuracy | Match rate vs manual verification | >98% |
| Response time | Time from upload to extraction complete | <30s |
Logging
log.info(
{
flyerId,
itemCount: extractedItems.length,
processingTime: duration,
modelVersion: response.model,
tokenUsage: response.usage,
},
'Flyer extraction completed',
);
Environment Configuration
| Variable | Purpose |
|---|---|
VITE_GOOGLE_GENAI_API_KEY |
Gemini API key (production) |
VITE_GOOGLE_GENAI_API_KEY_TEST |
Gemini API key (test) |
Note: Use separate API keys for production and test to avoid rate limit conflicts and enable separate billing tracking.
Testing AI Features
Unit Tests
Mock the Gemini API response:
vi.mock('@google/generative-ai', () => ({
GoogleGenerativeAI: vi.fn().mockImplementation(() => ({
getGenerativeModel: () => ({
generateContent: vi.fn().mockResolvedValue({
response: {
text: () => JSON.stringify({ items: mockItems }),
},
}),
}),
})),
}));
Integration Tests
Use recorded responses for deterministic testing:
// Save real API responses to fixtures
const fixtureResponse = await fs.readFile('fixtures/gemini-response.json');
Related Documentation
- OVERVIEW.md - Subagent system overview
- CODER-GUIDE.md - For implementing AI features
- TESTER-GUIDE.md - Testing AI features
- INTEGRATIONS-GUIDE.md - External API patterns
- ../adr/0041-ai-gemini-integration-architecture.md - AI integration ADR
- ../adr/0046-image-processing-pipeline.md - Image processing
- ../getting-started/ENVIRONMENT.md - Environment configuration