Comprendre

Les agents autonomes, prochaine frontière du legal NLP ?

La capacité à raisonner est une des pierres d'achoppement du Deep Learning appliquée au droit. Une solution consiste à s'appuyer sur des ontologies avec les imperfections associées. Une autre approche vient d'émerger avec les agents "autonomes".

Raphaël d'Assignies
24 mai 2023

Je soulignais, dans mon billet introductif de janvier dernier (déjà !), que la capacité à raisonner était une des pierres d’achoppement du Deep Learning appliquée au droit. Une solution consiste à s’appuyer sur des ontologies avec les imperfections associées. Une autre approche vient d’émerger avec les agents « autonomes ». La question est désormais de savoir si ces outils sont capables de pallier les limitations précédemment évoquées.

Comment souvent, je vous propose de procéder en deux temps. Tout d’abord, une explication à la fois historique et fonctionnelle du phénomène. Et ensuite, pour améliorer notre culture d’interface, on mettra en œuvre un projet concret d’agents à partir du framework langchain.

Depuis quelques semaines, la communauté des développeurs s’active pour intégrer de nouveaux outils associés au LLM pour les rendre autonomes. Ce développement récent, qui fait écho pour certains à la notion plus générale d’Intelligence artificielle générale (AGI), est une piste très intéressante pour explorer de nouveaux services en matière de legaltech.

Au préalable, il est utile de rappeler les grandes lignes de l’évolution très récente des LLM et de son outil de pilotage qu’est le prompting.

Cette évolution, très rapide, se découpe en plusieurs étapes clés.

La première a consisté à utiliser le prompting de manière figée pour donner des instructions au modèle en vu de compléter une séquence de mots. On s’intéresse plus à l’efficience de l’instruction qu’à sa mise en musique dans un ensemble de tâches plus complexes.

La deuxième vise à rendre dynamique l’instruction donnée au modèle. Par exemple, on peut vouloir donner une instruction générique de type « Réponds à la question suivante à la manière de…, {Question} — {Contexte} — {Réponse} » où la question et le contexte sont insérés dans le modèle de prompt.

La troisième technique consiste justement à enchaîner les modèles de prompt dynamique dans une application qui fait appel à des outils tiers. Dans notre exemple précédent, la question peut émaner d’un internaute et le contexte être issu d’une recherche réalisée dans une base documentaire métier. L’enchaînement consiste à mettre en musique ces différents éléments. Grâce à cette technique, on peut demander au LLM d’effectuer de nombreuses tâches tout au long de la chaîne de traitement : répondre à une question en fonction du contexte, classifier un contenu, résumé un texte, formater une réponse…

C’est l’approche que l’on retrouve dans la plupart des outils de recherche documentaire. C’est cette technique que nous avons utilisée dans notre service de démonstration concernant le RGPD.

La quatrième, qui est fondée sur le concept d’agents autonomes, est apparue très récemment dans différents projets : AutoGPT, BabyAGI, CAMEL et Generative Agents dont certains connaissent un succès fulgurant.

Il faut avouer que le concept est passionnant car il réunit un ensemble de techniques qui permettent de rendre un système en partie autonome. Cette autonomie est loin d’être aussi parfaite que les démonstrations parfois emphatiques qui fleurissent sur le web le montrent mais le concept est posé et, dans notre domaine, il y a énormément de cas d’usage à en tirer. Avant de les évoquer, il nous faut décrire les quatre piliers de l’autonomie puis leur traduction fonctionnelle et concrète.

Les conditions de l’autonomie

Je me m’attarderai pas sur la notion d’autonomie, et moins encore sur les idées associées par certains comme l’AGI. Ce qui m’intéresse ici, c’est de décrire les outils techniques qui permettent de bâtir une application capable d’effectuer des tâches avec une – relative – « autonomie ».

Quatre aptitudes sont nécessaires pour atteindre l’autogestion.

La première aptitude peut être qualifiée de capacité à penser sur sa propre pensée ou métacognition.

Pour acquérir cette capacité, il est nécessaire de pouvoir :

  • découper un problème posé en plusieurs étapes ;
  • faire appel aux outils mis à sa disposition ;
  • éventuellement critiquer et modifier son raisonnement en fonction des résultats des étapes intermédiaires ;
  • éventuellement prioriser et planifier les actions à accomplir pour résoudre un problème.

