1. Go to this page and download the library: Download obaid/laravel-clutch library. Choose the download type require.
2. Extract the ZIP file and open the index.php.
3. Add this code to the index.php.
<?php
require_once('vendor/autoload.php');
/* Start to develop here. Best regards https://php-download.com/ */
obaid / laravel-clutch example snippets
$session = Clutch::agent(ResearchAgent::class)->for($user)->create();
$result = $session->prompt('Research our competitors and recommend a wedge.');
$response = (new ResearchAgent)->prompt('Research our competitors and write a brief.');
namespace App\Ai\Agents;
use Clutch\Laravel\Facades\Clutch;
use Laravel\Ai\Concerns\RemembersConversations;
use Laravel\Ai\Contracts\Agent;
use Laravel\Ai\Contracts\HasTools;
use Laravel\Ai\Contracts\RemembersConversations as RemembersConversationsContract;
use Laravel\Ai\Promptable;
use Stringable;
class ResearchAgent implements Agent, HasTools, RemembersConversationsContract
{
use Promptable;
// Laravel AI persists this agent's conversation, which is what lets a
// harness session continue across requests, workers and deployments.
use RemembersConversations;
public function instructions(): Stringable|string
{
return 'You research competitors and write positioning briefs.';
}
public function tools(): iterable
{
return Clutch::policy([new SearchWeb, new FetchPage]);
}
}
use Clutch\Laravel\Facades\Clutch;
use App\Ai\Agents\ResearchAgent;
$session = Clutch::agent(ResearchAgent::class)
->for($request->user())
->name('Competitor research')
->create();
$result = $session->prompt(
'Research our three closest competitors and recommend a positioning wedge.'
);
return [
'session_id' => $session->id,
'run_id' => $result->run->id,
'text' => $result->text,
'artifacts' => $result->artifacts,
'usage' => $result->usage,
];
$session = Clutch::session($sessionId)->authorizeFor($request->user());
$result = $session->prompt('Turn the recommendation into a one-page strategy memo.');
$run = $session->queue('Analyze every page on our website and produce a content gap report.');
return response()->accepted([
'session_id' => $session->id,
'run_id' => $run->id,
'status' => $run->status,
]);
Route::post('/research', function (Request $request) {
$session = Clutch::session($request->session_id)->authorizeFor($request->user());
return $session
->stream('Create the report and explain your progress.')
->usingVercelDataProtocol();
});
use Laravel\Ai\Concerns\InteractsWithApprovals;
use Laravel\Ai\Contracts\Approvable;
class PublishArticle implements Tool, Approvable
{
use InteractsWithApprovals;
// ...
}
$run = Clutch::run($runId)->authorizeFor($user);
$run->approve(
approvalId: $approvalId,
reason: 'The content has been reviewed and may be published.',
);
$run->reject(
approvalId: $approvalId,
reason: 'Do not publish this draft. Remove the unsupported claim first.',
);
Event::listen(ApprovalRequested::class, function (ApprovalRequested $event) {
$event->approval->session->participant->notify(
new AgentNeedsYou($event->approval)
);
});
use Clutch\Laravel\Enums\PermissionMode;
$session = Clutch::agent(PublishingAgent::class)
->for($user)
->permissions(PermissionMode::ApproveSensitive)
->create();
use Clutch\Laravel\Skills\Skill;
use Clutch\Laravel\Skills\SkillRegistry;
app(SkillRegistry::class)->add(new Skill(
name: 'refund-policy',
description: 'How we decide and process a refund.',
content: <<<'TEXT'
Refunds under $50 are automatic. Above that, check the account age first...
TEXT,
));
$decision = $this->pause('sign-off', ['draft' => $draft], 'Check the pricing claim.');
Workflow::resume($run->id, ['approved' => false, 'reason' => 'Fix the pricing claim.']);
$draft = $this->prompt("Write it up:\n{$research}", WriterAgent::class)->text;
Workflow::resume($run->id, ['approved' => true]);
public function produces(): array
{
return ['reports/*.md'];
}
public function handle(array $payload): mixed
{
$this->stage(['brief.txt' => $payload['brief']]);
// ... work happens, writing into the workspace ...
$this->workspace()->put('reports/findings.md', $findings);
}
Clutch::agent(ResearchAgent::class)
->for($user)
->sliceAfterSeconds(240) // below your worker's timeout
->create();
// config/clutch.php
'guards' => [
'remind_after_repeats' => 3, // tell the model it is going in circles
'block_after_repeats' => 8, // refuse, and say why
'tool_timeout_seconds' => 60, // bound one call, not just the whole run
'tool_timeouts' => ['scrape_page' => 120],
],
$run = Clutch::run($runId)->authorizeFor($user);
$run->cancel(reason: 'The request is no longer needed.');
public function handle(Request $request): string
{
foreach ($this->pages() as $page) {
if (RunContext::current()?->isCancelled()) {
break;
}
$this->analyze($page);
}
}
use Clutch\Laravel\Artifacts\Artifact;
use Clutch\Laravel\Runtime\RunContext;
$artifact = Artifact::fromStorage(disk: 's3', path: 'reports/content-gap-2026-08-24.pdf')
->name('Content gap report')
->mimeType('application/pdf')
->metadata(['pages' => 18]);
RunContext::current()->artifacts()->add($artifact);
Artifact::fromContents($markdown, 'reports/gap.md')->name('Content gap report');
use Clutch\Laravel\Contracts\IdempotentTool;
use Clutch\Laravel\Data\ToolInvocation;
use Laravel\Ai\Contracts\Tool;
final class PublishArticle implements Tool, IdempotentTool
{
public function idempotencyKey(ToolInvocation $invocation): string
{
// Key the side effect, not the call. Two retries of "publish article 42"
// must produce the same key even though the tool-call IDs differ.
return 'publish-article:'.$invocation->arguments['article_id'];
}
// Standard Laravel AI tool methods...
}
use Clutch\Laravel\Events\ClutchEventRecorded;
Event::listen(ClutchEventRecorded::class, function (ClutchEventRecorded $event) {
// Audit, analytics, metering, notifications, or your own broadcasting.
});
use Clutch\Laravel\Testing\DriverContractTests;
it('passes the harness driver contract', function () {
$result = DriverContractTests::for(new MyDriver)->run();
expect($result['passed'])->toContain('round-trips a checkpoint');
});
use Clutch\Laravel\Facades\Clutch;
use Clutch\Laravel\Runtime\ClutchResult;
Clutch::fake([
'Draft a brief' => ClutchResult::text('The proposed brief...'),
]);
$session = Clutch::agent(ResearchAgent::class)->for($user)->create();
$session->queue('Draft a brief');
Clutch::assertSessionCreated(ResearchAgent::class);
Clutch::assertRunQueued('Draft a brief');
Clutch::assertRunCompleted();
Clutch::assertNothingAwaitingApproval();
bash
composer h tables.
php artisan vendor:publish --provider="Clutch\Laravel\ClutchServiceProvider"
# Laravel AI's conversation tables, if you have not published them already.
# Session context lives there, so this step is not optional.
php artisan vendor:publish --provider="Laravel\Ai\AiServiceProvider"
php artisan migrate