Table of Contents

🇬🇧 English version

Voir aussi : Retour à l'index

Limites et contraintes connues

  • InMemoryUnitOfWork n'a par conception aucune étape de persistance durable : les agrégats vivent dans les repositories in-memory et SaveChangesAsync se réduit au dispatch (réel et ordonné) des événements de domaine. Un adapter durable remplacerait cet adapter dans son ensemble — il n'y a plus de promesse de migration EF dans le code.
  • Persistance d'état d'exécution (R3.8) : par défaut, les états d'exécution de crew (ICrewExecutionStateManager) sont in-memory only (pas de reprise après crash). La persistance durable est opt-in : enregistrer un state store de checkpointing (AddOrkeonCheckpointing / AddOrkeonSqliteCheckpointing / AddOrkeonPostgresCheckpointing) et activer AddCrewExecutionStatePersistence(...) (ou la section Orkeon:ExecutionState:Persistence). Une fois activée, les états sont persistés à chaque transition et rechargés/réutilisés après redémarrage — voir Sous-systèmes opt-in. Limites v1 : métadonnées persistées en chaînes invariantes, ToolsUsed non persisté.
  • Le framework cible .NET 10 — version courante 1.0.0-rc.4 (définie dans src/Directory.Build.props). .NET 11 (GA le 10 novembre 2026) n'est pas encore une cible : ci.yml construit et teste la solution avec le SDK 11.x (canal preview, non bloquant jusqu'à la GA) en guise d'alerte précoce, et les deux dotnet tools (orkeon, orkeon-repl) acceptent un runtime majeur plus récent (RollForward=Major) pour qu'une machine n'ayant que .NET 11 puisse les exécuter. Le target framework, global.json et les images de base des conteneurs bougent le jour de la GA, pas avant.
  • Le périmètre fonctionnel est gelé à 16 fournisseurs LLM (quatorze vendeurs et deux agrégateurs — OpenRouter et Mammouth AI, l'unique exception motivée au gel, intégrés documentation d'abord et en attente de leur première campagne), 79 outils intégrés et 6 stores de mémoire tant que de vrais utilisateurs n'en demandent pas davantage : un mainteneur unique porte toute la surface, et chaque ajout est un coût permanent. Pas de 17ᵉ fournisseur : les endpoints qui parlent le dialecte OpenAI n'ont pas besoin de fournisseur (pointez Orkeon:Llm:BaseUrl dessus) ; les outils s'écrivent en scripts .ork.ts ou en plugins. La règle et ce qui reste bienvenu sont dans CONTRIBUTING.fr.md.
  • LlmConfig.ApiKeySecretName est réservée, pas résolue. Le framework ne transforme jamais un nom de secret en clé d'API : chaque garde de fournisseur et chaque chemin de requête lit LlmConfig.ApiKey, donc une config ne portant que ApiKeySecretName est non configurée et tout appel répond qu'une clé d'API est requise. Les fabriques *WithSecret (WithDefaultModelSecret, Gpt35TurboWithSecret, ClaudeWithSecret) enregistrent le nom, rien de plus. Un hôte qui garde ses clés dans un coffre résout le nom lui-même et affecte le résultat à ApiKey ; les chemins supportés pour la fournir sont le fichier de settings (Llm:ApiKey) et l'environnement (ORKEON_Llm__ApiKey). ApiKey portait un attribut [Obsolete] qui orientait les appelants vers ApiKeySecretName jusqu'à la rc.3 ; il a été retiré parce qu'il désignait un chemin inexistant.
  • Le streaming LLM est uniforme à la frontière du framework : les 16 providers héritent du lecteur de streaming SSE/NDJSON de HttpLlmProviderBase (IStreamingLlmProvider). SupportsStreaming n'est pas un true inconditionnel : il suit le fait que le provider soit réellement utilisable (une clé d'API quand le fournisseur en exige une, plus l'endpoint de ressource pour Azure), si bien qu'un provider non configuré déclare false et que tout appelant qui teste la capacité prend le chemin tamponné — celui qui rend la réponse explicite « API key is required » — au lieu d'une branche SSE qui se terminerait sans le moindre chunk. L'exception restante par provider est le /api/chat d'Ollama (voir l'entrée Ollama ci-dessous)
  • FlowEngine et le système de Flows (steps séquentiels, parallèles, décisionnels) existent en infrastructure mais constituent un système d'orchestration alternatif distinct des Crews
  • Défauts DI délibérément minimaux — IAgentPlanner (plan fixe 4 étapes), ITaskDelegator (refuse la délégation), IKnowledgeStore (in-memory, non persistant ; son SearchAsync ignore la requête) et IAgentExecutionService sont des remplaçants qui s'annoncent chacun par un Warning unique nommant la remédiation ; rien n'appelle un LLM ni ne persiste implicitement. Inventaire complet et gestes de remplacement : Comportements par défaut
  • Les outils custom doivent implémenter IBaseTool ou hériter de ToolBase<TReq,TRes>. Le chargement de plugins au runtime existe (opt-in AddOrkeonPlugins(...) — découverte par répertoire, AssemblyLoadContext collectables isolés, voir Plugins) ; limites v1 : les assemblies chargées s'exécutent en pleine confiance (pas de sandbox), et il n'y a ni manifeste de plugin ni hot-reload
  • Plusieurs sous-systèmes (A2A, monitoring backend, NIST, DLP, rate-limiting d'outils, rotation de clés, benchmarking, validation multi-modale, hooks de kickoff, sous-système RAG depuis RAG-01/C6 — AddOrkeonRag + AddOrkeonRagTools depuis RAG-02, plus les opt-ins RAG AddOrkeonOnnxReranker depuis RAG-04 et le transport AddOrkeonRagWebFallback depuis RAG-06) sont opt-in : ils ne sont plus enregistrés par AddOrkeonInfrastructure()/AddOrkeonApplication() et s'activent par leur extension dédiée — voir Sous-systèmes opt-in
  • A2A — la persistance des tâches est opt-in (PUB-08) : enregistrez un state store de checkpointing plus AddOrkeonA2ATaskPersistence(), et GET /a2a/tasks/{id} répond 200 avec le cycle de vie enregistré (404 pour un id inconnu) tandis que DELETE enregistre l'annulation (404 pour un id inconnu ; l'annulation reste consultative — le travail en cours n'est pas interrompu). Sans l'opt-in, GET continue de renvoyer un 501 explicite plutôt que de fabriquer un état. Les écarts plus larges vis-à-vis de la spec v1.0 (bindings, push notifications, cycle de vie à 9 états, A2A-Version) sont inventoriés dans la matrice de conformité A2A.
  • A2A mTLS — A2AClient charge le certificat client (A2ASecurityOptions.ClientCertificatePath) et applique toujours la validation complète du certificat serveur — il n'existe délibérément aucun opt-out « accepter n'importe quel certificat » ; un serveur auto-signé ou de dev local épingle sa CA via TrustedCertificateAuthorities. Côté serveur, quand RequireMutualTls = true, A2AServer n'accepte que les certificats clients qui chaînent vers l'une des TrustedCertificateAuthorities ou correspondent à une empreinte épinglée de TrustedClientCertificateThumbprints (403 sinon) et refuse de démarrer si aucune ancre de confiance n'est configurée (fail-closed). La révocation n'est pas vérifiée (CA privées sans endpoints CRL/OCSP supposées). L'épinglage CA côté client ne couvre que la chaîne inconnue — un mismatch de nom d'hôte n'est jamais contourné. Les requêtes sans schéma d'authentification autorisé sont rejetées (401) quand AllowedAuthSchemes est défini. La terminaison TLS mutuelle réelle requiert un binding HTTPS de HttpListener (réservation HTTP.sys / certificat serveur) configuré au niveau de l'hôte ; l'endpoint de découverte /.well-known/agent.json reste public.
  • La sélection sémantique d'agents (OrkeonApplicationOptions.AgentSelectionStrategy = Embedding) hérite depuis RAG-02/C5 de la chaîne de résolution d'embeddings réelle : le SimpleEmbeddingService basé sur un hachage a été supprimé, et l'IEmbeddingService par défaut adapte le port IEmbeddingProvider (BGE local si AddOrkeonLocalEmbeddings() est enregistré → provider distant via Orkeon:Embeddings → échec explicite InvalidOperationException au premier usage, message actionnable). Plus aucun repli non sémantique silencieux : sans embedding provider, la stratégie Embedding échoue bruyamment — configurer un provider, ou préférer la stratégie Skill (correspondance lexicale, sans dépendance) ou le défaut FirstFit (premier agent disponible)
  • Corrigé depuis RAG-01, rebasé en RAG-02 — le chemin RAG ne se dégrade plus silencieusement : le pipeline d'ingestion (Orkeon.Rag) embedde les chunks à l'ingestion et le document store cherche par similarité vectorielle scorée (les scores survivent de bout en bout jusqu'à ScoredChunk), et le port IEmbeddingProvider se résout BGE local (si AddOrkeonLocalEmbeddings() est enregistré) → provider distant via Orkeon:Embeddings → échec explicite (InvalidOperationException au premier usage, message actionnable). HashBasedEmbeddingProvider est un double de test uniquement — plus jamais résolu implicitement
  • Tool calling natif — Ollama (levé en LLM-07) : OllamaLlmProvider atteint désormais /api/chat — et est câblé avec la stratégie native OpenAI — dès que la conversation déclare des outils, rejoue des appels d'outils, ou transporte une image. Il remet en forme la réponse d'Ollama (message.tool_calls avec arguments en objet JSON, sans tableau choices) dans le corps OpenAI que lit l'unique parser du framework (choices[0].message.tool_calls, arguments en chaîne JSON), et assigne des ids d'appel positionnels puisqu'Ollama n'en émet pas. Limites restantes : tout le reste — conversations simples, génération mono-prompt, streaming — passe encore par /api/generate (le streaming NDJSON et la grammar GBNF n'existent que là), donc le protocole texte de repli reste aux commandes pour les modèles sans support des tools ; le streaming de /api/chat n'est pas implémenté, un appel streamé garde donc le chemin prompt-complétion ; et les images doivent être fournies en octets, puisqu'Ollama prend des payloads base64 nus et ne télécharge jamais une URL distante.
  • Profils RAG adaptive/corrective — les nÅ“uds dépendant d'un LLM se dégradent hors ligne : depuis RAG-06 la route Adaptive-RAG Iterative délègue au pipeline graphe corrective (le repli documenté RAG-05 vers quality est levé ; l'étape route trace delegate=corrective et RagTrace.Route porte la décision). Limites honnêtes restantes : le classifieur de complexité par défaut est l'heuristique déterministe ; le classifieur llm requiert un IChatClient enregistré (sans lui, repli sur l'heuristique avec avertissement) ; et sans vrai LLM le graphe correctif ne peut pas combler un fossé de vocabulaire — en évaluation --offline le stub extractif alimente rewrite_query/evaluate/check_groundedness : le parseur tolérant du grader pêche des mots de grade dans les passages recopiés (verdicts pseudo-aléatoires ; le repli du vérificateur d'ancrage reste sûrement grounded), et une réécriture déclenchée dégénère en le préfixe fixe du stub — une seule probe dénuée de sens partagée par tous les cas — donc les chiffres corrective offline mesurent les garde-fous de la boucle plus cette dégradation, pas la qualité de réécriture, et peuvent tomber sous quality (tableau mesuré et analyse dans examples/rag/eval/README.md ; le mécanisme de bout en bout — verdict de l'évaluateur → réécriture → citation récupérée sur le cas seedé q-007 — est prouvé par CorrectiveRagMechanismSlowTests avec un LLM scripté, étiqueté comme tel). Même note offline qu'avant pour les transformers de requête LLM (multi-query/rag-fusion/hyde) : le chemin mesuré offline est QueryTransform.Mode=none + routage heuristique.
  • Les profils RAG balanced/quality/adaptive exigent les paquets du reranker ONNX : leurs presets activent l'étage de rerank cross-encoder, et le résolveur de profils échoue explicitement à la première requête (InvalidOperationException actionnable) quand Orkeon.Rag.Onnx + Orkeon.Rag.Onnx.Model ne sont pas référencés et qu'AddOrkeonOnnxReranker() n'est pas enregistré. fast (le défaut) et corrective (le graphe corrige en bouclant, pas d'étage de rerank linéaire) fonctionnent sans les paquets ONNX. adaptive en a besoin car sa route SingleShot délègue à balanced.
  • Runtime natif ONNX — les suites qui le chargent : deux suites de tests chargent la bibliothèque native ONNX Runtime — tests/tools/Orkeon.Tools.Embeddings.Local.Tests (embeddings BGE-micro-v2 locaux) et tests/rag/Orkeon.Rag.Onnx.Tests (reranker cross-encoder ms-marco) — et exigent donc une plateforme où cette bibliothèque se charge. Jusqu'au 2026-09-07 cette entrée décrivait aussi un SIGSEGV (code 139) qui emportait le processus après le passage des tests Embeddings.Local, le qualifiait d'artefact de teardown du runtime natif (PUB-17 / SONAR-14), et faisait tolérer exactement cette forme par ci.yml et publish.yml. Ce n'était pas un artefact de teardown. Dispose_Releases_Embedder appelait EmbedBatchAsync sur un provider libéré, et le provider — qui posait un drapeau _disposed qu'il ne relisait jamais — déréférençait la session native libérée : un comportement indéfini, qui apparaissait tantôt comme une OnnxRuntimeException fantaisiste sur un tas encore chaud, tantôt comme un SIGSEGV tuant le processus sur un tas sali, parfois avant même qu'un seul résultat ait été rapporté. La tolérance ne pouvait pas davantage l'attraper, puisque la comptabilité qu'elle comparait venait du processus crashé lui-même. LocalEmbeddingProvider lève désormais ObjectDisposedException depuis EmbedBatchAsync et Dimensions ; la suite exécute ses 11 tests et sort en 0 (17 exécutions d'affilée), les deux étapes CI sont redevenues de simples dotnet test, et integration.yml n'écarte plus le projet. Les deux suites ont par ailleurs été mesurées vertes dans un conteneur le 2026-09-07 : la consigne antérieure de ne les lancer que sur une machine hôte ou en CI est abandonnée.
  • Orkeon.Tools.Embeddings.Local repose sur une pré-release d'un amont archivé — l'opt-in d'embeddings locaux on-device est bâti sur SmartComponents.LocalEmbeddings 0.1.0-preview10148 (épinglé dans Directory.Packages.props), qui est la seule version existante : l'amont n'a jamais publié de version stable, et le dépôt visé par le projectUrl du paquet (dotnet-smartcomponents/smartcomponents) est archivé — le code a déménagé vers dotnet/smartcomponents, qui ne livre toujours pas de 1.0. Attendre un amont stable n'est pas un plan. Le volet licence est tranché et documenté (MIT pour le wrapper comme pour les poids BGE-micro-v2 embarqués, provenance établie à la main dans THIRD-PARTY-NOTICES.md sections 1-2) ; ce qui reste est une contrainte de packaging : NuGet refuse de packager un paquet stable qui dépend d'une pré-release (NU5104). Une 1.0.0 stable d'Orkeon ne peut donc pas porter cette dépendance dans son paquet principal — c'est exactement pour cela que cet opt-in est livré comme paquet Orkeon.Tools.Embeddings.Local à part plutôt que fondu dans le parapluie Orkeon (PUB-25). Conséquence pour un consommateur : référencer cet opt-in fait entrer une dépendance pré-release dans votre propre graphe, et si vous publiez ensuite un paquet stable qui en dépend, vous heurtez NU5104 à votre tour. Échappatoires, de la moins coûteuse à la plus coûteuse : (a) ne pas référencer l'opt-in — le port IEmbeddingProvider résout alors un provider distant depuis Orkeon:Embeddings (voir l'entrée sélection sémantique d'agents ci-dessus) ; (b) implémenter IEmbeddingProvider sur votre propre pile d'embeddings ; (c) vendorer le wrapper d'inférence amont (MIT, donc autorisé) dans une assembly que vous contrôlez et retirer la référence de paquet. Décision (2026-09-11) : la préversion est assumée, pas vendorisée. Orkeon.Tools.Embeddings.Local sera livré en 1.0.0 stable dépendant de SmartComponents.LocalEmbeddings 0.1.0-preview10148 ; NU5104 est réduit au silence dans ce seul projet de packaging (l'ombrelle, Orkeon.Tools et la CLI ne portent jamais la dépendance), un pack stable de toute la ligne réussit, et le README dit en une ligne quel opt-in reste sur une préversion et pourquoi. Vendoriser le wrapper d'inférence (issue (c)) reste possible pour un consommateur qui exige un graphe entièrement stable ; cela ne vaut pas une seconde copie d'un wrapper MIT pour le framework lui-même. Les dotnet tools orkeon / orkeon-repl et les installeurs ne sont pas concernés par NU5104 — un paquet tool est self-contained et c'est sa propre version que NuGet contrôle.
  • Le repli web RAG est un double opt-in, désactivé par défaut : le nÅ“ud web_fallback du graphe correctif ne s'exécute que quand les deux interrupteurs sont activés — Orkeon:Rag:Corrective:WebFallback (politique : le graphe a-t-il le droit de sortir du store local) et Orkeon:Rag:WebFallback (transport : endpoint SearxNG, AddOrkeonRagWebFallback n'enregistre le retriever que si activé avec un endpoint non vide). Chaque page téléchargée est filtrée par PromptInjectionDocumentValidator avant d'entrer dans le working set (les pages Rejected n'entrent jamais ; Suspicious suit la politique configurée) — voir Sécurité
  • MCP — client/serveur dual-era depuis PUB-07 : le client sonde avec server/discover et parle la révision moderne stateless (2026-07-28 — _meta par requête, renégociation de version sur UnsupportedProtocolVersionError) ou retombe sur le handshake initialize legacy (2025-11-25, 2025-06-18, 2024-11-05 ; 2025-03-26 est exclue à dessein — seule révision qui impose le batching JSON-RPC, que cette implémentation ne parle pas) ; le serveur sert les deux ères simultanément. Limites assumées de la surface moderne : pas de subscriptions/listen (notifications poussées par le serveur), pas de requêtes multi-aller-retour (un résultat input_required remonte comme erreur d'outil), pas d'autorisation MCP (OAuth) ; le transport HTTP est le mode réponse JSON du Streamable HTTP — en-têtes modernes envoyés et corps SSE déballé jusqu'à son message final, mais les flux à l'initiative du serveur ne sont pas consommés. L'interop contre les serveurs de référence (MCP Inspector) n'a pas encore tourné — suivie dans la note de clôture de PUB-07.
  • LanceDB — LanceDbMemoryProvider cible désormais un serveur LanceDB Cloud/Enterprise distant (protocole Lance REST Namespace, payloads Arrow IPC) ; il n'existe plus de store JSON local. Limites de l'API distante (détails dans Système de mémoire) : SearchAsync exige un index FTS sur content (créé automatiquement à la création de la table, sinon l'erreur serveur est propagée — jamais simulée localement) ; HybridSearchAsync émet deux requêtes serveur (classements _distance/_score côté serveur) et fusionne localement les deux listes classées, comme les rerankers des SDK officiels ; l'API delete ne renvoyant pas de compteur, DeleteAsync/UpdateAsync font une requête d'existence préalable pour préserver leur contrat booléen ; la conversion score = 1 − _distance n'est pertinente que pour la métrique cosine (défaut)
  • spawn_agent n'est enregistré par aucune racine de composition livrée — la classe (SpawnAgentTool, self-spawn sous budget du mode autonome) est livrée dans Orkeon.Infrastructure, mais ni orkeon run ni le REPL ne la mettent dans le registre de tools ; un hôte qui veut le self-spawn l'enregistre explicitement avec un IAgentFactory. Voir docs/fr/tools/inventory.md et docs/fr/orchestration/autonomous.md.
  • Les espaces de noms de mounts par exécution ne sont entrés que par orkeon-host — IFileSystemScope / AsyncLocalFileSystemScope (Domain) donnent à un flux d'exécution son propre FileSystemRegistry, et FileSystemService consulte cette surcharge ambiante à chaque opération : deux crews dans le même processus peuvent donc adresser chacun son /output sur des dossiers physiques différents — le rejet des chemins virtuels en double est par instance, jamais global. orkeon-host est la seule racine de composition livrée qui entre un namespace, par crew hébergé, depuis Orkeon:Host:Crews:*:Mounts (CrewRunner). Tous les autres points d'entrée — orkeon run, orkeon forge, orkeon rag — tournent sur le registre de boot, un jeu de mounts plat par processus dont les chemins virtuels doivent être globalement uniques : d'où le fait qu'ils réservent leurs racines contre le fichier de settings au lieu de les cloisonner, et que l'hôte renomme encore les crews auxquels il n'accorde aucun mount (/crews, /crews-1, …). Un hôte qui veut son propre namespace le compose avec ScopedMountComposition.ForExecution : entrer dans un scope remplace le jeu de boot au lieu de fusionner avec lui, donc la composition reporte les mounts de boot et laisse ceux de l'exécution les masquer aux chemins qu'ils revendiquent — les abandonner emporte /llm-logs et /sandbox avec le run, et emporte la racine depuis laquelle le runner charge le crew. Un second portail, process-global, subsiste dans tous les cas — les racines autorisées d'IPathValidator sont figées au boot, donc un dossier accordé hors de la racine du workspace résout dans le namespace puis se fait refuser par le validateur ; PathSecurity:AdditionalAllowedDirectories est l'endroit où un opérateur l'élargit. Voir Conformité VFS.