Aller au contenu

AgentInterface

L'interface AgentInterface définit un agent IA capable d'accomplir des tâches complexes impliquant potentiellement plusieurs appels LLM, des boucles de raisonnement ou l'orchestration de sous-systèmes.

À la différence d'un AiToolInterface (fonction simple appelée par le LLM), un agent est invoqué directement par l'application pour accomplir un objectif de haut niveau.

Namespace

ArnaudMoncondhuy\SynapseCore\Contract\AgentInterface

Contrat complet

use ArnaudMoncondhuy\SynapseCore\Agent\Input;
use ArnaudMoncondhuy\SynapseCore\Agent\Output;

interface AgentInterface
{
    public function getName(): string;
    public function getLabel(): string;
    public function getDescription(): string;
    public function call(Input $input, array $options = []): Output;
}

Méthodes

Méthode Rôle
getName(): string Identifiant unique de l'agent (recommandé : snake_case, ex : 'preset_validator').
getLabel(): string Libellé court lisible (affiché dans l'admin). AbstractAgent fournit une implémentation par défaut (snake_case → Title Case).
getDescription(): string Description en langage naturel — utilisée dans l'admin et pour l'auto-documentation.
call(Input, array): Output Exécute la logique de l'agent. Retourne un Output structuré (réponse texte, données, usage, debugId, ...).

Alignement symfony/ai (vocabulaire uniquement, pas de migration)

Les noms call(), Input et Output sont volontairement alignés sur Symfony\AI\Agent\AgentInterface / Symfony\AI\Agent\Input / Symfony\AI\Agent\Output.

Pas de migration prévue

C'est un alignement de vocabulaire, pas un chemin de migration. symfony/ai est encore en développement et aucune adoption n'est planifiée. L'intérêt est de ne pas construire une "deuxième réalité" qui serait douloureuse à rapprocher plus tard si le jour vient. Une réévaluation éventuelle n'est pas attendue avant au moins un an, et même à ce moment-là rien n'est décidé.

Écarts assumés :

  • getLabel() et getDescription() sont des ajouts Synapse (utiles pour l'admin UI), absents de symfony/ai.
  • Le contexte d'exécution (AgentContext : traçabilité, profondeur, budget) est transporté via $options['context'], pas en paramètre typé. Cela garde la signature call() call-compatible mot pour mot avec symfony/ai.

AgentInterface vs SynapseAgent

AgentInterface est le contrat PHP pour les agents "code" (classes fournies par le bundle ou l'application hôte, découvertes par auto-configuration DI). SynapseAgent est l'entité Doctrine pour les agents "config" (système prompt, preset, ton, outils configurés depuis l'admin). Les deux mondes sont unifiés derrière le même contrat via AgentResolver et la classe d'adaptation ConfiguredAgent.


Cas d'usage typiques

  • Analyse multi-documents complexe
  • Validation d'un preset par simulation (PresetValidatorAgent — exemple interne du bundle)
  • Génération de rapports structurés après plusieurs étapes de réflexion
  • Orchestration de sous-agents (via AgentResolver + AgentContext::createChild(), dans la limite de synapse.agents.max_depth)

Exemple : Agent d'analyse de document

La façon recommandée est d'étendre AbstractAgent (qui garantit l'AgentContext et fournit buildAskOptions()) :

namespace App\Agent;

use ArnaudMoncondhuy\SynapseCore\Agent\AbstractAgent;
use ArnaudMoncondhuy\SynapseCore\Agent\AgentContext;
use ArnaudMoncondhuy\SynapseCore\Agent\Input;
use ArnaudMoncondhuy\SynapseCore\Agent\Output;
use ArnaudMoncondhuy\SynapseCore\Engine\ChatService;

final class DocumentAnalyzerAgent extends AbstractAgent
{
    public function __construct(private readonly ChatService $chatService) {}

    public function getName(): string
    {
        return 'document_analyzer';
    }

    public function getDescription(): string
    {
        return 'Analyse un document et en extrait les points clés, les risques et les actions suggérées.';
    }

    public function getSystemPrompt(): string
    {
        return 'Analyse le document fourni et retourne : '
             . '1) les points clés 2) les risques 3) les actions suggérées.';
    }

    protected function execute(Input $input, AgentContext $context): Output
    {
        $result = $this->chatService->ask(
            $input->getMessage(),
            $this->buildAskOptions(['stateless' => true]),
            $input->getAttachments(),
        );

        return Output::fromChatServiceResult($result);
    }
}

Exécution programmatique depuis l'application hôte

use ArnaudMoncondhuy\SynapseCore\Agent\AgentResolver;
use ArnaudMoncondhuy\SynapseCore\Agent\Input;

public function __construct(private readonly AgentResolver $agents) {}

public function analyze(string $document): array
{
    $context = $this->agents->createRootContext(userId: 'user-42', origin: 'direct');
    $agent = $this->agents->resolve('document_analyzer', $context);

    $output = $agent->call(
        Input::ofMessage($document),
        ['context' => $context],
    );

    return $output->getData();
}

Enregistrement automatique

Rien à déclarer côté hôte : toute classe qui implémente AgentInterface et qui se trouve dans les services auto-découverts (src/ par défaut sous services.yaml) est taggée automatiquement synapse.agent via registerForAutoconfiguration() du bundle. Le CodeAgentRegistry la prend en compte, et AgentResolver::resolve($name) sait la retourner.

Voir le guide Custom Agents pour un exemple complet.


Voir aussi