Pour obtenir ces effets, les agents utilisent la capacité de compréhension des LLM. En effet, ces modèles possèdent une aptitude à comprendre le langage de sorte qu’ils sont capables de l’utiliser comme un outil de métacognition. Entendons-nous sur ce point pour ne pas partir dans des débats inutiles : la compréhension n’est pas celle des humains mais une capacité à analyser des phrases pour en extraire les différents sens.

Concrètement, cet effet technique est obtenu grâce au prompting dans ses différentes déclinaisons : Chain of Thought, Calibrate Before Use, Self-Asking, Recursively Criticize and Improve, Automatic Prompt Engineering,

La deuxième capacité est la mémorisation.

En effet, le raisonnement nécessite de stocker les résultats intermédiaires pour pouvoir les réutiliser ultérieurement. Il ne suffit pas de découper un problème, encore faut-il se souvenir des entrées/sorties de chaque solution apportée aux sous-problèmes !

Dans cette optique, il est fait appel à différentes techniques de mémorisation. La principale technique consiste à stocker les informations dans des bases de données. Ces informations sont ensuite réinjectées au fur et à mesure dans la chaîne et font l’objet éventuellement de différents traitements par le LLM.

Le troisième pilier est l’interaction avec le monde externe

Les LLM embarquent une connaissance tirée de leur jeu d’apprentissage. Mais, comme on l’a décrit, cette connaissance est figée, sujette à caution et le modèle présente des symptômes d’hallucinations.

Afin de le rendre autonome, il faut le doter d’outils qui vont lui permettre de dynamiser sa connaissance et alimenter sa capacité à raisonner.

Ces outils, de différentes natures, peuvent être déclarés et découverts par le système. Les outils les plus évidents sont ceux connectés au monde : Wikipedia, la recherche Google, un service météo ou de prévision boursière. On peut également envisager de faire appel à un humain expert dans un domaine.

Pour les insérer, il suffit de les décrire sous forme textuelle. Le modèle ira puiser dans la description l’opportunité ou non de s’en servir en fonction du problème qu’il a à résoudre.

Mais on peut aller plus loin. Après tout, il existe des hubs de modèles comme Hugging Face. Chaque modèle fait l’objet d’une description (model card). Pourquoi ne pas laisser le LLM aller rechercher dans ces centaines de modèles celui qui convient le mieux au problème qu’il a à résoudre ? C’est précisément l’objet d’un papier comme HuggingGPT. De même, il peut utiliser la documentation d’une API pour en découvrir les fonctionnalités et les utiliser dynamiquement en se servant de sa capacité à programmer. On voit toutes les perspectives que cela ouvre.

La mise en œuvre concrète peut donner lieu à de multiples variations et ne donne pas toujours les résultats attendus. Elle repose, encore une fois, sur la mise en musique de prompts enchaînés.

La mise en œuvre : une subtile orchestration d’agents et de prompts

Il y a de nombreuses approches et des raffinements divers pour mettre en œuvre les agents. En ce domaine, la créativité est de mise. Je vais prendre la manière dont BabyAGI aborde le problème. Le framework LangChain implémente cette approche comme l’illustre le code suivant.

import wikipedia

from langchain.docstore.document import Document
from langchain.chat_models import ChatOpenAI
from langchain.experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner
from langchain.llms import OpenAI
from langchain import Wikipedia
from langchain.agents.tools import Tool

def search(query:str=None) -> Document : 
    wikipedia.set_lang("fr")
    return Document(page_content=wikipedia.summary(query))

llm = OpenAI(temperature=0)
tools = [
    Tool(
        name = "Search",
        func=search,
        description="utile pour connaître des informations générales et encyclopédiques"
    )
]
model = ChatOpenAI(temperature=0)
planner = load_chat_planner(model)
executor = load_agent_executor(model, tools, verbose=True)
agent = PlanAndExecute(planner=planner, executer=executor, verbose=True)
agent.run("Quelle est la date de naissance de la femme du Général de Gaulle ?")

On aperçoit une séparation en plusieurs agents :

1. Un premier est chargé de la planification, autrement dit, il découpe le problème en plusieurs tâches élémentaires ; il agit comme un analyseur de problème.

Le prompt est d’ailleurs explicite :

