Agentic RAG on SQL without changing your schema

A lot of teams are making the same expensive mistake right now. They decide to “AI-ify” their data, so they start by dumping the whole relational database into a vector store. What comes out is slow, costly, and blind to every structured relationship they spent years building in their SQL tables. Agentic RAG on SQL is the alternative, and it needs neither a migration nor a schema change.

The data is fine. The retrieval strategy is the problem. In a typical WordPress or enterprise setup, your reports, contracts and proposals already sit in LONGTEXT or JSON columns. Index them semantically and you throw away SQL’s aggregate functions. A hybrid approach keeps both, and it keeps the bill down.

The hybrid retrieval problem: SQL vs vector

When a client asks “how many projects over $1M were approved last year?”, vector search is the wrong tool. It will hunt for semantically similar projects and get the arithmetic wrong. Ask instead “what are the common trends in our winning proposals?” and a SQL LIKE query has nothing to offer.

What you need is an orchestrator, a ReAct agent, that reads the question and picks the tool off the belt. I went into shaping the underlying data for this in vector search optimization and JSON flattening.

Architecture: routing via tool docstrings

The agent reasons about the question before it searches anything. You give it two tools: search_database for SQL and search_articles for the vector store. Everything then rides on the docstrings. Leave them vague and the agent will invent SQL queries, or call the vector store to count rows.

# Example of a precise Tool Docstring for Agentic RAG on SQL
@tool
def search_database(query: str):
    """
    Executes raw SQL on the 'articles' table. 
    Use this for: 
    - Calculations (COUNT, SUM, AVG)
    - Specific date ranges (WHERE published_date > '2023-01-01')
    - Exact metadata matching (category='tech')
    """
    return db.execute(query)

Post-filtering in FAISS

A war story. I built a hybrid system for a custom WooCommerce dashboard once. SQL produced a list of “High Value” customer IDs, and those IDs went to a vector store to find similar purchase intents. It kept answering “No results found” with the data sitting right there.

The culprit was post-filtering. Most vector databases, FAISS included, run the semantic search first, take the global top 10 matches, and then apply your metadata filter. If none of your SQL-filtered rows made that global top 10, you get zero results. Either move to a vector store that supports pre-filtering, or pass the full content retrieved from SQL back into the LLM for a refined search. The second option works and gets expensive quickly.

Implementing a dispatcher in WordPress

In a WordPress environment you are not running raw Python on the front end, so an API bridge sits in between. This is how I usually structure the dispatcher that handles routing before anything reaches the expensive LLM layers.

<?php
/**
 * bbioon_rag_dispatcher
 * Decides if we need a direct WPDB query or a Vector API call.
 */
function bbioon_rag_dispatcher( $user_query ) {
    $intent = bbioon_get_query_intent( $user_query ); // Minimal LLM call for intent

    if ( 'computation' === $intent ) {
        global $wpdb;
        // Handle via SQL
        return $wpdb->get_results( "SELECT COUNT(*) FROM {$wpdb->prefix}posts WHERE post_type = 'proposal'" );
    }

    // Otherwise, route to our Vector/RAG endpoint
    return bbioon_call_agentic_rag_service( $user_query );
}
?>

Scaling this eventually means outgrowing plain MySQL, which I covered in scaling historical data analysis. The LangChain SQL Agent documentation has the more involved chain-of-thought patterns.

If this kind of retrieval work is eating your dev hours, hand it to me. I have been wrestling with WordPress and awkward database architectures since the 4.x days.

Pragmatic takeaway

Do not throw out your SQL schema because AI is the new toy. This works when the database stays the source of truth for structure and the vector store handles meaning. Mirror the categorical metadata, keep the dates in SQL, and let a ReAct agent bridge the two. That is the version that ships instead of getting rebuilt.

author avatar
Ahmad Wael
I'm a WordPress and WooCommerce developer with 15+ years of experience building custom e-commerce solutions and plugins. I specialize in PHP development, following WordPress coding standards to deliver clean, maintainable code. Currently, I'm exploring AI and e-commerce by building multi-agent systems and SaaS products that integrate technologies like Google Gemini API with WordPress platforms, approaching every project with a commitment to performance, security, and exceptional user experience.