Laravel AI Agents: Tool-Calling &amp; Conversation Persistence | 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 Prism ](#building-tool-calling-agents-in-laravel-with-prism)
2. [  Defining a Tool ](#defining-a-tool)
3. [  Running a Multi-Turn Agent Loop ](#running-a-multi-turn-agent-loop)
4. [  Persisting Conversation History ](#persisting-conversation-history)
5. [  Authorising Tool Execution ](#authorising-tool-execution)
6. [  Key Takeaways ](#key-takeaways)

  ![Laravel AI SDK: Tool-Calling Agents and Conversation Persistence](https://cdn.msaied.com/260/8c84f424e42da01993c9ba4b8eb19655.png)

  #laravel   #ai   #agents   #prism   #llm  

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

     21 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 Prism  ](#building-tool-calling-agents-in-laravel-with-prism)
2. [  02   Defining a Tool  ](#defining-a-tool)
3. [  03   Running a Multi-Turn Agent Loop  ](#running-a-multi-turn-agent-loop)
4. [  04   Persisting Conversation History  ](#persisting-conversation-history)
5. [  05   Authorising Tool Execution  ](#authorising-tool-execution)
6. [  06   Key Takeaways  ](#key-takeaways)

 Building Tool-Calling Agents in Laravel with Prism
--------------------------------------------------

Large language models become genuinely useful when they can *act* — querying a database, calling an API, or reading a file — rather than just generating text. The [Prism](https://prismphp.com) package gives Laravel developers a clean, driver-agnostic interface for exactly this. This article focuses on two hard problems: **wiring tools safely** and **persisting conversation state between HTTP requests** without leaking memory or context.

---

### Defining a Tool

Prism tools are plain PHP objects that declare their schema and a handler closure. Keep them thin — delegate real work to your existing service layer.

```php
use EchoLabs\Prism\Tool;

$orderLookup = Tool::as('get_order')
    ->for('Fetch an order by its ID')
    ->withStringParameter('order_id', 'The UUID of the order')
    ->using(function (string $order_id): string {
        $order = Order::findOrFail($order_id);
        return json_encode([
            'status'  => $order->status,
            'total'   => $order->total_cents,
            'shipped' => $order->shipped_at?->toIso8601String(),
        ]);
    });

```

The `using` closure **must return a string** — that string is injected back into the model's context as the tool result. Returning structured JSON is idiomatic.

---

### Running a Multi-Turn Agent Loop

A single `generate()` call is not enough for agents. You need an agentic loop that keeps running until the model stops requesting tools.

```php
use EchoLabs\Prism\Prism;
use EchoLabs\Prism\Enums\Provider;
use EchoLabs\Prism\ValueObjects\Messages\UserMessage;

$messages = [
    new UserMessage('What is the status of order 550e8400-e29b-41d4-a716-446655440000?'),
];

$response = Prism::text()
    ->using(Provider::OpenAI, 'gpt-4o')
    ->withMessages($messages)
    ->withTools([$orderLookup])
    ->withMaxSteps(5)   // hard cap — never let the loop run unbounded
    ->generate();

echo $response->text;

```

`withMaxSteps` is your circuit breaker. Without it, a confused model can spin indefinitely, burning tokens and time.

---

### Persisting Conversation History

HTTP is stateless; agents are not. The naive approach — storing the full message array in the session — breaks under load and leaks data across users. A better pattern: persist messages to a `conversations` table and rehydrate on each request.

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

```

```php
// Rehydrating messages for Prism
use EchoLabs\Prism\ValueObjects\Messages\AssistantMessage;
use EchoLabs\Prism\ValueObjects\Messages\UserMessage;

$stored = ConversationMessage::where('conversation_id', $id)
    ->orderBy('id')
    ->get();

$messages = $stored->map(fn ($row) => match ($row->role) {
    'user'      => new UserMessage($row->content),
    'assistant' => new AssistantMessage($row->content),
    default     => null,
})->filter()->values()->all();

```

After each agent run, persist the new messages returned in `$response->messages` back to the table. This keeps your PHP process stateless while the conversation lives safely in Postgres.

---

### Authorising Tool Execution

Tools run server-side with your application's full privileges. Always scope them to the authenticated user:

```php
->using(function (string $order_id) use ($user): string {
    $order = Order::where('user_id', $user->id)
        ->findOrFail($order_id); // 404 if not owned
    // ...
})

```

Never trust the model to enforce ownership — it will not.

---

### Key Takeaways

- **Tools return strings.** Encode complex data as JSON; the model reads it as context.
- **Always set `withMaxSteps`.** Unbounded agentic loops are a production incident waiting to happen.
- **Persist messages in the database**, not the session. Rehydrate per request for true statelessness.
- **Authorise inside the tool closure**, not outside it. The model controls which tool is called; you control what it can see.
- **Keep tools thin.** Delegate to services so tools remain testable in isolation without mocking the LLM.

 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&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) 

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

  3 questions  

     Q01  Can I use providers other than OpenAI with Prism tool-calling?        Yes. Prism supports Anthropic, Gemini, Ollama, and others via its driver system. Tool-calling availability depends on the underlying model's capabilities — check the provider's documentation for function-calling support before switching drivers. 

      Q02  How do I test a tool-calling agent without hitting the real API?        Prism ships with a fake driver you can bind in tests. Use `Prism::fake()` to return pre-scripted responses, then assert that your tool closures were invoked with the expected arguments using standard Pest expectations on the side-effects they produce. 

      Q03  What is a safe value for withMaxSteps in production?        It depends on your use case, but 5–10 steps covers the vast majority of real workflows. Set it conservatively, monitor average steps per conversation in your logs, and raise it only when you have evidence a legitimate task requires more. 

  Continue reading

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

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

 [ ![Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel](https://cdn.msaied.com/295/d977bd189583149245c03d6d763d9db5.png) laravel database performance 

### Read/Write Splitting, Connection Pooling, and Sticky Reads in Laravel

Learn how Laravel's database layer handles read/write splitting, when sticky reads matter, and how to layer Pg...

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

 26 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/readwrite-splitting-connection-pooling-and-sticky-reads-in-laravel-2) [ ![Laravel Service Container: Contextual Binding, Tagging, and Method Injection](https://cdn.msaied.com/294/e5b9d047bd33c3f8b80069ef6a178884.png) laravel service-container dependency-injection 

### Laravel Service Container: Contextual Binding, Tagging, and Method Injection

Go beyond basic binding. Learn how contextual binding resolves different implementations per consumer, how tag...

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

 26 Jun 2026     3 min read  

  Read    

 ](https://www.msaied.com/articles/laravel-service-container-contextual-binding-tagging-and-method-injection-1) [ ![PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain](https://cdn.msaied.com/293/f392a5ea52536901eac9677ffa2d070d.png) laravel postgresql eloquent 

### PostgreSQL JSONB in Laravel: Indexing, Querying, and Casting Without the Pain

JSONB columns unlock flexible schemas, but most Laravel apps leave performance on the table. Learn how to inde...

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

 25 Jun 2026     4 min read  

  Read    

 ](https://www.msaied.com/articles/postgresql-jsonb-in-laravel-indexing-querying-and-casting-without-the-pain) 

   [  ![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)
