DEV Community

Cover image for 🙏 Quand l'orchestration garantit l'exécution, et non la valeur
Mathieu Ledru
Mathieu Ledru

Posted on

🙏 Quand l'orchestration garantit l'exécution, et non la valeur

Que se passe-t-il lorsqu'un système d'orchestration exécute un travail sans valeur ajoutée ?

Il ne s'agit pas d'une question rhétorique. C'est une expérience d'ingénierie.

J'ai mis en place un pipeline multi-agents sur Darkwood Flow. Cinq agents effectuent un travail utile. Puis j'en ai ajouté un sixième.

Il n'a aucun outil. Il ne connaît rien du code source. Il n'examine jamais les demandes de fusion. Il ne corrige jamais les bugs. Il se contente de dire :

Merci.

ou:

Ça a l'air bien.

Le système d'orchestration en était parfaitement satisfait.

C'est ce qui s'est avéré être la partie intéressante.

L'activité n'est pas un débit

Cinq agents participent à l'expérience et effectuent des tâches utiles : récupération de la documentation, lecture des métadonnées Git/PR, exploration des fonctionnalités PHP, extraction des principes de l'IA open source et calcul d'un cadre de coût. Un sixième agent, ThanksAgent, n'analyse ni ne transforme aucune donnée, ne tient compte d'aucun contexte et renvoie un signal social constant.

Et pourtant, le sixième agent encore :

  • est programmĂ©
  • occupe la concurrence
  • gĂ©nère des Ă©vĂ©nements Flow
  • apparaĂ®t dans l'observabilitĂ©
  • consomme des messages
  • contribue Ă  la surcharge d'orchestration
  • donne l'impression d'activitĂ©

Valeur produite = 0.

C’est là la principale distinction de l’article :

L'activité ne correspond pas au débit.

L'exécution n'est pas une valeur.

L'orchestration peut garantir l'exécution des tâches. Elle ne peut pas garantir leur utilité.

Et la phrase qui devrait figurer à côté de chaque tableau de bord multi-agents :

Le planificateur ne peut pas faire la distinction entre le travail productif et le travail performatif.

Si nous voulons que les systèmes d'agents optimisent la valeur plutôt que l'activité, la valeur doit faire partie du modèle observable de l'environnement d'exécution.

Cinq agents utiles et un inutile

La démo se trouve dans un projet console Symfony 8 : thanks-agent sous content/thanks-agent. Il ne s'agit pas d'une présentation du framework Symfony, mais d'une démonstration de chargement de flux Darkwood avec une interface en ligne de commande minimale.

Chaque agent est une implémentation de l'interface JobInterface de Flow. Les pondérations des valeurs sont explicites :

Agent Valeur RĂ´le
FetchDocumentationAgent +25 Charger des extraits de code source
GithubAgent +40 Métadonnées PR / git
PHPAgent +30 Sonde de capacité PHP 8.6
MozillaAgent +20 Principes de l'IA open source
CostAgent +15 Analyse coûts-résultats
SummaryAgent +15 Synthèse Fan-in
ThanksAgent +0 Toujours "Merci." / "Ça a l'air bien." / "Bon travail."

Le logiciel Thanks Agent est en lui-mĂŞme d'un ennui presque agressif :

final class ThanksAgent extends AbstractAgent
{
    private const RESPONSES = ['Thanks.', 'Looks good.', 'Good job.'];

    protected function produce(AgentPacket $packet): array
    {
        $output = self::RESPONSES[array_rand(self::RESPONSES)];

        return [
            'output' => $output,
            'tokens' => 2,
            'messages' => 28, // noise: many empty acknowledgements
            'didWork' => false,
            'payload' => [
                'analyzed' => false,
                'transformed' => false,
                'expertise' => null,
                'contextHeld' => false,
            ],
        ];
    }
}
Enter fullscreen mode Exit fullscreen mode

Sa disponibilité est excellente. Il n'a jamais entraîné de régression. Il n'a jamais rien produit non plus.

Effectuez la comparaison :

php bin/console thanks-agent:compare --both --save
Enter fullscreen mode Exit fullscreen mode

La machine le planifiera, le mesurera et le signalera — avec le même sérieux qu'elle accorde aux agents qui travaillent réellement.

