Accueil / Articles / Notes pratiques : Création d’un agent de support LEPA avec LangGraph

Notes pratiques : Création d’un agent de support LEPA avec LangGraph

Guide pas à pas fonctionnel des notes pratiques : Création d’un agent de support LEPA avec LangGraph : contrats, vérifications et emplacements pour du code intégrable destinés aux équipes utilisant ce modèle.

2082 mots

Utilisez ceci comme une version révisée destinée aux opérateurs des idées présentées dans « Construire un agent de support LEPA avec LangGraph » : étapes claires, emplacements de code ordonnés et notes de récupération qui survivent au transfert de tâches. L’étape « Aperçu » 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 indiquer une seule responsabilité plutôt qu’un processus embrouillé.

Ce que vous vouliez que le premier graphe fasse

Pour cette étape, définissez les entrées, le responsable de l’étape et les critères de fin 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 terminaisons 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.

User message
     ↓
Notice who is speaking (teacher / admin / unknown)
     ↓
Classify the topic (grades, login, …)
     ↓
Too vague? Ask a clarifying question
     ↓
Otherwise continue toward docs + an answer

Mise en place du projet (intentionnellement ennuyeuse)

Pour une configuration de projet maintenue intentionnellement en phase initiale, il convient de définir les entrées, le responsable de chaque étape ainsi que 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 avoir à 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. Imposez une approbation humaine pour les actions qui entraînent des dépenses ou modifient des données de production. Une connexion effectuée en temps de compilation ne garantit pas pour autant la complétude du processus métier.

PRJ-02/
├── app/
│   ├── state.py      # SupportState
│   ├── graph.py      # StateGraph wiring
│   ├── agents/       # intake, knowledge, support
│   ├── nodes/        # classify, ask_clarification
│   └── tools/        # search_knowledge (next article)
├── knowledge/        # LEPA support Markdown
├── api/              # FastAPI (later article)
└── tests/

État : l’objet qui se déplace à travers le graphe

Pour l’objet d’état de cette étape, 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é. 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 devoir lire l’ensemble du système. Mettez en place 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 processus métier. Pour l’objet d’état de cette étape, 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é. 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 ensemble de processus embrouillés.

e.

messages                 # conversation turns (add_messages reducer)
user_role                # teacher / admin / unknown
issue_category           # grades, authentication, …
clarification_needed     # should we ask for more detail?
clarification_question   # what we ask
retrieved_documents      # doc snippets (later step)
final_answer             # what we return to the user
conversation_summary     # reserved for later — unused in v1
messages: Annotated[list, add_messages]
{"user_role": "teacher"}
{"issue_category": "grades", "clarification_needed": False}

Nœuds : une tâche par nœud

Lorsque vous travaillez avec des nœuds où chaque étape correspond à une tâche, 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. 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. Faites un point 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.

(state) → partial update

Entrée des données

Lors de la phase d’initialisation, notez d’abord le contrat : les donné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. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de l’environnement de démonstration à des environnements partagés. Créez un point 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.

Classer

Lors du traitement de l’étape Classify, 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 flags fonctionnels doivent être regroupés en un seul endroit que les opérateurs peuvent auditer sans devoir lire l’ensemble du système. Créez des points de contrôle après les étapes coûteuses. Le mécanisme 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 traitement de l’étape Classify, 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. 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 pipeline embrouillé.

Demander des précisions

La phase de clarification des demandes fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Recueillez un transcript idéal, un cas d’échec et la note de réversion avant d’élargir le périmètre. Considérez 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 masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

Connaissances + support

La phase de soutien par les connaissances fonctionne le mieux lorsqu’elle est considérée comme une surface mesurable. Capturez un transcript exemplaire, un cas d’échec et la note de réversion avant d’élargir le périmètre. Enregistrez les temps d’exécution ainsi que le coût des tokens ou des requêtes à côté des résultats fonctionnels. Une visibilité précoce des coûts évite les factures inattendues lorsque le processus passe de l’environnement de démonstration aux environnements partagés. Gardez l’état du graphe plat et structuré. Les blocs imbriqués masquent l’identité du nœud qui a modifié tel champ et perturbent la reprise après interruption.

