Skip to content

Why I Chose Atlas Over Laravel AI SDK

When Laravel AI SDK launched, I gave it a fair evaluation. I've been building Atlas to solve the same problem, so a first-party solution deserved a real look.

Laravel AI SDK is a solid package. But after comparing them side by side, Atlas fits better for how I build. Here's what I found.

Where Atlas Leads

The differences come down to agent architecture, pipelines, and tooling.

Agent Registry and Auto-Discovery

Atlas resolves agents by key, class, or instance with zero configuration. Laravel AI SDK requires manual wiring for each agent.

Pipelines and Runtime Middleware

Atlas has global, per-request, conditional, and prioritized pipeline hooks. You can attach middleware inline on any call. Laravel AI SDK has simple middleware classes, but nothing this flexible.

php
Atlas::agent('support')
    ->middleware([
        'agent.before_execute' => [RateLimitCheck::class, AuditLog::class],
        'agent.after_execute' => NotifySlack::class,
    ])
    ->chat($message);

System Prompt Variables and Decorators

Atlas supports {variable} interpolation in system prompts, plus decorators that modify agent behavior at runtime without touching the class. Neither exists in Laravel AI SDK.

php
// Agent definition with variable placeholders
public function systemPrompt(): ?string
{
    return <<<PROMPT
    You are a support agent for {company_name}.
    The customer's name is {customer_name} on the {plan_name} plan.
    Today's date is {current_date}.
    PROMPT;
}

// Variables injected at runtime, not hardcoded in the class
Atlas::agent('support')
    ->withVariables([
        'company_name' => $tenant->name,
        'customer_name' => $user->name,
        'plan_name' => $user->subscription->plan,
        'current_date' => now()->toDateString(),
    ])
    ->chat($message);

The agent class stays generic. The caller provides the context. With Laravel AI SDK, you'd concatenate these values directly in instructions() or pass them through constructor injection, neither of which separates the template from the data cleanly.

Tool Registry and Context

Tools in Atlas are auto-discovered and receive a ToolContext on every execution with metadata and an agent reference. Your tools always know who called them and why.

MCP Tools on Agents

Atlas supports MCP tools natively on agent definitions through Prism Relay. Laravel AI SDK handles MCP separately.

Where It Felt Like Hacking

This pushed me away more than anything. Laravel AI SDK configures agents using PHP attributes:

php
#[Provider(Lab::Anthropic)]
#[Model('claude-sonnet-4-20250514')]
#[MaxSteps(10)]
#[MaxTokens(4096)]
#[Temperature(0.7)]
#[Timeout(120)]
class SupportAgent implements Agent
{
    use Promptable;

    public function instructions(): string
    {
        return 'You are a support agent...';
    }
}

The problem is that attributes are compile-time constants. If you need to change maxSteps or temperature per-request (say, a premium user gets more reasoning steps, or a complex task needs a higher token limit), you can't. The prompt() method only accepts runtime overrides for provider, model, and timeout. Everything else is locked to whatever you set in the attribute.

The workaround is creating separate agent classes per configuration variant. A QuickSupportAgent with #[MaxSteps(3)] and a DeepSupportAgent with #[MaxSteps(15)]. You end up with a class for every combination instead of just configuring the one you have.

Atlas handles this differently. Agent settings are methods, not attributes, and decorators can override any of them at runtime:

php
// A decorator that adjusts behavior based on context
class PremiumSupportDecorator extends AgentDecorator
{
    public function appliesTo(AgentContract $agent): bool
    {
        return $agent->key() === 'support';
    }

    public function maxSteps(): ?int
    {
        return 15; // Premium users get more reasoning steps
    }

    public function maxTokens(): ?int
    {
        return 8000;
    }
}

Or skip the decorator class entirely and adjust inline:

php
Atlas::agent('support')
    ->withVariables([
        'company_name' => $tenant->name,
        'customer_name' => $user->name,
    ])
    ->usingTemperature(0.3)
    ->withMaxSteps($user->isPremium() ? 15 : 5)
    ->chat($message);

No subclassing, no attribute duplication. The agent definition stays clean and runtime adjustments happen where they're used.

The Persistence Problem