Que consomme exactement un agent ?

C'est là que la blague devient ingénierie.

Le « coût » ne se résume pas à un seul chiffre. Dans un environnement multi-agents, un participant peut consommer :

Dimension Ce que cela signifie
Emplacement du planificateur En cours de développement sous MaxIpStrategy
Heure réelle Latence visible par l'homme
Attente d'E/S Flux, sockets, HTTP
Processeur Analyse syntaxique, notation, sérialisation
Jetons Utilisation du modèle (simulée dans cette démo)
Messages Accusés de réception informels, échanges sur les outils
Contexte Tranches de mémoire / invite / index
Journalisation / traçage Volume d'observabilité
Examen humain File d'attente d'attention et de décision
Capacité de validation Tests, validations, acceptation

L'agent Thanks utilise très peu de ressources CPU ou de jetons. Il consomme néanmoins des emplacements de planification, des messages, des événements et de l'énergie narrative.

Voilà le schéma organisationnel en miniature : la visibilité et la signalisation sont plus faciles à mesurer que les résultats, les systèmes tendent donc à récompenser ce qui est bruyant, présent et constamment « vert ».

Les humains le font. Les équipes le font. L'intégration continue le fait. Les agents le feront — sauf si les modèles d'exécution le valorisent explicitement.

Darkwood Flow tel qu'il existe aujourd'hui

Avant d'affirmer que Flow « résout le problème des agents », précisez ce qu'est Flow.

Darkwood Flow (darkwood/flow, PHP ≥ 8.5) est un pipeline asynchrone linéaire inspiré de FBP :

flowchart LR
    IP[Ip] --> Job[Job]
    Job --> Driver[Driver]
    Driver --> Events[PUSH PULL POP ASYNC POOL]
    Events --> Job
Enter fullscreen mode Exit fullscreen mode

Modèle conceptuel :

Ip → Job → Driver

Vous composez des étapes avec FlowFactory, envoyez des paquets d'informations et utilisez await() jusqu'à ce que le flux soit vidé. La concurrence se traduit par de nombreuses adresses IP en transit, limitées par des stratégies telles que MaxIpStrategy — et non par un DAG classique de branches et de jointures.

Flow n'est pas ReactPHP. Ce n'est pas Amp. Il ne prétend pas les remplacer. Des pilotes optionnels peuvent être installés sur ces backends ; la question intéressante pour Darkwood est :

De quel minimum de machinerie d'exécution Flow a-t-il besoin pour orchestrer un véritable travail PHP asynchrone ?

Éléments principaux utilisés dans la démo :

  • Flow\FlowFactory
  • Flow\Ip
  • Flow\JobInterface
  • Flow\Driver\FiberDriver (par dĂ©faut)
  • Flow\Driver\StreamSelectDriver (tâches du gĂ©nĂ©rateur + stream_select)
  • Flow\IpStrategy\MaxIpStrategy
  • ÉvĂ©nements : PUSH, PULL, POP, ASYNC, POOL — et, nouveautĂ©, COMPLETE / ERROR

Deux facteurs sont importants pour cet article.

FiberDriver exécute chaque tâche dans une instance PHP Fiber. Les délais coopératifs appellent Fiber::suspend(). Il s'agit du chemin par défaut pour thanks-agent:compare. Un problème pratique est apparu lors des mesures : la résolution des délais Fiber dans le pilote actuel est suffisamment grossière pour que les tâches d'agent nécessitant de nombreuses attentes se regroupent près des limites d'une seconde. Il s'agit d'une caractéristique du pilote, et non d'une affirmation concernant l'« intelligence de l'agent ».

StreamSelectDriver est différent : les tâches peuvent renvoyer un Generator qui produit des jetons d'attente — waitReadable, waitWritable, waitDelay — et le pilote multiplexe ces attentes avec la fonction native stream_select(). C'est le chemin emprunté par le Flow Runner de thanks-agent:bench-io et par l'option --driver=stream_select lors de la comparaison. Il est considéré comme expérimental dans la documentation de Flow, et c'est normal : l'expérience Thanks Agent nécessite une véritable attente de disponibilité, et non une simulation avec usleep.

