Making AI Features in Laravel Easier to Manage
I've been building AI-driven features in Laravel for a while now. Not just chatbots, but things like support agents, research helpers, and internal tools that need to reason over data. The pattern was always the same: the feature started simple, then slowly became hard to manage.
Prompts ended up scattered across services. Tool calls leaked into business logic. Every new agent meant more duplicated setup and more places for things to drift.
After solving these problems slightly differently in a few projects, it became clear the issue wasn't the LLMs. It was the lack of structure around how AI features lived inside a Laravel app.
That's what Atlas is meant to solve.
The Problem
Most AI features start as a single call to a model. That works until you need more than one agent, more than one tool, or when you want to add logging across all your AI operations.
At that point, you're left with questions like:
- Where do agent configurations live?
- How do tools get access to services without coupling everything together?
- How do you add logging or auth checks without modifying every call site?
Without a clear structure, those concerns end up spread across the codebase.
How Atlas Helps
Atlas provides an organizational layer for AI features in Laravel. It sits on top of Prism PHP and adds structure without hiding or replacing it.
Let's say you're building a help desk agent that can look up customer orders. Here's how that looks with Atlas.
Define the agent:
use Atlasphp\Atlas\Agents\AgentDefinition;
class HelpDeskAgent extends AgentDefinition
{
public function provider(): ?string
{
return 'anthropic';
}
public function model(): ?string
{
return 'claude-sonnet-4-20250514';
}
public function systemPrompt(): ?string
{
return <<<PROMPT
You are a helpful support agent for {company_name}.
The customer's name is {customer_name}.
Help them with order inquiries using the lookup_order tool.
PROMPT;
}
public function tools(): array
{
return [
LookupOrderTool::class,
];
}
}Define the tool:
use Atlasphp\Atlas\Tools\ToolDefinition;
use Atlasphp\Atlas\Tools\Support\ToolContext;
use Atlasphp\Atlas\Tools\Support\ToolParameter;
use Atlasphp\Atlas\Tools\Support\ToolResult;
class LookupOrderTool extends ToolDefinition
{
public function __construct(
private OrderService $orders
) {}
public function name(): string
{
return 'lookup_order';
}
public function description(): string
{
return 'Look up order details by order ID';
}
public function parameters(): array
{
return [
ToolParameter::string('order_id', 'The order ID to look up', required: true),
];
}
public function handle(array $params, ToolContext $context): ToolResult
{
$order = $this->orders->find($params['order_id']);
if (! $order) {
return ToolResult::error('Order not found');
}
return ToolResult::json([
'id' => $order->id,
'status' => $order->status,
'total' => $order->total,
'created_at' => $order->created_at->toDateString(),
]);
}
}Use the agent:
use Atlasphp\Atlas\Atlas;
$response = Atlas::agent(HelpDeskAgent::class)
->withVariables([
'company_name' => 'Acme Store',
'customer_name' => 'Sarah',
])
->chat('Where is my order #12345?');
echo $response->text();
// "Hi Sarah! I found your order #12345. It's currently out for delivery
// and should arrive today. Is there anything else I can help with?"That's it. The agent configuration, the tool logic, and the business service all live in separate, focused places.
What This Gets You
Agents are reusable. Define them once and use them anywhere in your app. Change the model or prompt in one place.
Tools stay focused. They're just classes with typed parameters. Laravel resolves dependencies through the container, so your tools can use whatever services they need.
Business logic stays separate. The OrderService doesn't know anything about AI. The tool is just a bridge (like a controller)
Pipelines for Cross-Cutting Concerns
Need to add logging to all AI operations? Or check auth before certain agents run? Atlas pipelines let you hook into any operation without modifying your agents or tools:
use Atlasphp\Atlas\Contracts\PipelineContract;
use Closure;
class LogAgentCalls implements PipelineContract
{
public function handle(mixed $data, Closure $next): mixed
{
Log::info('Agent started', ['agent' => $data['agent']->key()]);
$result = $next($data);
Log::info('Agent completed');
return $result;
}
}Register it once and it applies everywhere. Or attach middleware inline for a single request:
Atlas::agent(HelpDeskAgent::class)
->middleware([
'agent.before_execute' => LogAgentCalls::class,
])
->chat('Hello');You can also switch providers or models at runtime without changing your agent class:
Atlas::agent(HelpDeskAgent::class)
->withProvider('openai', 'gpt-4o')
->chat('Hello');When It Makes Sense
If you're making simple one-off calls to an LLM, you probably don't need this.
But if AI features are becoming a real part of your application; multiple agents, tools that call your services, a need for consistent logging or error handling; Atlas is designed to fill that gap.
It's the organizational layer I kept missing while building AI features in Laravel.
Thank you for reading, I hope this helps you build better AI integrated features.
Atlas is open source and available on GitHub. Full documentation at atlasphp.org.
