Laravel AI SDK: Tool-Calling Agents Guide | Mohamed Said        [  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MH.png)   Mohamed Said Laravel Backend Engineer  ](https://www.msaied.com) [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles  ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

       [  ](https://github.com/EG-Mohamed)       

 [ Home ](https://www.msaied.com) [ Projects ](https://www.msaied.com/projects) [ Articles ](https://www.msaied.com/articles) [ Certificates ](https://www.msaied.com/certificates) [ Contact ](https://www.msaied.com#contact-section) 

  [ home ](https://www.msaied.com)    [ articles ](https://www.msaied.com/articles)    Laravel AI SDK: Tool-Calling Agents and Conversation Persistence        On this page       1. [  Building Tool-Calling Agents in Laravel with Conversation Persistence ](#building-tool-calling-agents-in-laravel-with-conversation-persistence)
2. [  Defining Typed Tools ](#defining-typed-tools)
3. [  Persisting Conversation History ](#persisting-conversation-history)
4. [  Running the Agent Loop ](#running-the-agent-loop)
5. [  Structured Output Contracts ](#structured-output-contracts)
6. [  Key Takeaways ](#key-takeaways)

  ![Laravel AI SDK: Tool-Calling Agents and Conversation Persistence](https://cdn.msaied.com/321/6c6dac115e95b0b01f6e7c04c430614c.png)

  #laravel   #ai   #agents   #llm  

 Laravel AI SDK: Tool-Calling Agents and Conversation Persistence 
==================================================================

     29 Jun 2026      3 min read    ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said  

       Table of contents

1. [  01   Building Tool-Calling Agents in Laravel with Conversation Persistence  ](#building-tool-calling-agents-in-laravel-with-conversation-persistence)
2. [  02   Defining Typed Tools  ](#defining-typed-tools)
3. [  03   Persisting Conversation History  ](#persisting-conversation-history)
4. [  04   Running the Agent Loop  ](#running-the-agent-loop)
5. [  05   Structured Output Contracts  ](#structured-output-contracts)
6. [  06   Key Takeaways  ](#key-takeaways)

 Building Tool-Calling Agents in Laravel with Conversation Persistence
---------------------------------------------------------------------

The Laravel AI SDK (the `prism` package from EchoLabs, or the first-party `laravel/ai` direction emerging in the ecosystem) gives you a clean PHP-first interface for building agents that can invoke tools, maintain conversation history, and return structured output. This article focuses on the practical wiring: typed tool definitions, safe state persistence, and avoiding the common trap of leaking conversation context across unrelated sessions.

### Defining Typed Tools

A tool is a callable the LLM can request. Keep each tool as a dedicated class implementing a `Tool` contract so it stays testable in isolation.

```php
use EchoLabs\Prism\Tool;
use EchoLabs\Prism\Schema\StringSchema;
use EchoLabs\Prism\Schema\ObjectSchema;

class LookupOrderTool extends Tool
{
    public function __construct(private OrderRepository $orders) {}

    public function name(): string { return 'lookup_order'; }

    public function description(): string
    {
        return 'Fetch the current status of a customer order by order ID.';
    }

    public function parameters(): ObjectSchema
    {
        return new ObjectSchema(
            name: 'lookup_order_params',
            description: 'Parameters for order lookup',
            properties: [new StringSchema('order_id', 'The UUID of the order')],
            requiredFields: ['order_id'],
        );
    }

    public function handle(string $order_id): string
    {
        $order = $this->orders->findOrFail($order_id);
        return json_encode(['status' => $order->status, 'eta' => $order->eta]);
    }
}

```

Bind it in a service provider so the container resolves its dependencies automatically:

```php
$this->app->bind(LookupOrderTool::class, fn ($app) =>
    new LookupOrderTool($app->make(OrderRepository::class))
);

```

### Persisting Conversation History

The most common mistake is storing conversation turns in the session or, worse, in a static property that survives across Octane workers. Use a proper `conversations` table instead.

```php
// Migration
Schema::create('ai_conversation_turns', function (Blueprint $table) {
    $table->id();
    $table->ulid('conversation_id')->index();
    $table->string('role'); // user | assistant | tool
    $table->text('content');
    $table->json('tool_calls')->nullable();
    $table->timestamps();
});

```

A thin repository hydrates the message array Prism expects:

```php
class ConversationRepository
{
    public function history(string $conversationId): array
    {
        return AiConversationTurn::where('conversation_id', $conversationId)
            ->orderBy('id')
            ->get()
            ->map(fn ($turn) => [
                'role' => $turn->role,
                'content' => $turn->content,
            ])
            ->all();
    }

    public function append(string $conversationId, string $role, string $content): void
    {
        AiConversationTurn::create(compact('conversationId', 'role', 'content'));
    }
}

```

### Running the Agent Loop

```php
use EchoLabs\Prism\Prism;
use EchoLabs\Prism\Enums\Provider;

class AgentService
{
    public function __construct(
        private ConversationRepository $conversations,
        private LookupOrderTool $lookupOrder,
    ) {}

    public function respond(string $conversationId, string $userMessage): string
    {
        $this->conversations->append($conversationId, 'user', $userMessage);

        $response = Prism::text()
            ->using(Provider::OpenAI, 'gpt-4o-mini')
            ->withMessages($this->conversations->history($conversationId))
            ->withTools([$this->lookupOrder])
            ->withMaxSteps(5)
            ->generate();

        $reply = $response->text;
        $this->conversations->append($conversationId, 'assistant', $reply);

        return $reply;
    }
}

```

`withMaxSteps` caps the tool-call loop so a misbehaving model cannot spin indefinitely and exhaust your token budget.

### Structured Output Contracts

When you need machine-readable responses, use a schema-bound response instead of parsing free text:

```php
use EchoLabs\Prism\Schema\ObjectSchema;
use EchoLabs\Prism\Schema\StringSchema;
use EchoLabs\Prism\Schema\EnumSchema;

$schema = new ObjectSchema(
    name: 'support_decision',
    description: 'Triage decision for a support ticket',
    properties: [
        new EnumSchema('priority', 'Ticket priority', ['low', 'medium', 'high']),
        new StringSchema('summary', 'One-sentence summary'),
    ],
    requiredFields: ['priority', 'summary'],
);

$response = Prism::structured()
    ->using(Provider::OpenAI, 'gpt-4o-mini')
    ->withPrompt($ticketBody)
    ->withSchema($schema)
    ->generate();

$decision = $response->structured; // typed array matching your schema

```

### Key Takeaways

- **Persist turns to the database**, never to session or static state — especially critical under Octane.
- **Cap `withMaxSteps`** to prevent runaway tool-call loops from burning tokens.
- **One class per tool** keeps each tool independently testable and container-resolvable.
- **Structured output schemas** eliminate brittle regex parsing of LLM responses.
- **Bind tools via the service container** so they receive their own dependencies cleanly.

 Found this useful?

          [  ](https://twitter.com/intent/tweet?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-ai-sdk-tool-calling-agents-and-conversation-persistence-1&text=Laravel+AI+SDK%3A+Tool-Calling+Agents+and+Conversation+Persistence) [  ](https://www.linkedin.com/sharing/share-offsite/?url=https%3A%2F%2Fwww.msaied.com%2Farticles%2Flaravel-ai-sdk-tool-calling-agents-and-conversation-persistence-1) 

 Frequently Asked Questions 
----------------------------

  2 questions  

     Q01  How do I prevent conversation history from growing too large and exceeding the context window?        Implement a sliding-window strategy in your repository: return only the last N turns, or summarise older turns into a single system message. You can also store a `summary` column on the conversation and regenerate it periodically with a background job. 

      Q02  Can tool-calling agents be tested without hitting the real OpenAI API?        Yes. Prism ships with a fake driver you can swap in tests via `Prism::fake()`. You provide canned responses and assert which tools were called, keeping your test suite fast and deterministic. 

  Continue reading

 More Articles 
---------------

 [ View all    ](https://www.msaied.com/articles) 

 [ ![Filament v5 Preview: Schema Unification, Performance Shifts, and How to Prepare](https://cdn.msaied.com/340/1a05ca68637b898b676efb66f22e627f.png) filament laravel php 

### Filament v5 Preview: Schema Unification, Performance Shifts, and How to Prepare

Filament v5 is reshaping how panels, forms, and tables are composed. This deep-dive covers the confirmed archi...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/filament-v5-preview-schema-unification-performance-shifts-and-how-to-prepare) [ ![Laravel 13: New Features, Helpers, and Practical Upgrade Notes](https://cdn.msaied.com/339/58c4fa6fe9b6d25a2dac17c621b6f4c6.png) laravel laravel-13 upgrade 

### Laravel 13: New Features, Helpers, and Practical Upgrade Notes

Laravel 13 ships with async-first defaults, a leaner bootstrapping layer, and several quality-of-life helpers....

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-13-new-features-helpers-and-practical-upgrade-notes) [ ![Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping](https://cdn.msaied.com/337/05b39d16d0f88a5fb94d0cf74049b88b.png) laravel laravel-12 upgrade 

### Laravel 12: Structured Route Files, Slim Skeletons, and the New Application Bootstrapping

Laravel 12 ships with a leaner skeleton, first-class route file organisation, and a revised application bootst...

  ![Mohamed Said](https://cdn.msaied.com/01KT78WE565VEMM3PSNQAAB0MJ.jpg)  Mohamed Said 

 1 Jul 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-12-structured-route-files-slim-skeletons-and-the-new-application-bootstrapping) 

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

Explore

- [Home](https://www.msaied.com)
- [Projects](https://www.msaied.com/projects)
- [Articles](https://www.msaied.com/articles)
- [Certificates](https://www.msaied.com/certificates)
- [Contact](https://www.msaied.com#contact-section)

Connect

- [   hello@msaied.com ](mailto:hello@msaied.com)
- [   +20 109 461 9204 ](tel:+201094619204)

© 2026 Mohamed Said. All rights reserved.

 [  ](https://github.com/EG-Mohamed) [  ](https://www.linkedin.com/in/msaiedm/) [  ](https://wa.me/201094619204) [  ](mailto:hello@msaied.com) [  ](https://drive.google.com/file/u/0/d/1MF20IPRJyzfy32mhEutjL5EpSls0w2Q8/view)