MaxIpStrategy gère la concurrence. Elle encapsule une autre stratégie IP (FIFO linéaire par défaut) et refuse de traiter des paquets supplémentaires tant que processing >= max. Il s'agit de concurrence d'exécution, et non de capacité de supervision. Confondre les deux peut mener à une situation où six terminaux et un seul utilisateur épuisé.

flowchart TB
    subgraph fanOut [Fan-out via multiple Ips]
        Dispatch[Push one Ip per agent]
        Dispatch --> Doc[FetchDocumentation]
        Dispatch --> Gh[Github]
        Dispatch --> Php[PHP]
        Dispatch --> Moz[Mozilla]
        Dispatch --> Cost[Cost]
        Dispatch --> Thanks[Thanks]
    end
    subgraph fanIn [Fan-in outside the graph API]
        Doc --> Store[RunStore]
        Gh --> Store
        Php --> Store
        Moz --> Store
        Cost --> Store
        Thanks --> Store
        Store --> Summary[SummaryAgent]
        Summary --> Board[Scoreboard]
    end
Enter fullscreen mode Exit fullscreen mode

Cette honnêteté est importante. La démo ressemble à une fonction de dispersion/rassemblement. Flow n'expose pas encore de moteur DAG général. Prétendre le contraire reviendrait à appliquer le comportement de Thanks Agent à la documentation.

Flow ne sait pas ce que signifie « utile »

Un environnement d'exécution comprend la sémantique opérationnelle :

  • pousser / tirer / Ă©clater
  • rĂ©partition asynchrone
  • profondeur de la piscine
  • succès / erreur
  • durĂ©e

Il ne sait pas automatiquement si la chaîne « Merci. » a créé une valeur.

La valeur doit donc être modélisée explicitement.

Dans la démo, App\Model\AgentId::valueWeight() et App\Instrumentation\ValueRegistry attribuent des scores de production. didWork: false force ThanksAgent à obtenir un score nul même si quelqu'un oublie ultérieurement la table de pondération.

Intégré à Flow pour être réutilisé :

namespace Flow\Instrumentation;

final readonly class ValueTag
{
    public function __construct(
        public string $label,
        public int $value,
    ) {}
}
Enter fullscreen mode Exit fullscreen mode

ValueTag est une primitive. Le tableau de bord des agents, qui contient des avis, reste dans l'application. Cette séparation est intentionnelle : tout ce qui est spécifique aux agents reste dans thanks-agent ; tout ce qui concerne l'intégration des jeunes diplômés en comptabilité dans Flow.

Coût sans valeur

Coûts simulés, planification réelle

Information importante : dans cette expérience, les coûts des jetons sont simulés. Le registre de démonstration utilise un taux fixe.

public const RATE_PER_1K_TOKENS = 0.002;
Enter fullscreen mode Exit fullscreen mode

Les E/S correspondent à des fichiers de configuration et à des paires de sockets différées, et non à des factures API LLM en temps réel. Les temps d'exécution et le nombre d'événements Flow sont mesurés. Les montants indiqués sont fournis à titre indicatif afin de mieux visualiser l'ampleur du problème.

La démo connecte l'instrumentation au bus d'événements existant de Flow — elle n'invente pas de système de télémétrie parallèle :

final class MetricsSubscriber implements EventSubscriberInterface
{
    public static function getSubscribedEvents(): array
    {
        return [
            Event::PUSH => ['onPush', -100],
            Event::PULL => ['onPull', -100],
            Event::POP => ['onPop', -100],
            Event::ASYNC => ['onAsync', -100],
        ];
    }

    public function onPull(PullEvent $event): void
    {
        // FiberDriver polls PULL in a tight loop; only meter productive pulls.
        if ([] !== $event->getIps()) {
            $this->ledger->recordPull();
        }
    }
}
Enter fullscreen mode Exit fullscreen mode

Cette garde onPull est en elle-même une leçon : si vous considérez chaque sondage de boucle active comme du « travail », votre tableau de bord FinOps devient une fiction inspirée de la boucle d’événements.

Exécution de la comparaison (PHP 8.5.4, FiberDriver)

À partir des rapports générés sous var/reports/ (compare-*-20260808-144317.json) :

