Weave Code
Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Sql Agent Laravel Package

knobik/sql-agent

Self-learning text-to-SQL agent for Laravel that turns natural language into accurate SQL using knowledge bases, memory, and live schema context. Includes SQL safety guardrails and a built-in chat UI.

View on GitHub
Deep Wiki
Context7

Getting Started

Calibrate expectations first. At v0.9.x, with 19 stars and a single maintainer, this is best treated as an accelerator for internal tooling — an admin analytics console, a support-team "ask the database" box, a reporting assistant — not as a customer-facing feature or a replacement for hand-written queries. Keep your integration thin so you can swap it out. Pin the exact version in composer.json ("knobik/sql-agent": "0.9.1"), because the README explicitly warns the API is still stabilizing.

Install and configure in four steps:

composer require knobik/sql-agent
php artisan sql-agent:install
SQL_AGENT_LLM_PROVIDER=openai
SQL_AGENT_LLM_MODEL=gpt-4o
OPENAI_API_KEY=sk-...

Then point the agent at a read-only replica or a dedicated read-only DB user before you run anything — that is the single most important setup decision, more than model choice. The package has SQL guardrails, but defence-in-depth via grants costs you nothing.

Where to look first, in order:

  1. config/sql-agent.php — database/connection, allowed and restricted tables, row limits, statement restrictions, search driver, and the agent.tools array for custom tools.
  2. The published knowledge/ directory — tables/*.json, business/*.json, queries/*.json / *.sql. This is where accuracy actually comes from; read it before you touch prompts or models.
  3. The docs site (knobik.github.io/sql-agent) for configuration, tools and evaluation references, plus the changelog for each minor bump.

First use case worth shipping: a read-only reporting assistant over a handful of well-understood tables (orders, customers, line items). Start in tinker rather than building UI:

use Knobik\SqlAgent\Facades\SqlAgent;

$r = SqlAgent::run('Who are our top 5 customers by spending?');
dump($r->sql, $r->results, $r->answer);

Read the generated SQL and compare it to what you would have written. That diff tells you exactly what to add to the knowledge base — a metric definition in business/, a column note in tables/, or a known-good join in queries/. Repeat until three or four real questions come back correct, then wire up the Livewire chat UI or your own endpoint.

Implementation Patterns

Put the facade behind your own boundary. Calling SqlAgent::run() directly in a controller is fine for a spike, but wrap it in a small action/service class with your own DTO. You get a place to add caching, auditing, rate limiting, and a seam to swap the package later.

final class AskDatabase
{
    public function __invoke(string $question): array
    {
        $r = SqlAgent::run($question);

        QueryAudit::create([
            'user_id'  => auth()->id(),
            'question' => $question,
            'sql'      => $r->sql,
            'answer'   => $r->answer,
        ]);

        return ['answer' => $r->answer, 'sql' => $r->sql, 'rows' => $r->results];
    }
}

Treat the knowledge base as versioned code. knowledge/tables/*.json, business/*.json and queries/*.sql belong in the repo and in pull requests. In practice, a reviewed business/metrics.json that says "revenue = SUM(order_items.total_cents) net of refunds" moves accuracy more than upgrading from gpt-4o-mini to gpt-4o. Onboard data-adjacent engineers to author these files; it is the highest-leverage maintenance work in the whole system.

Pre-populate tables to cut tool calls. Layers 5 and 6 (introspect_schema, custom tools) run inside the agent's tool loop, which means extra LLM round-trips and extra latency. Every table you describe properly in tables/*.json is a round-trip the agent usually does not have to make.

Expose the SQL, not just the answer. ->sql is a trust feature for internal users — analysts will correct the agent when they can see the query. Gate it behind a permission if table names are sensitive.

Use custom tools for institutional context. Anything in agent.tools is callable by the LLM during the loop. Wire a tool that searches your internal docs/wiki for term definitions, or one that maps a business term to a canonical column. This is the cleanest extension point in the package and avoids stuffing long documentation into the system prompt.

Run the self-learning loop deliberately, not passively. The save_learning tool persists error→fix pairs. That is genuinely useful, but it is a cache, not truth: schedule a periodic review of accumulated learnings, prune anything tied to a column or table that no longer exists, and reset them after a major migration. Otherwise you accumulate stale advice that quietly biases every future query.

Build an evaluation set from real questions. The package ships an evaluation framework — use it. Take 20–50 questions your team actually asks, capture expected result shapes, and run the suite whenever you change model, prompt, knowledge files, or package version. This is the only reliable way to tell whether a model swap is an improvement or a regression, because output is non-deterministic.

Hook the events for observability. Log question, generated SQL, latency, token usage, and failure reason per call. You will want this the first time someone asks why a number looks wrong. Alert on failure rate and on p95 latency, not just cost.

For UI: the Livewire chat is the fast path for an internal tool, and debug mode (showing all executed queries) is what you want enabled in staging. For anything customer-facing or heavily branded, build your own endpoint around the facade and skip the shipped UI entirely.

Multi-database: multi-connection support exists (added in 0.6.0), but scope each agent to one connection. Cross-database joins are not a thing here.

In tests, never hit a real LLM. Mock the facade as the README shows:

SqlAgent::shouldReceive('run')
    ->with('How many users?')
    ->andReturn(new AgentResponse(
        answer: 'There are 100 users.',
        sql: 'SELECT COUNT(*) FROM users',
        results: [['count' => 100]],
    ));

Also block outbound HTTP in your test suite so an accidental live call fails loudly rather than costing money in CI.

Gotchas and Tips

Minor versions have moved things. 0.8.0 replaced QueryPatternSearch with SearchManager across services — if you extended or type-hinted the old class, that bump breaks you. Read every changelog before bumping; the releases are terse but they do flag renames.

Restricted tables had a leak. 0.9.1 fixed restricted tables leaking through references on permitted ones. If you are on anything earlier and rely on table restrictions for confidentiality, upgrade — and independently verify by reading generated SQL in debug mode. Never rely on config-level restrictions alone when a read-only DB user would also do the job.

Same question, different SQL. Output is non-deterministic; do not write exact-SQL assertions. Assert on result sets, row counts, or key values, and accept that evaluations will occasionally flake for legitimate reasons.

Silent row-limit truncation. Row limits protect you, but a detail query will return a partial set and the agent's prose answer may describe it as complete. For "list all X" style prompts, either raise the limit for the session or rephrase the workflow toward aggregates.

Schema drift is the quiet killer. New column, renamed table, changed enum → the agent keeps confidently generating SQL against the old shape. Add a post-deploy step that refreshes introspection, and grep knowledge/ for anything referring to dropped columns after each migration.

Ambiguity gets resolved by invention. Without a business/*.json definition, the model will pick a plausible interpretation of "revenue", "active customer", or "last month" — and it may differ from yours. Also watch implicit unit conversions: the README's own example does SUM(o.total_amount) / 100, implying cents. State units explicitly in table metadata or you will get 100×-wrong answers that look reasonable.

Semantic search needs pgvector. The vector driver requires PostgreSQL with pgvector; everything else falls back to database full-text search. If retrieval quality seems poor, check which driver is actually active before blaming the model — bad retrieval means the agent never sees the query pattern that would have solved the problem. Re-embedding after knowledge edits costs money and time; batch your knowledge changes.

Debugging order of operations: (1) did retrieval surface the right knowledge — inspect the prompt context; (2) was the SQL syntactically valid but semantically wrong — knowledge problem; (3) was it invalid SQL — schema/tool problem; (4) only then consider the model. Reaching for a bigger model first is the expensive wrong move.

Prompt injection is a real surface. Any user who can type a question can try to steer the agent toward data in tables you would rather they not see, within your configured permissions. Require auth, rate-limit per user, log every generated query, and constrain the DB user. Treat this like exposing a query console, because that is what it is.

Latency will surprise you. The tool loop means multiple sequential LLM calls per question. Anything user-facing beyond ~10–15s belongs in a queued job with a polling or broadcast UX. Check the streaming option in the docs if you need progressive output — but keep your PHP and web-server timeouts in mind.

No built-in answer cache. Identical dashboard questions will re-bill the LLM every time. A simple Cache::remember keyed on a normalized question plus your knowledge-base version hash saves real money on internal tools with repeating questions.

Cost control levers, in order of impact: smaller model for simple lookup questions, cached results, fewer introspect_schema round-trips via good table metadata, and shorter conversation histories.

Keep the integration thin. One maintainer, 19 stars, zero dependents, beta API. Facade behind your own interface, package pinned, changelog watched. If it stalls, you should be able to rip it out in an afternoon without touching your domain code.

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
skyraptor/chatter
edlugz/sasapay
abbasudo/artisan-route
mirvan/ippanel
ignibyte/aic-framework
glavweb/data-schema-bundle
glavweb/datagrid-bundle
tacticmedia/scim-server
uni-method/frame
neika-ai/sdk
adachsoft/php-code-style
pharaonic/laravel-notification-dependencies
beautinow/translug
dev-brainiac/bot-cloaked
octoberrain/html
octoberrain/extension
octoberrain/exception
octoberrain/contracts
adachsoft/api-version-bundle
cgv/localized-resource