Arêtes : comment se déplace le contrôle

Le fonctionnement optimal des flux de contrôle lorsqu’ils sont traités 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. 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 devoir lire l’ensemble du graphe. Maintenez un état du graphe plat et typé. Les blocs imbriqués masquent l’identité du nœud qui a écrit tel champ, ce qui perturbe la reprise après interruption.

Flux normaux

La phase des arêtes normales 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 réussi 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 intégrante du produit, et non d’une mise en forme ultérieure. Gardez l’état du graphe plat et typé ; les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

graph.add_edge(START, "intake")
graph.add_edge("intake", "classify")
graph.add_edge("knowledge", "support")
graph.add_edge("support", END)
When this node finishes, always go there next.

Arêtes conditionnelles

La phase des arêtes conditionnelles 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. Préférez des unités petites et testables à des 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.

def route_after_classify(state: SupportState) -> Literal["clarify", "continue"]:
    if state.get("clarification_needed"):
        return "clarify"
    return "continue"
graph.add_conditional_edges(
    "classify",
    route_after_classify,
    {
        "clarify": "ask_clarification",
        "continue": "knowledge",
    },
)
"help"  → clarify → ask_clarification → END
"How do I enter grades?" → continue → knowledge → support → END

DÉBUT et FIN

Les étapes START et END fonctionnent le mieux lorsqu’elles sont considérées 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. 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 complétion partielle silencieuse. Maintenez l’état du graphe plat et typé. Les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

START = where the runtime begins
END   = where this run stops
START → intake → classify → …
…
ask_clarification → END
support → END

La topologie complète (telle qu’elle a été construite)

La topologie complète, considérée comme une étape, fonctionne le mieux lorsqu’elle est traité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. 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 évite les factures inattendues lorsque le processus passe de la démonstration aux environnements partagés. Gardez l’état du graphe plat et typé ; les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

START
  → intake
  → classify
  → conditional
        ├─ clarify  → ask_clarification → END
        └─ continue → knowledge → support → END

Comment une requête se déplace à travers le graphe

Le fonctionnement du processus de progression d’une demande fonctionne le mieux lorsqu’il est considéré 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. 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 devoir lire l’ensemble du schéma. Maintenez un état du schéma plat et typé. Les blocs imbriqués masquent l’identité du nœud qui a écrit tel champ, ce qui perturbe la reprise après interruption.

Question claire

La phase de question claire 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 rollback avant d’élargir le périmètre. Documentez ensemble le parcours réussi 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’une mise en forme ultérieure. Gardez l’état des graphes plat et typé ; les blocs imbriqués masquent le fait que tel nœud a écrit telle champ et perturbent la reprise après interruption.

User: "Why aren't grades showing?"
        ↓
intake        → user_role = unknown (unless they said teacher/admin)
        ↓
classify      → issue_category = grades, clarification_needed = False
        ↓
route         → "continue"
        ↓
knowledge     → search docs (next article)
        ↓
support       → final_answer
        ↓
END

Question vague

User: "help"
        ↓
intake
        ↓
classify      → unknown + clarification_needed = True
        ↓
route         → "clarify"
        ↓
ask_clarification → asks which LEPA area
        ↓
END

compile() et invoke() : le moment de l’exécution

return graph.compile(checkpointer=checkpointer)
from langchain_core.messages import HumanMessage
from app.graph import app_graph
result = app_graph.invoke(
    {"messages": [HumanMessage(content="How do I enter grades?")]}
)
print(result["final_answer"])

Ce que vous avez délibérément reporté

Points clés

Liens

Liste de contrôle opérationnelle