Métrique Sans remerciements Avec remerciements Delta
Valeur 145 145 0
Coût (simulé) 0,012474 0,022094 +0,00962
Messages 19 48 +29
Jetons (simulés) 887 897 +10
Temps d'exécution (ms) 958,47 1002,48 +44
Flux push/pull/pop/asynchrone 5 / 5 / 5 / 5 6 / 6 / 6 / 6 +1 chacun

ThanksAgent seul dans cette exécution :

Champ Valeur
Valeur 0
Messages 28
Jetons 2
Coût 0,008904
a travaillé faux
Sortie "Bon travail."

Résultats utiles inchangés. Ressources augmentées. Variation de valeur nulle.

Voilà toute la thèse sous forme de tableau.

Le verdict affiché au tableau d'affichage n'est pas un argument marketing ; il est imprimé par le commandement :

Le sixième agent n'apporte aucune valeur ajoutée ; il consomme toujours des ressources en matière de planification, de contexte, de journalisation et d'observabilité.

Coût par requête vs coût par résultat utile

La facturation basée sur les requêtes brutes est une unité de mesure imprécise. La facture d'une IA reflète davantage le produit du débit, de la consommation et du comportement du tokenizer (et des nouvelles tentatives) que le prix affiché sur une grille tarifaire. L'unité pertinente est le coût par résultat positif, et non le coût par requête.

ThanksAgent rend la pathologie évidente :

  • Le coĂ»t de la demande peut ĂŞtre minime
  • rĂ©sultats utiles = 0
  • le coĂ»t par rĂ©sultat utile tend vers l'infini

Un agent bon marché qui ne produit rien n'est pas bon marché. C'est du bruit subventionné.

Le registre Flow promu présente la même forme de métrique :

$ledger->recordComplete('GithubAgent', tokens: 100);
$ledger->recordError('CostAgent', tokens: 32);

$snapshot = $ledger->snapshot();
// totals.costPerSuccessfulOutcome = totalCost / successes
Enter fullscreen mode Exit fullscreen mode

La supervision est également une ressource rare

Une fois que l'expérience peut exécuter six agents sans problème, une autre question se pose : l'ajout de capacité d'exécution finit par déplacer le goulot d'étranglement.

Distinguer soigneusement :

Capacité Ce qu'elle mesure
Concurrence des modèles Nombre d'appels de modèles possibles
Concurrence d'exécution Nombre de tâches que Flow maintient en cours d'exécution
Débit utile Résultats acceptés / fusionnés / validés
Supervision humaine Nombre de contextes ouverts qu'une personne peut suivre
Capacité de validation Bande passante d'acceptation/rejet automatisée

Les agents ne se fatiguent pas. Les superviseurs, si. Déplacer le travail vers des environnements de test distants permet de redistribuer la supervision au sein d'une équipe ; cela ne crée pas une attention infinie.

La métaphore organisationnelle émerge de l'environnement d'exécution lui-même. Si le planificateur accepte sans problème un participant se contentant de dire « Ça a l'air bien », les humains seront finalement sollicités pour surveiller ce participant également — ou pour l'ignorer tant qu'il occupe des ressources (travail en cours, journaux, tableaux de bord). Les agents parallèles créent un inventaire : branches, demandes de fusion, questions ouvertes, contextes inachevés. L'humain devient le bus de données. L'attente n'est pas forcément du gaspillage ; c'est l'intolérance aux terminaux inactifs qui engendre la fatigue.

Analysez la situation selon la théorie des contraintes : lorsqu’on supprime un goulot d’étranglement, les stocks s’accumulent devant le suivant. Le parallélisme des agents réduit le temps d’attente des machines, mais peut accroître celui des opérateurs. La loi de Little sert d’avertissement informel : plus de travail en cours à un rythme de service fixe signifie des délais de livraison plus longs, sans pour autant prétendre que cette démonstration a été réalisée à l’échelle d’une usine. Ce que l’expérience montre est plus simple : on peut ajouter un agent qui augmente le nombre de messages et d’événements de planification sans modifier la valeur globale.

Conséquence opérationnelle pour la politique d'orchestration : privilégier une tâche bien définie avec un processus d'acceptation automatisé plutôt que quatre tâches vagues qui renvoient toutes les questions à la même personne. Le modèle multi-agents n'est viable que si la validation ne l'est pas.

Cessez de mesurer le nombre de tâches en cours d'exécution. Mesurez plutôt le nombre de décisions en attente.

