Laravel Daily's

Cut Your AI Bill in Half: Prompt Caching and Smarter Model Selection with the Laravel AI SDK

Laravel AI SDK cost optimization with prompt caching, cheaper models, embeddings, and provider failover

AI costs usually grow through repetition.

Your agent sends the same instructions on every request. Your tools keep the same schemas. Your application generates embeddings for content it has already processed. A capable model handles simple classification tasks that a smaller model could complete.

The Laravel AI SDK gives you practical controls for each problem. You can cache stable prompt sections, choose models by cost or capability, cache embeddings, and fail over to another provider when needed.

These changes do not require a new architecture. They fit into the agent classes and configuration you already use in a modern PHP web framework.

Start with the cost model

Every AI request has a few cost drivers:

  • Input tokens sent to the provider.
  • Output tokens generated by the model.
  • Embedding requests for search and retrieval.
  • The model selected for each task.
  • Duplicate requests caused by retries or repeated content.
  • Provider outages that trigger expensive emergency handling.

The largest savings often come from reducing repeated input. Long instructions and tool definitions can consume thousands of tokens before a user writes a single sentence.

Prompt caching addresses that repeated prefix. Model selection addresses unnecessary capability. Embedding caching addresses repeated vector generation.

Laravel AI SDK integrated into a Laravel application dashboard

Cache stable instructions and tools

The Laravel AI SDK supports prompt caching through two agent attributes:

  • CacheInstructions caches the agent’s instructions.
  • CacheToolDefinitions caches the agent’s tool schemas.

Use them when the relevant content remains stable across requests.

<?php

namespace App\Ai\Agents;

use Laravel\Ai\Attributes\CacheInstructions;
use Laravel\Ai\Attributes\CacheToolDefinitions;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;

#[CacheInstructions]
#[CacheToolDefinitions]
class SupportAgent implements Agent
{
    use Promptable;

    public function instructions(): string
    {
        return 'You are a support agent. Give concise, accurate answers.';
    }

    public function tools(): iterable
    {
        return [
            new LookupOrder,
            new SearchHelpCenter,
        ];
    }
}

The provider writes the cache on the first request. Later requests read the cached prefix instead of processing it as fresh input. Providers that do not support these attributes ignore them, so the same agent can remain compatible with provider failover.

Do not cache instructions that change on every request. For example, instructions containing the current date or request-specific account data create a new cache entry each time.

That pattern pays the write cost without creating useful cache hits. In that case, cache only the stable tool definitions:

use Laravel\Ai\Attributes\CacheToolDefinitions;

#[CacheToolDefinitions]
class ReportAgent implements Agent
{
    use Promptable;

    public function instructions(): string
    {
        return "Analyze this report for {$this->team->name}.";
    }
}

Cached prefixes are retained for five minutes by default. Anthropic supports a longer retention period when you provide a duration.

#[CacheInstructions('1h')]
#[CacheToolDefinitions('1h')]
class LongRunningAgent implements Agent
{
    use Promptable;
}

The durations must match. Caching instructions for one hour while caching tool definitions for five minutes creates an invalid configuration.

Use cache_control for growing conversations

Attribute-based caching works well when you know which parts of the request remain stable. Anthropic also supports automatic caching through provider options.

Implement HasProviderOptions and return cache_control for Anthropic requests:

<?php

namespace App\Ai\Agents;

use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasProviderOptions;
use Laravel\Ai\Enums\Lab;
use Laravel\Ai\Promptable;

class ConversationAgent implements Agent, HasProviderOptions
{
    use Promptable;

    public function providerOptions(Lab|string $provider): array
    {
        return match ($provider) {
            Lab::Anthropic => [
                'cache_control' => [
                    'type' => 'ephemeral',
                ],
            ],

            default => [],
        };
    }
}

This places a breakpoint after the last request block. As the conversation grows, the breakpoint moves forward. Previous turns become cache reads, while only the new content is written.

This is useful for long conversations. You do not need to manually decide where the stable context ends.

You can also combine automatic caching with the cache attributes. Use the approach that matches your prompt structure. The provider options documentation explains how to return different options for each provider.

Measure cache reads and writes

Do not treat caching as a configuration-only change. Measure it.

The response usage object exposes prompt cache metrics:

$response = (new SupportAgent)->prompt($message);

$cacheReads = $response->usage->cacheReadInputTokens;
$cacheWrites = $response->usage->cacheWriteInputTokens;

logger()->info('AI prompt usage', [
    'cache_reads' => $cacheReads,
    'cache_writes' => $cacheWrites,
]);

cacheWriteInputTokens represents tokens written to the provider cache. cacheReadInputTokens represents tokens served from cached input.

A healthy workload should show cache reads after the first request. If writes dominate, inspect your prompt construction. You may be changing the supposed stable prefix on every request.

Track these values beside provider, model, route, and tenant. This makes cost changes visible in Nightwatch or your existing logging system.

Select the right model for each agent

Caching reduces input cost. Model selection reduces the price of every request.

The Laravel AI SDK provides three useful model strategies:

  • UseCheapestModel selects the provider’s cheapest text model.
  • UseSmartestModel selects the provider’s most capable text model.
  • Model pins an explicit model name.

