Accueil / Articles / Notes pratiques : Tout ce qu’il vous faut pour la conception de mémoires agentives

Notes pratiques : Tout ce qu’il vous faut pour la conception de mémoires agentives

Guide pratique détaillé des notes pratiques : tout ce dont vous avez besoin pour la conception de mémoires agnitives : contrats, vérifications et emplacements de code prêts à l’emploi pour les équipes qui implémentent ce modèle.

4588 mots

Les notes suivantes reconstituent une approche pratique pour aborder le sujet « Tout ce dont vous avez besoin pour la conception de mémoires agnitives ». L’accent est mis sur les contrats, les vérifications et les placeholders de code à insérer, plutôt que sur une présentation motivante. Lors de la phase d’aperçu, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les flags fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du système.

Table des matières

L’étape du Tableau des matières fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript parfait, un cas d’échec et la note de réversion avant d’élargir le périmètre. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et le traitement des messages non livrés font partie du produit, et non d’une mise en forme ultérieure. Maintenez l’état des graphes simple et typé. Les blocs imbriqués masquent le fait que tel nœud a écrit tel champ et perturbent la reprise après interruption.

L’agent oubliait constamment

L’agent oubliait constamment que le travail en étapes fonctionne le mieux lorsqu’il est traité comme une surface mesurable. Capturez un exemple réussi, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé. Maintenez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

Pourquoi les fenêtres de contexte échouent (et l’étude qui l’a prouvé)

La phase « Pourquoi les fenêtres de contexte échouent » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Traitez cette phase comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez toute complétion partielle silencieuse. Maintenez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit tel champ et perturbent la reprise après interruption. La phase « Pourquoi les fenêtres de contexte échouent » fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent se trouver en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du graphe.

Mémoire à court terme vs mémoire à long terme : La distinction fondamentale

Pour l’étape Mémoire à court terme vs Mémoire à long terme, définissez les entrées, le responsable de l’étape et les critères de sortie avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’une mise en forme ultérieure. Imposez une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. La connexion en temps de compilation ne garantit pas la complétude du fonctionnement commercial.

Comment l’industrie est parvenue à quatre types de mémoire

Pour l’étape « Comment l’industrie s’est convergée », définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Faites approuver par des humains les actions qui entraînent des dépenses ou modifient des données de production. La connexion en temps de compilation ne garantit pas la complétude du processus métier.

Décisions concernant la base de données de production : SQL, Vector ou Graph

Pour l’étape SQL des décisions relatives à la base de données de production, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès et refusez toute exécution partielle silencieuse. Faites approuver par un humain les actions qui entraînent des dépenses ou modifient des données de production. La connexion en temps de compilation ne garantit pas l’exhaustivité du processus métier. Pour l’étape SQL des décisions relatives à la base de données de production, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans droits de lecture.

tout le graphe.

Niveau 1 : Mémoire tampon avec synthèse

Lors du traitement de l’étape de mémoire tampon du Niveau 1, notez d’abord les exigences : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’améliorations ultérieures. Créez un point de contrôle après les étapes coûteuses. Le redémarrage ne doit pas facturer à nouveau la même appel de LLM lorsque l’opérateur tente à nouveau un nœud ultérieur.

from collections import deque
from typing import List, Dict

class BufferMemory:
    def __init__(self, max_turns: int = 10):
        # Each turn = one user message + one assistant message
        self.history: deque = deque(maxlen=max_turns * 2)

    def add_message(self, role: str, content: str):
        self.history.append({"role": role, "content": content})

    def get_context(self) -> List[Dict]:
        return list(self.history)

    def token_estimate(self) -> int:
        total_chars = sum(len(m["content"]) for m in self.history)
        return total_chars // 4  # rough approximation

    def clear(self):
        self.history.clear()
import os
from collections import deque
from typing import List, Dict

from openai import OpenAI