C’est pourquoi Flow livre désormais un budget de ressources, et non une superstition :

use Flow\Budget\SupervisionBudget;

$budget = new SupervisionBudget(5);

if (!$budget->tryAcquire()) {
    // reassign, queue, or refuse — do not silently spawn more agents
}

$concurrency = $budget->clampConcurrency($requested);
Enter fullscreen mode Exit fullscreen mode

max = 5 est une valeur par défaut pratique pour les démonstrations, et non une loi fondamentale. L'abstraction importe plus que la valeur : la concurrence est une ressource limitée et supervisée, et l'environnement d'exécution doit rendre l'épuisement explicite.

Feuille de route (non déployée) : Métriques de profondeur de la file d’attente de décision — questions ouvertes en attente de traitement humain, histogrammes des temps de réponse, contrôles de validation avant diffusion. La démo démontre la nécessité ; Flow ne mesure pas encore le temps d’attente humain.

Disperser/rassembler sans prétendre que Flow est un DAG

L'atelier d'orchestration de la démo est le véritable cœur de l'architecture :

$flow = $this->flowFactory->create(static function () use ($execute, $errorJob, $concurrency, $dispatcher) {
    yield [
        'job' => $execute,
        'errorJob' => $errorJob,
        'ipStrategy' => new MaxIpStrategy($concurrency),
        'dispatcher' => $dispatcher,
    ];
}, ['driver' => $driver]);

foreach ($agentIds as $agentId) {
    $flow(new Ip(new AgentPacket($agentId, $runId)));
}
$flow->await();

// Fan-in outside the driver loop
$summaryAgent->executeSync(new AgentPacket(AgentId::Summary, $runId));
Enter fullscreen mode Exit fullscreen mode

Ce que cela fait concrètement :

  1. Dispersion — une adresse IP par identité d'agent
  2. Parallélisme — MaxIpStrategy limite le travail en cours
  3. Sac partagé — chaque agent écrit un AgentResult dans RunStore
  4. Rejoindre — après await(), SummaryAgent lit le sac de manière synchrone.
  5. Tableau de bord — rapport au niveau de l'application, et non un nœud du graphe de flux
flowchart LR
    Ips[Multiple Ips] --> Stage[ExecuteAgentJob]
    Stage --> Store[RunStore]
    Store --> Summary[SummaryAgent executeSync]
    Summary --> Report[RunReport]
Enter fullscreen mode Exit fullscreen mode

Pourquoi ne pas parler de DAG ? Parce que la topologie publique de Flow reste une liste chaînée d'étapes. L'état de l'application correspond à l'entrée. C'est suffisant pour une démonstration, mais insuffisant pour gérer les jointures typées, les échecs partiels par branche ou la compilation de graphes réutilisables.

Une véritable API de jointure/DAG serait justifiée lorsque :

  • Plusieurs producteurs doivent se synchroniser avec un schĂ©ma, et non avec un ensemble modifiable partagĂ©.
  • L'annulation doit se propager Ă  toutes les succursales
  • Le mĂŞme graphique est rĂ©utilisĂ© pour tous les produits, et non pour une seule dĂ©monstration.

En attendant, l'honnêteté vaut mieux que les schémas idéalistes.

L'asynchrone attend efficacement

La deuxième commande :

php bin/console thanks-agent:bench-io --save
Enter fullscreen mode Exit fullscreen mode

compare quatre exécuteurs sur les mêmes six tâches nécessitant beaucoup d'attente (cinq utiles + Merci), en utilisant des sockets différés synthétiques sur PHP 8.5.4 :

Coureur Mur ms Valeur Statut
séquentiel 599,9 130 ligne de base
stream_select 166,8 130 ~3,6× amélioration du mur
php86_poll — — ignoré (Io\Poll\Context indisponible sur 8.5.4)
flow_stream_select 178,7 130 Flow StreamSelectDriver (~+12 ms par rapport à la sélection brute)

Contexte : E/S synthétiques locales, coûts de jetons simulés, ThanksAgent inclus, valeur toujours 130 (pas de résumé dans l'ensemble de tâches du banc d'E/S).

