RUNLOCALAIv38
->Will it run?Best GPUCompareTroubleshootStartLearnPulseModelsHardwareToolsBench
Run check
RUNLOCALAI

Independently operated catalog for local-AI hardware and software. Hand-written verdicts. Source-cited claims. Reproducible commands when we have them.

OP·Fredoline Eruo
DIR
  • Models
  • Hardware
  • Tools
  • Benchmarks
TOOLS
  • Will it run?
  • Compare hardware
  • Cost vs cloud
  • Choose my GPU
  • Prompting kits
  • Quick answers
REF
  • All buyer guides
  • Learn local AI
  • Methodology
  • Glossary
  • Errors KB
  • Trust
EDITOR
  • About
  • Author
  • How we make money
  • Editorial policy
  • Contact
LEGAL
  • Privacy
  • Terms
  • Sitemap
MAIL · MONTHLY DIGEST
Get monthly local AI changes
Monthly recap. No spam.
DISCLOSURE

Some links on this site are affiliate links (Amazon Associates and other first-class retailers). When you buy through them, we earn a small commission at no extra cost to you. Affiliate links do not influence our verdicts — there are cards we rate highly that we don't have affiliate relationships with, and cards that sell well that we refuse to recommend. Read more →

© 2026 runlocalai.coIndependently operated
RUNLOCALAI · v38
  1. >
  2. Home
  3. /Learn
  4. /Courses
  5. /Data Analysis with Local AI
  6. /Ch. 5
Data Analysis with Local AI

05. Natural Language Queries

Chapter 5 of 18 · 20 min
KEY INSIGHT

Natural language query interfaces enable non-technical access to data, but must handle ambiguity explicitly and maintain conversation context across multiple turns.

Beyond SQL generation, natural language interfaces provide conversational data exploration. Users ask follow-up questions, request clarifications, and refine queries through dialogue. Implementing this requires managing conversation context and handling ambiguous requests gracefully.

Building a Query Interface

A complete natural language query interface handles multiple turns, maintains context, and provides clear responses. The implementation combines text-to-SQL with response formatting and error handling.

import ollama
import sqlite3
from dataclasses import dataclass
from typing import Optional

@dataclass
class QueryContext:
    """Manages conversation context for natural language queries."""
    
    schema: str
    history: list = None
    
    def __post_init__(self):
        if self.history is None:
            self.history = []
    
    def add_turn(self, question: str, sql: str, result: str):
        self.history.append({
            'question': question,
            'sql': sql,
            'result': result
        })
    
    def get_context_prompt(self) -> str:
        context = f"Schema:\n{self.schema}\n\n"
        context += "Previous queries in this session:\n"
        for turn in self.history[-3:]:  # Last 3 turns
            context += f"Q: {turn['question']}\nSQL: {turn['sql']}\n\n"
        return context

def natural_language_query(
    question: str, 
    ctx: QueryContext, 
    conn: sqlite3.Connection
) -> dict:
    """Process natural language question and return results."""
    
    # Build context-aware prompt
    context = ctx.get_context_prompt()
    prompt = f"""{context}
    
    Current question: {question}
    
    Generate SQL query. Return ONLY the SQL query."""
    
    # Generate query
    response = ollama.chat(
        model='llama3.2',
        messages=[{'role': 'user', 'content': prompt}]
    )
    
    sql = response['message']['content'].strip()
    
    # Execute and format results
    try:
        df = pd.read_sql_query(sql, conn)
        ctx.add_turn(question, sql, "Success")
        return {
            'sql': sql,
            'success': True,
            'results': df.to_dict('records'),
            'row_count': len(df)
        }
    except Exception as e:
        ctx.add_turn(question, sql, f"Error: {e}")
        return {
            'sql': sql,
            'success': False,
            'error': str(e)
        }

# Usage example
schema = """CREATE TABLE sales (id, product, amount, date)"""
ctx = QueryContext(schema=schema)

# First question
result1 = natural_language_query(
    "What are total sales by product?",
    ctx,
    conn
)
print(f"SQL: {result1['sql']}")
print(f"Results: {result1.get('row_count', 0)} rows")

# Follow-up question
result2 = natural_language_query(
    "Show the same for last quarter",
    ctx,
    conn
)
print(f"SQL: {result2['sql']}")

The context mechanism allows follow-up questions to reference previous queries without repeating details. The model understands "show the same" refers to the previous breakdown.

Handling Ambiguity

Natural language contains inherent ambiguity. "Show sales" might mean total sales, sales per day, or sales per region. Resolving ambiguity requires either asking for clarification or making reasonable assumptions and explaining them.

def resolve_ambiguity(question: str, candidates: list, ctx: QueryContext) -> str:
    """Handle ambiguous questions by proposing clarifications."""
    
    prompt = f"""Question: {question}
    Possible interpretations:
    {chr(10).join(f"- {c}" for c in candidates)}
    
    If ambiguous, ask a clarifying question. If clear, state the assumption being made."""
    
    response = ollama.chat(
        model='llama3.2',
        messages=[{'role': 'user', 'content': prompt}]
    )
    
    return response['message']['content']

# Example ambiguity handling
question = "Show me sales trends"
candidates = [
    "Daily sales over time",
    "Monthly sales comparison by region",
    "Product category sales comparison"
]
clarification = resolve_ambiguity(question, candidates, ctx)
print(clarification)

When ambiguity exists, the response asks a specific question to resolve it rather than picking arbitrarily. Users appreciate being asked rather than receiving unexpected results.

EXERCISE

Build a simple natural language query interface for a dataset. Test with 15 questions including follow-ups and ambiguous requests. Document how the system handles each case.

← Chapter 4
Text-to-SQL
Chapter 6 →
Automated Visualization