Article
· Juil 28 17m de lecture

Création d'agents IA : de zéro à héros

Découvrez comment concevoir des agents IA évolutifs et autonomes qui combinent raisonnement, recherche vectorielle et intégration d'outils à l'aide de LangGraph.

cover

C'est trop long, vous n'avez pas lu

  • Les agents IA sont des systèmes proactifs qui combinent mémoire, contexte et initiative pour automatiser des tâches dépassant le simple champ d'action des chatbots.
  • LangGraph est un framework qui nous permet de créer des workflows d'IA complexes, en utilisant des nœuds (tâches) et des arêtes (connexions) avec une gestion d'état intégrée.
  • Ce guide vous explique comment créer un agent de support client basé sur l'IA qui classe les priorités, identifie les sujets pertinents et détermine si une escalade ou une réponse automatique est nécessaire.

Alors, les agents IA, c'est quoi exactement?

Soyons réalistes, le terme "agents IA" peut faire penser à des robots qui vont envahir votre bureau. En réalité, il s'agit de vos assistants proactifs qui peuvent rationaliser des flux de travail complexes et éliminer les tâches répétitives. Considérez-les comme la prochaine étape évolutive après les chatbots: ils ne se contentent pas d'attendre des instructions, ils lancent des actions, coordonnent plusieurs étapes et s'adaptent tout au long du processus.

Autrefois, créer un système "intelligent" signifiait jongler avec différents modèles pour la compréhension du langage, la génération de code, la recherche de données, etc., puis les assembler à la va-vite. Auparavant, vous passiez la moitié de votre temps dans l'enfer de l'intégration, et l'autre moitié à corriger les erreurs.

Les agents renversent la tendance. Ils regroupent le contexte, l'initiative et l'adaptabilité en un seul flux orchestré. Il ne s'agit pas seulement d'automatisation, mais d'intelligence au service d'une mission. Et grâce à des frameworks tels que LangGraph, la création de votre propre équipe d'agents peut même devenir... oserais-je dire, amusante?

image

LangGraph, c'est quoi exactement?

LangGraph est un cadre innovant qui révolutionne la manière dont nous construisons des applications complexes impliquant des modèles linguistiques à grande échelle (LLM).

Imaginez que vous dirigez un orchestre: chaque instrument (ou "nœud") doit savoir quand entrer, à quel volume jouer et dans quel ordre. LangGraph, dans ce cas, est votre baguette, qui vous donne les informations suivantes:

  • Structure graphique: utilise une structure graphique avec des nœuds et des arêtes, permettant aux développeurs de concevoir des flux de travail flexibles et non linéaires qui s'adaptent aux branches et aux boucles. Elle reflète les processus décisionnels complexes qui ressemblent au fonctionnement des voies neuronales.
  • Gestion d'état: LangGraph offre des outils intégrés pour la persistance d'état et la récupération d'erreurs, simplifiant la gestion des données contextuelles à différentes étapes d'une application. Il peut basculer efficacement entre la mémoire à court terme et la mémoire à long terme, améliorant ainsi la qualité des interactions grâce à des outils tels que Zep.
  • Intégration d'outils: Avec LangGraph, les agents LLM peuvent facilement collaborer avec des services ou des bases de données externes pour récupérer des données du monde réel, améliorant ainsi la fonctionnalité et la réactivité de vos applications.
  • Human-in-the-Loop: Au-delà de l'automatisation, LangGraph s'adapte aux interventions humaines dans les flux de travail, qui sont essentielles pour les processus décisionnels nécessitant une supervision analytique ou une réflexion éthique.

Lorsque vous créez un chatbot doté d'une mémoire réelle, un moteur d'histoires interactives ou une équipe d'agents chargés de résoudre un problème complexe, LangGraph transforme les tâches fastidieuses en une machine à états simple et visuelle.

Pour commencer

Pour commencer à utiliser LangGraph, vous aurez besoin d'une configuration de base qui implique généralement l'installation de bibliothèques essentielles telles que langgraph et langchain-openai. Ensuite, vous pourrez définir les nœuds (tâches) et les bords (connexions) au sein du graphe, en mettant efficacement en œuvre des points de contrôle pour la mémoire à court terme et en utilisant Zep pour les besoins en mémoire plus persistants.