class SummarizingBufferMemory:

    def __init__(self, max_turns: int = 8, summary_batch: int = 4):
        self.client = OpenAI(
            base_url="https://openrouter.ai/api/v1",
            api_key=os.environ["OPENROUTER_API_KEY"],
        )
        self.model = "deepseek/deepseek-v4-flash-0731"
        self.recent: deque = deque(maxlen=max_turns * 2)
        self.rolling_summary: str = ""
        self.summary_batch = summary_batch

    def add_message(self, role: str, content: str):
        if len(self.recent) == self.recent.maxlen:
            self._compress_oldest()
        self.recent.append({"role": role, "content": content})

    def _compress_oldest(self):
        batch = [self.recent.popleft() for _ in range(min(self.summary_batch * 2, len(self.recent)))]
        text = "\n".join(f"{m['role']}: {m['content']}" for m in batch)
        response = self.client.chat.completions.create(
            model=self.model,
            max_tokens=300,
            messages=[
                {
                    "role": "system",
                    "content": "You compress conversation history. Reply with the summary only.",
                },
                {
                    "role": "user",
                    "content": (
                        "Summarize this conversation segment in 2-4 sentences. "
                        "Preserve any decisions made, constraints stated, and conclusions reached.\n\n"
                        f"{text}"
                    ),
                },
            ],
        )
        new_summary = response.choices[0].message.content.strip()
        if self.rolling_summary:
            self.rolling_summary = f"{self.rolling_summary} | {new_summary}"
        else:
            self.rolling_summary = new_summary

    def get_context(self) -> List[Dict]:
        context = []
        if self.rolling_summary:
            context.append({
                "role": "system",
                "content": f"[Prior conversation summary: {self.rolling_summary}]",
            })
        context.extend(list(self.recent))
        return context

Niveau 2 : Mémoire épisodique avec validation des citations

Lorsque vous travaillez sur l’étape de la mémoire épisodique de niveau 2, notez d’abord les éléments essentiels : les entrées requises, le signal de succès, ainsi que ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité et non un processus embrouillé. Créez des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur réessaie un nœud ultérieur.

import json
import sqlite3
from datetime import datetime, timedelta
from dataclasses import dataclass, field

@dataclass
class Episode:
    task_type: str
    user_query: str
    outcome: str
    tools_used: list
    citations: list  # source references for validation
    duration_seconds: float
    success: bool
    session_id: str = ""
    created_at: str = field(default_factory=lambda: datetime.utcnow().isoformat())

class EpisodicMemory:
    EXPIRY_DAYS = 28  # GitHub Copilot's production default

    def __init__(self, db_path: str = "episodes.db"):
        self.conn = sqlite3.connect(db_path, check_same_thread=False)
        self._init_schema()

    def _init_schema(self):
        self.conn.executescript("""
            CREATE TABLE IF NOT EXISTS episodes (
                id          INTEGER PRIMARY KEY AUTOINCREMENT,
                task_type   TEXT NOT NULL,
                user_query  TEXT,
                outcome     TEXT,
                tools_used  TEXT,
                citations   TEXT,
                duration_s  REAL,
                success     INTEGER,
                session_id  TEXT,
                created_at  TEXT
            );
            CREATE INDEX IF NOT EXISTS idx_task_type ON episodes (task_type, success, created_at);
        """)
        self.conn.commit()

    def record(self, ep: Episode):
        self.conn.execute(
            """
            INSERT INTO episodes
                (task_type, user_query, outcome, tools_used, citations,
                 duration_s, success, session_id, created_at)
            VALUES (?, ?, ?, ?, ?, ?, ?, ?, ?)
            """,
            (
                ep.task_type, ep.user_query, ep.outcome,
                json.dumps(ep.tools_used), json.dumps(ep.citations),
                ep.duration_seconds, int(ep.success),
                ep.session_id, ep.created_at,
            ),
        )
        self.conn.commit()

    def recall_similar(self, task_type: str, limit: int = 3) -> list[Episode]:
        cutoff = (datetime.utcnow() - timedelta(days=self.EXPIRY_DAYS)).isoformat()
        cursor = self.conn.execute(
            """
            SELECT task_type, user_query, outcome, tools_used, citations,
                   duration_s, success, session_id, created_at
            FROM   episodes
            WHERE  task_type = ? AND success = 1 AND created_at > ?
            ORDER  BY created_at DESC
            LIMIT  ?
            """,
            (task_type, cutoff, limit),
        )
        return [
            Episode(
                task_type=r[0], user_query=r[1], outcome=r[2],
                tools_used=json.loads(r[3]), citations=json.loads(r[4]),
                duration_seconds=r[5], success=bool(r[6]),
                session_id=r[7], created_at=r[8],
            )
            for r in cursor.fetchall()
        ]

    def validate_citations(self, episode: Episode, validator_fn) -> bool:
        """
        validator_fn(citation: str) -> bool
        Check if cited sources are still valid (file exists, URL responds, etc.)
        Return False if any citation fails; the episode should be discarded.
        """
        return all(validator_fn(c) for c in episode.citations)

