Laravel AI Agents: Streaming, Token Budgets &amp; Typed Output | Mohamed Said       [Skip to content](#main)  [ ![](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png) Mohamed SaidLaravel Backend Engineer ](https://www.msaied.com/public) - [Home](https://www.msaied.com/public)
- [Projects](https://www.msaied.com/public/projects)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [About](https://www.msaied.com/public#about)

           [  Contact](https://www.msaied.com/public#contact) Menu 

Menu
----

Close 

 - [HomeStart here](https://www.msaied.com/public)
- [ProjectsCase studies](https://www.msaied.com/public/projects)
- [ArticlesEngineering notes](https://www.msaied.com/public/articles)
- [CertificatesCredentials](https://www.msaied.com/public/certificates)
- [AboutHow I work](https://www.msaied.com/public#about)
- [ContactGet in touch](https://www.msaied.com/public#contact)

  [Start a conversation](https://www.msaied.com/public#contact) [WhatsApp](https://wa.me/201094619204) [Email](mailto:hello@msaied.com) 

 1. [Home](https://www.msaied.com/public)
2. /
3. [Articles](https://www.msaied.com/public/articles)
4. /
5. Production AI Agents in Laravel: Streaming, Token Budgets, and Structured Output Contracts

 Production AI Agents in Laravel: Streaming, Token Budgets, and Structured Output Contracts
===========================================================================================

 Build reliable AI agents in Laravel with SSE streaming, hard token budgets enforced at the application layer, and typed structured output contracts that survive model upgrades without silent failures.

 ![](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp) [Mohamed Said](https://www.msaied.com/public#person) Published 25 Jul 2026 · Updated 25 Jul 2026 · 3 min read

ShareCopy linkCopied

 ![Production AI Agents in Laravel: Streaming, Token Budgets, and Structured Output Contracts](https://cdn.msaied.com/466/15db89e4226fa2eecdd82ea72f2d2c01.png) 

  On this page +1. [The Gap Between Demo and Production AI Agents](#the-gap-between-demo-and-production-ai-agents)
2. [Streaming with Server-Sent Events](#streaming-with-server-sent-events)
3. [Enforcing Token Budgets at the Application Layer](#enforcing-token-budgets-at-the-application-layer)
4. [Structured Output Contracts with Readonly DTOs](#structured-output-contracts-with-readonly-dtos)
5. [Prompt-Side Contract Enforcement](#prompt-side-contract-enforcement)
6. [Key Takeaways](#key-takeaways)

 The Gap Between Demo and Production AI Agents
---------------------------------------------

Most Laravel AI tutorials stop at `$client->chat()`. Production agents need three things demos skip: streaming responses that don't time out, hard token budgets that protect your bill, and structured output contracts that fail loudly when a model returns garbage.

This article tackles all three with concrete, opinionated patterns.

---

Streaming with Server-Sent Events
---------------------------------

Laravel's `StreamedResponse` is the right primitive. Pair it with a generator-based OpenAI client call and you get true SSE without a WebSocket server.

```php
// routes/api.php
Route::post('/agent/stream', AgentStreamController::class);

// app/Http/Controllers/AgentStreamController.php
final class AgentStreamController
{
    public function __invoke(AgentRequest $request, AgentService $agent): StreamedResponse
    {
        return response()->stream(
            function () use ($request, $agent): void {
                foreach ($agent->stream($request->validated('prompt')) as $chunk) {
                    echo "data: " . json_encode(['text' => $chunk]) . "\n\n";
                    ob_flush();
                    flush();
                }
                echo "data: [DONE]\n\n";
            },
            headers: ['Content-Type' => 'text/event-stream', 'X-Accel-Buffering' => 'no']
        );
    }
}

```

The `X-Accel-Buffering: no` header is critical when Nginx sits in front — without it, Nginx buffers the entire response.

---

Enforcing Token Budgets at the Application Layer
------------------------------------------------

Don't rely solely on `max_tokens` in the API call. A multi-turn agent accumulates context silently. Track token usage yourself and abort before you hit a cost cliff.

```php
final class TokenBudget
{
    private int $used = 0;

    public function __construct(private readonly int $limit) {}

    public function consume(int $tokens): void
    {
        $this->used += $tokens;
        if ($this->used > $this->limit) {
            throw new TokenBudgetExceededException(
                "Budget of {$this->limit} tokens exceeded (used: {$this->used})"
            );
        }
    }

    public function remaining(): int
    {
        return max(0, $this->limit - $this->used);
    }
}

```

Inject a `TokenBudget` into your agent loop and call `consume()` after each API response using the usage object the API returns. This gives you per-request, per-user, or per-tenant budget enforcement — whichever granularity your SaaS needs.

```php
$budget = new TokenBudget(limit: 8_000);

foreach ($turns as $turn) {
    $response = $this->client->chat($turn->toMessages(), maxTokens: $budget->remaining());
    $budget->consume($response->usage->totalTokens);
    // ...
}

```

---

Structured Output Contracts with Readonly DTOs
----------------------------------------------

JSON mode is not a contract. Models hallucinate keys, change nesting, or return `null` where you expect a string. Validate every structured response against a typed DTO immediately after deserialization.

```php
readonly class SentimentResult
{
    public function __construct(
        public readonly string $sentiment,  // 'positive'|'negative'|'neutral'
        public readonly float  $confidence, // 0.0–1.0
        public readonly string $summary,
    ) {}

    public static function fromArray(array $data): self
    {
        $validated = validator($data, [
            'sentiment'  => ['required', 'string', Rule::in(['positive','negative','neutral'])],
            'confidence' => ['required', 'numeric', 'min:0', 'max:1'],
            'summary'    => ['required', 'string', 'max:500'],
        ])->validate();

        return new self(...$validated);
    }
}

```

Call `SentimentResult::fromArray(json_decode($response->content, true))` and let Laravel's validator throw a `ValidationException` on malformed output. This surfaces model regressions immediately rather than letting bad data propagate into your database.

### Prompt-Side Contract Enforcement

Include the schema in your system prompt as a JSON Schema snippet. Models with JSON mode enabled respect it more reliably than natural-language instructions alone. Store the schema alongside the DTO so they stay in sync:

```php
public static function jsonSchema(): array
{
    return [
        'type' => 'object',
        'required' => ['sentiment', 'confidence', 'summary'],
        'properties' => [
            'sentiment'  => ['type' => 'string', 'enum' => ['positive','negative','neutral']],
            'confidence' => ['type' => 'number', 'minimum' => 0, 'maximum' => 1],
            'summary'    => ['type' => 'string', 'maxLength' => 500],
        ],
    ];
}

```

---

Key Takeaways
-------------

- Use `StreamedResponse` with `X-Accel-Buffering: no` for true SSE through Nginx.
- Track token usage from API responses; enforce budgets in application code, not just `max_tokens`.
- Validate every structured model response against a typed readonly DTO using Laravel's validator.
- Co-locate the JSON Schema with the DTO so prompt and validation contracts never drift.
- Throw typed exceptions (`TokenBudgetExceededException`, `ValidationException`) so callers can handle failures explicitly.

- [laravel](https://www.msaied.com/public/articles?search=laravel)
- [ai](https://www.msaied.com/public/articles?search=ai)
- [agents](https://www.msaied.com/public/articles?search=agents)
- [streaming](https://www.msaied.com/public/articles?search=streaming)
- [structured-output](https://www.msaied.com/public/articles?search=structured-output)

 Frequently asked questions 
---------------------------

  Why not just use `max\_tokens` to control costs instead of a custom TokenBudget?`max\_tokens` caps a single API call but doesn't track cumulative usage across a multi-turn conversation. A TokenBudget class aggregates usage from every turn's response object, giving you per-request or per-user cost control that `max\_tokens` alone cannot provide.

   Does JSON mode from OpenAI guarantee the response matches my DTO?No. JSON mode guarantees valid JSON syntax, not that the keys, types, or values match your schema. Always validate the decoded array against your DTO's rules immediately after deserialization and throw on failure.

   How do I prevent Nginx from buffering my SSE stream?Set the `X-Accel-Buffering: no` response header. Nginx respects this header and disables proxy buffering for that response, allowing chunks to reach the client as they are flushed.

   ![Mohamed Said](https://cdn.msaied.com/01M22N44A70A5MC2S599JP0MPH.webp)About the author
----------------

[Mohamed Said](https://www.msaied.com/public#person)Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

[About](https://www.msaied.com/public#about) [GitHub ↗](https://github.com/EG-Mohamed) [LinkedIn ↗](https://www.linkedin.com/in/msaiedm/) [WhatsApp ↗](https://wa.me/201094619204) [Email Address ↗](mailto:hello@msaied.com) [My CV ↗](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)  

   [Previous articleLaravel AI SDK: Tool-Calling Agents with Conversation Persistence](https://www.msaied.com/public/articles/laravel-ai-sdk-tool-calling-agents-with-conversation-persistence) [Next articleClean Architecture Testing with Pest: Actions, Fakes, and Architectural Rules](https://www.msaied.com/public/articles/clean-architecture-testing-with-pest-actions-fakes-and-architectural-rules)  

   On this page
-------------

1. [The Gap Between Demo and Production AI Agents](#the-gap-between-demo-and-production-ai-agents)
2. [Streaming with Server-Sent Events](#streaming-with-server-sent-events)
3. [Enforcing Token Budgets at the Application Layer](#enforcing-token-budgets-at-the-application-layer)
4. [Structured Output Contracts with Readonly DTOs](#structured-output-contracts-with-readonly-dtos)
5. [Prompt-Side Contract Enforcement](#prompt-side-contract-enforcement)
6. [Key Takeaways](#key-takeaways)

 ###  Have a technical challenge?

 Tell me what you’re building. I reply within two working days.

[Start a conversation](https://www.msaied.com/public#contact) 

   Related articles
-----------------

 [ ![](https://cdn.msaied.com/740/cce86edc21eddcbdd2f2454fadaf9c70.png)  · 3 min read### The Pipeline Pattern in Laravel: Custom Pipelines Beyond Middleware

5 Oct 2026 ](https://www.msaied.com/public/articles/the-pipeline-pattern-in-laravel-custom-pipelines-beyond-middleware-1) [ ![](https://cdn.msaied.com/739/2d6897fdcdcf090613f96f72a64b8a78.png)  · 4 min read### MySQL Full-Text Search in Laravel: Indexes, Relevance Scoring, and Boolean Mode

4 Oct 2026 ](https://www.msaied.com/public/articles/mysql-full-text-search-in-laravel-indexes-relevance-scoring-and-boolean-mode) [ ![](https://cdn.msaied.com/738/073696a3fefe18bec825beec5ac658f5.png)  · 4 min read### Laravel Queue Rate-Limited Middleware: Throttling Jobs Without Losing Work

4 Oct 2026 ](https://www.msaied.com/public/articles/laravel-queue-rate-limited-middleware-throttling-jobs-without-losing-work) 

  Have a technical challenge?
----------------------------

Tell me what you’re building. I reply within two working days.

 [Discuss your project ↗](https://www.msaied.com/public#contact) 

  © 2026 Mohamed Said · Built with Laravel, meant to last.Senior Backend Engineer specializing in Laravel, scalable SaaS platforms, APIs, and cloud infrastructure. I build secure, high-performance web applications that help businesses grow.

 - [Home](https://www.msaied.com/public)
- [Articles](https://www.msaied.com/public/articles)
- [Certificates](https://www.msaied.com/public/certificates)
- [GitHub](https://github.com/EG-Mohamed)
- [LinkedIn](https://www.linkedin.com/in/msaiedm/)
- [WhatsApp](https://wa.me/201094619204)
- [Email Address](mailto:hello@msaied.com)
- [My CV](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
- [Sitemap](https://www.msaied.com/public/sitemap.xml)