Lorsque vous utilisez LangGraph, gardez à l'esprit les remarques suivantes:

  • Flexibilité de conception: Tirez parti de la puissante structure graphique pour prendre en compte les ramifications et les interactions potentielles du flux de travail qui ne sont pas strictement linéaires.
  • Interaction prudente avec les outils: Améliorez les capacités du LLM à l'aide d'outils externes, mais ne les remplacez pas. Fournissez à chaque outil des descriptions complètes pour permettre une utilisation précise.
  • Utilisation de solutions de mémoire riches: Utilisez la mémoire de manière efficace, soyez attentif à la fenêtre contextuelle du LLM et envisagez d'intégrer des solutions externes pour la gestion automatiquedu contenu factuel.

Nous avons abordé les bases de LangGraph, passons maintenant à un exemple pratique. Pour cela, nous allons développer un agent IA spécialement conçu pour le support client.

Cet agent recevra les demandes par e-mail, analysera la description du problème dans le corps du message, puis déterminera la priorité de la demande et le sujet/la catégorie/le secteur approprié.

Alors, attachez vos ceintures et c'est parti!

buckle up

Pour commencer, nous devons définir ce qu'est un "outil". Vous pouvez le considérer comme un "assistant manager" spécialisé pour votre agent, lui permettant d'interagir avec des fonctionnalités externes.

Le décorateur @tool est ici essentiel. LangChain simplifie la création d'outils personnalisés, ce qui signifie que vous définissez d'abord une fonction Python, puis appliquez le décorateur @tool.

tools

Illustrons cela en créant notre premier outil. Cet outil aidera l'agent à classer la priorité d'un ticket d'assistance informatique en fonction du contenu de l'e-mail:

    from langchain_core.tools import tool

    @tool
    def classify_priority(email_body: str) -> str:
        """Classify the priority of an IT support ticket based on email content."""
        prompt = ChatPromptTemplate.from_template(
            """Analyze this IT support email and classify its priority as High, Medium, or Low.

            High: System outages, security breaches, critical business functions down
            Medium: Non-critical issues affecting productivity, software problems
            Low: General questions, requests, minor issues

            Email: {email}

            Respond with only: High, Medium, or Low"""
        )
        chain = prompt | llm
        response = chain.invoke({"email": email_body})
        return response.content.strip()

Excellent! Nous avons maintenant une invite qui demande à l'IA de recevoir le corps de l'e-mail, de l'analyser et de classer sa priorité comme Élevée, Moyenne ou Faible.

C'est tout! Vous venez de créer un outil accessible à votre agent!

Créons maintenant un outil similaire pour identifier le sujet principal (ou la catégorie) de la demande de support:


@tool def identify_topic(email_body: str) -> str: """Identify the main topic/category of the IT support request.""" prompt = ChatPromptTemplate.from_template( """Analyze this IT support email and identify the main topic category. Categories: password_reset, vpn, software_request, hardware, email, network, printer, other Email: {email} Respond with only the category name (lowercase with underscores).""" ) chain = prompt | llm response = chain.invoke({"email": email_body}) return response.content.strip()

Nous devons maintenant créer un état, et dans LangGraph, cette petite partie est plutôt importante.

Considérez-le comme le système nerveux central de votre graphe. C'est ainsi que les nœuds communiquent entre eux, en se passant des petits mots comme des surdoués à l'école.

Selon la documentation:

“Un état est une structure de données partagée qui représente l'instantané actuel de votre application.”

Et en pratique? L'état est un message structuré qui circule entre les nœuds. Il transporte le résultat d'une étape comme entrée pour la suivante. En gros, c'est le ciment qui maintient l'ensemble de votre flux de travail.

Par conséquent, avant de construire le graphique, nous devons d'abord définir la structure de notre état. Dans cet exemple, notre état sera composé des éléments suivants:

  • La demande de l'utilisateur (corps de l'email)
  • La priorité attribuée
  • Le sujet identifié (catégorie)

