L'idée principale de LangChain Agent est d'utiliser LLM comme un cerveau pour penser automatiquement, prendre des décisions et exécuter différentes actions pour finalement accomplir nos tâches cibles.

Conseil : D'un point de vue développement, cela signifie que nous développons diverses API à l'avance, puis donnons une tâche à l'Agent, permettant à LLM d'analyser quelle API appeler pour accomplir la tâche.

Pour mieux comprendre le problème que LangChain Agent vise à résoudre, considérons un exemple.

Par exemple :

Si nous voulons rechercher si "Docker peut être utilisé comme solution de déploiement en production", nous rechercherions d'abord "introduction à Docker" sur Baidu, parcourrions les résultats de la recherche, puis rechercherions plus en détail "avantages et inconvénients du déploiement Docker" et parcourrions les résultats, et ainsi de suite, jusqu'à parvenir à une conclusion.

LangChain Agent vise à simuler ce processus. Je peux pré-emballer une série d'outils (par exemple, recherche Baidu, outils d'extraction de contenu d'URL), puis donner à l'Agent une tâche cible "Docker peut-il être utilisé comme solution de déploiement en production ?" L'Agent construira ensuite des invites pour appeler LLM. Afin de réaliser cette tâche, la prochaine étape consiste à exécuter quelle action (c'est-à-dire, appeler quel outil). L'IA renverra l'outil à appeler, le code exécutera cet outil, puis transmettra les résultats de l'exécution de l'outil à l'IA, et demandera la prochaine étape pour exécuter quel outil. Répéter ce processus permettra de compléter la tâche mentionnée précédemment.

Conseil : Depuis la sortie du modèle GPT, c'est une capacité explosive, permettant à LLM d'agir comme le cerveau, de penser activement, puis d'appeler diverses API que nous avons développées. Cela améliore considérablement les capacités de LLM. Actuellement, cette fonctionnalité est encore à l'étape expérimentale et appellera à plusieurs reprises LLM, donc elle consomme assez de jetons. Accomplir une tâche peut coûter des dizaines de milliers de jetons en quelques minutes. Si vous voulez économiser de l'argent, il est recommandé de laisser d'abord LLM effectuer des tâches de jugement logique simples.

Concepts Principaux

Explorons maintenant les composants et concepts associés.

Agent

