1 points par GN⁺ 2 시간 전 | 1 commentaires | Partager sur WhatsApp
  • Forge est une couche de fiabilité pour l’appel d’outils des LLM auto-hébergés, conçue pour améliorer la stabilité des petits modèles locaux dans des workflows d’agents en plusieurs étapes
  • Ses fonctions clés comprennent le rescue parsing pour récupérer les appels d’outils erronés, le déclenchement de nouvelles tentatives, l’imposition d’étapes obligatoires, un budget de tokens tenant compte de la VRAM et une compression de contexte hiérarchique
  • La meilleure configuration auto-hébergée actuelle, Ministral-3 8B Instruct Q8 sur llama-server, atteint 86,5 % sur 26 scénarios d’évaluation et 76 % sur le niveau le plus difficile
  • Trois modes d’utilisation sont proposés : confier toute la boucle d’agent à WorkflowRunner, insérer le middleware Guardrails dans une boucle d’orchestration existante, ou l’appliquer de manière transparente via un serveur proxy compatible OpenAI
  • WorkflowRunner gère le prompt système, l’exécution des outils, la compression de contexte et les garde-fous, tandis que SlotWorker ajoute une file de priorité et une préemption automatique aux slots d’inférence GPU partagés
  • Le serveur proxy se lance avec python -m forge.proxy et applique les garde-fous entre des clients compatibles OpenAI comme opencode, Continue ou aider, et un serveur de modèles local
  • Pour les requêtes avec outils, le proxy injecte automatiquement un outil synthétique respond, afin que le modèle appelle respond(message="...") au lieu de produire du texte brut ; il le retire ensuite dans la réponse pour que le client voie une réponse texte normale
  • Les backends pris en charge sont Ollama, llama-server (llama.cpp), Llamafile et Anthropic ; llama-server offre les meilleures performances et le plus de contrôle, Ollama la configuration la plus simple, Llamafile l’exécution en binaire unique, et Anthropic sert de référence frontier et de support pour les workflows hybrides
  • L’installation se fait avec pip install forge-guardrails, le client Anthropic s’ajoute avec pip install "forge-guardrails[anthropic]", et les prérequis sont Python 3.12+ ainsi qu’un backend LLM en cours d’exécution
  • Le harnais d’évaluation comprend 26 scénarios pour mesurer la fiabilité des appels d’outils en plusieurs étapes selon les combinaisons modèle/backend ; il est divisé entre les niveaux de référence OG-18 et 8 niveaux advanced_reasoning
  • La configuration de test inclut 865 tests unitaires déterministes ne nécessitant aucun backend LLM, ainsi qu’un harnais d’évaluation destiné aux backends réels
  • Le framework de garde-fous Forge et son étude d’ablation ont été publiés sous le titre Forge: A Reliability Layer for Self-Hosted LLM Tool-Calling, avec une licence MIT

