laragentic / agents
Agentic loops and AI agent capabilities for the Laravel AI SDK
Installs: 23
Dependents: 0
Suggesters: 0
Security: 0
Stars: 2
Watchers: 0
Forks: 0
Open Issues: 1
pkg:composer/laragentic/agents
Requires
- php: ^8.2
- illuminate/contracts: ^12.0
- illuminate/support: ^12.0
- laravel/ai: ^0.1.5
Requires (Dev)
- orchestra/testbench: ^10.0
- pestphp/pest: ^3.0
- pestphp/pest-plugin-laravel: ^3.0
This package is auto-updated.
Last update: 2026-02-15 23:12:51 UTC
README
Add one trait. Get autonomous AI agents.
Laragentic extends the Laravel AI SDK with agentic loops — letting your agents autonomously reason, call tools, and iterate until they reach an answer.
class ChatAgent implements Agent, HasTools { use Promptable, ReActLoop; // ← add this trait // ... your existing agent code } $result = (new ChatAgent)->reactLoop('What is the weather in Tokyo?'); echo $result->text(); // "It's 22°C and partly cloudy in Tokyo."
Your agent now thinks, calls tools on its own, reads the results, and keeps going until it has a complete answer — all in one method call.
See It In Action
ReAct Loop: Autonomous Reasoning + Acting
The agent thinks, searches, calculates, and synthesizes — all on its own.
Plan-Execute Loop: Multi-Step Planning & Synthesis
Watch the agent create a plan, execute each step, and synthesize the final answer.
Complete Chat Agent with Tools
A full conversational agent with weather lookup and calculator capabilities.
Want to try these examples? Clone the laragentic-app-examples repository — a complete Laravel app with all demos ready to run.
Why Laragentic?
The Laravel AI SDK gives you agents, tools, streaming, and conversations. But when you call prompt(), the agent responds once — if it wants to call a tool, inspect the result, and reason further, you have to write that loop yourself.
Laragentic adds that missing piece:
| What you get | What it does |
|---|---|
| ReAct Loop | Think → call tools → observe results → repeat until done |
| Plan-Execute Loop | Create a plan → execute each step → synthesize a final answer |
| Chain-of-Thought Loop | Reason iteratively → evaluate understanding → continue until confident |
| Agent Skills System | Dynamic skill loading with progressive disclosure and auto-discovery |
| Lifecycle Callbacks | Hook into every phase to stream progress, log, or broadcast |
| Configurable Limits | Set max iterations/steps to control cost and prevent runaway loops |
| Adaptive Replanning | The Plan-Execute loop revises its plan mid-execution if a step fails |
Zero configuration required. Add use ReActLoop to your agent, call ->reactLoop(), and it works.
Requirements
- PHP 8.2+
- Laravel 12.x
- Laravel AI SDK (
laravel/ai)
Installation
composer require laragentic/agents
Optionally publish the configuration:
php artisan vendor:publish --tag=agentic-config
Quick Start
This guide assumes you have a Laravel 12 app with Inertia, React, and the Laravel AI SDK already installed.
You'll create three files to get a working agent that calls tools autonomously and streams real-time progress to a frontend:
| File | What it does |
|---|---|
app/Tools/WeatherTool.php |
A tool the agent can call |
app/Agents/ChatAgent.php |
An agent with the ReAct loop |
resources/js/pages/chat.tsx |
A React frontend that streams progress |
Step 1: Create a Tool
Tools implement Laravel\Ai\Contracts\Tool. They have a name, a description (so the LLM knows when to use them), a JSON schema for parameters, and a handle() method that does the work.
Create app/Tools/WeatherTool.php:
<?php namespace App\Tools; use Illuminate\Contracts\JsonSchema\JsonSchema; use Laravel\Ai\Contracts\Tool; use Laravel\Ai\Tools\Request; use Stringable; class WeatherTool implements Tool { public function name(): string { return 'get_weather'; } public function description(): Stringable|string { return 'Get the current weather for a given city.'; } public function handle(Request $request): Stringable|string { $city = $request['city'] ?? 'Unknown'; // Replace with a real API call (e.g. OpenWeatherMap) return "Weather in {$city}: Partly Cloudy, 22°C, Humidity: 65%"; } public function schema(JsonSchema $schema): array { return [ 'city' => $schema->string() ->description('The city name, e.g. "Tokyo", "London"') ->required(), ]; } }
Step 2: Create an Agent
Add the ReActLoop trait to a standard Laravel AI SDK agent. Your agent must implement HasTools so the SDK registers your tools with the LLM.
Create app/Agents/ChatAgent.php:
<?php namespace App\Agents; use App\Tools\WeatherTool; use Laravel\Ai\Contracts\Agent; use Laravel\Ai\Contracts\HasTools; use Laravel\Ai\Promptable; use Laragentic\Loops\ReActLoop; class ChatAgent implements Agent, HasTools { use Promptable, ReActLoop; public function instructions(): string { return 'You are a helpful assistant that can answer questions and use tools.'; } public function tools(): iterable { return [new WeatherTool]; } }
That's it — ChatAgent can now autonomously call get_weather, read the result, and produce a final answer.
Step 3: Add Routes
Add two routes to routes/web.php — one serves the frontend page, the other streams agent progress via Server-Sent Events.
Use reactLoopStream() inside response()->eventStream() — this streaming variant propagates yield values from your callbacks to the HTTP response.
<?php use Illuminate\Support\Facades\Route; use Inertia\Inertia; use App\Agents\ChatAgent; use Illuminate\Http\StreamedEvent; Route::get('/', function () { return Inertia::render('chat'); }); Route::get('/chat', function () { $agent = new ChatAgent; return response()->eventStream(function () use ($agent) { $agent ->onBeforeAction(function (string $tool, array $args, int $iteration) { yield new StreamedEvent( event: 'action', data: ['tool' => $tool, 'status' => 'calling'], ); }) ->onAfterAction(function (string $tool, array $args, string $result, int $iteration) { yield new StreamedEvent( event: 'action', data: ['tool' => $tool, 'result' => $result, 'status' => 'complete'], ); }) ->onLoopComplete(function ($response, int $iterations) { yield new StreamedEvent( event: 'complete', data: ['text' => $response->text, 'iterations' => $iterations], ); }); // Use the streaming variant — yields propagate from callbacks yield from $agent->reactLoopStream(request()->input('message')); }); });
Important: Use
reactLoopStream()(notreactLoop()) insideeventStream()closures. The streaming variant is a Generator that propagatesyieldvalues from your callbacks. Without it, callback yields stay in their own scope and never reach the HTTP response.
Step 4: Create the Frontend
Install the Laravel stream React hook:
npm install @laravel/stream-react
Create resources/js/pages/chat.tsx:
import { useEffect, useState } from "react"; export default function Chat() { const [actions, setActions] = useState< { tool: string; status: string; result?: string }[] >([]); const [response, setResponse] = useState<string | null>(null); const [error, setError] = useState<string | null>(null); useEffect(() => { const eventSource = new EventSource( "/chat?message=What+is+the+weather+in+Tokyo", ); eventSource.addEventListener("action", (e) => { const data = JSON.parse(e.data); setActions((prev) => [...prev, data]); }); eventSource.addEventListener("complete", (e) => { const data = JSON.parse(e.data); setResponse(data.text); eventSource.close(); }); eventSource.onerror = (e) => { console.error("EventSource error", e); setError("Connection error"); eventSource.close(); }; return () => eventSource.close(); }, []); return ( <div> <h1>Chat</h1> {actions.length > 0 && ( <div> <h3>Tool calls:</h3> {actions.map((a, i) => ( <div key={i}> <strong>{a.tool}</strong> — {a.status} {a.result && <pre>{a.result}</pre>} </div> ))} </div> )} {response && ( <div> <strong>Response:</strong> {response} </div> )} {error && <div>Error: {error}</div>} {!response && !error && <p>Loading...</p>} </div> ); }
Visit http://localhost:8000 and you'll see your agent reason through the question, call the weather tool, and stream the final answer — all in real time.
For more streaming patterns and complete examples, see the Tutorials section.
ReAct Loop
The ReAct (Reasoning + Acting) loop gives your agent an autonomous think-act-observe cycle:
User Goal
↓
[THOUGHT] — LLM reasons about what to do next
↓
[ACTION] — LLM calls one or more tools
↓
[OBSERVATION] — Tool results fed back to the LLM
↓
Goal finished? → No → back to THOUGHT
→ Yes → LLM produces final answer
Example: "What's the weather in Tokyo and should I bring an umbrella?"
- Thought — I need the current weather → calls
get_weather - Action —
get_weather(city: "Tokyo")→ Rain expected, 85% - Observation — Tool result fed back to LLM
- Thought — Rain > 50%, recommend umbrella → no more tools needed
- Final Answer — "Yes, bring an umbrella."
Usage
// Basic $result = (new ChatAgent)->reactLoop('What is the weather in Tokyo?'); echo $result->text();
// With a specific provider and model $result = (new ChatAgent)->reactLoop( prompt: 'What is the weather in Tokyo?', provider: 'anthropic', model: 'claude-sonnet-4-5', );
Configuration
Override the max iterations per-call, in config, or via environment variables:
// Fluent API (per-call) $result = (new ChatAgent) ->maxIterations(5) ->reactLoop('Hello!');
// config/agentic.php 'react' => [ 'max_iterations' => 10, 'throw_on_max_iterations' => false, ],
AGENTIC_MAX_ITERATIONS=10 AGENTIC_THROW_ON_MAX_ITERATIONS=false
Callbacks
Hook into any phase of the loop. Callbacks are optional — the loop works perfectly without them.
$result = (new ChatAgent) ->onBeforeAction(function (string $tool, array $args, int $iteration) { broadcast(new ToolCallStarted($tool, $args)); }) ->onAfterAction(function (string $tool, array $args, string $result, int $iteration) { broadcast(new ToolCallCompleted($tool, $result)); }) ->onObservation(function (string $observation, int $iteration) { broadcast(new ObservationReady($observation, $iteration)); }) ->reactLoop('What is the weather in Tokyo?');
| Callback | When it fires | Parameters |
|---|---|---|
onLoopStart |
Loop begins | string $prompt |
onLoopComplete |
Final answer produced | AgentResponse $response, int $totalIterations |
onMaxIterationsReached |
Iteration limit hit | AgentResponse $response, int $totalIterations |
onIterationStart |
Each iteration starts | int $iteration |
onIterationEnd |
Each iteration ends | int $iteration, AgentResponse $response |
onBeforeThought |
Before LLM prompt | string $prompt, int $iteration |
onAfterThought |
After LLM responds | AgentResponse $response, int $iteration |
onBeforeAction |
Before tool call | string $tool, array $args, int $iteration |
onAfterAction |
After tool returns | string $tool, array $args, string $result, int $iteration |
onObservation |
Observation ready | string $observation, int $iteration |
Result
The reactLoop() method returns a LoopResult that wraps the final AgentResponse with loop metadata:
$result = (new ChatAgent)->reactLoop('Hello!'); $result->text(); // Final response text $result->conversationId; // Conversation ID $result->iterations; // Total iterations executed $result->completed(); // true if LLM gave a final answer $result->reachedMaxIterations; // true if iteration limit was hit $result->steps; // Array of LoopStep objects $result->allToolCalls(); // All tool calls across all iterations
Customization
Override these methods on your agent to customize loop behavior:
class MyAgent implements Agent, HasTools { use Promptable, ReActLoop; // Customize how tool results are presented to the LLM protected function formatObservation(array $toolCallRecords): string { /* ... */ } // Customize when the loop should stop protected function loopShouldTerminate(AgentResponse $response): bool { /* ... */ } }
Plan-Execute Loop
The Plan-Execute loop separates planning from execution — ideal for complex, multi-step tasks:
User Task
↓
[PLAN] — LLM creates a step-by-step plan
↓
[EXECUTE 1] — LLM executes step 1 (may use tools)
↓
[EXECUTE 2] — LLM executes step 2 (with prior context)
↓
... — Continue for each step
↓
[SYNTHESIZE] — LLM combines all results into a final answer
Best for:
- Complex multi-step tasks with sequential dependencies
- Tasks requiring coordination of multiple tools
- Work where you want visibility into the reasoning process
- Scenarios where adaptive replanning adds resilience
Usage
<?php namespace App\Agents; use Laravel\Ai\Contracts\Agent; use Laravel\Ai\Contracts\HasTools; use Laravel\Ai\Promptable; use Laragentic\Loops\PlanExecuteLoop; class ResearchAgent implements Agent, HasTools { use Promptable, PlanExecuteLoop; public function instructions(): string { return 'You are a research assistant. Create clear plans and execute methodically.'; } public function tools(): iterable { return [new WebSearch, new Calculator]; } }
$result = (new ResearchAgent)->planExecute( 'Research Q4 sales data, analyze trends, and create an executive summary.', ); echo $result->text(); // The synthesized final answer echo $result->stepsExecuted(); // Number of steps completed
// With a specific provider and model $result = (new ResearchAgent)->planExecute( task: 'Research Q4 sales data, analyze trends, and create an executive summary.', provider: 'anthropic', model: 'claude-sonnet-4-5', timeout: 120, // 2 minute timeout );
Configuration
// Fluent API (per-call) $result = (new ResearchAgent) ->maxSteps(5) ->allowReplan() ->maxReplans(2) ->planExecute('Analyze this data...');
// config/agentic.php 'plan_execute' => [ 'max_steps' => 10, 'allow_replan' => true, 'max_replans' => 3, 'throw_on_max_steps' => false, ],
AGENTIC_PLAN_MAX_STEPS=10 AGENTIC_PLAN_ALLOW_REPLAN=true AGENTIC_PLAN_MAX_REPLANS=3 AGENTIC_PLAN_THROW_ON_MAX_STEPS=false
Adaptive Replanning
When enabled, the loop detects step failures and asks the LLM to create a revised plan:
$result = (new ResearchAgent) ->allowReplan() ->maxReplans(2) ->onReplan(function (array $newSteps, int $replanCount) { logger()->warning("Plan revised (attempt {$replanCount})", $newSteps); }) ->planExecute('Analyze sales data. If the database is unavailable, use cached reports.'); $result->wasReplanned(); // true if the plan was revised $result->replans; // Number of times the plan was revised
Callbacks
$result = (new ResearchAgent) ->onPlanCreated(function (array $steps) { broadcast(new PlanReady($steps)); }) ->onBeforeStep(function (int $stepNumber, string $description, int $totalSteps) { broadcast(new StepStarted($stepNumber, $description, $totalSteps)); }) ->onAfterStep(function (int $stepNumber, string $description, AgentResponse $response, int $totalSteps) { broadcast(new StepCompleted($stepNumber, $response->text)); }) ->onBeforeSynthesis(function (array $steps) { broadcast(new SynthesisStarted(count($steps))); }) ->planExecute('Create a marketing report...');
| Callback | When it fires | Parameters |
|---|---|---|
onLoopStart |
Loop begins | string $task |
onLoopComplete |
Synthesis done | AgentResponse $response, int $stepsExecuted |
onPlanCreated |
Plan parsed | array $stepDescriptions |
onBeforeStep |
Before step execution | int $stepNumber, string $description, int $totalSteps |
onAfterStep |
After step execution | int $stepNumber, string $description, AgentResponse $response, int $totalSteps |
onReplan |
Plan revised | array $newStepDescriptions, int $replanCount |
onBeforeSynthesis |
Before synthesis | array $executedPlanSteps |
onAfterSynthesis |
After synthesis | AgentResponse $response |
onMaxStepsReached |
Step limit hit | AgentResponse $lastResponse, int $stepsExecuted |
Result
$result = (new ResearchAgent)->planExecute('Analyze trends'); $result->text(); // Synthesized final answer $result->plan; // Original plan step descriptions $result->steps; // Array of PlanStep objects $result->stepsExecuted(); // Number of steps completed $result->totalPlannedSteps(); // Total steps in the plan $result->completed(); // true if all steps + synthesis ran $result->reachedMaxSteps; // true if step limit was hit $result->wasReplanned(); // true if plan was revised $result->replans; // Number of revisions $result->stepResults(); // [{step, description, result}, ...]
Customization
Override these methods on your agent to customize planning, execution, and synthesis:
class MyAgent implements Agent, HasTools { use Promptable, PlanExecuteLoop; // Customize the planning prompt protected function buildPlanningPrompt(string $task): string { /* ... */ } // Customize how each step is executed protected function buildStepExecutionPrompt( string $task, array $plan, int $stepNumber, string $stepDescription, array $previousSteps, ): string { /* ... */ } // Customize the synthesis prompt protected function buildSynthesisPrompt(string $task, array $steps): string { /* ... */ } // Customize plan parsing protected function parsePlanSteps(string $planText): array { /* ... */ } // Customize failure detection for replanning protected function shouldReplan(AgentResponse $response): bool { /* ... */ } // Customize the replanning prompt when a step fails protected function revisePlan( string $task, array $originalPlan, array $completedSteps, int $failedStepNumber, AgentResponse $failedResponse, ?string $provider, ?string $model, ?int $timeout, ): array { /* ... */ } }
Chain-of-Thought Loop
The Chain-of-Thought (CoT) loop implements iterative self-reflection reasoning where the agent progressively builds understanding until confident:
User Problem
↓
[REASONING ITERATION 1]
├─ Step-by-step analysis of problem
├─ Identify what's known and unknown
├─ Call tools if information needed
└─ Self-evaluate: "Do I understand enough to solve this?"
↓
Confident? → No → [REASONING ITERATION 2]
→ Yes → [FINAL ANSWER]
Best for:
- Complex reasoning problems requiring deep analysis
- Mathematical, logical, or analytical tasks
- Scenarios where understanding must build progressively
- Problems where agent should evaluate its own confidence
Usage
<?php namespace App\Agents; use Laravel\Ai\Contracts\Agent; use Laravel\Ai\Contracts\HasTools; use Laravel\Ai\Promptable; use Laragentic\Loops\ChainOfThoughtLoop; class MathAgent implements Agent, HasTools { use Promptable, ChainOfThoughtLoop; public function instructions(): string { return 'You are a mathematics expert. Think through problems step by step, ' . 'evaluate your understanding, and provide clear reasoning.'; } public function tools(): iterable { return [new CalculatorTool, new WebSearchTool]; } }
// Basic usage - agent iterates until confident $result = (new MathAgent)->chainOfThought( 'A train leaves Tokyo at 9am traveling 80km/h. Another leaves Osaka at 10am ' . 'traveling 100km/h toward Tokyo. They are 400km apart. When do they meet?' ); echo $result->text(); // Final answer with complete reasoning echo $result->reasoningIterations; // Number of iterations taken (e.g., 3) // Get all reasoning steps foreach ($result->steps as $step) { echo "Iteration {$step->iteration}: {$step->reasoning()}\n"; if ($step->hasToolCalls()) { echo "Tools used: " . count($step->toolCalls) . "\n"; } }
// With specific provider and model $result = (new MathAgent)->chainOfThought( prompt: 'Analyze this complex problem...', provider: 'anthropic', model: 'claude-opus-4-6', );
Configuration
Override the max reasoning iterations per-call, in config, or via environment variables:
// Fluent API (per-call) $result = (new MathAgent) ->maxReasoningIterations(10) ->chainOfThought('Complex problem...');
// config/agentic.php 'chain_of_thought' => [ 'max_reasoning_iterations' => 5, 'throw_on_max_iterations' => false, 'require_confidence_check' => true, ],
AGENTIC_COT_MAX_ITERATIONS=5 AGENTIC_COT_THROW_ON_MAX=false
Callbacks
Hook into any phase of the reasoning loop. Callbacks are optional — the loop works perfectly without them.
$result = (new MathAgent) ->onIterationStart(function (int $iteration) { broadcast(new ReasoningIterationStarted($iteration)); }) ->onAfterReasoning(function (AgentResponse $response, int $iteration) { broadcast(new ReasoningStepComplete($response->text, $iteration)); }) ->onBeforeAction(function (string $tool, array $args, int $iteration) { broadcast(new ToolCallStarted($tool, $args, $iteration)); }) ->onReflection(function (string $reflectionPrompt, int $iteration) { logger()->info("Iteration {$iteration}: Entering reflection phase"); }) ->chainOfThought('Complex problem requiring deep analysis...');
| Callback | When it fires | Parameters |
|---|---|---|
onLoopStart |
Loop begins | string $prompt |
onLoopComplete |
Final answer produced | AgentResponse $response, int $totalIterations |
onMaxIterationsReached |
Iteration limit hit | AgentResponse $response, int $totalIterations |
onIterationStart |
Each iteration starts | int $iteration |
onIterationEnd |
Each iteration ends | int $iteration, AgentResponse $response |
onBeforeReasoning |
Before LLM reasoning | string $prompt, int $iteration |
onAfterReasoning |
After LLM responds | AgentResponse $response, int $iteration |
onBeforeAction |
Before tool call | string $tool, array $args, int $iteration |
onAfterAction |
After tool returns | string $tool, array $args, string $result, int $iteration |
onReflection |
Building reflection prompt | string $reflectionPrompt, int $iteration |
Result
The chainOfThought() method returns a CoTResult that wraps the final AgentResponse with reasoning metadata:
$result = (new MathAgent)->chainOfThought('Problem...'); $result->text(); // Final response text $result->conversationId(); // Conversation ID $result->reasoningIterations; // Total iterations executed $result->completed(); // true if agent became confident $result->reachedMaxIterations; // true if iteration limit was hit $result->steps; // Array of CoTStep objects $result->allToolCalls(); // All tool calls across all iterations $result->reasoningSteps(); // Array of reasoning text from each step $result->confidentStep(); // The step where agent became confident
Customization
Override these methods on your agent to customize reasoning behavior:
class MyAgent implements Agent, HasTools { use Promptable, ChainOfThoughtLoop; // Customize initial reasoning prompt protected function buildInitialPrompt(string $userProblem): string { /* ... */ } // Customize reflection prompt between iterations protected function buildReflectionPrompt(?string $observation): string { /* ... */ } // Customize when reasoning is considered complete protected function isReasoningComplete(AgentResponse $response): bool { /* ... */ } // Customize how tool results are presented protected function formatObservation(array $toolCallRecords): string { /* ... */ } }
Agent Skills System
The Agent Skills System brings dynamic, context-aware instruction loading to your agents. Following the agentskills.io specification, this system enables progressive disclosure — loading specialized knowledge only when needed, minimizing context usage while maximizing agent effectiveness.
What are Agent Skills?
An Agent Skill is a self-contained package of specialized instructions and resources that teaches an agent how to perform a specific task exceptionally well. Think of skills as expert domain knowledge that can be loaded on-demand.
Key Benefits:
- ✅ Progressive Disclosure — Load only what you need, when you need it
- ✅ Modular Knowledge — Organize specialized instructions into reusable skills
- ✅ Auto-Discovery — Agents automatically find relevant skills for tasks
- ✅ Context Efficiency — Reduce token usage with targeted skill loading
- ✅ Easy Sharing — Share skills across teams and projects
Quick Start
1. Add the Trait
use Laragentic\Skills\HasAgentSkills; class MyAgent implements Agent, HasTools { use Promptable, ReActLoop, HasAgentSkills; protected function baseInstructions(): string { return 'You are a helpful assistant.'; } public function instructions(?string $query = null): string { return $this->enhanceInstructionsWithSkills( $this->baseInstructions(), $query ?? '' ); } }
2. Load Skills
Manual Loading:
$agent = new MyAgent; $agent->withSkill('code-review'); $result = $agent->reactLoop('Review this PHP code for security issues');
Auto-Resolution:
$agent = new MyAgent; $agent->autoResolveSkills(threshold: 0.3, limit: 3); $result = $agent->reactLoop('Analyze this code for vulnerabilities'); // Automatically loads 'code-review' skill based on query relevance
Example Skills
Laragentic includes three production-ready skills in the examples/skills/ directory:
code-review
Comprehensive code review for security, performance, and best practices.
- OWASP Top 10 security analysis
- Performance bottleneck detection
- SOLID principles enforcement
- Language-specific guidance (PHP, JavaScript, Python)
data-analysis
Expert data analysis, statistical modeling, and insight generation.
- Exploratory data analysis (EDA)
- Statistical testing and hypothesis validation
- Visualization recommendations
- Actionable business insights
api-testing
API endpoint testing, validation, and test suite generation.
- HTTP method testing (GET, POST, PUT, DELETE, etc.)
- JSON schema validation
- Security testing (SQL injection, XSS, auth issues)
- Performance analysis and error handling assessment
Creating Custom Skills
Skills are stored in app/Skills/ (configurable) with this structure:
app/Skills/
my-skill/
SKILL.md # Required: metadata + instructions
scripts/ # Optional: executable scripts
references/ # Optional: reference documents
assets/ # Optional: images, diagrams
SKILL.md Format:
--- name: my-skill description: Brief description of what this skill does tags: [relevant, tags, here] version: 1.0.0 author: Your Name --- # My Skill Title You are an expert in [domain]. Your task is to [specific task]. ## Guidelines 1. [Guideline 1] 2. [Guideline 2] ## Output Format Provide your response in this structure: - [Section 1] - [Section 2] ## Best Practices - [Practice 1] - [Practice 2]
Example:
mkdir -p app/Skills/security-audit
Create app/Skills/security-audit/SKILL.md:
--- name: security-audit description: Perform comprehensive security audits on applications tags: [security, audit, vulnerability, penetration-testing] version: 1.0.0 --- # Security Audit Skill You are a cybersecurity expert conducting thorough security audits... [Detailed instructions here]
Then use it:
$result = (new MyAgent) ->withSkill('security-audit') ->reactLoop('Audit this application for security vulnerabilities');
Auto-Resolution
Let agents automatically discover and load relevant skills based on query content:
$agent = (new MyAgent)->autoResolveSkills( threshold: 0.3, // Minimum relevance score (0.0 - 1.0) limit: 3 // Maximum skills to load ); // Query mentions "security" → auto-loads code-review skill $result = $agent->reactLoop('Review this code for security issues'); // Query mentions "data analysis" → auto-loads data-analysis skill $result = $agent->reactLoop('Analyze this dataset for trends');
How Relevance Scoring Works:
The resolver calculates a relevance score (0.0 - 1.0) for each skill:
- Exact name match in query: +1.0
- Partial name match: +0.5
- Description keyword match: +0.7 per keyword
- Tag match: +0.5 per tag
Skills above the threshold are loaded automatically.
Integration with Loops
Skills work seamlessly with all Laragentic loops:
// ReAct Loop with Skills $result = (new MyAgent) ->withSkill('code-review') ->reactLoop('Review this code for security issues'); // Plan-Execute Loop with Skills $result = (new MyAgent) ->withSkill('data-analysis') ->planExecute('Analyze Q4 sales data and generate insights'); // Chain-of-Thought Loop with Skills $result = (new MyAgent) ->withSkill('api-testing') ->chainOfThought('How should we test this REST API?');
Callbacks
Track skill loading and resolution with callbacks:
$agent = (new MyAgent) ->autoResolveSkills() ->onSkillLoaded(function ($skill) { Log::info('Skill loaded', ['skill' => $skill->name()]); }) ->onSkillResolved(function ($skills, $query) { Log::info('Skills resolved', [ 'count' => count($skills), 'names' => array_map(fn($s) => $s->name(), $skills), 'query' => $query, ]); });
Configuration
Configure the Skills system in config/agentic.php:
'skills' => [ 'enabled' => true, 'path' => app_path('Skills'), 'auto_resolve' => false, 'resolution_threshold' => 0.3, 'resolution_limit' => 3, ],
Environment variables:
AGENTIC_SKILLS_ENABLED=true AGENTIC_SKILLS_PATH=/path/to/skills AGENTIC_SKILLS_AUTO_RESOLVE=false AGENTIC_SKILLS_THRESHOLD=0.3 AGENTIC_SKILLS_LIMIT=3
Progressive Disclosure
The Skills System minimizes context usage through three levels:
Level 1: Skill Index (No skills loaded)
Available Skills:
- code-review: Analyze code for security and performance
- data-analysis: Analyze datasets and generate insights
- api-testing: Test API endpoints and validate responses
Token usage: Minimal (just metadata)
Level 2: Full Instructions (Skills loaded)
# Active Skills
## Skill: code-review
You are an expert code reviewer...
[Full detailed instructions]
Token usage: As needed (only loaded skills)
Level 3: Resources (On demand) Scripts and references loaded only when explicitly requested.
Streaming with Skills
Skills work seamlessly with streaming responses:
$agent = (new MyAgent) ->autoResolveSkills() ->onSkillLoaded(function ($skill) { yield new StreamedEvent('skill-loaded', [ 'skill' => $skill->name() ]); }); return response()->eventStream(function () use ($agent) { yield from $agent->reactLoopStream(request('query')); });
Documentation
- Complete Tutorial — Comprehensive guide with examples and diagrams
- Skills README — System overview and quick reference
- Example Skills — Three production-ready skills
Learn more: See the complete Skills tutorial for advanced patterns, troubleshooting, and detailed examples.
Choosing a Loop
| ReAct Loop | Plan-Execute Loop | Chain-of-Thought Loop | |
|---|---|---|---|
| Pattern | Thought → Action → Observation | Plan → Execute Steps → Synthesize | Reasoning → Self-Eval → Confidence |
| Best for | Tool-driven tasks, real-time reasoning | Multi-step workflows, sequential tasks | Deep analysis, progressive reasoning |
| Tool calls | Each iteration may call tools | Each step may call tools | Each iteration may call tools |
| Adaptiveness | Continuous (every iteration) | Optional replanning on failure | Continuous self-reflection |
| Token usage | Lower per-iteration | Higher (planning + steps + synthesis) | Medium (iterative reasoning) |
| Visibility | Per-iteration callbacks | Per-step + plan + synthesis callbacks | Per-iteration + reflection callbacks |
When to use each:
- ReAct Loop — Tool-heavy tasks requiring rapid action and observation (weather queries, data lookups, calculations)
- Plan-Execute Loop — Complex workflows with sequential dependencies (research reports, multi-stage analysis)
- Chain-of-Thought Loop — Deep reasoning problems requiring progressive understanding (math proofs, legal analysis, complex decision-making)
Use multiple loops together — an agent can use ReActLoop, PlanExecuteLoop, ChainOfThoughtLoop and choose which loop to call:
class HybridAgent implements Agent, HasTools { use Promptable, ReActLoop, PlanExecuteLoop, ChainOfThoughtLoop; public function solve(string $problem, string $approach) { return match($approach) { 'action' => $this->reactLoop($problem), // Fast tool-driven 'workflow' => $this->planExecute($problem), // Multi-step plan 'reasoning' => $this->chainOfThought($problem), // Deep analysis }; } } // Quick tool-driven question $result = (new HybridAgent)->reactLoop('What is the weather in Tokyo?'); // Complex multi-step task $result = (new HybridAgent)->planExecute('Research Q4 sales and create a report.'); // Deep reasoning problem $result = (new HybridAgent)->chainOfThought('Analyze the trade-offs between approaches A and B.');
Testing
Unit Tests
Unit tests use faked responses and do not require API keys:
composer test # or vendor/bin/pest tests/Unit
Integration Tests
Integration tests make real API calls and require valid API keys in your .env file. They are grouped under integration and will be skipped automatically if the required key is not set:
# Run only integration tests vendor/bin/pest --group=integration # Run everything vendor/bin/pest # Exclude integration tests during development vendor/bin/pest --exclude-group=integration
Required .env variables for integration tests:
ANTHROPIC_API_KEY=your-key-here
Test Coverage
composer test-coverage
Working Examples
Want to see complete working code? Check out our Examples Repository — a full Laravel application with:
- ✅ ReAct Loop demo with streaming UI
- ✅ Plan-Execute Loop demo with real-time progress
- ✅ Complete chat agent with tools
- ✅ Production-ready React frontends
- ✅ Server-Sent Events (SSE) streaming
- ✅ Ready to clone and run
Tutorials
Comprehensive streaming examples and documentation are available in the tutorial/ folder:
- Complete Working Example — Ready-to-use chat agent with streaming, tools, and frontend code
- Quick Reference — Callback cheat sheet and essential streaming patterns
- Streaming ReAct Loop — Stream real-time progress updates from ReAct loops
- Streaming Plan-Execute Loop — Stream planning, execution, and synthesis progress
- Streaming Chain-of-Thought Loop — Stream iterative self-reflection and deep reasoning
Note: The tutorial folder is excluded from production installs via
.gitattributes.
📚 New to PHP or Laravel?
Code with PHP is a free, comprehensive learning platform I created to help developers master modern PHP and Laravel from first principles.
🎯 Learn by Building: Create your own router, ORM, and MVC framework before touching Laravel
🚀 Modern PHP 8.4: Property hooks, asymmetric visibility, and latest features
💼 Production-Ready: PSR standards and real-world best practices
🔄 Multiple Paths: Dedicated series for TypeScript, Java, Python, and Ruby developers
Popular Series:
- PHP Basics — Master modern PHP from zero to building your own blog
- Build a CRM with Laravel 12 — Complete CRM system with authentication, teams, and pipelines
- AI/ML for PHP Developers — Build intelligent applications with machine learning
- Claude for PHP Developers — Integrate Claude AI into your applications
Start Learning at codewithphp.com →
Our Other Packages
Explore our comprehensive suite of PHP packages for AI development:
🤖 Agentic AI & Claude Integration
- claude-php/claude-php-agent — Comprehensive agentic framework with extensive agent types (ReAct, Plan-Execute, Reflection, Hierarchical, MAKER, and more), advanced patterns, MCP server integration, and detailed tutorials
- claude-php/Claude-PHP-SDK — PHP SDK for Claude with complete 1-for-1 functionality of the Official Python SDK, including tool use, vision, streaming, and batch processing
- claude-php/Claude-PHP-SDK-Laravel — Official Laravel integration for the Claude PHP SDK with service providers, facades, and comprehensive documentation
🔧 Developer Tools & Automation
- dalehurley/phpbot — PHP CLI AI assistant that turns natural-language requests into concrete actions with multi-tier routing, on-device Apple Intelligence support, and auto-created reusable skills
- dalehurley/cross-vendor-dmf — Cross-Vendor Dynamic Model Fusion (DMF) framework for vendor-agnostic AI orchestration using Claude Opus 4.5, OpenAI GPT-5.1, and Google Gemini 3 Pro
🌐 Model Context Protocol (MCP)
- dalehurley/php-mcp-sdk — PHP implementation of the Model Context Protocol (MCP) with complete MCP 2025-06-18 specification support, enabling seamless integration between LLM applications and external data sources
- dalehurley/laravel-php-mcp-sdk — Comprehensive Laravel wrapper around the PHP MCP SDK with full MCP 2025-06-18 specification support, multiple transport types, and Laravel-native integration
📚 Learning Resources
- dalehurley/codewithphp — Comprehensive, open-source learning platform with tutorial-based resources for modern PHP development, featuring hands-on reproducible tutorials across multiple series including AI/ML, Laravel CRM, and more
License
MIT License. See LICENSE for details.


