Building Intelligent Task Filters with OpenAI: From Natural Language to SQL-like Queries
Building Intelligent Task Filters with OpenAI: From Natural Language to SQL-like Queries
How I built an AI-powered natural language interface for my personal task management system
The Problem: Filter Complexity vs. User Experience
Task management systems face a fundamental UX challenge: powerful filtering requires complex syntax that intimidates users. Traditional approaches force users to choose between:
- Simple filters: Easy to use but limited functionality
- Advanced filters: Powerful but require learning query syntax
In building my personal ADHD-optimized task management system, I discovered a third path: AI-powered natural language filtering that combines the best of both worlds.
Architecture Overview
My solution uses OpenAI’s GPT-4 to translate natural language requests into executable filter expressions for Vikunja, an open-source task management platform.
User Input: "urgent tasks due this week"
↓
OpenAI Translation Layer
↓
Filter Expression: 'labels = "urgent" && due_date >= now/w && due_date <= now/w+1w'
↓
Vikunja API Execution
Implementation Deep Dive
1. The Translation Engine
My OpenAIDecisionEngine serves as the intelligent middleware between user intent and database queries:
func (e *OpenAIDecisionEngine) SuggestFilter(ctx context.Context, request string) (*models.FilterSuggestionResponse, error) {
log.Info("OpenAI: Suggesting a filter expression", "request", request)
prompt := e.buildFilterSuggestionPrompt(request)
response, err := e.callOpenAI(ctx, prompt)
if err != nil {
log.Warn("Filter engine failed, using fallback", "error", err)
return e.getFallbackFilter(), nil
}
return e.parseFilterSuggestionResponse(response)
}
2. Prompt Engineering for Filter Generation
The key to reliable AI filtering is a comprehensive prompt that teaches the model Vikunja’s filter syntax:
func (e *OpenAIDecisionEngine) buildFilterSuggestionPrompt(request string) string {
return fmt.Sprintf(`# Vikunja Filter Expression Builder
You are an expert at creating Vikunja filter expressions. Convert this natural language request into valid filter syntax:
## Request: %s
## Available Fields
- **done** - Task completion status (true/false)
- **priority** - Priority level (1-5, where 5 is highest)
- **due_date** - Task due date with date math support
- **labels** - Task labels/tags
- **project_id** - Numeric project identifier
## Operators & Date Math
- Comparison: =, !=, >, >=, <, <=
- Logic: && (AND), || (OR)
- Date math: now/d (today), now/w (this week), now+7d (in 7 days)
## Response Format (JSON):
{
"filter": "done = false && priority >= 3",
"reasoning": "Explanation of filter logic",
"confidence": 0.95,
"strategy": "priority_focused"
}
Respond with valid JSON only.`, request)
}
3. Robust Response Parsing with Fallbacks
Production-quality hobby systems require graceful degradation. My parser implements multiple fallback layers:
func (e *OpenAIDecisionEngine) parseFilterSuggestionResponse(response string) (*models.FilterSuggestionResponse, error) {
var aiResponse struct {
Filter string `json:"filter"`
Reasoning string `json:"reasoning"`
Confidence float64 `json:"confidence"`
Strategy string `json:"strategy"`
}
if err := json.Unmarshal([]byte(response), &aiResponse); err != nil {
log.Warn("Falling back to basic filter due to parse error")
return &models.FilterSuggestionResponse{
Filter: "done = false", // Safe fallback
Reasoning: "AI parsing failed, using basic incomplete tasks filter",
Confidence: 0.3,
Strategy: "fallback",
Fallback: true,
}, nil
}
// Validate and set defaults...
return &models.FilterSuggestionResponse{
Filter: aiResponse.Filter,
Reasoning: aiResponse.Reasoning,
Confidence: aiResponse.Confidence,
Strategy: aiResponse.Strategy,
Fallback: false,
}, nil
}
Multi-Modal Filtering: The Best of Both Worlds
My system supports both AI-generated and direct filter expressions through a unified interface:
func (s Service) GetFilteredTasks(ctx context.Context, filter string, useAI bool) ([]models.Task, error) {
finalFilter := filter
if useAI {
aiFilter, err := s.FocusEngine.SuggestFilter(ctx, filter)
if err != nil {
log.Warn("AI filter failed, using original", "original", filter)
} else {
finalFilter = aiFilter.Filter
}
}
return s.Vikunja.GetFilteredTasks(ctx, finalFilter)
}
Users can seamlessly switch between:
- Natural language:
"urgent tasks due this week" - Direct expressions:
"done = false && priority >= 3"
Real-World Examples
Simple Requests
Input: "unfinished tasks"
Output: "done = false"
Complex Temporal Logic
Input: "high priority tasks due this week"
Output: "priority >= 4 && due_date >= now/w && due_date <= now/w+1w"
Project-Scoped Filtering
Input: "incomplete tasks in project 8"
Output: "done = false && project_id = 8"
Performance & Reliability Considerations
Even for personal projects, reliability matters when you depend on the system daily.
1. Caching Strategy
- Cache common filter patterns to reduce API calls
- Implement request deduplication for identical queries within time windows
2. Error Handling
- Always provide functional fallback filters
- Log confidence scores for monitoring AI accuracy
- Implement circuit breakers for OpenAI API failures
3. Cost Optimization
- Use shorter, focused prompts to minimize token usage
- Implement intelligent prompt caching
- Direct filter expressions bypass AI processing entirely
Real-World Performance
In my personal use of this system, I’ve found:
- Significant time savings when quickly filtering tasks during focus sessions
- 3-second average response time for AI-generated filters
- Near-perfect filter execution thanks to robust fallback handling
- Much less frustration compared to manually writing filter syntax
Key Learnings
1. Prompt Engineering is Critical
Investing time in comprehensive prompt engineering pays massive dividends in accuracy and reliability.
2. Fallbacks Aren’t Optional
Production AI systems must gracefully degrade. A working basic filter beats a perfect filter that fails.
3. Multi-Modal Approaches Win
Power users still want direct control. Offer both AI assistance and manual precision.
Building for One: Why Quality Matters in Personal Projects
1. Daily Dependence
When you use your own system every day, reliability isn’t optional. A broken filter during a focus session breaks your flow.
2. ADHD Considerations
Poor error handling creates frustration that can derail entire work sessions. Graceful fallbacks preserve momentum.
3. Learning Opportunity
Building production-quality personal tools teaches patterns applicable to professional development.
4. Portfolio Value
Well-engineered hobby projects demonstrate technical judgment and user empathy to potential employers.
Future Enhancements
Learned Filter Patterns
Track which AI-generated filters I modify most often to improve future suggestions.
Contextual Awareness
Incorporate my current project focus, time of day, and work patterns into filter generation.
Voice Integration
Add voice input for hands-free filtering during focus sessions.
Conclusion
AI-powered filtering transforms complex query interfaces into intuitive natural language interactions. By combining OpenAI’s language understanding with solid engineering practices, I created a system that works reliably for my daily task management needs.
The key insight: AI should enhance capability, not replace control. My multi-modal approach proves that even personal projects benefit from providing both intelligent assistance and direct user control.
Building this system taught me that good engineering principles apply regardless of project scale. Whether it’s enterprise software or a personal productivity tool, users (even if that user is yourself) deserve thoughtful, reliable experiences.
This implementation is part of my ADHD-optimized task management system, designed to reduce my cognitive load while maintaining powerful functionality. The codebase demonstrates practical AI integration patterns that scale from personal projects to production systems.
Technical Specifications
- AI Model: OpenAI GPT-4
- Backend: Go with structured logging
- Query Target: Vikunja API with SQL-like filter syntax
- Response Time: <3 seconds for AI-generated filters
- Fallback Strategy: Multi-layer graceful degradation
- Personal Project Status: ✅ Daily use and continuously improving
Comments