Aller au contenu
Nibiru docsv0.9.2

Plugin d'agent

Un outil d'agent utilisant le style ReAct. Étendez l'Outil pour lui donner toute capacité PHP que vous pouvez écrire.

Stable Reading time ~ 3 min Edit on GitHub

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.

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(*)…)
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()`.)

Nibiru embarque trois :

OutilCe qu’il fait
Tools\PdoQueryLecture 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\HttpGetEffectue une requête GET sur une URL HTTP/HTTPS avec des en-têtes optionnels. Retourne le corps, tronqué à 8 Ko.
Tools\FileReadLit 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 task
echo $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.

É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.

$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 ».

  • Les blocs PdoQuery bloquent 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 HttpGet permettent n’importe quelle URL par défaut. Verrouillez via une liste autorisée dans [AI] http_allowed_hosts[] (prévu), ou écrivez une sous-classe RestrictedHttpGet qui filtre les URLs.
  • Les FileRead bloquent ... Il est limité à la racine de l’application.
  • Nombre maximal d’itérations. agent.max_iterations = 6 dans 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.
  • Oublier withTools(). Sans outils, l’agent est simplement un Chat ré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.”