L'exécution séquentielle est facile à comprendre et coûteuse en temps réel lorsque la tâche est soumise à des temps d'attente. stream_select() multiplexe l'état de disponibilité. Le StreamSelectDriver de Flow encapsule les tâches génératrices qui génèrent des jetons d'attente (waitReadable / waitWritable / waitDelay).

L'API Poll native de PHP 8.6 (Io\Poll\*) est la primitive la plus intéressante à long terme : elle utilise des backends de type epoll/kqueue lorsqu'ils sont disponibles, typés avec Time\Duration. Point important : l'interrogation n'est pas une boucle d'événements. La planification reste du ressort de l'utilisateur.

La réponse de Flow est une interface légère — et non un clone d'Amp :

interface PollerInterface
{
    /**
     * @param list<resource> $read
     * @param list<resource> $write
     * @return array{0: list<resource>, 1: list<resource>}
     */
    public function poll(array $read, array $write, float $timeoutSeconds): array;

    public function name(): string;
}
Enter fullscreen mode Exit fullscreen mode

Implémentations actuelles : StreamSelectPoller, NativePollPoll (utilisé par défaut en l’absence de Poll dans PHP 8.6). Flow\IO\Duration fait le lien avec Time\Duration lorsqu’il est présent. clamp() est utilisé dans SupervisionBudget lorsqu’il est disponible.

flowchart TB
    Jobs[Jobs] --> Scheduler[Flow Driver / Scheduler]
    Scheduler --> Poller[PollerInterface]
    Poller --> OS[OS mux stream_select or Io_Poll]
    OS --> Ready[Ready I/O]
    Ready --> Scheduler
    Scheduler --> Cont[Job continuation]
Enter fullscreen mode Exit fullscreen mode

Conclusion modeste seulement :

Dans ce test de performance local, le multiplexage des tâches à forte attente a permis de réduire le temps d'exécution d'environ 600 ms en mode séquentiel à environ 167 ms avec stream_select, tandis que le pilote StreamSelect de Flow est resté dans le même ordre de grandeur (environ 179 ms). La fonction Poll de PHP 8.6 n'a pas été testée car elle n'est pas disponible sur la version 8.5.4.

Aucune affirmation selon laquelle Flow serait « plus rapide que React/Amp ». Ces bibliothèques n'ont pas été testées.

L'interrogation n'est pas non plus une valeur

Voici le rappel éditorial.

Un meilleur système de sondage peut exécuter plus rapidement des tâches inutiles.

Améliorer epoll, Fibers, les limites de concurrence ou la densité des agents cloud ne résout pas le problème de Thanks Agent. Cela ne fait qu'amplifier ce que le système optimise.

Les performances amplifient tout ce que le système optimise.

Si l'objectif d'optimisation est l'activité, vous obtenez plus d'activité.

Si l'objectif d'optimisation est d'obtenir des résultats positifs, vous obtenez un débit utile.

Le test d'E/S attribue toujours la valeur 0 Ă  ThanksAgent tout en lui allouant un emplacement de planification. Multiplexeur plus rapide, mĂŞme nombre d'emplacements vides.

Le chemin malheureux doit être facturé

La pull request Symfony AI #2363 est un exemple technique externe, et non une dépendance de Darkwood. Voici l'information pertinente :

Les flux de données peuvent inclure des métadonnées d'utilisation et générer une erreur (par exemple, en cas de dépassement du débit maximal). Si la comptabilité ne se finalise que dans le cas d'un fonctionnement normal, le fournisseur vous a quand même facturé et votre comptabilité est erronée.

Orientation de la PR : terminaux mutuellement exclusifs — complet vs erreur — avec écouteurs d’erreur explicites.

Flow reflète désormais ce cycle de vie au niveau de la couche événementielle :

  • Flow\Event::COMPLETE → CompleteEvent
  • Flow\Event::ERROR → ErrorEvent
  • Flow\Instrumentation\CostLedger::recordComplete / recordError
  • CostLedgerSubscriber peut Ă©couter les deux
flowchart TB
    Start[Start] --> Exec[Execute]
    Exec --> Success[Success]
    Exec --> Failure[Failure]
    Exec --> Abandon[Abandon / Cancel]
    Success --> Complete[CompleteEvent]
    Failure --> Error[ErrorEvent]
    Abandon --> Hole[Accounting hole today]
    Complete --> Ledger[CostLedger]
    Error --> Ledger
