Weave Code
Code Weaver
Helps Laravel developers discover, compare, and choose open-source packages. See popularity, security, maintainers, and scores at a glance to make better decisions.
Feedback
Share your thoughts, report bugs, or suggest improvements.
Subject
Message

Synapse Core Laravel Package

arnaudmoncondhuy/synapse-core

View on GitHub
Deep Wiki
Context7
docs/guides/custom-agents.md

Créer un agent en code (host-side)

Synapse unifie deux mondes d'agents derrière un seul contrat :

  1. Agents "code" : classes PHP fournies par l'application hôte (ou par Synapse lui-même) qui implémentent AgentInterface. Ce guide couvre ce cas.
  2. Agents "config" : entités SynapseAgent gérées depuis l'admin (système prompt, preset, ton, outils). Voir Tones & Presets.

Les deux sont résolvables par nom via AgentResolver::resolve($name).

!!! info "Alignement symfony/ai (vocabulaire uniquement)" Les noms AgentInterface::call(), Input et Output sont alignés sur symfony/ai. Aucune migration n'est prévue — c'est un alignement de vocabulaire pour laisser une porte ouverte à coût nul. Voir AgentInterface.


1. Écrire l'agent

La façon recommandée est d'étendre AbstractAgent plutôt qu'implémenter AgentInterface directement. AbstractAgent garantit la présence de l'AgentContext, injecte automatiquement le system prompt, et fournit les helpers de traçabilité.

// src/Agent/BulletinAnalyzerAgent.php
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 BulletinAnalyzerAgent extends AbstractAgent
{
    public function __construct(private readonly ChatService $chatService) {}

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

    public function getDescription(): string
    {
        return 'Analyse un bulletin scolaire et produit une synthèse structurée.';
    }

    public function getSystemPrompt(): string
    {
        return 'Tu es un expert en analyse pédagogique. Extrais les points forts, '
             . 'les points à améliorer et formule des conseils constructifs.';
    }

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

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

C'est tout. Aucune configuration à ajouter : tant que votre services.yaml auto-déclare les classes sous src/, Synapse découvre l'agent automatiquement via le tag DI synapse.agent (auto-configuré par le bundle).

!!! note "AbstractAgent vs AgentInterface directement" Vous pouvez également implémenter AgentInterface directement, mais vous devrez alors gérer manuellement l'extraction de l'AgentContext depuis $options['context']. AbstractAgent est préférable car il centralise cette validation et lève une LogicException explicite si le contexte est absent.


2. L'invoquer depuis un contrôleur / un handler / une commande

Injectez AgentResolver (jamais l'agent directement — vous perdriez le contexte) :

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

final class BulletinController extends AbstractController
{
    public function __construct(private readonly AgentResolver $agents) {}

    #[Route('/bulletin/analyze', methods: ['POST'])]
    public function analyze(Request $request): Response
    {
        // Contexte racine avec la profondeur max configurée (synapse.agents.max_depth).
        $context = $this->agents->createRootContext(
            userId: $this->getUser()?->getUserIdentifier(),
            origin: 'direct',
        );

        $agent = $this->agents->resolve('bulletin_analyzer', $context);

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

        return new JsonResponse([
            'answer' => $output->getAnswer(),
            'debug_id' => $output->getDebugId(),
            'usage' => $output->getUsage(),
        ]);
    }
}

3. Composition agent → agent (avec garde-fou)

Un agent peut en invoquer un autre. Le garde-fou de profondeur (synapse.agents.max_depth, défaut : 2 via AgentContext::DEFAULT_MAX_DEPTH) empêche les boucles ou les pyramides infinies. Pour un appel enfant depuis AbstractAgent, utilisez $context directement :

protected function execute(Input $input, AgentContext $context): Output
{
    $parent = $context;

    // Profondeur +1, même user, préserve le workflow englobant.
    $childCtx = $parent->createChild(
        parentRunId: $parent->getRequestId(),
        childOrigin: 'code',
    );

    $summarizer = $this->agents->resolve('document_summarizer', $childCtx);
    $summary = $summarizer->call(Input::ofMessage($input->getMessage()), ['context' => $childCtx]);

    // ... combine avec d'autres étapes
    return Output::ofData(['answer' => $summary->getAnswer()]);
}

Si la profondeur dépasse la limite, AgentResolver::resolve() lève AgentDepthExceededException et dispatche AgentDepthLimitReachedEvent.


4. Collisions de noms

Un agent code et un agent config (BDD) qui partagent le même namel'agent code gagne, un warning est loggé. Deux agents code avec le même name → exception fatale au boot (erreur de programmation à corriger).

Règle pratique : choisissez des noms snake_case spécifiques (bulletin_analyzer, pas analyzer).


5. Traçabilité automatique

Chaque appel d'agent produit un SynapseDebugLog avec, si un AgentContext est fourni via $options['context'] :

  • agent_run_id (UUID de cette exécution logique),
  • parent_run_id (UUID de l'exécution parente — null pour un appel racine),
  • depth (profondeur d'imbrication),
  • origin (direct | code | config | ephemeral | workflow).

Dans l'admin Synapse, la page Debug propose un filtre "Masquer les appels enfants" (racines uniquement par défaut), et la vue détail affiche l'arbre des appels descendants.


Voir aussi

Weaver

How can I help you explore Laravel packages today?

Conversation history is not saved when not logged in.
Prompt
Add packages to context
No packages found.
comsave/common
alecsammon/php-raml-parser
chrome-php/wrench
lendable/composer-license-checker
typhoon/reflection
mesilov/moneyphp-percentage
mike42/gfx-php
bookdown/themes
aura/view
aura/html
aura/cli
povils/phpmnd
nayjest/manipulator
omnipay/tests
psr-mock/http-message-implementation
psr-mock/http-factory-implementation
psr-mock/http-client-implementation
voku/email-check
voku/urlify
rtheunissen/guzzle-log-middleware