1 commentaires

 
GN⁺ 2 시간 전
Avis sur Hacker News
  • J’aime travailler dans ce domaine et je trouve ça utile, mais j’évite les LLM basés sur le cloud et j’utilise surtout des modèles locaux de 4B à 30A3B paramètres
    Du coup, même si je manque de repères sur les performances ou la précision des tout derniers LLM, je pense bien connaître le niveau qu’on peut attendre des modèles locaux et leurs goulots d’étranglement
    J’ai parcouru l’article rapidement et lu le résumé, et même s’il y est dit qu’un simple réglage peut rendre les choses 10 fois plus rapides ou plus lentes, les métriques et les données semblent presque uniquement centrées sur la précision. Il faut parler de la vitesse
    En particulier, dans les workflows agentiques et avec des modèles locaux, la précision des appels de fonctions/outils n’est plus, à mon avis, un gros problème depuis environ QwenCoder3 sur les 6 à 12 derniers mois ; le vrai enjeu, c’est la gestion du contexte et l’impact sur le temps. Si l’agent change souvent de prompt, les optimisations temporelles comme le prompt caching sautent
    Là, on dirait qu’on ajoute encore des couches et des wrappers comme des garde-fous et des retries, et sur des modèles locaux, surtout pour un usage agentique, ça peut devenir inutilisable à cause de la latence
    Désolé si c’est déjà traité de front, mais il y a si peu de discussion sur l’impact temporel que ça donne l’impression soit de masquer l’ampleur réelle de l’amélioration, soit de l’exagérer. J’aimerais entendre un retour sur la vitesse. Le fait que d’autres ne soulèvent pas ça m’inquiète un peu aussi : soit je m’y prends mal, soit personne n’utilise vraiment de modèles locaux

  • Je répète depuis longtemps qu’avec un harness correct, même de petits modèles locaux peuvent être étonnamment bons
    Si on a un système qui peut tout essayer, il suffit d’empêcher qu’il produise un mauvais résultat au passage pour qu’il finisse par trouver la bonne réponse

    • Le problème, c’est qu’on obtient une qualité comparable à celle d’un junior à qui on donne un temps illimité et à qui on dit de continuer à essayer différentes approches jusqu’à atteindre l’objectif
      Si la tâche est assez complexe, même les modèles récents ont ce problème, et il est encore plus amplifié avec les petits modèles
    • J’aime bien cette façon de poser le problème. En faisant ce travail, les petits modèles m’ont paru assez impressionnants
      Leur raisonnement est souvent tout à fait correct et, dans bien des cas, suffisant. Il faut juste parfois les remettre sur les rails, puis ils se débrouillent
    • Si j’ai bien compris, le modèle peut réussir parce qu’il sait reconnaître quand il a tort
  • Je suis ravi de voir quelqu’un construire bien mieux quelque chose que je voulais prendre le temps de faire moi-même. Une question : y a-t-il, par exemple, un potentiel de parallélisation dans une boucle de retry ?
    Les modèles locaux peuvent généralement gérer correctement un nombre limité de requêtes simultanées, de l’ordre de quelques dizaines, même sur du matériel grand public, et ça peut faire monter les tokens/seconde effectifs d’un facteur supérieur à 10
    Je réfléchis depuis un moment à des workflows qui exploiteraient ça, et « corrige cette erreur » semble pouvoir être un cas d’usage, même si ce n’est pas parfait. Curieux d’avoir ton avis

  • J’ai quelques idées dans cet espace et je suis en train de les intégrer à mon harness. Mon harness est assez spécialisé, donc je ne sais pas à quel point c’est généralisable
    Je découpe le problème en une exécution planifiée, et je fournis un plan initial qui inclut des objectifs explicites, comme quels outils l’agent d’exécution doit appeler et à quoi ressemble une exécution réussie. Le harness exécute ensuite ce plan dans l’ordre
    Pour chaque étape impliquant un appel d’outil, je décompose cet appel en composants. Le harness demande à l’agent des valeurs de paramètres valides pour les arguments actuels de l’outil, et la définition de l’outil contient un validateur pour chaque argument. Si la validation échoue, le harness rembobine la conversation et injecte la raison de l’échec dans la tentative suivante
    Une fois qu’une réponse valide est obtenue pour un argument, on passe au suivant, et quand tous les arguments sont remplis, on appelle l’outil. On fournit aussi les attentes initiales de l’agent, les valeurs réelles et les erreurs survenues, puis on lui demande s’il est satisfait du résultat
    S’il ne l’est pas, l’agent donne une raison, le harness rembobine la conversation, injecte la raison du retry, puis recommence tout le processus d’appel d’outil depuis le début
    L’agent peut demander une replanification s’il découvre des défauts dans le plan initial, et le harness essaie aussi de replanifier s’il y a trop d’échecs consécutifs
    Cette approche est assez efficace pour réduire les échecs d’appel d’outil. Un avantage, c’est que le sous-agent reçoit un historique de conversation parfait, sans erreurs. En revanche, je n’ai pas encore benchmarké si le taux réel d’achèvement des tâches est meilleur

    • J’ai fait des expérimentations avec une philosophie similaire sur un harness de codage agentique basé sur de petits modèles, construit au-dessus de forge
      Concernant le rembobinage de conversation, j’ai implémenté un repliage des appels d’outil similaire pour l’agent principal, c’est-à-dire celui qui parle à l’utilisateur. Une fois la tâche terminée, les traces des appels d’outil étaient repliées pour garder un contexte propre ; c’était davantage une question d’hygiène que de taille
      La partie où le harness pousse le modèle dans ses retranchements est un peu différente, et je n’ai pas essayé cette approche. Forge s’appuie sur l’autocorrection du modèle pour éviter un mode d’erreur dédié, mais si on pouvait abstraire et automatiser le questionnement à partir de choses comme un schéma, ça semble possible
      Globalement, j’aime bien l’idée d’un historique de conversation propre, mais pour des outils avec beaucoup d’arguments, ça peut générer bien plus d’allers-retours que de simplement « laisser échouer une première fois puis donner un coup de pouce ». Cela dit, c’est une idée intéressante pour des scénarios ou des tâches plus difficiles
    • J’utilise Strix Halo et, comme c’est lent sur les contextes longs, j’avais pensé à la même approche. Avec ça, on devrait pouvoir garder un contexte de moins de 10 000 tokens
      Si on peut dépasser les 50k tokens/seconde avec de petits modèles, ce serait assez énorme
      Mais pour l’instant, je suis pris par le boulot et d’autres projets, donc je n’ai testé que quelques dizaines de prompts pour voir si c’était faisable
    • Par curiosité, je bricole ça moi-même avec gemma4, et je suis surpris de voir jusqu’où ça va, bien plus que je ne l’aurais cru
  • Excellent. Je ne peux pas utiliser l’inférence locale pour l’instant à cause des coûts, mais quand j’utilise de petits modèles via OpenRouter, les appels d’outil m’inquiètent
    Je développe Dokimasia (do-kee-ma-see-ah), un framework de tests d’arguments orienté pytest, et j’aimerais bien avoir ton avis : https://github.com/deevus/dokimasia
    Les tests d’arguments ne sont peut-être pas ce dont Forge a besoin, mais comme tu construis des outils IA en profondeur, je me suis dit que tu aurais peut-être un avis

    • Idée intéressante. En substance, ça ressemble à une formalisation d’une couche d’abstraction destinée à tester plusieurs types d’intégrations présents dans l’écosystème IA, comme MCP ou les skills
      Ça semble être un niveau au-dessus de Forge, davantage orienté vers le test des workflows réels et des points d’intégration qui y apparaissent, par exemple si tel ou tel outil fournit un accès MCP
      Je ne pense pas qu’il y aurait de gros problèmes à superposer les deux
      Ce qui m’intéresse, c’est la façon dont tu gères la non-déterminisme de ces modèles. Parfois ils font correctement l’appel d’outil, parfois ils crachent du JSON invalide. La suite de tests fait-elle plusieurs tentatives ?
  • L’ambiguïté autour des appels d’outil existe même à l’échelle des modèles de pointe. J’utilise Claude Code, Codex et Gemini CLI tous les jours en parallèle pour développer, et le mode d’échec le plus fréquent, c’est quand grep/find se termine avec le code de sortie 1, donc aucune correspondance trouvée
    Le modèle lit ça non pas comme « la recherche s’est exécutée et n’a rien trouvé », mais comme « l’outil a échoué », puis il abandonne ou retente avec une syntaxe à peine modifiée au lieu d’élargir la recherche
    Une couche de retry avec petits coups de pouce correspond presque exactement, en 1:1, à ce que je fais manuellement plusieurs fois par heure. Quelque chose comme : « non, l’outil n’a pas échoué ; c’est juste qu’il n’y a pas ce motif dans ce fichier. Essaie plutôt X »
    Encoder cela au niveau du framework semble être la bonne direction
    Je me demande aussi si tu as vu si ce type de garde-fous réduit l’écart avec les petits modèles de pointe sur les tâches longues. Intuitivement, j’ai du mal à croire qu’un écart du genre 87→99 sur Sonnet se maintienne au-delà d’environ 50 étapes ; à partir de là, la dérive du contexte doit sans doute dominer davantage que la sémantique du retry

    • Au moins sur les grands modèles de pointe, oui, ils gardent clairement l’avantage à ce stade. Je n’ai simplement pas eu le temps de formaliser ces résultats
      Pour donner un indice utile, forge s’intéresse techniquement à l’exécution des appels d’outil, pas à la qualité intrinsèque du modèle. La vraie réponse, c’est ceci
      Sur les petits modèles de l’ordre de 14B, le facteur limitant était l’attention effective. Même quand tout tient largement dans la fenêtre de contexte d’entraînement, on commence à voir une dégradation après un certain point. Je n’ai pas de chiffre précis, mais des modèles comme Opus peuvent tenir beaucoup plus longtemps à ce seuil
      J’ai aussi construit une fonctionnalité de repliage de l’historique des messages d’appels d’outil, que je pourrais un jour utiliser directement dans forge. En gros, elle compresse intelligemment l’historique des messages pour que le modèle perde moins le fil
      Malgré cela, la suite d’évaluation de codage de mon harness agentique contient des tâches de refactoring et d’ajout de fonctionnalités, toutes réalisées dans de vrais dépôts sandboxés. De petits modèles peuvent quand même mener ce type de tâches jusqu’à 50 à 60 appels d’outil. En revanche, je ne leur confierais probablement pas plus de deux tâches de ce genre dans la même session
  • C’est un peu hors sujet, mais si tu travailles chez Texas Instruments, je me demande si tu pourrais découvrir ce qu’il est advenu du statut de propriété intellectuelle de la machine Lisp TI Explorer
    Je sais qui possède la propriété intellectuelle de Genera, mais je n’ai jamais réussi à savoir pour l’OS Lisp de TI

    • Qui détient la propriété intellectuelle de Genera ?
  • Pour ceux qui réfléchissent plus largement à la pile de « sécurité agentique », cette direction me semble complémentaire d’outils comme kontext-cli de Kontext (github.com/kontext-dev/kontext-cli) ou OneCLI (github.com/onecli/onecli)

  • Il y a ce passage : « les mêmes poids Mistral-Nemo 12B ont 7 % de précision avec l’appel de fonction natif de llama-server, contre 83 % en mode prompt dans Llamafile »
    Je pensais que Llamafile n’était qu’un simple empaquetage du modèle et de llama.cpp dans un binaire unique ; est-ce que cette différence vient du fait que Llamafile injecte un prompt système par défaut, tandis que l’endpoint brut de llama-server est appelé sans harness ?
    Ça ressemble à une comparaison entre des pommes et une tarte aux pommes, avec des ingrédients manquants entre les deux

    • Ça m’a surpris aussi. L’article donnait un exemple extrême, mais réel. Dans ce cas précis, il est probable que le template natif d’appel de fonction ait simplement fonctionné
      Mais ça n’explique pas l’écart d’environ +4 points entre le prompt de Lamaserver et llamafile, ni l’écart d’environ +30 points pour Ollama, qui se situe presque au milieu entre llamaserver natif et llamafile
      Le backend de serving a eu un impact sur presque toutes les familles de modèles, et c’est un sujet dont je n’avais en réalité presque jamais vu parler
  • C’est vraiment une excellente direction. On obtient des résultats absurdement bons même avec des modèles bonsaï en 1 bit, et ça fonctionne aussi très bien avec lmstudio
    Maintenant, brancher une 7900XTX dans une machine de récup, la poser au sous-sol, lui balancer des objectifs délirants puis l’oublier devient un scénario totalement réaliste