Plugin d'agent
Un outil d'agent utilisant le style ReAct. Étendez l'Outil pour lui donner toute capacité PHP que vous pouvez écrire.
Le plugin Agent vous permet de donner à un modèle LLM la capacité d’agir — d’exécuter des requêtes SQL, d’appeler des points de terminaison HTTP, de lire des fichiers ou de faire n’importe quoi que vous pouvez exprimer comme une méthode PHP. Il exécute une boucle de style ReAct : pensez → appel d’outil → observez → répétez → répondez.
Cinq lignes, un agent
Section intitulée « Cinq lignes, un agent »use Nibiru\Module\Ai\Ai;use Nibiru\Module\Ai\Plugin\Tools\PdoQuery;
$ai = new Ai();echo $ai->agent() ->withTools([new PdoQuery()]) ->run('How many active users do we have?');// → "We have 1,247 active users." (after the agent ran SELECT count(*)…)Comment ça fonctionne
Section intitulée « Comment ça fonctionne »user task ↓LLM gets system prompt with tool definitions ↓LLM emits ```tool {"tool":"pdo_query","args":{"sql":"SELECT…"}}``` ↓Agent runs the tool, captures result ↓LLM gets observation, decides: more tools or final answer? ↓"FINAL: 1,247 active users."Le protocole utilise un sentinelle JSON encadrée — \“tool {…}```— que tout modèle peut produire. Aucune API native d'appel d'outil n'est requise, donc cela fonctionne sur tous les modèles Ollama par défaut. (Les modèles qui prennent en charge l'appel natif d'un outil peuvent être intégrés via une sous-classe qui remplaceparseToolCall()`.)
Outils intégrés
Section intitulée « Outils intégrés »Nibiru embarque trois :
| Outil | Ce qu’il fait |
|---|---|
Tools\PdoQuery | Lecture seule unique SELECT sur la base de données de l’application. Bloque INSERT/UPDATE/DELETE/DROP/TRUNCATE/ALTER. Retourne jusqu’à 50 lignes sous forme de JSON. |
Tools\HttpGet | Effectue une requête GET sur une URL HTTP/HTTPS avec des en-têtes optionnels. Retourne le corps, tronqué à 8 Ko. |
Tools\FileRead | Lit un fichier de projet par chemin relatif. Bloque la traversée ... Retourne jusqu’à 8 Ko. |
use Nibiru\Module\Ai\Plugin\Tools;
$agent = $ai->agent()->withTools([ new Tools\PdoQuery(), new Tools\HttpGet(), new Tools\FileRead(),]);
// Multi-step taskecho $agent->run( 'Read application/controller/loginController.php and tell me ' . 'whether it implements rate limiting.');L’agent appellera file_read avec le chemin, observera la source et répondra en fonction de ce qu’il a vraiment vu — pas sur ce qu’il imagine.
Écrire un outil personnalisé
Section intitulée « Écrire un outil personnalisé »Étendre Tool:
namespace App\AiTools;
use Nibiru\Module\Ai\Plugin\Tool;
class StripeRefund extends Tool{ public function name(): string { return 'stripe_refund'; }
public function description(): string { return 'Issue a Stripe refund for a charge ID.'; }
public function schema(): array { return [ 'charge_id' => [ 'type' => 'string', 'description' => 'A Stripe charge ID, e.g. ch_3K…', 'required' => true, ], 'amount_cents' => [ 'type' => 'integer', 'description' => 'Amount to refund in cents. Omit for full refund.', 'required' => false, ], ]; }
public function execute(array $args): mixed { $stripe = new \Stripe\StripeClient(getenv('STRIPE_SECRET_KEY')); $refund = $stripe->refunds->create(array_filter([ 'charge' => $args['charge_id'], 'amount' => $args['amount_cents'] ?? null, ])); return json_encode([ 'refund_id' => $refund->id, 'status' => $refund->status, 'amount' => $refund->amount, ]); }}Puis branchez-le :
$ai->agent() ->withTools([new \App\AiTools\StripeRefund(), new Tools\PdoQuery()]) ->run('Refund order #4421 — they were charged twice.');L’agent utilisera pdo_query pour trouver la charge, puis appellera stripe_refund avec cet identifiant de charge.
Examen de la trace
Section intitulée « Examen de la trace »$agent = $ai->agent()->withTools([new Tools\PdoQuery()]);$answer = $agent->run('How many products in the gold-plating category?');
foreach ($agent->trace() as $step) { echo "Step {$step['step']}: action={$step['action']}\n obs={$step['observation']}\n";}Utile pour le débogage, les traces d’audit ou la construction d’une interface utilisateur « montrer votre travail ».
Sécurité
Section intitulée « Sécurité »- Les blocs
PdoQuerybloquent les écritures. Si vous avez besoin d’accès en écriture, créez une sous-classe plus privilégiée avec un fil de trame d’audit. Ne levez pas la restriction en lecture seule dans l’outil intégré. - Les
HttpGetpermettent n’importe quelle URL par défaut. Verrouillez via une liste autorisée dans[AI] http_allowed_hosts[](prévu), ou écrivez une sous-classeRestrictedHttpGetqui filtre les URLs. - Les
FileReadbloquent... Il est limité à la racine de l’application. - Nombre maximal d’itérations.
agent.max_iterations = 6dans le fichier INI empêche les boucles infinies. Augmentez avec prudence. - Délai d’expiration de l’outil.
agent.tool_timeout = 30(secondes). Un outil qui bloque ne bloquera pas la requête indéfiniment.
Trépieds courants
Section intitulée « Trépieds courants »- Oublier
withTools(). Sans outils, l’agent est simplement unChatrégulier. - Laisser l’agent voir les secrets. Ne mettez jamais de clés API, de mots de passe bruts ou d’informations personnelles identifiables (PII) dans la réponse d’un outil — le modèle reçoit la chaîne complète.
- Longues sorties d’outils. Chaque observation est ajoutée à la conversation. Un outil qui déverse 50 Ko épuisera rapidement le contexte. Les outils intégrés sont limités à 8 Ko ; faites de même avec vos outils personnalisés.
- Pas d’appel d’outil dans la réponse = réponse finale. Si le modèle produit une réponse finale qui ressemble à un appel d’outil mais ne valide pas, l’agent la traite comme finale. Soyez explicite dans l’invite : “Sortez un appel d’outil OU une réponse finale, jamais les deux.”