← Laragents 2 / 18

Installation

composer require edulazaro/laragents

PHP 8.2 and Laravel 12. The one hard dependency beyond the framework is dragonmantank/cron-expression, which is what decides whether a scheduled agent is due.

The service provider registers itself. Publish the config and the migration:

php artisan vendor:publish --tag=laragents-config
php artisan vendor:publish --tag=laragents-migrations
php artisan migrate

That creates seven tables:

laragents_agents          the standing instruction: persona, tools, trigger
laragents_agent_tasks     one run of one agent
laragents_agent_logs      what happened during it
laragents_chat_sessions   one conversation
laragents_chat_messages   its turns, tool calls included
laragents_memories        what is remembered between conversations
laragents_rules           what must be obeyed within them

One tag for all seven, not one per table. They were split for a while so that an app wanting only a chat would not get empty agent tables, and that saved nothing worth having: a session points at an agent, so the foreign key needs the agents table to exist whether you use agents or not.

The API key

OPENAI_API_KEY=sk-...
ANTHROPIC_API_KEY=sk-ant-...

Only the provider you actually call needs one. There is no default model anywhere in the package: every call names the model it wants, because a default is how one renamed operation quietly moves to a more expensive tier and nobody finds out until the invoice.

The optional opt-out migration

php artisan vendor:publish --tag=laragents-optout-migration

Separate tag, and separate for a reason: it is the only migration in the package that touches a table the package does not own. It adds a share_memories boolean to your users table, so a person can keep their memories out of the pool everyone in the tenant reads. Skip it and everyone shares, which is a legitimate product decision and the default.

It asks which table rather than guessing, and it skips a column that is already there:

// config/laragents.php
'memory' => [
    'actor_table' => 'users',
],

Nothing else is required

Installed and migrated, the package talks to OpenAI, meters nothing, redacts nothing, and has the two memory tools registered. That is a working starting point, and everything past it is opt-in:

// app/Providers/AppServiceProvider.php
use EduLazaro\Laragents\Laragents;

public function boot(): void
{
    Laragents::tools([ListProperties::class, ScheduleViewing::class]);
    Laragents::meterWith(LarameterRecorder::class);
    Laragents::redactWith(MyRedactor::class);
}

Each of those is covered in its own chapter: Tools, Usage and credits, Privacy.

The scheduler, if you use agents

The package registers its own scheduled commands, so schedule:work or your cron entry is all that is needed:

laragents:run-due-agents            every minute
laragents:cleanup-stale-tasks       every five minutes
laragents:distil-inactive-sessions  every thirty minutes

The first is what makes a scheduled agent actually fire. Turn the lot off with 'agents' => ['schedule' => false] and register them yourself.

And a worker

Agent runs and memory distillation both go to the queue, so something has to consume it. The failure mode when nothing does is the expensive one: no error, no exception, no log. The agent simply never runs, and you find out when somebody asks why the documents were never triaged.

Give agents a queue of their own if you also run a chat:

'agents' => [
    'queue' => 'agents',
],

An agent run is a loop of model calls and can take minutes. Sharing a queue with anything interactive means one run holds a worker while a person waits for a reply.