Keyboard shortcuts

Press or to navigate between chapters

Press S or / to search in the book

Press ? to show this help

Press Esc to hide this help

Vue d’ensemble du protocole Ctx

À chaque invocation d’un agent, le runtime injecte un objet ctx dans la méthode appelée. C’est l’unique surface par laquelle l’agent accède au backend Apollia : LLM, mémoire, outils, autres agents, secrets, événements. Tout passe par là.

Ctx n’est pas une classe mais un typing.Protocol. L’agent dépend d’un contrat, pas d’une implémentation. En production, le runtime injecte un objet PyO3 piloté par le Rust ; en test, apollia.testing.mock injecte un faux ctx qui implémente la même surface (cf. chapitre 24).

from apollia import agent, skill
from apollia.types import Ctx

@agent(name="demo", version="0.1.0", description="Demo agent.")
class Demo:
    @skill("demo.do", description="Pattern d'utilisation de ctx.")
    async def do(self, query: str, ctx: Ctx) -> dict:
        response = await ctx.llm.chat(system="You are helpful.", user=query)
        ctx.logger.info("llm done", extra={"latency_ms": response.latency_ms})
        await ctx.memory.record(f"queried: {query}", importance=0.4)
        return {"answer": response.content}

Les 14 services

ctx expose exactement 14 services. Aucun attribut « magique » au niveau racine en dehors de cette liste.

ServiceTypeRôleChapitre
ctx.llmLlmProxyGénération, streaming, embeddings11
ctx.memoryMemoryInterfaceMémoire épisodique, sémantique, procédurale, FTS512
ctx.toolsToolProxyOutils natifs Apollia et serveurs MCP13
ctx.a2aA2AInterfaceAppel d’autres agents, discovery14
ctx.datasourcesDatasourcesInterfaceYAML déclarés dans le manifeste15
ctx.templatesTemplatesInterfaceTemplates Jinja2 du package15
ctx.secretsSecretsInterfaceLecture seule de credentials16
ctx.eventsEventsInterfaceStreaming UI, observabilité ReAct17
ctx.loggerlogging.LoggerLogging stdlib piped vers Rust tracing17
ctx.budgetBudgetViewVue lecture seule du StepBudget17
ctx.notifyNotifyInterfaceNotifications desktop, webhook18
ctx.profileProfileInterfaceProfil utilisateur canonique18
ctx.workspaceWorkspaceContextRègles et sections d’APOLLIA.md18
ctx.sttSttInterfaceSpeech-to-Text18

À cela s’ajoute la fonction libre apollia.react(ctx, system, user, tools=..., max_steps=...) qui n’est pas un service de ctx mais utilise plusieurs services en interne (cf. chapitre 14).


Pourquoi un Protocol

Trois bénéfices concrets :

  1. Autocomplete IDE. Tapez ctx. dans n’importe quel IDE qui comprend Python : les 14 services apparaissent, chacun avec ses méthodes typées.
  2. mypy --strict passe sur un agent moyen. Aucun # type: ignore ni getattr(...) défensif.
  3. Mock testing trivial. Le mock fourni par apollia.testing.mock implémente le Protocol sans héritage. Vous pouvez aussi écrire votre propre class FakeLlm qui n’expose que les deux méthodes que votre test utilise.

Le runtime Rust est tenu d’exposer exactement ces 14 services. Toute dérive entre la définition Python et l’implémentation Rust est détectée au boot par le validateur (cf. chapitre 27).


Catégoriser pour mémoriser

Les 14 services se rangent dans 5 catégories :

  • IA et raisonnement : ctx.llm, plus la fonction libre apollia.react.
  • Mémoire et profil utilisateur : ctx.memory, ctx.profile.
  • Outils et A2A : ctx.tools, ctx.a2a.
  • Données et contenu de l’agent : ctx.datasources, ctx.templates, ctx.secrets.
  • Observabilité et I/O annexes : ctx.events, ctx.logger, ctx.budget, ctx.notify, ctx.workspace, ctx.stt.

Cette taxonomie est non-normative. Elle aide juste à savoir où chercher.


Que faire si un service manque

Tous les services sont toujours injectés. Le cas où ctx.notify ou ctx.stt n’existerait pas n’arrive jamais en production : si le runtime ne pouvait pas fournir un service (configuration cassée, version incompatible), il refuserait de démarrer la tâche.

En test, un service peut être absent si votre mock customisé ne l’implémente pas. Dans ce cas, l’appel lève une AttributeError claire au runtime du test. Préférez apollia.testing.mock(MyAgent) qui injecte les 14 services en stubs.


Anti-patterns

Ne pas stocker ctx dans un attribut de l’agent (self.ctx = ctx). Chaque invocation reçoit son propre ctx lié à la tâche en cours ; le réutiliser hors de la tâche d’origine produit des comportements indéterminés (budget incorrect, mémoire fuyant entre tâches, événements émis sur la mauvaise session).

Ne pas importer un service en haut du fichier (from apollia.context.llm import LlmProxy). Ces modules définissent uniquement le Protocol ; ils n’ont aucune implémentation. Utilisez les attributs de ctx.

Ne pas dupliquer une fonctionnalité que ctx expose déjà. Par exemple, ne ré-implémentez pas un cache LLM : ctx.llm peut s’appuyer sur le cache du runtime configuré globalement. De même pour la mémoire, la recherche, et l’A2A.


ADRs

  • ADR-101 : Ctx exhaustif et typé via Protocol
  • ADR-098 : Decorator-first (le contrat qui injecte ctx)

(ADRs disponibles prochainement, cf. l’encadré “ADRs et wiki” en introduction.)