C'est simple et clair, vous pouvez donc vous déplacer à travers le graphe comme un pro.

    from typing import TypedDict

    # Définition de la structure d'état
    class TicketState(TypedDict):
        email_body: str
        priority: str
        topic: str


    # Initialisation d'état
    initial_state = TicketState(
        email_body=email_body,
        priority="",
        topic=""
    )

Nœuds et bords: Composants clés de LangGraph

Les éléments fondamentaux de LangGraph sont les nœuds et les bords.

  • Nœuds: Il s'agit des unités opérationnelles du graphe qui effectuent le travail proprement dit. Un nœud se compose généralement d'un code Python capable d'exécuter n'importe quelle logique, qu'il s'agisse de calculs ou d'interactions avec des modèles linguistiques (LLM) ou des intégrations externes. Les nœuds sont essentiellement similaires aux fonctions ou agents individuels de la programmation traditionnelle.
  • Bords: les bords définissent le flux d'exécution entre les nœuds et déterminent ce qui se passe ensuite. Elles agissent comme des connecteurs qui permettent à l'état de passer d'un nœud à l'autre en fonction de conditions prédéfinies. Dans le contexte de LangGraph, les bords sont essentiels pour orchestrer la séquence et le flux de décision entre les nœuds.

Pour comprendre le fonctionnement des bords, prenons l'exemple simple d'une application de messagerie:

  • Nœuds sont similaires aux utilisateurs (ou à leurs appareils) qui participent activement à une conversation.
  • Bords symbolisent les fils de discussion ou les connexions entre les utilisateurs qui facilitent la communication.

Lorsqu'un utilisateur sélectionne un fil de discussion pour envoyer un message, un bord est créée, le reliant à un autre utilisateur. Chaque interaction, qu'il s'agisse d'envoyer un message textuel, vocal ou vidéo, suit une séquence prédéfinie, comparable au schéma structuré de l'état de LangGraph. Cela garantit l'uniformité et l'interprétabilité des données transmises le long des bords.

Contrairement à la nature dynamique des applications pilotées par les événements, LangGraph utilise un schéma statique qui reste cohérent tout au long de l'exécution. Il simplifie la communication entre les nœuds, permettant aux développeurs de s'appuyer sur un format stable, garantissant ainsi une communication fluide au niveau des bords.

Conception d'un flux de travail de base

L'ingénierie des flux dans LangGraph peut être conceptualisée comme la conception d'une machine d'état. Dans ce paradigme, chaque nœud représente un état ou une étape de traitement distinct, tandis que les arêtes définissent les transitions entre ces états. Cette approche est particulièrement avantageuse pour les développeurs qui cherchent à trouver un équilibre entre les séquences de tâches déterministes et les capacités décisionnelles dynamiques de l'IA. Commençons à construire notre flux en initialisant StateGraph avec la classe TicketState que nous avons définie précédemment.

    from langgraph.graph import StateGraph, START, END

    workflow = StateGraph(TicketState)

Ajout de nœuds: Les nœuds sont des éléments fondamentaux, définis pour exécuter des tâches spécifiques telles que la classification de la priorité d'un ticket ou l'identification de son sujet.

Chaque fonction de nœud reçoit l'état actuel, effectue son opération et renvoie un dictionnaire permettant de mettre à jour l'état:

   def classify_priority_node(state: TicketState) -> TicketState:
        """Node to classify ticket priority."""
        priority = classify_priority.invoke({"email_body": state["email_body"]})
        return {"priority": priority}

    def identify_topic_node(state: TicketState) -> TicketState:
        """Node to identify ticket topic."""
        topic = identify_topic.invoke({"email_body": state["email_body"]})
        return {"topic": topic}


    workflow.add_node("classify_priority", classify_priority_node)
    workflow.add_node("identify_topic", identify_topic_node)

Les méthodes classify_priority_node et identify_topic_node modifieront le TicketState et enverront la saisie du paramètre.

Création des bords: Définissez les bords pour connecter les nœuds:


workflow.add_edge(START, "classify_priority") workflow.add_edge("classify_priority", "identify_topic") workflow.add_edge("identify_topic", END)