Use the cheapest model for predictable, low-complexity work:

use Laravel\Ai\Attributes\UseCheapestModel;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Promptable;

#[UseCheapestModel]
class TicketClassifier implements Agent
{
    use Promptable;
}

This is a good fit for classification, short summaries, routing, and simple extraction. Use the smartest model for tasks that need deeper reasoning or complex tool use:

use Laravel\Ai\Attributes\UseSmartestModel;

#[UseSmartestModel]
class ContractReviewer implements Agent
{
    use Promptable;
}

These attributes are convenient, but they are not fixed pricing contracts. The underlying model can change as providers release new models. That can change response behavior, supported parameters, latency, and cost.

Pin a model when behavior and budget must remain stable:

use Laravel\Ai\Attributes\Model;

#[Model('claude-sonnet-5')]
class StableSupportAgent implements Agent
{
    use Promptable;
}

A practical production policy is simple:

  1. Start with UseCheapestModel for routine work.
  2. Test quality with representative inputs.
  3. Escalate difficult cases to a stronger agent.
  4. Use Model when you need predictable behavior.

The SDK’s agent configuration supports these attributes alongside providers, token limits, temperature, and timeouts.

Bright Laravel AI SDK illustration showing embedding caching and provider failover

Cache embeddings at the framework level

Prompt caching belongs to the AI provider. Embedding caching belongs to your Laravel application.

Enable it in config/ai.php:

'caching' => [
    'embeddings' => [
        'cache' => true,
        'store' => env('CACHE_STORE', 'database'),
        'individually' => true,
    ],
],

The default duration is 30 days. The cache key includes the provider, model, dimensions, and input content. This prevents an embedding from one model being reused for a different model or vector size.

You can enable caching for one operation instead:

use Laravel\Ai\Embeddings;
use Laravel\Ai\Enums\Lab;

$response = Embeddings::for([
    'Laravel is a PHP web application framework.',
])
    ->cache(seconds: 3600)
    ->generate(Lab::OpenAI, 'text-embedding-3-small');

This stores the result for one hour. It is useful for content that changes often or for a migration where you want to limit the cache lifetime.

For stable documentation, a longer duration makes more sense:

$response = Embeddings::for($documents)
    ->cache(seconds: 2592000)
    ->generate();

With individually enabled, each input receives its own cache entry. A later batch can reuse embeddings even when the input order changes. Set it to false when the entire batch should be cached as one unit.

You can also use Laravel’s Stringable API:

$embedding = str($content)->toEmbeddings(cache: true);

$shortLived = str($content)->toEmbeddings(cache: 3600);

Read more in the SDK’s embedding caching documentation.

Keep costs predictable with provider failover

Failover protects availability, but it can also affect cost.

The SDK lets you provide an ordered list of providers:

use Laravel\Ai\Enums\Lab;

$response = (new SupportAgent)->prompt(
    'Summarize this ticket.',
    provider: [Lab::OpenAI, Lab::Anthropic],
);

You can specify a model for each provider with an associative array:

$response = (new SupportAgent)->prompt(
    'Summarize this ticket.',
    provider: [
        Lab::OpenAI->value => 'gpt-4.1-mini',
        Lab::Anthropic->value => 'claude-haiku-4-5',
    ],
);

Failover occurs for provider failures such as rate limits, overloads, unavailable services, and insufficient credits. It does not occur for every exception. Validation errors and malformed requests usually need application-level handling.

The fallback model should match the primary model’s role. A cheap summarizer should not silently fail over to your most expensive reasoning model. Configure fallback providers and models as a deliberate cost policy.

Prompt cache behavior may also change during failover. A provider that does not support the cache attributes will ignore them. Embedding cache keys include the provider and model, so a fallback provider creates a separate entry rather than mixing incompatible vectors.

A practical optimization checklist

Use this sequence when optimizing an existing Laravel AI feature:

  • Keep system instructions stable.
  • Cache stable instructions and tool definitions.
  • Use cache_control for long Anthropic conversations.
  • Log cacheReadInputTokens and cacheWriteInputTokens.
  • Use UseCheapestModel for routine tasks.
  • Use UseSmartestModel only where quality requires it.
  • Pin models with Model when behavior must remain stable.
  • Enable embedding caching for repeated content.
  • Set embedding durations based on content freshness.
  • Match failover models to the same cost and capability tier.
  • Review usage by provider, model, feature, and tenant.

You do not need to reduce quality to control AI spending. Remove repeated work first. Then assign each task the smallest model that can handle it.

That is the advantage of using a unified set of PHP developer tools for the full AI workflow. Whether you are building a support agent, a semantic search feature, or an application that needs to build REST API with PHP, the Laravel AI SDK gives you clear places to manage cost, reliability, and model behavior.

Laravel ecosystem stack showing Laravel AI, Cloud, Nightwatch, and the framework

Start with the Laravel AI SDK, measure your cache usage, and tune model selection with real application data. Small changes in prompt structure and model routing can make a large difference at scale.

Previous
Custom Error Pages in Inertia 3.x: Beautiful 404s and 500s in Your Laravel + Vue App
Next
Build a Real-World Team Dashboard with Laravel, Vue, and Inertia: A Production Walkthrough