Niveau 3 : Mémoire sémantique avec recherche hybride Weaviate

Lors du travail sur l’étape de la mémoire sémantique de niveau 3, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez toute exécution partielle silencieuse. Créez des points de contrôle après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel du LLM lorsque l’opérateur réessaie un nœud ultérieur. Lors du travail sur l’étape de la mémoire sémantique de niveau 3, notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’intégrité des modifications ultérieures du code. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du graphe.

import uuid as uuid_lib
from datetime import datetime
import weaviate
from weaviate.classes.config import Configure, Property, DataType, VectorDistances
from weaviate.classes.query import MetadataQuery, HybridFusion, Filter

def embed(text: str) -> list[float]:
    """
    Swap in any embedding source: OpenAI, Cohere, sentence-transformers, etc.
    Returns a normalized float vector.
    """
    from sentence_transformers import SentenceTransformer
    _model = SentenceTransformer("all-MiniLM-L6-v2")
    return _model.encode(text, normalize_embeddings=True).tolist()

class WeaviateSemanticMemory:
    COLLECTION = "AgentMemory"

    def __init__(self, host: str = "localhost", port: int = 8080):
        self.client = weaviate.connect_to_local(host=host, port=port)
        self._ensure_collection()

    def _ensure_collection(self):
        if self.client.collections.exists(self.COLLECTION):
            return
        self.client.collections.create(
            name=self.COLLECTION,
            # We provide our own vectors; no built-in vectorizer needed.
            # Swap to Configure.Vectorizer.text2vec_openai() if you prefer managed embedding.
            vectorizer_config=Configure.Vectorizer.none(),
            vector_index_config=Configure.VectorIndex.hnsw(
                distance_metric=VectorDistances.COSINE
            ),
            properties=[
                Property(name="content",     data_type=DataType.TEXT),
                Property(name="task_type",   data_type=DataType.TEXT),
                Property(name="source",      data_type=DataType.TEXT),
                Property(name="citations",   data_type=DataType.TEXT),
                Property(name="session_id",  data_type=DataType.TEXT),
                Property(name="confidence",  data_type=DataType.NUMBER),
                Property(name="created_at",  data_type=DataType.TEXT),
            ],
        )

    def store(
        self,
        content: str,
        task_type: str = "",
        source: str = "agent",
        citations: str = "",
        session_id: str = "",
        confidence: float = 1.0,
    ) -> str:
        collection = self.client.collections.get(self.COLLECTION)
        doc_id = str(uuid_lib.uuid4())
        collection.data.insert(
            properties={
                "content":    content,
                "task_type":  task_type,
                "source":     source,
                "citations":  citations,
                "session_id": session_id,
                "confidence": confidence,
                "created_at": datetime.utcnow().isoformat(),
            },
            vector=embed(content),
            uuid=doc_id,
        )
        return doc_id

    def retrieve_hybrid(
        self,
        query: str,
        n_results: int = 5,
        min_confidence: float = 0.6,
        task_type: str = None,
    ) -> list[dict]:
        collection = self.client.collections.get(self.COLLECTION)
        # Filter by confidence floor and optionally by task type
        confidence_filter = Filter.by_property("confidence").greater_or_equal(min_confidence)
        if task_type:
            active_filter = (
                Filter.by_property("task_type").equal(task_type) & confidence_filter
            )
        else:
            active_filter = confidence_filter
        results = collection.query.hybrid(
            query=query,
            vector=embed(query),
            limit=n_results,
            fusion_type=HybridFusion.RELATIVE_SCORE,
            filters=active_filter,
            return_metadata=MetadataQuery(score=True),
        )
        return [
            {
                "content":    obj.properties["content"],
                "score":      obj.metadata.score,
                "confidence": obj.properties.get("confidence", 1.0),
                "source":     obj.properties.get("source", ""),
                "citations":  obj.properties.get("citations", ""),
                "uuid":       str(obj.uuid),
            }
            for obj in results.objects
        ]

    def update_confidence(self, doc_id: str, new_confidence: float):
        collection = self.client.collections.get(self.COLLECTION)
        collection.data.update(
            uuid=doc_id,
            properties={"confidence": new_confidence},
        )

    def close(self):
        self.client.close()