Laravel AI SDK has built-in conversation persistence, which Atlas doesn't. But the implementation stores tools and attachments as JSON blobs inside meta columns instead of proper relationships.

That throws away what Eloquent is best at.

JSON blobs (Laravel AI SDK):

php
// Finding conversations that used a specific tool
$conversations = AgentConversation::all()->filter(function ($conv) {
    $meta = json_decode($conv->meta, true);
    $tools = data_get($meta, 'tools', []);
    return collect($tools)->contains('name', 'lookup_order');
});

Proper relationships (how it should work):

php
$conversations = Conversation::whereHas('tools', function ($q) {
    $q->where('name', 'lookup_order');
})->get();

$conversation = Conversation::with(['tools', 'attachments', 'messages'])->find($id);

Queryable, indexable, eager-loadable. JSON meta columns give you none of that.

Async and Queue Handling

Both packages support ->queue() with then/catch callbacks. The difference is how Atlas handles the job lifecycle.

Atlas serializes the full agent context into a clean, transportable payload. The InvokeAgent job reconstructs it on the worker side through AgentContext::fromArray(), then runs through the same executor pipeline as a synchronous call.

php
Atlas::agent('research')
    ->withVariables(['project' => $project->name])
    ->withMetadata(['user_id' => $user->id])
    ->queue('Summarize the latest findings')
    ->onQueue('ai')
    ->then(fn (AgentResponse $response) => $user->notify(
        new ResearchComplete($response->text())
    ))
    ->catch(fn (Throwable $e) => Log::error('Agent failed', [
        'error' => $e->getMessage(),
    ]));

The QueuedAgentResponse wrapper gives you fluent control over queue name, connection, and delay. Callbacks are serialized with SerializableClosure, so your success and error handling lives right next to the dispatch.

Atlas also supports queued streaming with broadcast(), which dispatches a BroadcastAgent job that streams chunks over WebSockets through Laravel's broadcasting system.

php
$requestId = Str::uuid()->toString();

Atlas::agent('writer')
    ->broadcast('Draft the report', $requestId);

// Frontend listens on: atlas.agent.writer.{$requestId}

With Laravel AI SDK, the async dispatch felt less transparent. Context packaging wasn't as clear, which made debugging failed queue jobs harder than it needed to be.

Where Laravel AI SDK Is Ahead

There are real gaps in Atlas worth acknowledging.

Provider failover. Array of providers with automatic cascade on rate limits. Atlas doesn't have this.

Conversation persistence. Despite the storage design issues, built-in persistence is more than Atlas offers today.

Why I Chose Atlas

This isn't a recommendation to use one over the other. Both packages are actively maintained and both solve real problems. You should evaluate them against your own requirements.

For me, the decision came down to hitting at least five areas where the SDK's limitations got in the way of how I build. Static agent configuration that couldn't flex at runtime. Persistence built on JSON blobs instead of relationships. Async handling that was harder to debug than it needed to be. No tool context. No pipeline system for cross-cutting concerns.

Any one of those on its own is fine. But I kept running into them, and at some point I was spending more time working around the package than building with it. The gaps in Atlas (failover, persistence, reranking) are real, but they live in areas where separate packages already exist. The things I rely on daily, agent architecture, runtime flexibility, tool context, middleware, those are where Atlas fits how I work.

The Full Comparison

FeatureAtlasAI SDK
Scaffold commands
Anonymous agents
Streaming + broadcast
Structured output
Queueing
Embeddings + pgvector
Agent registry + auto-discovery
Pipeline system (global, per-request, conditional)
Runtime middleware
System prompt variables
Agent decorators
Tool registry + auto-discovery
Tool context (metadata + agent ref)
MCP tools on agents
Context serialization for queues
Moderation
Models discovery
Provider failover
Conversation persistence

Atlas: 16/18 features supported. Laravel AI SDK: 10/18. The numbers don't tell the whole story, but they do show where each package focuses. Laravel AI SDK leans into retrieval, persistence, and provider breadth. Atlas leans into agent architecture and runtime flexibility. Decide based on which of those matters more for what you're building.


Atlas is open source and available on GitHub. Full documentation at atlasphp.org.