Enter fullscreen mode Exit fullscreen mode

Commande de démonstration :

php bin/console thanks-agent:run --simulate-error --with-thanks
Enter fullscreen mode Exit fullscreen mode

L'agent de coûts est contraint de suivre le chemin de défaillance ; les jetons sont toujours enregistrés. La valeur utile diminue ; le registre ne prétend pas que la défaillance soit sans conséquence.

Limitation explicitement mentionnée : l’abandon ou l’annulation d’une tâche sans exception constitue toujours une faille de conception. La démo ne contient que App\Orchestration\CancellationTicket, un stub, et non le cycle de vie complet de Flow. Tant que l’abandon ne sera pas géré comme un terminal à part entière, FinOps risque de ne pas détecter ce cas. La PR de Symfony AI est attentive à ce type de faille ; nous devrions l’être également.

Le contexte est aussi une ressource

Un agent de codage ne « comprend » pas comme par magie l'intégralité d'un dépôt. Il comprend la partie qu'il indexe et dont il fournit les outils. Une couverture linguistique incomplète produit des résultats incohérents. La qualité de l'index et la qualité du modèle sont des variables distinctes.

Pour Flow, l'affirmation « agent exécuté avec succès » est insuffisante. Une attribution d'exécution sérieuse nécessite en définitive :

  • modèle
  • outils
  • contexte / fraĂ®cheur de la carte de code
  • rapide
  • politique de vĂ©rification
  • coĂ»t
  • rĂ©sultat

Non livré : un portage de CodeMap. Non livré : un AgentRouter qui considère le prix comme un champ parmi d’autres, tels que le taux d’achèvement, la latence, la modalité, le coût de vérification et l’éligibilité à la concurrence. Ces éléments restent des éléments de la feuille de route, imposés par l’expérimentation : une fois que l’on peut mesurer la valeur par rapport au coût, on se rend compte à quel point le simple fait de dire « exécution réussie » est peu informatif.

La neutralité vis-à-vis des fournisseurs est l'autre contrainte majeure. Il est préférable de maîtriser la couche d'orchestration plutôt que de louer une pile verticale fermée. Le rôle de Flow est de rester neutre vis-à-vis des fournisseurs : un environnement Symfony natif où les modèles sont interchangeables, et non l'identité du produit.

Coût par résultat positif

Associer l'économie des résultats, le routage et ThanksAgent.

Stratégie Apparence bon marché Souvent le cas
Prix le plus bas par jeton Sur la ligne de facture Erreur si les nouvelles tentatives explosent
Le plus petit modèle toujours Sur la grille tarifaire Erreur si le taux d'achèvement s'effondre
La plupart des agents en parallèle Sur le tableau de bord Problème si la file d'attente de décision explose
ThanksAgent Sur jetons (2) Infini par résultat utile

Un modèle plus coûteux qui s'exécute correctement une seule fois peut surpasser un modèle bon marché qui échoue trois fois. Une exécution séquentielle plus lente avec validation automatisée peut surpasser un essaim parallèle qui confie cinq demandes de fusion à un seul humain.

Le champ costPerSuccessfulOutcome de la démo simule des calculs. C'est la forme qui importe pour les systèmes de production : terminaux de comptage, tentatives de récupération d'attributs, division par les résultats pertinents.

Ce que cela a changé dans Darkwood Flow

Déjà implémenté / promu dans Flow

Vérifié dans darkwood/src/Darkwood/Component/Flow/ :

Primitif RĂ´le
Flux\Instrumentation\Liste des coûts Comptabilité des coûts en fonction des résultats
Flow\Instrumentation\CostLedgerSubscriber Événement → pont de registre
Flow\Instrumentation\ValueTag Étiquette de valeur de production
Flow\Event::COMPLETE / ERROR Cycle de vie du terminal
Flow\Event\CompleteEvent / ErrorEvent Charges utiles du terminal
Flux\Budget\Budget de supervision En cours / porte de supervision
Flow\IO\PollerInterface Abstraction de multiplexage légère
Flow\IO\StreamSelectPoller stream_select backend
Flow\IO\NativePollPoller Sondage PHP 8.6 avec solution de repli
Flow\IO\Duration Assistant secondes → Durée native si disponible