Niveau 4 : Mémoire procédurale et schéma d’évolution des prompts

La phase de mémoire procédurale du Niveau 4 fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple réussi, un cas d’échec ainsi que la note de réversion avant d’élargir le périmètre. Documentez en même temps le parcours optimal et celui de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non traités font partie intégrante du produit, et non d’une mise en forme ultérieure. Fixez un budget de tokens par tour et par session. Les outils agents élargissent lourdement le contexte ; des plafonds stricts empêchent que les démonstrations se transforment en factures inattendues.

import json
import os
import re
from datetime import datetime, timedelta, timezone

from openai import OpenAI

MODEL = "deepseek/deepseek-v4-flash-0731"

client = OpenAI(
    base_url="https://openrouter.ai/api/v1",
    api_key=os.environ["OPENROUTER_API_KEY"],
)


class ProceduralMemory:
    def __init__(self, rules_path: str = "procedural_rules.json"):
        self.rules_path = rules_path
        self.rules: list[dict] = self._load()

    def _load(self) -> list[dict]:
        try:
            with open(self.rules_path) as f:
                return json.load(f)
        except FileNotFoundError:
            return []

    def add_rule(self, situation: str, action: str, reason: str, confidence: float = 1.0):
        self.rules.append({
            "situation":     situation,
            "action":        action,
            "reason":        reason,
            "confidence":    float(confidence),
            "added_at":      datetime.now(timezone.utc).isoformat(),
            "trigger_count": 0,
        })
        self._save()

    def get_applicable_rules(self, context: str, min_confidence: float = 0.7) -> list[dict]:
        relevant = []
        ctx_lower = context.lower()
        dirty = False
        for rule in self.rules:
            if rule.get("confidence", 1.0) < min_confidence:
                continue
            keywords = rule["situation"].lower().split()
            if not keywords:
                continue
            hits = sum(1 for kw in keywords if kw in ctx_lower)
            if hits >= max(1, len(keywords) // 3):
                rule["trigger_count"] = rule.get("trigger_count", 0) + 1
                relevant.append(rule)
                dirty = True
        if dirty:
            self._save()
        relevant.sort(key=lambda r: r.get("confidence", 1.0), reverse=True)
        return relevant[:5]

    def prune_stale(self, max_age_days: int = 60, min_triggers: int = 2):
        cutoff = (datetime.now(timezone.utc) - timedelta(days=max_age_days)).isoformat()
        self.rules = [
            r for r in self.rules
            if r.get("added_at", "") > cutoff or r.get("trigger_count", 0) >= min_triggers
        ]
        self._save()

    def _save(self):
        tmp = f"{self.rules_path}.tmp"
        with open(tmp, "w") as f:
            json.dump(self.rules, f, indent=2)
        os.replace(tmp, self.rules_path)


def _parse_json(text: str) -> dict:
    text = text.strip()
    fenced = re.search(r"```(?:json)?\s*(.*?)```", text, re.S)
    if fenced:
        text = fenced.group(1).strip()
    return json.loads(text)


def extract_rule_from_failure(failure_trace: str, memory: ProceduralMemory):
    response = client.chat.completions.create(
        model=MODEL,
        max_tokens=250,
        response_format={"type": "json_object"},
        messages=[
            {
                "role": "system",
                "content": "You return only a JSON object. No prose, no code fences.",
            },
            {
                "role": "user",
                "content": (
                    "A task failed. Extract one behavioral rule to prevent this failure.\n"
                    "Return ONLY valid JSON with keys: situation, action, reason, "
                    "confidence (0.0-1.0)\n"
                    f"Failure trace:\n{failure_trace}"
                ),
            },
        ],
    )

    try:
        rule = _parse_json(response.choices[0].message.content)
    except (json.JSONDecodeError, AttributeError, TypeError):
        return None

    if not all(k in rule for k in ("situation", "action", "reason")):
        return None

    memory.add_rule(
        situation=str(rule["situation"]),
        action=str(rule["action"]),
        reason=str(rule["reason"]),
        confidence=float(rule.get("confidence", 1.0)),
    )
    return rule

Le tout assemblé : Gestionnaire de mémoire asynchrone et conception du système

La phase Asynchrone de mise en réseau fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Maintenez l’état du graphe plat et typé. Les blocs imbriqués cachent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

import json
from concurrent.futures import ThreadPoolExecutor
from dataclasses import dataclass, field

@dataclass
class TaskResult:
    task_type: str
    query: str
    outcome: str
    tools_used: list
    citations: list
    duration_seconds: float
    success: bool
    new_facts: list[dict] = field(default_factory=list)
    session_id: str = ""

class MemoryManager:
    def __init__(
        self,
        weaviate_host: str = "localhost",
        episodic_db: str = "episodes.db",
        rules_path: str = "procedural_rules.json",
    ):
        self.buffer    = SummarizingBufferMemory(max_turns=8)
        self.episodic  = EpisodicMemory(db_path=episodic_db)
        self.semantic  = WeaviateSemanticMemory(host=weaviate_host)
        self.procedural = ProceduralMemory(rules_path=rules_path)
        self._pool = ThreadPoolExecutor(max_workers=2, thread_name_prefix="memory_write")

    def build_context(self, query: str, task_type: str) -> list[dict]:
        context: list[dict] = []
        # Procedural rules first: they constrain behavior throughout the task
        rules = self.procedural.get_applicable_rules(query)
        if rules:
            rules_text = "\n".join(
                f"- When '{r['situation']}': {r['action']} (reason: {r['reason']})"
                for r in rules
            )
            context.append({"role": "user", "content": f"[Behavioral rules:\n{rules_text}]"})
        # Semantic facts: domain knowledge and past discoveries
        facts = self.semantic.retrieve_hybrid(query, n_results=5, min_confidence=0.6)
        if facts:
            facts_text = "\n".join(f"- {f['content']}" for f in facts)
            context.append({"role": "user", "content": f"[Relevant knowledge:\n{facts_text}]"})
        # Past episodes: outcome templates for similar tasks
        episodes = self.episodic.recall_similar(task_type, limit=3)
        if episodes:
            ep_text = "\n".join(
                f"- Outcome: {e.outcome} (tools: {', '.join(e.tools_used)})"
                for e in episodes
            )
            context.append({"role": "user", "content": f"[Past similar tasks:\n{ep_text}]"})
        # Current conversation last: the model reads this most carefully
        context.extend(self.buffer.get_context())
        return context

    def record_turn(self, role: str, content: str):
        self.buffer.add_message(role, content)

    def persist(self, result: TaskResult):
        # Submit to thread pool and return immediately; never block the caller
        self._pool.submit(self._persist_worker, result)

    def _persist_worker(self, result: TaskResult):
        ep = Episode(
            task_type=result.task_type,
            user_query=result.query,
            outcome=result.outcome,
            tools_used=result.tools_used,
            citations=result.citations,
            duration_seconds=result.duration_seconds,
            success=result.success,
            session_id=result.session_id,
        )
        self.episodic.record(ep)
        for fact in result.new_facts:
            self.semantic.store(**fact)
        if not result.success:
            extract_rule_from_failure(result.outcome, self.procedural)

  def shutdown(self):
        self._pool.shutdown(wait=True)
        self.semantic.close()

Trois décisions qui définissent votre architecture

Les Trois décisions qui définissent une étape fonctionnent le mieux lorsqu’elles sont considérées comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définites des vérifications de succès et refusez toute mise en œuvre partielle silencieuse. Maintenez l’état du graphe plat et typé. Les blocs imbriqués masquent le fait que tel nœud a modifié tel champ et perturbent la reprise après interruption. Les Trois décisions qui définissent une étape fonctionnent le mieux lorsqu’elles sont considérées comme une surface mesurable. Capturez un enregistrement idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les administrateurs peuvent auditer sans devoir lire l’ensemble du graphe.

Empoisonnement de la mémoire : à quoi ressemblent ces attaques et comment s’en protéger

Pour le poisonage de la mémoire, définissez l’étape concernée, les entrées nécessaires, le responsable de cette étape ainsi que les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu, sans avoir à deviner l’état caché. Documentez conjointement le parcours normal et les scénarios de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations apportées ultérieurement. Imposez une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. Une configuration en temps de compilation ne garantit pas la complétude du fonctionnement commercial.

La recommandation concrète

Pour la phase réelle de recommandation, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier du code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférez des unités petites et testables aux scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé. Faites approuver par des humains les étapes qui entraînent des dépenses ou modifient des données de production. La connexion en temps de compilation ne garantit pas la complétude du processus métier.

Continuons à apprendre ensemble

Pour l’étape « Continuons à apprendre », définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définites des vérifications de succès et refusez les terminations partielles silencieuses. Faites approuver par un humain les cas où de l’argent est dépensé ou où des données de production sont modifiées. La connexion en temps de compilation ne revient pas à une complétude opérationnelle. Pour l’étape « Continuons à apprendre », définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.

Plus d’articles utiles

Lors de l’étape des « Plus d’articles utiles », notez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations ultérieures. Faites un point après les étapes coûteuses. Le système de reprise ne doit pas facturer à nouveau la même appel d’LLM lorsque l’opérateur réessaie un nœud ultérieur.

Liste de contrôle opérationnelle

Pour l’étape de la liste de contrôle opérationnelle, définissez les entrées, le responsable de l’étape et les critères d’arrêt avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché.

Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts permet d’éviter des factures inattendues lorsque le parcours passe de l’environnement de démonstration à des environnements partagés.

Faites approuver par un humain les étapes qui entraînent des dépenses ou modifient des données de production. Une connexion effectuée en temps de compilation ne garantit pas une couverture complète des besoins métier.

Rédigez un petit manuel d’utilisation : comment rotationner les clés, comment vider la file d’attente, comment annuler la dernière ingestion.

Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du schéma.

Faites approuver par un humain les étapes qui entraînent des dépenses ou modifient des données de production. Une connexion effectuée en temps de compilation ne garantit pas une couverture complète des besoins métier.

Au préalable de promouvoir le stack, figez les versions, conservez une transcription « or » pour le chemin critique, et confirmez les étapes de rollback. Les environnements partagés nécessitent des limites de débit, des vérifications de location, ainsi qu’un responsable clair pour la rotation des secrets. Préférez une fiabilité banale à des démonstrations ingénieuses ponctuelles.

Note de lot pour b038012e06fc : gardez les clés du fournisseur hors du repo, fixez un plafond pour les tokens par session, et stockez les transcriptions à côté des fichiers de configuration d’évaluation afin que les remplacements ultérieurs de modèles restent comparables.

Pour la note de renforcement du niveau 0, définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Considérez ce niveau comme un contrat entre les entrées et les sorties validées. Nommez les artefacts, définissez des vérifications de succès, et refusez toute complétion partielle silencieuse.

Détail de renforcement 0/804 : mesurez le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver le changement en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations anecdotiques.

Lors de la première étape de la note de renforcement, écrivez d’abord le contrat : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de garantir l’honnêteté des modifications de code ultérieures. Conservez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.

Détail de renforcement 1/804 : mesurez le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver le changement en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations anecdotiques.

La deuxième étape de la note de renforcement fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un enregistrement exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Préférez des unités petites et testables à des scripts complexes. Lorsqu’une étape échoue, l’échec doit pointer vers une seule responsabilité plutôt que vers un processus embrouillé.

Détail de renforcement 2/804 : mesurez le temps d’exécution, la classe d’erreur et l’utilisation des tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Pour la phase 3 des notes de renforcement, définissez les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Enregistrez les temps d’exécution ainsi que le coût des jetons ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts permet d’éviter des factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés.

Détail de renforcement 3/804 : mesurez le temps d’exécution réel, la catégorie de l’erreur et la consommation de jetons pour cette note, puis décidez s’il convient de conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.

Lors de la réalisation de l’étape 4 des notes de renforcement, notez d’abord les conditions contractuelles : entrées requises, signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Documentez ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie intégrante du produit, et non d’améliorations apportées ultérieurement.

Détail de renforcement 4/804 : mesurez le temps d’exécution, la catégorie de l’erreur et l’utilisation des tokens pour cette note, puis décidez si vous conservez la modification en vous basant sur un ensemble de questions prédéfinies plutôt que sur des observations subjectives.

L’étape 5 des notes de renforcement fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Recueillez un exemple idéal de fonctionnement, un cas d’échec et une note de réversion avant d’élargir le périmètre. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminations partielles silencieuses.

Détail de renforcement 5/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Pour la phase 6 de la note de renforcement, définir les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Conserver la configuration en dehors du code de l’application. Les fichiers d’environnement, les bases de données secrètes et les indicateurs fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans avoir à lire l’ensemble du système.

Détail de renforcement 6/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Lors de l’étape 7 des notes de renforcement, notez d’abord les éléments essentiels : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Préférez des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’erreur doit indiquer une responsabilité précise plutôt qu’un processus embrouillé.

Détail de renforcement 7/804 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de critères prédéfinis plutôt que sur des observations subjectives.

L’étape 8 des notes de renforcement fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal de fonctionnement, un cas d’échec et des notes de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût en tokens ou en requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite des surprises lors du passage de l’environnement de démonstration aux environnements partagés.

Détail de renforcement 8/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Pour l’étape 9 de la note de renforcement, définir les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Documenter ensemble le parcours normal et le parcours de récupération. Les tentatives répétées, les contrôles humains et la gestion des messages non livrés font partie du produit, et non d’une mise en forme ultérieure.

Détail de renforcement 9/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Lors de l’exécution de l’étape 10 des notes de renforcement, notez d’abord les conditions contractuelles : les entrées requises, le signal de succès et ce qui se passe en cas d’échec partiel. Cette liste de contrôle permet de rester honnête lors des modifications ultérieures du code. Considérez cette étape comme un contrat entre les entrées et les sorties validées. Donnez des noms aux artefacts, définez des vérifications de succès et refusez les terminaisons partielles silencieuses.

Détail de renforcement 10/804 : mesurez le temps d’exécution, la classe de l’erreur et la consommation de tokens pour cette note, puis décidez si vous souhaitez conserver la modification en vous basant sur un ensemble de questions prédéfini plutôt que sur des observations subjectives.

L’étape 11 des notes de renforcement fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un exemple idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Gardez la configuration en dehors du code de l’application. Les fichiers d’environnement, les stocks de secrets et les flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système.

Détail de renforcement 11/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.

Pour l’étape 12 du processus de renforcement, définir les entrées, le responsable de l’étape et les critères d’achèvement avant de modifier le code. Les opérateurs doivent pouvoir relancer l’étape à partir d’un point de contrôle connu sans deviner l’état caché. Préférer des unités petites et testables plutôt que des scripts complexes. Lorsqu’une étape échoue, l’échec doit indiquer une seule responsabilité plutôt qu’un processus embrouillé.

Détail de renforcement 12/804 : mesurer le temps d’exécution, la classe d’erreur et la consommation de tokens pour cette note, puis décider de conserver ou non le changement en se basant sur un ensemble de questions prédéfini plutôt que sur des anecdotes.