The classify_priority establishes the start, whereas the identify_topic determines the end of our workflow so far.

Compilation et exécution: Une fois les nœuds et les bords configurés, compilez le flux de travail et exécutez-le.


graph = workflow.compile() result = graph.invoke(initial_state)

Très bien! Vous pouvez également générer une représentation visuelle de notre flux LangGraph.

graph.get_graph().draw_mermaid_png(output_file_path="graph.png")

Si vous exécutez le code jusqu'à ce point, vous obtiendrez un graphe similaire au suivant:

first_graph.png

Cette illustration visualise une exécution séquentielle: démarrage, suivi du classement des priorités, puis identification du sujet et enfin terminaison.

L'un des aspects les plus puissants de LangGraph est sa flexibilité, qui nous permet de créer des flux et des applications plus complexes. Par exemple, nous pouvons modifier le flux de travail pour ajouter des bords depuis START vers les deux nœuds avec la ligne suivante:

    workflow.add_edge(START, "classify_priority")
    workflow.add_edge(START, "identify_topic")

Cette modification aura pour conséquence que l'agent exécutera simultanément classify_priority et identify_topic.

Une autre fonctionnalité très utile de LangGraph est la possibilité d'utiliser des bords conditionnels. Ils permettent au flux de travail de se ramifier en fonction de l'évaluation de l'état actuel, ce qui permet un routage dynamique des tâches.

Améliorons notre flux de travail. Nous allons créer un nouvel outil qui analyse le contenu, la priorité et le sujet de la demande afin de déterminer s'il s'agit d'un problème hautement prioritaire nécessitant une escalade (c'est-à-dire l'ouverture d'un ticket pour être traité par une équipe humaine). Si ce n'est pas le cas, une réponse automatisée sera générée pour l'utilisateur.


@tool def make_escalation_decision(email_body: str, priority: str, topic: str) -> str: """Decide whether to auto-respond or escalate to IT team.""" prompt = ChatPromptTemplate.from_template( """Based on this IT support ticket, decide whether to: - "auto_respond": Send an automated response for simple/common or medium priority issues - "escalate": Escalate to the IT team for complex/urgent issues Email: {email} Priority: {priority} Topic: {topic} Consider: High priority items usually require escalation, while complex technical issues necessitate human review. Respond with only: auto_respond or escalate""" ) chain = prompt | llm response = chain.invoke({ "email": email_body, "priority": priority, "topic": topic }) return response.content.strip()

De plus, si la demande est jugée de priorité faible ou moyenne (ce qui entraîne une décision "auto_respond"), nous effectuerons une recherche vectorielle pour récupérer les réponses historiques. Ces informations seront ensuite utilisées pour générer une réponse automatisée appropriée. Cependant, cela nécessitera deux outils supplémentaires:


@tool def retrieve_examples(email_body: str) -> str: """Retrieve relevant examples from past responses based on email_body.""" try: examples = iris.cls(__name__).Retrieve(email_body) return examples if examples else "No relevant examples found." except: return "No relevant examples found." @tool def generate_reply(email_body: str, topic: str, examples: str) -> str: """Generate a suggested reply based on the email, topic, and RAG examples.""" prompt = ChatPromptTemplate.from_template( """Generate a professional IT support response based on: Original Email: {email} Topic Category: {topic} Example Response: {examples} Create a helpful, professional response that addresses the user's concern. Keep it concise and actionable.""" ) chain = prompt | llm response = chain.invoke({ "email": email_body, "topic": topic, "examples": examples }) return response.content.strip()

Maintenant, définissons les nœuds correspondants à ces nouveaux outils:


def decision_node(state: TicketState) -> TicketState: """Node to decide on escalation or auto-response.""" decision = make_escalation_decision.invoke({ "email_body": state["email_body"], "priority": state["priority"], "topic": state["topic"] }) return {"decision": decision} def rag_node(state: TicketState) -> TicketState: """Node to retrieve relevant examples using RAG.""" examples = retrieve_examples.invoke({"email_body": state["email_body"]}) return {"rag_examples": examples} def generate_reply_node(state: TicketState) -> TicketState: """Node to generate suggested reply.""" reply = generate_reply.invoke({ "email_body": state["email_body"], "topic": state["topic"], "examples": state["rag_examples"] }) return {"suggested_reply": reply} def execute_action_node(state: TicketState) -> TicketState: """Node to execute final action based on decision.""" if state["decision"] == "escalate": action = f"🚨 ESCALATED TO IT TEAM\nPriority: {state['priority']}\nTopic: {state['topic']}\nTicket created in system." print(f"[SYSTEM] Escalating ticket to IT team - Priority: {state['priority']}, Topic: {state['topic']}") else: action = f"✅ AUTO-RESPONSE SENT\nReply: {state['suggested_reply']}\nTicket logged for tracking." print(f"[SYSTEM] Auto-response sent to user - Topic: {state['topic']}") return {"final_action": action} workflow.add_node("make_decision", decision_node) workflow.add_node("rag", rag_node) workflow.add_node("generate_reply", generate_reply_node) workflow.add_node("execute_action", execute_action_node)

Le bord conditionnel utilisera alors le résultat du nœud make_decision pour diriger le flux:

    workflow.add_conditional_edges(
        "make_decision",
        lambda x: x.get("decision"),
        {
            "auto_respond": "rag",
            "escalate": "execute_action"
        }
    )

Si l'outil make_escalation_decision (via decision_node) renvoie "auto_respond", le workflow passe par le nœud rag (pour récupérer des exemples), puis par generate_reply (pour rédiger la réponse) et enfin par execute_action (pour enregistrer la réponse automatique).

En revanche, si la décision est “escalate”, le flux contournera le RAG et passera aux étapes de génération, passant directement à execute_action pour gérer l'escalade. Pour compléter le graphe en ajoutant les bords standard restants, procédez comme suit:

    workflow.add_edge("rag", "generate_reply")
    workflow.add_edge("generate_reply", "execute_action")
    workflow.add_edge("execute_action", END)

Remarque sur le jeu de données: pour ce projet, le jeu de données utilisé pour alimenter la génération augmentée par récupération (RAG) provient du jeu de données Customer Support Tickets dataset on Hugging Face. Le jeu de données a été filtré afin de n'inclure que les éléments classés dans la catégorie de support technique 'Technical Support' et limité aux saisies en anglais English. Cela a permis de garantir que le système RAG ne récupère que des exemples très pertinents et spécifiques au domaine pour les tâches de support technique.

À ce stade, notre graphique devrait ressembler au suivant:

graph.png

Lorsque vous exécutez ce graphe avec un e-mail qui entraîne une classification de priorité élevée et une décision "escalate", vous obtenez la réponse suivante:

image.png

Au même moment, une demande classée comme faible priorité et donnant lieu à une décision « auto_respond » déclenchera une réponse similaire à la suivante:

image.png

Alors... Est-ce que tout est rose?

Pas tout à fait. Il y a quelques obstacles à éviter:

  • Confidentialité des données: Soyez prudent avec les informations sensibles, ces agents nécessitent des mesures de protection.
  • Coûts informatiques: Certaines configurations avancées nécessitent des ressources importantes.
  • Hallucinations: Les LLM peuvent parfois inventer des choses (même s'ils restent plus intelligents que la plupart des stagiaires).
  • Non-déterminisme: Une même saisie peut donner des résultats différents, ce qui est excellent pour la créativité, mais problématique pour les processus rigoureux.

Cependant, la plupart de ces lacunes peuvent être comblées grâce à une bonne planification, aux bons outils et, bien sûr, à un peu de réflexion.

LangGraph transforme les agents IA, qui ne sont encore que des mots à la mode, en solutions fonctionnelles et tangibles. Que vous souhaitiez automatiser votre service client, traiter vos tickets informatiques ou créer des applications autonomes, ce framework rend tout cela possible et même agréable.

Avez-vous des questions ou des commentaires? Parlons-en. La révolution de l'IA a besoin de créateurs comme vous.

Discussion (0)2
Connectez-vous ou inscrivez-vous pour continuer