Un Agent peut être compris comme notre assistant, agissant en notre nom pour prendre des décisions. Dans l'implémentation sous-jacente de LangChain Agent, c'est à travers LLM que la prochaine action (ou appel d'API) est déterminée. Le mode ReAct décrit le processus de prise de décision par l'IA. Ceux qui sont intéressés peuvent explorer cela plus en détail.

LangChain propose plusieurs types d'Agents pour différentes situations.

Outils

Je pense que les Outils sont mieux compris comme des APIs, pré-emballés avec diverses APIs fonctionnelles conçues pour étendre les capacités de LLM. LLM détermine quelle API spécifique appeler pour accomplir une tâche.

Boîtes à Outils

Les Boîtes à Outils fournissent généralement à LLM non pas un ou deux outils, mais un ensemble d'outils pour offrir à LLM plus d'options lors de l'accomplissement des tâches.

AgentExecutor

Le proxy exécuteur est responsable de l'exécution de l'outil (API) sélectionné par LLM. Le pseudo-code pour cette exécution est le suivant :

next_action = agent.get_action(...)
while next_action != AgentFinish:
    observation = run(next_action)
    next_action = agent.get_action(..., next_action, observation)
return next_action

Bien que le processus d'exécution ne soit pas compliqué, l'exécuteur gère de nombreuses questions détaillées, notamment :

  1. Gérer la situation où l'agent sélectionne un outil inexistant
  2. Gérer les situations d'erreur d'outil
  3. Gérer les situations où l'agent produit une sortie qui ne peut pas être résolue comme un appel d'outil
  4. Problèmes de débogage.

Démarrage Rapide

Cette section présente l'utilisation de base de l'Agent de LangChain.

1. Chargement de LLM

Tout d'abord, chargeons le modèle de langage (LLM) que nous utiliserons pour contrôler l'agent.

from langchain_openai import ChatOpenAI

llm = ChatOpenAI(model="gpt-3.5-turbo", temperature=0)

2. Définition des Outils

Ensuite, définissons quelques outils pour que l'Agent les appelle. Nous écrirons une fonction Python très simple pour calculer la longueur du mot en entrée.

from langchain.agents import tool

@tool
def get_word_length(word: str) -> int:
    """Retourne la longueur du mot."""
    return len(word)

get_word_length.invoke("abc")

Remarque : Les commentaires de fonction sont très importants. Ils indiquent à LLM quel problème il peut résoudre en les appelant. La fonction get_word_length indique à LLM qu'elle peut calculer la longueur d'un mot.

3

Définir un ensemble d'outils

tools = [get_word_length]

3. Créer une invite

Maintenant, créons une invite. Comme OpenAI Function Calling a été optimisé pour une utilisation d'outil, nous n'avons presque pas besoin d'instructions sur le raisonnement ou le format de sortie. Nous n'avons que deux variables d'entrée : input et agent_scratchpad. input représente la question d'entrée de l'utilisateur, et agent_scratchpad est un espace réservé pour les instructions d'appel de l'agent, qui seront insérées dans le modèle d'invite lors de l'exécution de la commande d'appel de l'outil.

from langchain_core.prompts import ChatPromptTemplate, MessagesPlaceholder

invite = ChatPromptTemplate.from_messages(
    [
        (
            "système",
            "Vous êtes un assistant très puissant mais vous ne comprenez pas la situation actuelle.",
        ),
        ("utilisateur", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

4. Associer des outils à LLM

Comment l'agent sait-il quels outils il peut utiliser ?

Cela dépend de la fonction d'appel d'outil OpenAI (de nombreux modèles prennent en charge des fonctionnalités similaires). Nous devons simplement indiquer au modèle le format d'appel d'outil défini.

llm_avec_outils = llm.bind_tools(outils)

5. Créer un agent

Maintenant que nous avons intégré le contenu précédent, nous pouvons procéder à la création du programme proxy. Nous importerons les deux dernières fonctions utilitaires pratiques : une pour formater les étapes intermédiaires (actions de proxy, sorties d'outils) en messages d'entrée pouvant être envoyés au modèle, et une autre pour convertir les messages de sortie en actions de proxy/terminaisons de proxy.

from langchain.agents.format_scratchpad.openai_tools import format_to_openai_tool_messages
from langchain.agents.output_parsers.openai_tools import OpenAIToolsAgentOutputParser

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(x["intermediate_steps"]),
    }
    | invite
    | llm_avec_outils
    | OpenAIToolsAgentOutputParser()
)

Définition de l'exécuteur d'agent

from langchain.agents import AgentExecutor

agent_executor = AgentExecutor(agent=agent, outils=outils, verbeux=True)

Démontrons le fonctionnement du programme proxy à travers un exemple :

list(agent_executor.stream({"input": "Combien de lettres y a-t-il dans le mot 'eudca' ?"}))

Exemple de journal de sortie de l'agent

> Entrée dans une nouvelle chaîne d'exécution d'agent...

Invocation : Exécution de `get_word_length` avec le paramètre `{'word': 'educa'}`

Il y a 5 lettres dans le mot "educa".

> Chaîne d'exécution terminée.

À travers cet exemple, nous avons démontré le processus complet du programme proxy.

Ajout de la fonctionnalité de mémoire à l'agent

Si nous voulons que l'agent se souvienne des conversations précédentes, c'est en fait assez simple : il nous suffit d'insérer le contenu retourné par l'IA dans l'invite et de le soumettre à l'IA ensemble.

Modification du modèle d'invite

Ci-dessous, nous modifions le modèle d'invite pour inclure une variable de modèle d'historique de conversation.

from langchain.prompts import MessagesPlaceholder

CLE_MEMOIRE = "historique_chat"
invite = ChatPromptTemplate.from_messages(
    [
        (
            "système",
            "Vous êtes un excellent assistant, mais pas doué pour calculer la longueur des mots.",
        ),
        MessagesPlaceholder(variable_name=CLE_MEMOIRE),
        ("utilisateur", "{input}"),
        MessagesPlaceholder(variable_name="agent_scratchpad"),
    ]
)

Modification de la définition du processus de l'agent

Modifier la définition du processus de l'agent pour fournir des données d'historique de conversation pour le modèle d'invite, comme le montre le code suivant, en ajoutant la gestion des paramètres historique_chat.

agent = (
    {
        "input": lambda x: x["input"],
        "agent_scratchpad": lambda x: format_to_openai_tool_messages(
            x["intermediate_steps"]
        ),
        "historique_chat": lambda x: x["historique_chat"],
    }
    | invite
    | llm_avec_outils
    | OpenAIToolsAgentOutputParser()
)
agent_executor = AgentExecutor(agent=agent, outils=outils, verbeux=True)
chat_history = []

input1 = "Combien de lettres y a-t-il dans le mot 'educa'?"
result = agent_executor.invoke({"input": input1, "chat_history": chat_history})
chat_history.extend(
    [
        HumanMessage(content=input1),
        AIMessage(content=result["output"]),
    ]
)
agent_executor.invoke({"input": "Ce mot existe-t-il vraiment?", "chat_history": chat_history})