SYSTEM_PROMPT = (
    "Let's first understand the problem and devise a plan to solve the problem."
    " Please output the plan starting with the header 'Plan:' "
    "and then followed by a numbered list of steps. "
    "Please make the plan the minimum number of steps required "
    "to accurately complete the task. If the task is a question, "
    "the final step should almost always be 'Given the above steps taken, "
    "please respond to the users original question'. "
    "At the end of your plan, say '<END_OF_PLAN>'"
)

2. Le deuxième est chargé de l’exécution. C’est lui qui va effectivement utiliser les outils à disposition pour effectuer des actions et retourner un résultat.

Son prompt est dynamique, c’est-à-dire qu’il est remplacé à chaque étape par l’action à effectuer, l’action précédente et la mémoire des étapes précédentes.

Dans notre exemple, l’outil est Wikipedia. On prend soin de mettre une description adéquate. En l’occurrence, elle est très lapidaire mais elle peut être très spécialisée et plus détaillée selon l’objectif. Elle va permettre à notre agent d’exécution de trouver les informations dont il a besoin pour répondre à la question qui est assez simple : « Quelle est la date de naissance de la femme du Général de Gaulle ? »

Si on exécute le code infra, voici la sortie :

> Entering new PlanAndExecute chain...
steps=[Step(value='Rechercher le nom de la femme du Général de Gaulle.'), Step(value='Trouver des sources fiables pour obtenir la date de naissance de la femme du Général de Gaulle.'), Step(value='Donner la date de naissance de la femme du Général de Gaulle.\n')]

L’agent planificateur a découpé le problème en trois étapes assez claires. On remarquera qu’elles correspondent à des verbes d’action. Maintenant l’agent exécuteur va utiliser l’outil qu’il a à disposition pour s’acquitter des actions demandées.

> Entering new AgentExecutor chain...
Action:
```
{
  "action": "Search",
  "action_input": "Quel est le nom de la femme du Général de Gaulle?"
}
```

Observation: page_content="Philippe de Gaulle, né le 28 décembre 1921 à Paris, est un officier général de marine et homme politique français.\n......" metadata={}
Thought:The name of the wife of General de Gaulle was Yvonne de Gaulle.

Action:
```
{
  "action": "Final Answer",
  "action_input": "Yvonne de Gaulle"
}
```
Step: Rechercher le nom de la femme du Général de Gaulle.
Response: Yvonne de Gaulle

> Entering new AgentExecutor chain...
Action:
```
{
  "action": "Search",
  "action_input": "Date de naissance Yvonne de Gaulle"
}
```
Observation: page_content="Yvonne de Gaulle, née Vendroux le 22 mai 1900 ...." metadata={}
Thought:I found a reliable source that states Yvonne de Gaulle was born on May 22, 1900 in Calais and died on November 8, 1979 in Paris.

Action:
```
{
  "action": "Final Answer",
  "action_input": "Yvonne de Gaulle est née le 22 mai 1900 à Calais et est décédée le 8 novembre 1979 à Paris."
}
```
(...)
*****

Step: Donner la date de naissance de la femme du Général de Gaulle.
Response: Yvonne de Gaulle est née le 22 mai 1900.
> Finished chain.

Pour chaque étape, on voit un découpage en :

  • le nom de l’étape qui correspond au sous-problème ;
  • l’action à effectuer. En l’occurrence search qui correspond au seul outil disponible mais l’action peut être également de donner la réponse ;
  • l’observation qui est le résultat de l’utilisation de l’outil. Dans notre exemple un texte issu de Wikipedia correspondant à la requête.

Cet enchaînement est lié à la mise en musique de différents agents qui utilisent, en réalité, la capacité des LLM à découper un problème, à produire une recherche dans un texte ou à répondre à une question.

Pour arriver à ce résultat, il existe plusieurs approches ayant chacune ses avantages et ses inconvénients. Elles reposent toutes sur un enchaînement programmatique de prompts et d’appels à des outils externes. La grande différence se situe souvent dans la manière de réfléchir aux tâches passées, les réagencer ou les modifier dynamiquement, etc.

Dans le prochain article, nous allons appliquer ce concept au droit. Dans cette optique, on va programmer :

  • un wrapper autour de Legifrance et de Judilibre ;
  • des classes qui vont simuler un agent autonome chargé de répondre à des questions juridiques avec les outils à disposition.