Documentation : Page d’instrumentation des flux dans l’arborescence de documentation des composants. Les tests couvrent le registre, le budget et le comportement de repli du système d’interrogation.

Une nuance importante concernant le câblage est à noter pour les lecteurs qui clonent la démo : l'application Symfony utilise toujours principalement App\Instrumentation\MetricsSubscriber pour la mesure via PUSH/PULL/POP/ASYNC. Les événements COMPLETE/ERROR et CostLedgerSubscriber de Flow sont des primitives prêtes à être adoptées ; le chemin --simulate-error de la démo enregistre actuellement les erreurs dans le registre de l'application. Il s'agit là de la migration en cours, où « la démo prouve que Flow est le leader », et non d'une affirmation selon laquelle chaque terminal de l'interface de ligne de commande (CLI) déclenche déjà un événement CompleteEvent.

L'application de fonctions partielles de PHP 8.6 mérite une brève mention : lier une configuration fixe aux fonctions appelables du pipeline ($invoke = $platform->invoke($model, ?)) pourrait simplifier le langage Flow DSL une fois la version 8.6 devenue la norme. Le code source n'en dépend pas encore. L'intégrer de force serait purement formel, voire paradoxal.

Démontré mais toujours au niveau de l'application (thanks-agent)

  • Tâches d'agent (ThanksAgent, …)
  • ValueRegistry / expĂ©rience utilisateur du tableau de bord
  • Sac de dispersion RunStore
  • App\Instrumentation\CostLedger + MetricsSubscriber (version dĂ©mo, compatible avec les agents)
  • Interface de ligne de commande : thanks-agent:compare, thanks-agent:bench-io, thanks-agent:run
  • Ébauche de CancellationTicket

Travaux futurs (feuille de route, et non affirmations de présence)

  • Pilote Poll natif PHP 8.6 intĂ©grĂ© Ă  la boucle await (au-delĂ  de l'assistant Poller)
  • Politique d'annulation et d'abandon de terminal de première classe
  • MĂ©triques de la file d'attente de dĂ©cision
  • Graphe rĂ©el / nĹ“uds de jonction si la demande de dispersion/rassemblement se renforce
  • AgentRouter, CodeMap, ModelPort
  • Application de fonctions partielles sous forme de DSL Ă  pipeline allĂ©gĂ© lorsque PHP 8.6 est la version de base

Ne surestimez pas les objectifs. L'objectif de Thanks Agent était d'imposer un premier niveau de vérité : la valeur et le coût font partie intégrante du vocabulaire de l'environnement d'exécution.

Fermeture

Nous savons déjà comment faire courir les agents.

Les fibres optiques, les collecteurs de données, les environnements de test cloud et les interfaces de ligne de commande multi-agents seront de plus en plus performants pour générer des données dynamiques. Ces données sont faciles à visualiser sur un tableau de bord. Elles sont faciles à mettre en valeur. Par conception, un agent Thanks optimise les données dynamiques.

Le problème le plus difficile consiste à décider quel travail devrait exister — et à prouver, avec des chiffres que l'environnement d'exécution peut observer, que ce travail a produit des résultats qui justifient l'utilisation des ressources.

Lorsqu'un système d'orchestration accepte sans problème d'exécuter un participant qui ne contribue en rien, la prochaine optimisation ne consistera probablement pas en l'ajout d'un autre agent.

C'est une meilleure définition de la valeur.

Si nous voulons que les systèmes d'agents optimisent la valeur plutôt que l'activité, la valeur doit faire partie du modèle observable de l'environnement d'exécution.

Après Thanks Agent, la mission de Darkwood Flow est plus claire : continuer à orchestrer le travail PHP asynchrone avec le moins de machinerie possible — et refuser de considérer les échanges entre agents comme du débit.

Références

Matériel technique

Frédéric Bouchery — J'ai arrêté d'exécuter plusieurs agents (En cours de développement, chaînes de validation, décision humaine comme contrainte)

Les idées concernant les index de code source, le routage des agents et la variance des factures d'IA circulent largement dans l'industrie ; cet essai les utilise comme contraintes d'ingénierie, et non comme commentaires sur une publication particulière.

Artefacts de Darkwood

Top comments (0)