{"id":11446,"date":"2026-05-02T04:54:03","date_gmt":"2026-05-02T04:54:03","guid":{"rendered":"https:\/\/programmingfields.com\/?p=11446"},"modified":"2026-05-02T06:34:55","modified_gmt":"2026-05-02T06:34:55","slug":"laravel-13-ai-agents","status":"publish","type":"post","link":"https:\/\/programmingfields.com\/laravel-13-ai-agents\/","title":{"rendered":"Laravel 13 AI Agents: Complete Guide with Real Code Examples (2026)"},"content":{"rendered":"\n<p>In our previous guide, we introduced the <a href=\"https:\/\/programmingfields.com\/laravel-13-ai-sdk-tutorial\/\">Laravel 13 AI SDK<\/a> and covered the basics of working with AI in Laravel. Now, let\u2019s take things a step further. Today, we are diving deep into Laravel 13 AI agents.<\/p>\n\n\n\n<p>So, what exactly is an agent?<\/p>\n\n\n\n<p>Well, an agent is not just a simple text generator. Instead, it is a dedicated PHP class that encapsulates instructions, tools, memory, and output logic \u2014 all in one place. In other words, it acts as a fully structured AI-powered component inside your application.<\/p>\n\n\n\n<p>To put it simply, think of it as a specialized AI assistant \u2014 whether that\u2019s a sales coach, a support bot, or even a document analyzer. Once you configure it, you can reuse it anywhere in your application, which makes your code much cleaner and easier to maintain.<\/p>\n\n\n\n<p>Even better, Laravel makes this process incredibly smooth. With the built-in <code>make:agent<\/code> Artisan command, you can generate agents instantly. That means no custom boilerplate and no manual wiring \u2014 just clean, structured PHP classes ready to use.<\/p>\n\n\n\n<p>In this guide, we will walk through everything you need to know about Laravel 13 AI agents. Specifically<strong>, <\/strong>we will cover how to create your first agent, how streaming works, how to use queues, how to integrate tools, manage memory, and finally, how to test everything properly.<\/p>\n\n\n\n<p>So, without further delay, let\u2019s get started.<\/p>\n\n\n\n<div id=\"ez-toc-container\" class=\"ez-toc-v2_0_82_2 counter-hierarchy ez-toc-counter ez-toc-light-blue ez-toc-container-direction\">\n<div class=\"ez-toc-title-container\">\n<p class=\"ez-toc-title\" style=\"cursor:inherit\">Table of Contents<\/p>\n<span class=\"ez-toc-title-toggle\"><a href=\"#\" class=\"ez-toc-pull-right ez-toc-btn ez-toc-btn-xs ez-toc-btn-default ez-toc-toggle\" aria-label=\"Toggle Table of Content\"><span class=\"ez-toc-js-icon-con\"><span class=\"\"><span class=\"eztoc-hide\" style=\"display:none;\">Toggle<\/span><span class=\"ez-toc-icon-toggle-span\"><svg style=\"fill: #999;color:#999\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" class=\"list-377408\" width=\"20px\" height=\"20px\" viewBox=\"0 0 24 24\" fill=\"none\"><path d=\"M6 6H4v2h2V6zm14 0H8v2h12V6zM4 11h2v2H4v-2zm16 0H8v2h12v-2zM4 16h2v2H4v-2zm16 0H8v2h12v-2z\" fill=\"currentColor\"><\/path><\/svg><svg style=\"fill: #999;color:#999\" class=\"arrow-unsorted-368013\" xmlns=\"http:\/\/www.w3.org\/2000\/svg\" width=\"10px\" height=\"10px\" viewBox=\"0 0 24 24\" version=\"1.2\" baseProfile=\"tiny\"><path d=\"M18.2 9.3l-6.2-6.3-6.2 6.3c-.2.2-.3.4-.3.7s.1.5.3.7c.2.2.4.3.7.3h11c.3 0 .5-.1.7-.3.2-.2.3-.5.3-.7s-.1-.5-.3-.7zM5.8 14.7l6.2 6.3 6.2-6.3c.2-.2.3-.5.3-.7s-.1-.5-.3-.7c-.2-.2-.4-.3-.7-.3h-11c-.3 0-.5.1-.7.3-.2.2-.3.5-.3.7s.1.5.3.7z\"\/><\/svg><\/span><\/span><\/span><\/a><\/span><\/div>\n<nav><ul class='ez-toc-list ez-toc-list-level-1 ' ><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-1\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#What_Is_a_Laravel_AI_Agent\" >What Is a Laravel AI Agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-2\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Installation_and_Setup\" >Installation and Setup<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-3\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Creating_Your_First_Agent_with_make_agent\" >Creating Your First Agent with make:agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-4\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Prompting_an_Agent\" >Prompting an Agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-5\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Conversation_Memory\" >Conversation Memory<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-6\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Option_1_%E2%80%94_Manual_Conversation_Context\" >Option 1 \u2014 Manual Conversation Context<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-7\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Option_2_%E2%80%94_Automatic_Memory_with_RemembersConversations\" >Option 2 \u2014 Automatic Memory with RemembersConversations<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-8\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Structured_Output\" >Structured Output<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-9\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Nested_Objects\" >Nested Objects<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-10\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Arrays_of_Objects\" >Arrays of Objects<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-11\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Attaching_Files_and_Images\" >Attaching Files and Images<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-12\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Streaming_Agent_Responses\" >Streaming Agent Responses<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-13\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Queueing_Agents_in_the_Background\" >Queueing Agents in the Background<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-14\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Creating_Tools_with_make_tool\" >Creating Tools with make:tool<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-15\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Registering_Tools_on_Your_Agent\" >Registering Tools on Your Agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-16\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Multiple_Tools_%E2%80%94_Real_Support_Agent\" >Multiple Tools \u2014 Real Support Agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-17\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Built-in_Similarity_Search_Tool\" >Built-in Similarity Search Tool<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-18\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Built-in_Provider_Tools\" >Built-in Provider Tools<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-19\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Web_Search\" >Web Search<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-20\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Web_Fetch\" >Web Fetch<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-21\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#File_Search\" >File Search<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-22\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Agent_Middleware\" >Agent Middleware<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-23\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Anonymous_Agents\" >Anonymous Agents<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-24\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Configuring_Agents_with_PHP_Attributes\" >Configuring Agents with PHP Attributes<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-25\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Smart_Model_Selection\" >Smart Model Selection<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-26\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Failover_Between_Providers\" >Failover Between Providers<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-27\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Testing_Your_Agents\" >Testing Your Agents<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-28\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Real-World_Example_A_Sales_Coach_Agent\" >Real-World Example: A Sales Coach Agent<\/a><ul class='ez-toc-list-level-3' ><li class='ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-29\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Step_1_%E2%80%94_Create_the_Tool\" >Step 1 \u2014 Create the Tool<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-30\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Step_2_%E2%80%94_Create_the_Agent\" >Step 2 \u2014 Create the Agent<\/a><\/li><li class='ez-toc-page-1 ez-toc-heading-level-3'><a class=\"ez-toc-link ez-toc-heading-31\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Step_3_%E2%80%94_Use_It_in_a_Controller\" >Step 3 \u2014 Use It in a Controller<\/a><\/li><\/ul><\/li><li class='ez-toc-page-1 ez-toc-heading-level-2'><a class=\"ez-toc-link ez-toc-heading-32\" href=\"https:\/\/programmingfields.com\/laravel-13-ai-agents\/#Final_Thoughts\" >Final Thoughts<\/a><\/li><\/ul><\/nav><\/div>\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"What_Is_a_Laravel_AI_Agent\"><\/span><strong>What Is a Laravel AI Agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Before we jump into writing code, it\u2019s important to clearly understand what an agent actually is.<\/p>\n\n\n\n<p>At its core, agents are the fundamental building blocks for interacting with AI providers in the <a href=\"https:\/\/programmingfields.com\/laravel-13-ai-sdk-tutorial\/\">Laravel AI SDK<\/a>. More importantly, each agent is a dedicated PHP class that encapsulates everything needed to interact with a large language model. Which includes instructions, conversation context, tools, and output schema. Because of this structure, agents allow you to organize your AI logic in a much more maintainable and reusable way.<\/p>\n\n\n\n<p>Once again, think of an agent as a specialized assistant \u2014 whether it\u2019s a sales coach, a document analyzer, or a support bot. You define it once, and then you can prompt it whenever needed throughout your application.<\/p>\n\n\n\n<p>This is exactly what makes agents so powerful. Unlike a one-off API call, an agent is reusable, structured, and scalable. As a result, your application becomes easier to extend and maintain over time.<\/p>\n\n\n\n<p>Finally, it\u2019s worth mentioning that the <a href=\"https:\/\/programmingfields.com\/laravel-13-ai-sdk-tutorial\/\">Laravel AI SDK<\/a> supports multiple providers. For example, you can work with <strong>OpenAI, Anthropic, Gemini, Azure, Groq, xAI, DeepSeek, Mistral, <\/strong>and more \u2014 all through a single, clean interface.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Installation_and_Setup\"><\/span><strong>Installation and Setup<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>First, install the Laravel AI package using the command below:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">composer require laravel\/ai<\/code><\/pre>\n\n\n\n<p>Next, publish the configuration and migration files:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan vendor:publish --provider=\"Laravel\\Ai\\AiServiceProvider\"<\/code><\/pre>\n\n\n\n<p>Then run the migrations. This creates the <code>agent_conversations<\/code> and <code>agent_conversation_messages<\/code> tables needed for conversation storage:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan migrate<\/code><\/pre>\n\n\n\n<p>Now add your API credentials to <code>.env<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">OPENAI_API_KEY=your-openai-key\nANTHROPIC_API_KEY=your-anthropic-key\nGEMINI_API_KEY=your-gemini-key<\/code><\/pre>\n\n\n\n<p>The SDK supports many providers. You only need to add keys for the ones you plan to use.<\/p>\n\n\n\n<p>That&#8217;s it. You are ready to build <strong>Laravel 13 AI agents<\/strong>.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Creating_Your_First_Agent_with_make_agent\"><\/span><strong>Creating Your First Agent with make:agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Here is the command that makes everything easier. Laravel provides the <code>make:agent<\/code> Artisan command to generate your agent class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:agent SalesCoach<\/code><\/pre>\n\n\n\n<p>This generates a new file at <code>app\/Ai\/Agents\/SalesCoach.php<\/code>. Open it and you will see:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Promptable;\nuse Stringable;\n\nclass SalesCoach implements Agent\n{\n    use Promptable;\n\n    \/**\n     * Get the instructions that the agent should follow.\n     *\/\n    public function instructions(): Stringable|string\n    {\n        return 'You are a sales coach, analyzing transcripts and providing feedback.';\n    }\n}<\/code><\/pre>\n\n\n\n<p>This is already a working Laravel AI agent. To begin with<strong>,<\/strong> it implements the Agent contract, while at the same time using the Promptable trait, and additionally defining a system prompt via <code>instructions()<\/code>.<\/p>\n\n\n\n<p>If you look closely, notice how clean it is. There are no configuration files, no service providers, and even more importantly, no manual wiring \u2014 instead<strong>,<\/strong> it\u2019s just a simple PHP class.<\/p>\n\n\n\n<p>Moreover<strong>,<\/strong> you can also generate an agent with structured output support right away:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:agent SalesCoach --structured<\/code><\/pre>\n\n\n\n<p>This adds the <code>HasStructuredOutput<\/code> interface and a <code>schema()<\/code> method to the generated class. We will cover structured output in detail shortly.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Prompting_an_Agent\"><\/span><strong>Prompting an Agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>To interact with your <strong>Laravel AI agent<\/strong>, create an instance and call <code>prompt()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new SalesCoach)\n    -&gt;prompt('Analyze this sales transcript...');\n\nreturn (string) $response;<\/code><\/pre>\n\n\n\n<p>The <code>make()<\/code> static method resolves the agent from the service container. This enables automatic dependency injection:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$agent = SalesCoach::make(user: $user);\n\n$response = $agent-&gt;prompt('Analyze this transcript...');<\/code><\/pre>\n\n\n\n<p>You can also override the provider, model, or timeout per prompt call:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Enums\\Lab;\n\n$response = (new SalesCoach)-&gt;prompt(\n    'Analyze this sales transcript...',\n    provider: Lab::Anthropic,\n    model:    'claude-opus-4-5-20251001',\n    timeout:  120,\n);<\/code><\/pre>\n\n\n\n<p>This is very useful. You can use a cheap model by default, but switch to a more capable one for complex tasks \u2014 all at runtime without changing agent configuration.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Conversation_Memory\"><\/span><strong>Conversation Memory<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>By default, each <code>prompt()<\/code> call is stateless. The agent does not remember previous messages.<\/p>\n\n\n\n<p>For chatbots, support systems, or any multi-turn conversation, you need memory. Laravel gives you two approaches.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Option_1_%E2%80%94_Manual_Conversation_Context\"><\/span><strong>Option 1 \u2014 Manual Conversation Context<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Implement the <code>Conversational<\/code> interface and define a <code>messages()<\/code> method that loads your conversation history:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse App\\Models\\History;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\Conversational;\nuse Laravel\\Ai\\Messages\\Message;\nuse Laravel\\Ai\\Promptable;\n\nclass SalesCoach implements Agent, Conversational\n{\n    use Promptable;\n\n    public function __construct(public readonly int $userId) {}\n\n    public function instructions(): string\n    {\n        return 'You are a sales coach.';\n    }\n\n    public function messages(): iterable\n    {\n        return History::where('user_id', $this-&gt;userId)\n            -&gt;latest()\n            -&gt;limit(50)\n            -&gt;get()\n            -&gt;reverse()\n            -&gt;map(fn ($msg) =&gt; new Message($msg-&gt;role, $msg-&gt;content))\n            -&gt;all();\n    }\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Option_2_%E2%80%94_Automatic_Memory_with_RemembersConversations\"><\/span><strong>Option 2 \u2014 Automatic Memory with RemembersConversations<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>This is the simpler approach. Add the <code>RemembersConversations<\/code> trait. Laravel handles everything automatically:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse Laravel\\Ai\\Concerns\\RemembersConversations;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\Conversational;\nuse Laravel\\Ai\\Promptable;\n\nclass SalesCoach implements Agent, Conversational\n{\n    use Promptable, RemembersConversations;\n\n    public function instructions(): string\n    {\n        return 'You are a sales coach.';\n    }\n}<\/code><\/pre>\n\n\n\n<p>To start a new conversation for a user:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new SalesCoach)-&gt;forUser($user)-&gt;prompt('Hello!');\n\n\/\/ Store this ID for continuing the conversation later\n$conversationId = $response-&gt;conversationId;<\/code><\/pre>\n\n\n\n<p>To continue an existing conversation:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new SalesCoach)\n    -&gt;continue($conversationId, as: $user)\n    -&gt;prompt('Tell me more about that.');<\/code><\/pre>\n\n\n\n<p>Laravel automatically loads previous messages and saves new ones after every interaction. Your <strong>Laravel AI agent<\/strong> remembers everything without you managing a single database query.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Structured_Output\"><\/span><strong>Structured Output<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Sometimes plain text is not enough. You want structured data \u2014 a score, a list of items, a JSON object your application can process.<\/p>\n\n\n\n<p>Implement the <code>HasStructuredOutput<\/code> interface and define a <code>schema()<\/code> method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse Illuminate\\Contracts\\JsonSchema\\JsonSchema;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\HasStructuredOutput;\nuse Laravel\\Ai\\Promptable;\n\nclass SalesCoach implements Agent, HasStructuredOutput\n{\n    use Promptable;\n\n    public function instructions(): string\n    {\n        return 'You are a sales coach. Analyze transcripts and return structured feedback with a score.';\n    }\n\n    public function schema(JsonSchema $schema): array\n    {\n        return [\n            'feedback' =&gt; $schema-&gt;string()-&gt;required(),\n            'score'    =&gt; $schema-&gt;integer()-&gt;min(1)-&gt;max(10)-&gt;required(),\n        ];\n    }\n}<\/code><\/pre>\n\n\n\n<p>Access the response like an array:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new SalesCoach)-&gt;prompt('Analyze this sales transcript...');\n\ndump($response['feedback']); \/\/ \"Great closing technique, but work on objection handling.\"\ndd($response['score']);    \/\/ 7<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Nested_Objects\"><\/span>Nested Objects<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">public function schema(JsonSchema $schema): array\n{\n    return [\n        'score'    =&gt; $schema-&gt;integer()-&gt;required(),\n        'metadata' =&gt; $schema-&gt;object(fn ($schema) =&gt; [\n            'confidence' =&gt; $schema-&gt;string()-&gt;enum(['low', 'medium', 'high'])-&gt;required(),\n            'language'   =&gt; $schema-&gt;string()-&gt;required(),\n        ])-&gt;required(),\n    ];\n}<\/code><\/pre>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Arrays_of_Objects\"><\/span><strong>Arrays of Objects<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">public function schema(JsonSchema $schema): array\n{\n    return [\n        'feedback' =&gt; $schema-&gt;array()\n            -&gt;items(\n                $schema-&gt;object(fn ($schema) =&gt; [\n                    'comment' =&gt; $schema-&gt;string()-&gt;required(),\n                    'score'   =&gt; $schema-&gt;integer()-&gt;required(),\n                ])\n            )\n            -&gt;required(),\n    ];\n}<\/code><\/pre>\n\n\n\n<p>Structured output is perfect for dashboards, automated reports, and feeding AI results directly into your database.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Attaching_Files_and_Images\"><\/span><strong>Attaching Files and Images<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Your <strong>Laravel AI agent<\/strong> can also read and analyze documents and images. Pass them as attachments when prompting:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Ai\\Agents\\SalesCoach;\nuse Laravel\\Ai\\Files;\n\n$response = (new SalesCoach)-&gt;prompt(\n    'Analyze the attached sales transcript...',\n    attachments: [\n        Files\\Document::fromStorage('transcript.pdf'),    \/\/ From filesystem disk\n        Files\\Document::fromPath('\/home\/laravel\/doc.md'), \/\/ From local path\n        $request-&gt;file('transcript'),                      \/\/ From file upload\n    ]\n);<\/code><\/pre>\n\n\n\n<p>For images, use the <code>Image<\/code> class:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new ImageAnalyzer)-&gt;prompt(\n    'What is in this image?',\n    attachments: [\n        Files\\Image::fromStorage('photo.jpg'),\n        Files\\Image::fromPath('\/home\/laravel\/photo.jpg'),\n        $request-&gt;file('photo'),\n    ]\n);<\/code><\/pre>\n\n\n\n<p>This opens powerful possibilities \u2014 invoice extraction, document summarization, image analysis \u2014 all within your Laravel agent.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Streaming_Agent_Responses\"><\/span><strong>Streaming Agent Responses<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Streaming sends words back as they are generated \u2014 just like ChatGPT. It makes your application feel fast and responsive.<\/p>\n\n\n\n<p>Call <code>stream()<\/code> instead of <code>prompt()<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Ai\\Agents\\SalesCoach;\n\nRoute::get('\/coach', function () {\n    return (new SalesCoach)-&gt;stream('Analyze this sales transcript...');\n});<\/code><\/pre>\n\n\n\n<p>Returning the stream from a route automatically sends an SSE (Server-Sent Events) response to the client. No manual headers needed.<\/p>\n\n\n\n<p>Use the <code>then<\/code> callback to run code after the full response is delivered:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Responses\\StreamedAgentResponse;\n\nRoute::get('\/coach', function () {\n    return (new SalesCoach)\n        -&gt;stream('Analyze this sales transcript...')\n        -&gt;then(function (StreamedAgentResponse $response) {\n            \/\/ $response-&gt;text, $response-&gt;events, $response-&gt;usage\n        });\n});<\/code><\/pre>\n\n\n\n<p>Or iterate through events manually:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$stream = (new SalesCoach)-&gt;stream('Analyze this...');\n\nforeach ($stream as $event) {\n    echo $event;\n}<\/code><\/pre>\n\n\n\n<p>For frontend integration with the Vercel AI SDK, use the built-in protocol adapter:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">return (new SalesCoach)\n    -&gt;stream('Analyze this...')\n    -&gt;usingVercelDataProtocol();<\/code><\/pre>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Queueing_Agents_in_the_Background\"><\/span><strong>Queueing Agents in the Background<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Some agent tasks are heavy. Long analyses, bulk processing, complex multi-step reasoning. Never block the HTTP request for these.<\/p>\n\n\n\n<p>Use the <code>queue()<\/code> method:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Illuminate\\Http\\Request;\nuse Laravel\\Ai\\Responses\\AgentResponse;\n\nRoute::post('\/coach', function (Request $request) {\n    (new SalesCoach)\n        -&gt;queue($request-&gt;input('transcript'))\n        -&gt;then(function (AgentResponse $response) {\n            \/\/ Save result to database, send notification, etc.\n        })\n        -&gt;catch(function (Throwable $e) {\n            \/\/ Handle the failure gracefully\n        });\n\n    return back();\n});<\/code><\/pre>\n\n\n\n<p>The user gets an instant response. The agent processes in the background. The result is handled in the <code>then<\/code> callback when ready.<\/p>\n\n\n\n<p>This is the proper production pattern for <strong>Laravel 13 AI agents<\/strong> that do heavy work.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Creating_Tools_with_make_tool\"><\/span><strong>Creating Tools with make:tool<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Tools give your agent the ability to take real actions \u2014 query your database, call an API, generate data. The agent decides on its own which tool to use and when.<\/p>\n\n\n\n<p>Create a tool with the <code>make:tool<\/code> Artisan command:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:tool GetOrderStatus<\/code><\/pre>\n\n\n\n<p>This generates <code>app\/Ai\/Tools\/GetOrderStatus.php<\/code>. Here is a complete, working tool:<\/p>\n\n\n\n<pre title=\"GetOrderStatus.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Tools;\n\nuse App\\Models\\Order;\nuse Illuminate\\Contracts\\JsonSchema\\JsonSchema;\nuse Laravel\\Ai\\Contracts\\Tool;\nuse Laravel\\Ai\\Tools\\Request;\nuse Stringable;\n\nclass GetOrderStatus implements Tool\n{\n    \/**\n     * Describe what this tool does.\n     * The agent reads this to decide when to use it.\n     *\/\n    public function description(): Stringable|string\n    {\n        return 'Fetch the current status of a customer order using the order number.';\n    }\n\n    \/**\n     * The logic that runs when the agent calls this tool.\n     *\/\n    public function handle(Request $request): Stringable|string\n    {\n        $order = Order::where('order_number', $request['order_number'])-&gt;first();\n\n        if (! $order) {\n            return \"No order found with number {$request['order_number']}.\";\n        }\n\n        return \"Order {$request['order_number']}: Status = {$order-&gt;status}, \" .\n               \"Total = \\${$order-&gt;total}, Placed on {$order-&gt;created_at-&gt;format('M d, Y')}.\";\n    }\n\n    \/**\n     * Define the input parameters this tool accepts.\n     *\/\n    public function schema(JsonSchema $schema): array\n    {\n        return [\n            'order_number' =&gt; $schema-&gt;string()-&gt;required(),\n        ];\n    }\n}<\/code><\/pre>\n\n\n\n<p>Notice how clean this structure is. The <code>description()<\/code> tells the agent <strong>when<\/strong> to use this tool. The <code>schema()<\/code> defines <strong>what input<\/strong> it accepts. The <code>handle()<\/code> contains the actual logic.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Registering_Tools_on_Your_Agent\"><\/span><strong>Registering Tools on Your Agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Return tools from the <code>tools()<\/code> method on your agent. Also implement <code>HasTools<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Ai\\Tools\\GetOrderStatus;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\HasTools;\nuse Laravel\\Ai\\Promptable;\n\nclass SupportAgent implements Agent, HasTools\n{\n    use Promptable;\n\n    public function instructions(): string\n    {\n        return 'You are a friendly customer support agent. Help customers with their orders.';\n    }\n\n    public function tools(): iterable\n    {\n        return [\n            new GetOrderStatus,\n        ];\n    }\n}<\/code><\/pre>\n\n\n\n<p>Now, when a user asks, <em><strong>What is the status of my order ORD-12345?<\/strong><\/em> \u2014 the agent decides on its own to call <code>GetOrderStatus<\/code>, gets the result, and returns a natural language answer.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Multiple_Tools_%E2%80%94_Real_Support_Agent\"><\/span><strong>Multiple Tools \u2014 Real Support Agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:tool GetOrderStatus\nphp artisan make:tool InitiateRefund\nphp artisan make:tool EscalateTicket<\/code><\/pre>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">public function tools(): iterable\n{\n    return [\n        new GetOrderStatus,\n        new InitiateRefund,\n        new EscalateTicket,\n    ];\n}<\/code><\/pre>\n\n\n\n<p>Each tool does one thing well. The agent handles the decision logic. Clean, maintainable, and very powerful.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Built-in_Similarity_Search_Tool\"><\/span><strong>Built-in Similarity Search Tool<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Laravel also provides a <code>SimilaritySearch<\/code> tool for RAG (Retrieval Augmented Generation). It lets your agent search vector embeddings stored in your database:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Models\\Document;\nuse Laravel\\Ai\\Tools\\SimilaritySearch;\n\npublic function tools(): iterable\n{\n    return [\n        SimilaritySearch::usingModel(Document::class, 'embedding'),\n    ];\n}<\/code><\/pre>\n\n\n\n<p>With more control over similarity threshold and filters:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">SimilaritySearch::usingModel(\n    model: Document::class,\n    column: 'embedding',\n    minSimilarity: 0.7,\n    limit: 10,\n    query: fn ($query) =&gt; $query-&gt;where('published', true),\n),<\/code><\/pre>\n\n\n\n<p>This gives your <strong>Laravel AI agent<\/strong> the ability to search your own knowledge base and answer questions based on your real content.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Built-in_Provider_Tools\"><\/span><strong>Built-in Provider Tools<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Provider tools are special tools implemented natively by the AI provider. Your application does not execute them \u2014 the provider does.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Web_Search\"><\/span><strong>Web Search<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Give your agent real-time web access:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Providers\\Tools\\WebSearch;\n\npublic function tools(): iterable\n{\n    return [\n        (new WebSearch)-&gt;max(5)-&gt;allow(['laravel.com', 'php.net']),\n    ];\n}<\/code><\/pre>\n\n\n\n<p>You can also set a user location to get region-specific results:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">(new WebSearch)-&gt;location(city: 'Bangalore', region: 'KA', country: 'IN');<\/code><\/pre>\n\n\n\n<p>Supported by <strong>Anthropic<\/strong>, <strong>OpenAI<\/strong>, and <strong>Gemini<\/strong>.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Web_Fetch\"><\/span><strong>Web Fetch<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Let your agent read specific web pages:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Providers\\Tools\\WebFetch;\n\npublic function tools(): iterable\n{\n    return [\n        (new WebFetch)-&gt;max(3)-&gt;allow(['docs.laravel.com']),\n    ];\n}<\/code><\/pre>\n\n\n\n<p>Perfect for agents that answer questions from your documentation or check external data.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"File_Search\"><\/span><strong>File Search<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>Search documents stored in a vector store:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Providers\\Tools\\FileSearch;\n\npublic function tools(): iterable\n{\n    return [\n        new FileSearch(stores: ['your-store-id']),\n    ];\n}<\/code><\/pre>\n\n\n\n<p>The agent can now search through uploaded PDFs and documents to find relevant information. Additionally<strong>,<\/strong> this capability is supported by <strong>OpenAI<\/strong> and <strong>Gemini<\/strong>. This makes it both flexible and powerful.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Agent_Middleware\"><\/span><strong>Agent Middleware<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Agents support middleware \u2014 just like HTTP routes. This lets you intercept and modify prompts before they reach the provider.<\/p>\n\n\n\n<p>Generate middleware with:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:agent-middleware LogPrompts<\/code><\/pre>\n\n\n\n<p>This creates a file in <code>app\/Ai\/Middleware\/<\/code>. Here is a logging middleware example:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Middleware;\n\nuse Closure;\nuse Illuminate\\Support\\Facades\\Log;\nuse Laravel\\Ai\\Prompts\\AgentPrompt;\n\nclass LogPrompts\n{\n    public function handle(AgentPrompt $prompt, Closure $next)\n    {\n        Log::info('AI Agent prompted', [\n            'prompt'  =&gt; $prompt-&gt;prompt,\n            'user_id' =&gt; auth()-&gt;id(),\n        ]);\n\n        return $next($prompt)-&gt;then(function ($response) {\n            Log::info('AI Agent responded', ['text' =&gt; $response-&gt;text]);\n        });\n    }\n}<\/code><\/pre>\n\n\n\n<p>Register it on your agent by implementing <code>HasMiddleware<\/code>:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Ai\\Middleware\\LogPrompts;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\HasMiddleware;\nuse Laravel\\Ai\\Promptable;\n\nclass SalesCoach implements Agent, HasMiddleware\n{\n    use Promptable;\n\n    public function instructions(): string\n    {\n        return 'You are a sales coach.';\n    }\n\n    public function middleware(): array\n    {\n        return [\n            new LogPrompts,\n        ];\n    }\n}<\/code><\/pre>\n\n\n\n<p>You can use middleware for logging, rate limiting, content moderation, authentication checks, or prompt injection protection.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Anonymous_Agents\"><\/span><strong>Anonymous Agents<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Sometimes you need a quick, one-off AI interaction. You do not want to create a full agent class for it.<\/p>\n\n\n\n<p>Laravel provides the <code>agent()<\/code> helper function:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use function Laravel\\Ai\\{agent};\n\n$response = agent(\n    instructions: 'You are an expert at software development.',\n    messages: [],\n    tools: [],\n)-&gt;prompt('Tell me about Laravel');<\/code><\/pre>\n\n\n\n<p>Anonymous agents also support structured output:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Illuminate\\Contracts\\JsonSchema\\JsonSchema;\nuse function Laravel\\Ai\\{agent};\n\n$response = agent(\n    schema: fn (JsonSchema $schema) =&gt; [\n        'number' =&gt; $schema-&gt;integer()-&gt;required(),\n    ],\n)-&gt;prompt('Generate a random number less than 100');\n\necho $response['number'];<\/code><\/pre>\n\n\n\n<p>Use anonymous agents for prototyping, quick scripts, or Artisan command utilities.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Configuring_Agents_with_PHP_Attributes\"><\/span><strong>Configuring Agents with PHP Attributes<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>This is one of the cleanest features of <strong>Laravel 13 AI agents<\/strong>. Instead of configuration arrays or method calls, you use <a href=\"https:\/\/programmingfields.com\/laravel-13-php-attributes-explained\/\">PHP Attributes<\/a> directly on the class.<\/p>\n\n\n\n<p>Here is a fully configured agent using attributes:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse Laravel\\Ai\\Attributes\\MaxSteps;\nuse Laravel\\Ai\\Attributes\\MaxTokens;\nuse Laravel\\Ai\\Attributes\\Model;\nuse Laravel\\Ai\\Attributes\\Provider;\nuse Laravel\\Ai\\Attributes\\Temperature;\nuse Laravel\\Ai\\Attributes\\Timeout;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Enums\\Lab;\nuse Laravel\\Ai\\Promptable;\n\n#[Provider(Lab::Anthropic)]\n#[Model('claude-opus-4-5-20251001')]\n#[MaxSteps(10)]\n#[MaxTokens(4096)]\n#[Temperature(0.7)]\n#[Timeout(120)]\nclass SalesCoach implements Agent\n{\n    use Promptable;\n\n    public function instructions(): string\n    {\n        return 'You are a sales coach.';\n    }\n}<\/code><\/pre>\n\n\n\n<p>Each attribute is self-explanatory:<\/p>\n\n\n\n<ul class=\"wp-block-list\">\n<li><code>#[Provider]<\/code> \u2014 Which AI provider to use<\/li>\n\n\n\n<li><code>#[Model]<\/code> \u2014 The specific model name<\/li>\n\n\n\n<li><code>#[MaxSteps]<\/code> \u2014 How many tool-calling steps the agent can take<\/li>\n\n\n\n<li><code>#[MaxTokens]<\/code> \u2014 Maximum number of tokens to generate<\/li>\n\n\n\n<li><code>#[Temperature]<\/code> \u2014 Creativity\/randomness from 0.0 to 1.0<\/li>\n\n\n\n<li><code>#[Timeout]<\/code> \u2014 HTTP timeout in seconds<\/li>\n<\/ul>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Smart_Model_Selection\"><\/span><strong>Smart Model Selection<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>You do not even need to specify a model name. Let Laravel choose automatically:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use Laravel\\Ai\\Attributes\\UseCheapestModel;\nuse Laravel\\Ai\\Attributes\\UseSmartestModel;\n\n\/\/ Uses the cheapest available model \u2014 great for simple tasks\n#[UseCheapestModel]\nclass SimpleSummarizer implements Agent\n{\n    use Promptable;\n}\n\n\/\/ Uses the most capable model \u2014 great for complex reasoning\n#[UseSmartestModel]\nclass ComplexAnalyzer implements Agent\n{\n    use Promptable;\n}<\/code><\/pre>\n\n\n\n<p>This is excellent for cost optimization. Simple tasks automatically use cheap models. Complex tasks automatically use smart models. No hard-coded model names anywhere.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Failover_Between_Providers\"><\/span><strong>Failover Between Providers<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>What happens if OpenAI goes down? Or you hit a rate limit?<\/p>\n\n\n\n<p>Laravel 13 has built-in failover support. Simply pass an array of providers:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">$response = (new SalesCoach)-&gt;prompt(\n    'Analyze this sales transcript...',\n    provider: [Lab::OpenAI, Lab::Anthropic],\n);<\/code><\/pre>\n\n\n\n<p>If OpenAI fails, Laravel automatically retries with Anthropic. No extra code. No try-catch blocks. It just works.<\/p>\n\n\n\n<p>You can also configure failover at the class level using the <code>#[Provider]<\/code> attribute with an array:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">#[Provider([Lab::OpenAI, Lab::Anthropic, Lab::Gemini])]\nclass SalesCoach implements Agent\n{\n    use Promptable;\n}<\/code><\/pre>\n\n\n\n<p>This makes your <strong>Laravel 13 AI agents<\/strong> resilient in production environments where API availability cannot be guaranteed.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Testing_Your_Agents\"><\/span><strong>Testing Your Agents<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Laravel provides a first-class testing API for <strong>Laravel AI agents<\/strong>. You never need to make real API calls during tests.<\/p>\n\n\n\n<p>Fake an agent&#8217;s responses like this:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">use App\\Ai\\Agents\\SalesCoach;\nuse Laravel\\Ai\\Prompts\\AgentPrompt;\n\n\/\/ Return the same fixed response every time\nSalesCoach::fake();\n\n\/\/ Return different responses in sequence\nSalesCoach::fake([\n    'First response',\n    'Second response',\n]);\n\n\/\/ Return responses dynamically based on the prompt\nSalesCoach::fake(function (AgentPrompt $prompt) {\n    return 'Response for: ' . $prompt-&gt;prompt;\n});<\/code><\/pre>\n\n\n\n<p>After prompting, make assertions:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">SalesCoach::assertPrompted('Analyze this...');\n\nSalesCoach::assertPrompted(function (AgentPrompt $prompt) {\n    return $prompt-&gt;contains('Analyze');\n});\n\nSalesCoach::assertNotPrompted('Something else');\n\nSalesCoach::assertNeverPrompted();<\/code><\/pre>\n\n\n\n<p>For queued agents:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">SalesCoach::assertQueued('Analyze this...');\nSalesCoach::assertNeverQueued();<\/code><\/pre>\n\n\n\n<p>Prevent untested prompts from slipping through:<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">SalesCoach::fake()-&gt;preventStrayPrompts();<\/code><\/pre>\n\n\n\n<p>With this setup, your entire AI layer is fully testable. No API keys, no network calls, and no unexpected costs during CI\/CD.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Real-World_Example_A_Sales_Coach_Agent\"><\/span><strong>Real-World Example: A Sales Coach Agent<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Let&#8217;s put everything together. Here is a complete <strong>Laravel 13 AI agent<\/strong> that analyzes sales transcripts. Also, remembers conversations, uses a custom tool, and returns structured output.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_1_%E2%80%94_Create_the_Tool\"><\/span><strong>Step 1 \u2014 Create the Tool<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<p>For creating the tool, you can hit the below command in the terminal.<\/p>\n\n\n\n<pre class=\"wp-block-code\"><code lang=\"bash\" class=\"language-bash\">php artisan make:tool RetrievePreviousTranscripts<\/code><\/pre>\n\n\n\n<pre title=\"RetrievePreviousTranscripts.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Tools;\n\nuse App\\Models\\SalesTranscript;\nuse Illuminate\\Contracts\\JsonSchema\\JsonSchema;\nuse Laravel\\Ai\\Contracts\\Tool;\nuse Laravel\\Ai\\Tools\\Request;\n\nclass RetrievePreviousTranscripts implements Tool\n{\n    public function __construct(private readonly int $userId) {}\n\n    public function description(): string\n    {\n        return 'Retrieve previous sales transcripts for this user to compare performance over time.';\n    }\n\n    public function handle(Request $request): string\n    {\n        $transcripts = SalesTranscript::where('user_id', $this-&gt;userId)\n            -&gt;latest()\n            -&gt;limit(3)\n            -&gt;get();\n\n        if ($transcripts-&gt;isEmpty()) {\n            return 'No previous transcripts found for this user.';\n        }\n\n        return $transcripts-&gt;map(\n            fn ($t) =&gt; \"Date: {$t-&gt;created_at-&gt;format('M d, Y')} | Score: {$t-&gt;score} | Summary: {$t-&gt;summary}\"\n        )-&gt;join(\"\\n\");\n    }\n\n    public function schema(JsonSchema $schema): array\n    {\n        return []; \/\/ No input parameters needed\n    }\n}<\/code><\/pre>\n\n\n\n<p>Then, we will create an agent.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_2_%E2%80%94_Create_the_Agent\"><\/span>Step 2 \u2014 Create the Agent<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<pre class=\"wp-block-code\"><code class=\"\">php artisan make:agent SalesCoach --structured<\/code><\/pre>\n\n\n\n<pre title=\"SalesCoach.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Ai\\Agents;\n\nuse App\\Ai\\Tools\\RetrievePreviousTranscripts;\nuse App\\Models\\User;\nuse Illuminate\\Contracts\\JsonSchema\\JsonSchema;\nuse Laravel\\Ai\\Attributes\\MaxSteps;\nuse Laravel\\Ai\\Attributes\\Provider;\nuse Laravel\\Ai\\Attributes\\UseSmartestModel;\nuse Laravel\\Ai\\Concerns\\RemembersConversations;\nuse Laravel\\Ai\\Contracts\\Agent;\nuse Laravel\\Ai\\Contracts\\Conversational;\nuse Laravel\\Ai\\Contracts\\HasStructuredOutput;\nuse Laravel\\Ai\\Contracts\\HasTools;\nuse Laravel\\Ai\\Enums\\Lab;\nuse Laravel\\Ai\\Promptable;\n\n#[Provider(Lab::Anthropic)]\n#[UseSmartestModel]\n#[MaxSteps(5)]\nclass SalesCoach implements Agent, Conversational, HasTools, HasStructuredOutput\n{\n    use Promptable, RemembersConversations;\n\n    public function __construct(public readonly User $user) {}\n\n    public function instructions(): string\n    {\n        return \"You are an expert sales coach analyzing call transcripts for {$this-&gt;user-&gt;name}.\n                Provide specific, actionable feedback. Reference previous transcripts when available.\n                Always give a score between 1 and 10.\";\n    }\n\n    public function tools(): iterable\n    {\n        return [\n            new RetrievePreviousTranscripts($this-&gt;user-&gt;id),\n        ];\n    }\n\n    public function schema(JsonSchema $schema): array\n    {\n        return [\n            'feedback'     =&gt; $schema-&gt;string()-&gt;required(),\n            'score'        =&gt; $schema-&gt;integer()-&gt;min(1)-&gt;max(10)-&gt;required(),\n            'strengths'    =&gt; $schema-&gt;array()-&gt;items($schema-&gt;string())-&gt;required(),\n            'improvements' =&gt; $schema-&gt;array()-&gt;items($schema-&gt;string())-&gt;required(),\n        ];\n    }\n}<\/code><\/pre>\n\n\n\n<p>And then we will use it in the controller.<\/p>\n\n\n\n<h3 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Step_3_%E2%80%94_Use_It_in_a_Controller\"><\/span>Step 3 \u2014 Use It in a Controller<span class=\"ez-toc-section-end\"><\/span><\/h3>\n\n\n\n<pre title=\"SalesCoachController.php\" class=\"wp-block-code\"><code lang=\"php\" class=\"language-php\">&lt;?php\n\nnamespace App\\Http\\Controllers;\n\nuse App\\Ai\\Agents\\SalesCoach;\nuse App\\Models\\SalesTranscript;\nuse Illuminate\\Http\\Request;\n\nclass SalesCoachController extends Controller\n{\n    public function analyze(Request $request)\n    {\n        $request-&gt;validate([\n            'transcript'      =&gt; 'required|string',\n            'conversation_id' =&gt; 'nullable|string',\n        ]);\n\n        $agent = SalesCoach::make(user: $request-&gt;user());\n\n        \/\/ Continue existing conversation or start a fresh one\n        if ($request-&gt;conversation_id) {\n            $agent = $agent-&gt;continue($request-&gt;conversation_id, as: $request-&gt;user());\n        } else {\n            $agent = $agent-&gt;forUser($request-&gt;user());\n        }\n\n        $response = $agent-&gt;prompt(\n            \"Please analyze this sales call transcript:\\n\\n{$request-&gt;transcript}\"\n        );\n\n        \/\/ Save the structured result to the database\n        SalesTranscript::create([\n            'user_id' =&gt; $request-&gt;user()-&gt;id,\n            'score'   =&gt; $response['score'],\n            'summary' =&gt; $response['feedback'],\n        ]);\n\n        return response()-&gt;json([\n            'feedback'        =&gt; $response['feedback'],\n            'score'           =&gt; $response['score'],\n            'strengths'       =&gt; $response['strengths'],\n            'improvements'    =&gt; $response['improvements'],\n            'conversation_id' =&gt; $response-&gt;conversationId,\n        ]);\n    }\n}<\/code><\/pre>\n\n\n\n<p>This is a production-ready Laravel 13 AI agent. It not only combines memory. This provides a custom tool, structured output, and PHP attribute-based configuration. But also keeps everything clean and structured. On top of that, it leverages the smartest available model. So, this ensures reliable and consistent results.<\/p>\n\n\n\n<p>As a result, everything works seamlessly together. This gives you a scalable and maintainable AI setup right inside your Laravel application.<\/p>\n\n\n\n<h2 class=\"wp-block-heading\"><span class=\"ez-toc-section\" id=\"Final_Thoughts\"><\/span><strong>Final Thoughts<\/strong><span class=\"ez-toc-section-end\"><\/span><\/h2>\n\n\n\n<p>Laravel 13 AI agents are genuinely one of the most exciting additions to the framework. And once you go through this guide, it becomes clear why.<\/p>\n\n\n\n<p>To begin with, the <code>make:agent<\/code> command gives you a clean and structured starting point. At the same time, the Promptable trait takes care of much of the underlying complexity. In addition, tools are implemented as dedicated classes with a clear and consistent interface, while memory is handled automatically through RemembersConversations.<\/p>\n\n\n\n<p>On top of that, features like streaming, queuing, and failover are all built in, which makes the overall developer experience much smoother.<\/p>\n\n\n\n<p>Equally important, the testing support allows you to build with confidence. There are no real API calls during tests, no unexpected costs, and no unnecessary complications \u2014 just clean, testable PHP code.<\/p>\n\n\n\n<p>This is Laravel at its best \u2014 where complex problems are simplified and powerful systems remain easy to maintain.<\/p>\n\n\n\n<p>So, start small. Build one agent. Give it a single tool. Prompt it and observe how it works. Then, gradually expand \u2014 add memory, introduce structured output, and integrate more tools as needed.<\/p>\n\n\n\n<p>Ultimately<strong>,<\/strong> the future of Laravel development is agentic. And with Laravel 13, that future is already here.<\/p>\n","protected":false},"excerpt":{"rendered":"<p>In our previous guide, we introduced the Laravel 13 AI SDK and covered the basics of working with AI in Laravel. Now, let\u2019s take things a step further. Today, we are diving deep into Laravel 13 AI agents. So, what exactly is an agent? Well, an agent is not just a simple text generator. Instead, [&hellip;]<\/p>\n","protected":false},"author":5,"featured_media":11450,"comment_status":"open","ping_status":"closed","sticky":false,"template":"","format":"standard","meta":{"_genesis_hide_title":false,"_genesis_hide_breadcrumbs":false,"_genesis_hide_singular_image":false,"_genesis_hide_footer_widgets":false,"_genesis_custom_body_class":"","_genesis_custom_post_class":"","_genesis_layout":"","jetpack_post_was_ever_published":false,"_jetpack_newsletter_access":"","_jetpack_dont_email_post_to_subs":false,"_jetpack_newsletter_tier_id":0,"_jetpack_memberships_contains_paywalled_content":false,"_jetpack_memberships_contains_paid_content":false,"footnotes":"","jetpack_publicize_message":"","jetpack_publicize_feature_enabled":true,"jetpack_social_post_already_shared":true,"jetpack_social_options":{"image_generator_settings":{"template":"highway","default_image_id":0,"font":"","enabled":false},"version":2}},"categories":[2564],"tags":[3143,3140,3112,3139,3118,3113,3141,3138,3130,3142,3144],"yst_prominent_words":[456],"class_list":{"0":"post-11446","1":"post","2":"type-post","3":"status-publish","4":"format-standard","5":"has-post-thumbnail","7":"category-laravel","8":"tag-ai-agent-in-laravel-13","9":"tag-ai-agents-in-laravel","10":"tag-laravel-13","11":"tag-laravel-13-ai-agents","12":"tag-laravel-13-ai-sdk","13":"tag-laravel-13-features","14":"tag-laravel-13-updates","15":"tag-laravel-ai-agents","16":"tag-laravel-ai-integration","17":"tag-laravel-make-ai-agent","18":"tag-openai-agent-in-laravel","19":"entry"},"jetpack_publicize_connections":[],"jetpack_featured_media_url":"https:\/\/i0.wp.com\/programmingfields.com\/wp-content\/uploads\/2026\/05\/Laravel-13-AI-Agents.png?fit=1672%2C941&ssl=1","jetpack_likes_enabled":true,"jetpack_sharing_enabled":true,"jetpack-related-posts":[],"_links":{"self":[{"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/posts\/11446","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/comments?post=11446"}],"version-history":[{"count":6,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/posts\/11446\/revisions"}],"predecessor-version":[{"id":11462,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/posts\/11446\/revisions\/11462"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/media\/11450"}],"wp:attachment":[{"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/media?parent=11446"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/categories?post=11446"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/tags?post=11446"},{"taxonomy":"yst_prominent_words","embeddable":true,"href":"https:\/\/programmingfields.com\/wp-json\/wp\/v2\/yst_prominent_words?post=11446"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}