Techniques de prompts

La notion de prompt est clairement associée pour le grand public aux modèles de langage depuis l’ouverture de ChatGPT, mais désigne en fait une amorce tout à fait courante consistant à poser une question et/ou donner une instruction que ce soit dans la “vraie vie” ou dans un contexte de programmation informatique.

Appliqué aux LLMs, le prompt engineering, élevé au rang de compétence en soi, voire de métier, recoupe les différentes méthodes et processus destinés à construire les invites de commande textuelles les plus clairement structurées possibles afin d’être interprétées et comprises au mieux par un modèle, et le guider pour produire la meilleure réponse.

Le métier de Prompt Engineer existera-t-il encore l'année prochaine ? Difficile à dire, mais en tout cas ce type d’ingénierie repose d’ores et déjà sur un ensemble de guidelines communes et de stratégies bien établies afin d’améliorer la qualité des instructions fournies aux LLMs en se fondant sur les qualités de compréhension du langage naturel de ces mêmes modèles. Ces lignes directrices de base pour la formulation des prompts sont notamment résumées sur cette page du site d’OpenAI et valables pour tous les modèles de langage : https://platform.openai.com/docs/guides/prompt-engineering. On y trouve entre autres les conseils suivants pour la rédaction de ses prompts : conduire le modèle à adopter un persona système, insérer des exemples, délimiter explicitement les différentes sections du prompt, décomposer une instruction complexe en sous-tâches atomiques séquencées, etc...


Les méthodes incitatives de prompting sont fondées sur le fait que la manière dont sont formulées les invites influencent fortement la teneur du contenu généré (et on sait pourquoi et comment). Mais il faut garder à l'esprit que la structuration des prompts est une technique parmi d'autres d'interactions avec les LLMs, les deux autres moyens étant le RAG et le fine-tuning.


Techniques de Prompt Engineering

Concernant les manières plus élaborées et stratégiques de communication avec les LLMs, voici un apercu très synthétique des principales techniques de prompt en vigueur, sachant que le raffinement peut être poussé très loin et la littérature sur le sujet très abondante.

  1. Zero-shot learning : désigne le fait de poser une question ou donner une instruction à un modèle dans un prompt “de base” sans contexte supplémentaire
  2. One-shot learning : désigne le fait de fournir dans le prompt un exemple clair et précis de ce que le modèle doit imiter pour générer sa réponse
  3. Few-shot learning : par extension du one-shot learning, désigne le fait d’ajouter plusieurs exemples directement dans le prompt afin de conditionner explicitement le contenu généré à suivre ces exemples
  4. Chain-of-Thought Prompting (CoT) ou multi-step prompts : désigne le fait de combiner les méthodes Zero-Shot et Few-Shot pour induire un modèle de langage à articuler un chaînage de raisonnements avant de répondre à une question finale. Dans ce processus incitant le LLM à enchainer sa pensée en une séquence ordonnée de petites étapes, l’output d’une étape sert d’input pour l’étape suivante, et pour peu que le LLM soit couplé à des outils permettant d’étendre ses capacités, on peut donc transformer un prompt bien conçu en un agent autonome capable de produire une suite d’instructions intermédiaires, et de les exécuter.

Question code, la plupart du temps ces différentes stratégies s’insèrent dans des templates de prompt (ou modèles de prompt) adaptables et réutilisables par le biais de variables, ces templates formant alors des patterns grâce auxquels on peut élaborer des prompts personnalisés.

Exemples

Tous les exemples suivants sont basés sur le framework Langchain, et pour l'accès au LLM on utilise la classe HuggingFaceEndpoint qui permet d'atteindre l'API d'inférence d'HuggingFace (ou des endpoints d'inférence déjà déployés, mais ce n'est pas le cas ici).

# pip install --upgrade --quiet huggingface_hub langchain langchain-community

# Installation des principaux packages 
from langchain_community.llms import HuggingFaceEndpoint
from langchain.chains import LLMChain
import os

# Initialisation des variables d'environnement
os.environ["HUGGINGFACEHUB_API_TOKEN"] = <YOUR_HF_TOKEN>
os.environ["HF_HUB_DISABLE_IMPLICIT_TOKEN"] = "1"
token = os.environ.get("HUGGINGFACEHUB_API_TOKEN")

# Composant Langchain d'interfaçage avec le LLM
repo_id = "mistralai/Mistral-7B-Instruct-v0.2"
llm = HuggingFaceEndpoint(
    repo_id=repo_id, max_length=128, temperature=0.5, token=token
)

Prompt Zero-shot : traduction

from langchain_core.prompts import PromptTemplate

template = """
    Translate the following text in {language}:

    {text}

    """

prompt = PromptTemplate(
        input_variables=["text", "language"],
        template=template,
    )
TEXT = "Large language models (LLMs) today, such as GPT-3.5 Turbo, GPT-4, and Claude 3, are tuned to follow instructions and are trained on large amounts of data. Large-scale training makes these models capable of performing some tasks in a 'zero-shot' manner. Zero-shot prompting means that the prompt used to interact with the model won't contain examples or demonstrations. The zero-shot prompt directly instructs the model to perform a task without any additional examples to steer it."
LANGUAGE = "french"
llm_chain = LLMChain(prompt=prompt, llm=llm)
print(llm_chain.run(text=TEXT, language=LANGUAGE))

Résultat

Les grands modèles de langage (LLMs) actuels, tels que GPT-3.5 Turbo, GPT-4 et Claude 3, sont ajustés pour suivre des instructions et ont été entraînés sur de grandes quantités de données. L'entraînement à grande échelle rend ces modèles capables de réaliser certaines tâches à la manière d'un 'zero-shot'. La demande zero-shot signifie que le contenu utilisé pour interagir avec le modèle ne contiendra pas d'exemples ou de démonstrations. La demande zero-shot donne directement des instructions au modèle pour effectuer une tâche sans aucun autre exemple pour le diriger.

Prompt One-shot : extraction d'entités nommées

L'exemple de texte passé en argument est un titre extrait d'un composant EAD du fonds Samivel décrit dans Calames.

from langchain import FewShotPromptTemplate 

template = """
    You are a named entities extractor and your task is to take the unstructured text provided and convert it into a well-organized array format using JSON. 
    Identify each named person and place entity mentioned in the text and use it as key-value in the JSON object.
    The returned Json array must then look like :
    [
      {{"person": "the person full name"}},
      {{"place": "the place full name"}}
    ]

    {text}

    """

prompt = PromptTemplate(
        input_variables=["text"],
        template=template,
    )
TEXT = "Documents concernant un contentieux de deux particuliers, M. Bergamelli et Deblainville, contre l'expropriation de leur propriété pour le télécabine du Signal dans la station des Contamines-Montjoie"
llm_chain = LLMChain(prompt=prompt, llm=llm)
print(llm_chain.run(text=TEXT))

Résultat

[
    {"person": "M. Bergamelli"},
    {"person": "Deblainville"},
    {"place": "Contamines-Montjoie"},
    {"place": "la station des Contamines-Montjoie"},
    {"place": "le télécabine du Signal"}
     ]


A noter que dans les familles de LLM dédiées aux tâches de NLP, il existe bien sûr des modèles pré-entraînés pour la reconnaissance d'entités nommées. Par exemple :

from huggingface_hub import InferenceClient
HF_TOKEN = <your_huggingface_token>
def ner(
    query: str,
    model="Jean-Baptiste/camembert-ner"
) -> List[Dict[str, Any]]:
    client = InferenceClient(model=model,token=HF_TOKEN)
    ners =  client.token_classification(query)
    #return ",".join([x["word"] for x in ners])
    return json.dumps([{'entity_group': x['entity_group'], 'word': x['word']} for x in ners])

ner(""Henri Bosco is born in Avignon city"")
# returns [{"entity_group": "PER", "word": "Henri Bosco"}, {"entity_group": "LOC", "word": "Avignon"}]

Prompt Few-shot : indexation disciplinaire de publications

Les exemples sont extraits du dataset du BSO national produit par le MESRI

examples = [
  {"title":"Differential distribution of lipids in epidermis, gastrodermis and hosted Symbiodinium in the sea anemone Anemonia viridis","category":"""Biologie fondamentale"""},
  {"title":"On the use of 31P NMR for the quantification of hydrosoluble phosphorus-containing compounds in coral host tissues and cultured zooxanthellae","category":"""Biologie fondamentale"""},
  {"title":"A two-dimensional Riemann solver with self-similar sub-structure – Alternative formulation based on least squares projection","category":"""Sciences physiques et astronomie"""},
  {"title":"Asteroid orbits with Gaia using random-walk statistical ranging","category":"""Sciences physiques et astronomie"""},
  {"title":"THERMAP: a mid-infrared spectro-imager for space missions to small bodies in the inner solar system","category":"""Sciences physiques et astronomie"""},
  {"title":"Ablation of the Stimulatory G Protein α-Subunit in Renal Proximal Tubules Leads to Parathyroid Hormone-Resistance With Increased Renal Cyp24a1 mRNA Abundance and Reduced Serum 1,25-Dihydroxyvitamin D","category":"""Biologie fondamentale"""},
  {"title":"The necromass of the Posidonia oceanica seagrass meadow: fate, role, ecosystem services and vulnerability","category":"""Biologie fondamentale"""},
  {"title":"Phylogeographic analysis of the brown algaCutleria multifida(Tilopteridales, Phaeophyceae) suggests a complicated introduction history","category":"""Biologie fondamentale"""},
  {"title":"Fermionic current and Schwinger effect in de Sitter spacetime","category":"""Sciences physiques et astronomie"""},
  {"title":"Comparison of semiclassical and Wigner function methods in pair production in rotating fields","category":"""Sciences physiques et astronomie"""},
  {"title":"The size of the primary cilium and acetylated tubulin are modulated during adipocyte differentiation: Analysis of HDAC6 functions in these processes","category":"""Biologie fondamentale"""},
  {"title":"Using optimality models to improve the efficacy of parasitoids in biological control programmes","category":"""Biologie fondamentale"""},
  {"title":"The effect of CO2 and Nd:YAP lasers on CAD/CAM Ceramics: SEM, EDS and thermal studies","category":"""Sciences physiques et astronomie"""},
  {"title":"A numerical investigation of coorbital stability and libration in three dimensions","category":"""Sciences physiques et astronomie"""},
  {"title":"Dark matter as residual of topological changes","category":"""Sciences physiques et astronomie"""},
  {"title":"Yeast2.0: a new chapter","category":"""Biologie fondamentale"""},
  {"title":"Superlogic manifolds and geometric approach to quantum logic","category":"""Sciences physiques et astronomie"""},
  {"title":"Generalized flyby trajectories around elongated minor celestial bodies as a rotating mass dipole","category":"""Sciences physiques et astronomie"""},
  {"title":"GTC/CanariCam observations of (99942) Apophis","category":"""Sciences physiques et astronomie"""},
  {"title":"A Critical Review of the Costs of Advertising: a Transformative Consumer Research Perspective","category":"""Biologie fondamentale"""},
  {"title":"Super-catastrophic disruption of asteroids at small perihelion distances","category":"""Sciences physiques et astronomie"""},
  {"title":"THE INFRARED SPECTRAL PROPERTIES OF MAGELLANIC CARBON STARS","category":"""Sciences physiques et astronomie"""},
  {"title":"Fast inversion of Zeeman line profiles using central moments","category":"""Sciences physiques et astronomie"""},
  {"title":"Geographic distance, water circulation and environmental conditions shape the biodiversity of Mediterranean rocky coasts","category":"""Biologie fondamentale"""},
  {"title":"A DGTD method for the numerical modeling of the interaction of light with nanometer scale metallic structures taking into account non-local dispersion effects","category":"""Sciences physiques et astronomie"""},
  {"title":"Molecular characterization ofWolbachiainfection in bed bugs (Cimex lectularius) collected from several localities in France","category":"""Biologie fondamentale"""},
  {"title":"RISC-mediated control of selected chromatin regulators stabilizes ground state pluripotency of mouse embryonic stem cells","category":"""Biologie fondamentale"""},
  {"title":"Thermal and menthol stress induce different cellular events during sea anemone bleaching","category":"""Biologie fondamentale"""},
  {"title":"Born of Chaos","category":"""Biologie fondamentale"""},
  {"title":"Schwinger effect and backreaction in de Sitter spacetime","category":"""Sciences physiques et astronomie"""},
  {"title":"Chunk formation in immediate memory and how it relates to data compression","category":"""Biologie fondamentale"""},
  {"title":"Reducing Stereotype Threat With Embodied Triggers","category":"""Biologie fondamentale"""},
  {"title":"Inhomogeneous matter distribution and supernovae","category":"""Sciences physiques et astronomie"""},
  {"title":"Successive Phases of the Metabolic Response to Stress","category":"""Biologie fondamentale"""}
]

template = """
   User: {title} 
   AI: {category}
    """

prompt = PromptTemplate( input_variables=["title","category"], template=template )

prefix = """
You are an AI assistant expert in indexing scientific publications with two disciplinary categories denominated in French 'Sciences physiques et astronomie' and 'Biologie fondamentale'.
The user gives as input the title of a publication and you answer contains as output the correct category to classify the article.
Here are some examples of the desired output.
"""

suffix = """ 
User: {title} 
AI: 
""" 

few_shot_prompt_template = FewShotPromptTemplate( examples=examples, 
                                                 example_prompt=prompt, 
                                                 prefix=prefix, 
                                                 suffix=suffix, 
                                                 input_variables=["title"], 
                                                 example_separator="\n\n"
                                                 )

Pour voir un nouveau prompt formalisé selon le template incluant les exemples :

print(few_shot_prompt_template.format(title="New spectro-photometric characterization of the substellar object HR 2562 B using SPHERE"))

Voir le prompt

You are an AI assistant expert in indexing scientific publications with two disciplinary categories denominated in French 'Sciences physiques et astronomie' and 'Biologie fondamentale'.
The user gives as input the title of a publication and you answer contains as output the correct category to classify the article.

   User: Differential distribution of lipids in epidermis, gastrodermis and hosted Symbiodinium in the sea anemone Anemonia viridis 
   AI: Biologie fondamentale

   User: On the use of 31P NMR for the quantification of hydrosoluble phosphorus-containing compounds in coral host tissues and cultured zooxanthellae 
   AI: Biologie fondamentale

   User: A two-dimensional Riemann solver with self-similar sub-structure – Alternative formulation based on least squares projection 
   AI: Sciences physiques et astronomie

   User: Asteroid orbits with Gaia using random-walk statistical ranging 
   AI: Sciences physiques et astronomie

   User: THERMAP: a mid-infrared spectro-imager for space missions to small bodies in the inner solar system 
   AI: Sciences physiques et astronomie

   User: Ablation of the Stimulatory G Protein α-Subunit in Renal Proximal Tubules Leads to Parathyroid Hormone-Resistance With Increased Renal Cyp24a1 mRNA Abundance and Reduced Serum 1,25-Dihydroxyvitamin D 
   AI: Biologie fondamentale

   User: The necromass of the Posidonia oceanica seagrass meadow: fate, role, ecosystem services and vulnerability 
   AI: Biologie fondamentale

   User: Phylogeographic analysis of the brown algaCutleria multifida(Tilopteridales, Phaeophyceae) suggests a complicated introduction history 
   AI: Biologie fondamentale

   User: Fermionic current and Schwinger effect in de Sitter spacetime 
   AI: Sciences physiques et astronomie

   User: Comparison of semiclassical and Wigner function methods in pair production in rotating fields 
   AI: Sciences physiques et astronomie

   User: The size of the primary cilium and acetylated tubulin are modulated during adipocyte differentiation: Analysis of HDAC6 functions in these processes 
   AI: Biologie fondamentale

   User: Using optimality models to improve the efficacy of parasitoids in biological control programmes 
   AI: Biologie fondamentale

   User: The effect of CO2 and Nd:YAP lasers on CAD/CAM Ceramics: SEM, EDS and thermal studies 
   AI: Sciences physiques et astronomie

   User: A numerical investigation of coorbital stability and libration in three dimensions 
   AI: Sciences physiques et astronomie

   User: Dark matter as residual of topological changes 
   AI: Sciences physiques et astronomie

   User: Yeast2.0: a new chapter 
   AI: Biologie fondamentale

   User: Superlogic manifolds and geometric approach to quantum logic 
   AI: Sciences physiques et astronomie

   User: Generalized flyby trajectories around elongated minor celestial bodies as a rotating mass dipole 
   AI: Sciences physiques et astronomie

   User: GTC/CanariCam observations of (99942) Apophis 
   AI: Sciences physiques et astronomie

   User: A Critical Review of the Costs of Advertising: a Transformative Consumer Research Perspective 
   AI: Biologie fondamentale

   User: Super-catastrophic disruption of asteroids at small perihelion distances 
   AI: Sciences physiques et astronomie

   User: THE INFRARED SPECTRAL PROPERTIES OF MAGELLANIC CARBON STARS 
   AI: Sciences physiques et astronomie

   User: Fast inversion of Zeeman line profiles using central moments 
   AI: Sciences physiques et astronomie

   User: Geographic distance, water circulation and environmental conditions shape the biodiversity of Mediterranean rocky coasts 
   AI: Biologie fondamentale

   User: A DGTD method for the numerical modeling of the interaction of light with nanometer scale metallic structures taking into account non-local dispersion effects 
   AI: Sciences physiques et astronomie

   User: Molecular characterization ofWolbachiainfection in bed bugs (Cimex lectularius) collected from several localities in France 
   AI: Biologie fondamentale

   User: RISC-mediated control of selected chromatin regulators stabilizes ground state pluripotency of mouse embryonic stem cells 
   AI: Biologie fondamentale

   User: Thermal and menthol stress induce different cellular events during sea anemone bleaching 
   AI: Biologie fondamentale

   User: Born of Chaos 
   AI: Biologie fondamentale

   User: Schwinger effect and backreaction in de Sitter spacetime 
   AI: Sciences physiques et astronomie

   User: Chunk formation in immediate memory and how it relates to data compression 
   AI: Biologie fondamentale

   User: Reducing Stereotype Threat With Embodied Triggers 
   AI: Biologie fondamentale

   User: Inhomogeneous matter distribution and supernovae 
   AI: Sciences physiques et astronomie

   User: Successive Phases of the Metabolic Response to Stress 
   AI: Biologie fondamentale

User: New spectro-photometric characterization of the substellar object HR 2562 B using SPHERE 
AI:

Et pour générer la réponse sur un nouveau titre :

print(llm(few_shot_prompt_template.format(title="Immune Priming and Trans-Generational Protection From Parasites" ) ))

Résultat

Biologie fondamentale

Chain-of-Thought prompt (exemple basique)

from langchain.chains import SimpleSequentialChain, LLMChain

# init chain
template='''You are a librarian in charge of a project to {project}. 
Your first job is to list all the existing software (proprietary and open source, paid and free...) already used in libraries that can be compliant to the project'''
prompt=PromptTemplate(input_variables=[ "project"], template=template)
software_list_chain=LLMChain(prompt=prompt, llm=llm)

# next chain
template='''Given the software list {software},make a list of three recommended software programs knowing that the budget for this project is 0 dollars'''
prompt=PromptTemplate(input_variables=["software"], template=template)
recommandation_list_chain=LLMChain(prompt=prompt, llm=llm)

project = "create a digital library to put online digital reproductions of a heritage collection of photographs."

final_chain=SimpleSequentialChain(chains=[software_list_chain, recommandation_list_chain], verbose=True)
output=final_chain.run(project)

Résultat

> Entering new SimpleSequentialChain chain...
.

Here are some software that can be used for creating and managing digital libraries in libraries:

1. **Fedora Commons**: An open-source digital repository system that is widely used in libraries and cultural institutions. It allows for the management, preservation, and dissemination of digital content.
2. **DSpace**: Another popular open-source digital repository system that is widely used in libraries and academic institutions. It provides features such as metadata management, access control, and digital preservation.
3. **Islandora**: An open-source digital repository platform built on Fedora Commons that is designed for cultural heritage institutions. It includes a user-friendly interface and integrates with various tools and services for managing digital content.
4. **DigitalAssets**: A proprietary digital asset management system that is used by many libraries and cultural institutions. It provides features such as metadata management, access control, and digital preservation.
5. **ContentDM**: A proprietary digital preservation and access system developed by OCLC that is used by many libraries and archives. It allows for the management, preservation, and dissemination of digital content and provides features such as metadata management and access control.
6. **Greenstone**: An open-source digital library system that allows for the creation of searchable CD-ROMs and web-based digital libraries. It is particularly useful for creating digital libraries from large collections of text, images, and multimedia.
7. **PastPerfect Online**: A proprietary digital asset management system that is specifically designed for museums and cultural institutions. It provides features such as metadata management, access control, and digital preservation.
8. **CollectiveAccess**: An open-source digital collections management system that allows for the management of digital and physical collections. It includes features such as metadata management, access control, and digital preservation.
9. **MARCive Web**: A proprietary digital preservation and access system that allows for the preservation and dissemination of digital content in various formats. It integrates with ILS systems and provides features such as metadata management and access control.
10. **Preservica**: A proprietary digital preservation platform that is designed for long-term preservation of digital content. It provides features such as metadata management, access control, and digital preservation tools such as format migration and emulation.

These are just a few of the many software options
.

Given the budget constraint of 0 dollars, it is not possible to recommend any commercial or proprietary software as they typically require licensing fees. However, there are several open-source digital repository and digital library software options that can be used for free. Here are three recommended software programs:

1. **Fedora Commons**: An open-source digital repository system that is widely used in libraries and cultural institutions. It allows for the management, preservation, and dissemination of digital content. It is free to use and has a large community of developers and users.
2. **Greenstone**: An open-source digital library system that allows for the creation of searchable CD-ROMs and web-based digital libraries. It is particularly useful for creating digital libraries from large collections of text, images, and multimedia. It is free to use and has a large user community.
3. **Islandora**: An open-source digital repository platform built on Fedora Commons that is designed for cultural heritage institutions. It includes a user-friendly interface and integrates with various tools and services for managing digital content. It is free to use and has a growing community of developers and users.

These software options are free to use and have large communities of developers and users, making them good choices for a digital library project with a 0 dollar budget.

> Finished chain.

Chain-of-Thought prompt (plus complexe) : extraction d'entités nommées et de leurs identifiants wikidata (via une requête sur le Sparql endpoint de Wikidata).

Avec la notion de CoT, on bascule du prompt mobilisant les facultés conversationnelles d'un LLM au prompt (plus quelques fonctions additionnelles) intégrant le LLM dans un système d'agent autonome capable de raisonner et de fonctionner comme un cerveau humain, c'est-à dire de décomposer des tâches complexes en étapes plus petites, de planifier l'exécution de ces tâches, de faire appels à des outils externes pour augmenter son corpus initial d'informations en temps réel (appels à des API, webscrapping,...) etc... Les systèmes et applications d'agents autonomes, et maintenant multi-agents, méritant un billet complet, l'exemple ci-dessous ne fait qu'effleurer l'éventail des possibilités offertes.

from langchain.agents import Tool, initialize_agent, AgentType
from langchain.tools.base import StructuredTool
import requests
from typing import Optional, List, Dict, Any
import json

# helpers functions
def ner(
    query: str,
    model="Jean-Baptiste/camembert-ner"
) -> List[Dict[str, Any]]:
    from huggingface_hub import InferenceClient
    client = InferenceClient(model=model,token=HF_TOKEN)
    ners =  client.token_classification(query)
    #return ",".join([x["word"] for x in ners])
    return json.dumps([{'entity_group': x['entity_group'], 'word': x['word']} for x in ners])

def get_nested_value(o: dict, path: list) -> any:
    current = o
    for key in path:
        try:
            current = current[key]
        except:
            return None
    return current

def vocab_lookup(
    search: str,
    entity_type: str = "item",
    url: str = "https://www.wikidata.org/w/api.php",
    srqiprofile: str = None,
) -> Optional[str]:
    headers = {"Accept": "application/json"}
    if entity_type == "item":
        srnamespace = 0
        srqiprofile = "classic_noboostlinks" if srqiprofile is None else srqiprofile
    elif entity_type == "property":
        srnamespace = 120
        srqiprofile = "classic" if srqiprofile is None else srqiprofile
    else:
        raise ValueError("entity_type must be either 'property' or 'item'")

    params = {
        "action": "query",
        "list": "search",
        "srsearch": search,
        "srnamespace": srnamespace,
        "srlimit": 1,
        "srqiprofile": srqiprofile,
        "srwhat": "text",
        "format": "json",
    }

    response = requests.get(url, headers=headers, params=params)

    if response.status_code == 200:
        title = get_nested_value(response.json(), ["query", "search", 0, "title"])
        if title is None:
            return f"I couldn't find any {entity_type} for '{search}'. Please rephrase your request and try again"
        # if there is a prefix, strip it off
        return title.split(":")[-1]
    else:
        return "Sorry, I got an error. Please try again."

def run_sparql(
    query: str,
    url="https://query.wikidata.org/sparql",
) -> List[Dict[str, Any]]:
    headers = {"Accept": "application/json"}
    response = requests.get(
        url, headers=headers, params={"query": query, "format": "json"}
    )

    if response.status_code != 200:
        return "That query failed. Perhaps you could try a different one?"
    print(response.json())
    results = get_nested_value(response.json(), ["results", "bindings"])
    return json.dumps(results)

# Langchain tools
tools = [
    StructuredTool.from_function(
        func=ner,
        name="NER extraction",
        description="useful when you need to extract named entities from a text. Use this more than the other tools if the question is about extracting named entities",
        handle_tool_error=True,
    ),
     Tool(
        name="ItemLookup",
        func=(lambda x: vocab_lookup(x, entity_type="item")),
        description="useful for when you need to know the wikidata q-number for an item",
        handle_tool_error=True,
    ),
    Tool(
        name="PropertyLookup",
        func=(lambda x: vocab_lookup(x, entity_type="property")),
        description="useful for when you need to know the wikidata p-number for a property",
        handle_tool_error=True,
    ),
    Tool(
        name="SparqlQueryRunner",
        func=run_sparql,
        description="useful for getting results from a wikibase",
        handle_tool_error=True,
    ),
]

# Prompt template
template = """
You are an AI assistant expert in NLP task and wikidata requests.
With the following text :

{text}

Your task is to follow this plan : 
1. Extract all the named entities in the text.
2. For each entity value, retrieve the wikipedia q-number
"""

prompt = PromptTemplate(
        input_variables=["text"],
        template=template,
    )

llm_chain = LLMChain(llm=llm,
    prompt=PromptTemplate.from_template(template)
)

agent = initialize_agent(
    tools=tools,
    agent_type=AgentType.ZERO_SHOT_REACT_DESCRIPTION,
    llm=llm,
    verbose=True
)

input = "Henri Bosco is a french writer"
print(agent.run(input))

Résultat

> Entering new AgentExecutor chain...
 I need to extract named entities from the text
Action: NER extraction
Action Input: "Henri Bosco is a french writer"
Observation: [{"entity_group": "PER", "word": "Henri Bosco"}]
Thought: I now have the name of the writer, I need to find his q-number
Action: ItemLookup
Action Input: "Henri Bosco"
Observation: Q1399264
Thought: I now know the final answer
Final Answer: Henri Bosco is a French writer. His q-number is Q1399264.
> Finished chain.
Henri Bosco is a French writer. His q-number is Q1399264.

Juste pour le fun, autre exemple avec les mêmes fonctions et un autre type d'agent spécifiquement orienté sur la planification de tâche

from langchain_experimental.plan_and_execute import PlanAndExecute, load_agent_executor, load_chat_planner

planner = load_chat_planner(llm)
executor = load_agent_executor(llm, tools, verbose=True)
agent = PlanAndExecute(planner=planner, executor=executor, verbose=True)

prompt = "Extracts all named entities from the following sentence 'Henri bosco is a french writer'. If there are entities of type Person, run the right queries on wikidata to find the birth dates of each entity Person."

print(agent.run({"input":prompt}))

Résultat

> Entering new PlanAndExecute chain...
steps=[Step(value="Identify the named entities in the sentence 'Henri bosco is a french writer'."), Step(value='Classify each named entity into its respective type.'), Step(value='For each named entity of type Person, perform a query on Wikidata to find the corresponding ID.'), Step(value='Use the Wikidata ID to run a query to find the birth date of the person.'), Step(value='Store the birth date in a data structure for later use.'), Step(value='Given the above steps taken, please respond to the users original question.\n\n')]

> Entering new AgentExecutor chain...
Action:

{
  "action": "NER extraction",
  "action_input": {
    "query": "Henri bosco is a french writer",
    "model": "Jean-Baptiste/camembert-ner"
  }
}

Thought:
Observation: [{"entity_group": "PER", "word": "Henri bosco"}]
Thought: I have extracted 'Henri bosco' as a PERSON.

Now, I need to extract the named entities 'French' and 'writer'.

Action:

{
  "action": "NER extraction",
  "action_input": {
    "query": "Henri bosco is a french writer",
    "model": "Jean-Baptiste/camembert-ner"
  }
}

Thought:
Observation: [{"entity_group": "PER", "word": "Henri bosco"}]
 I have already extracted 'Henri bosco'.

> Finished chain.
*****

Step: Identify the named entities in the sentence 'Henri bosco is a french writer'.

Response:  I have already extracted 'Henri bosco'.
> Entering new AgentExecutor chain...
Question: input question to answer
Thought: Consider previous steps. I already identified 'Henri bosco' as a person.
Action:

{
  "action": "Final Answer",
  "action_input": "Henri bosco is a person."
}
*****

Step: Classify each named entity into its respective type.

Response: Henri bosco is a person.

> Entering new AgentExecutor chain...
Thought: I need to use the ItemLookup tool to find the Wikidata ID for the person named 'Henri bosco'.

Action:

{
  "action": "ItemLookup",
  "action_input": {
    "tool_input": "Henri bosco"
  }
}

Observation: Q1399264
Thought: I will now respond with the Wikidata ID for 'Henri bosco'.

Action:

{
  "action": "Final Answer",
  "action_input": "Q1399264"
}

> Finished chain.
*****

Step: For each named entity of type Person, perform a query on Wikidata to find the corresponding ID.

Response: Q1399264

> Entering new AgentExecutor chain...
Thought: I need to use the ItemLookup tool to get the birth date property and its value for the given person.
Action:

{
  "action": "PropertyLookup",
  "action_input": {
    "tool_input": "P569"
  }
}

Observation: P569
Thought: Now I need to use the SparqlQueryRunner tool to get the birth date.

Action:

{
  "action": "SparqlQueryRunner",
  "action_input": {
    "tool_input": "{\"sparql\": \"SELECT ?birth_date WHERE { wd:Q1399264 wdt:P569 ?birth_date . }\", \"format\": \"json\"}"
  }
}

Observation: That query failed. Perhaps you could try a different one?
Thought: I'll try a more specific query.

Action:

{
  "action": "SparqlQueryRunner",
  "action_input": {
    "tool_input": "{\"sparql\": \"SELECT ?birth_date WHERE { wd:Q1399264 wdt:P569 ?birth_date . FILTER(lang(?birth_date)='en') }\", \"format\": \"json\"}"
  }
}

Observation: That query failed. Perhaps you could try a different one?
Thought: I'll try a different property.
...........................
> Entering new AgentExecutor chain...
Thought: I need to use the SparqlQueryRunner tool to get the birth date of Henri Bosco from Wikidata.

Action:

{
  "action": "SparqlQueryRunner",
  "action_input": {
    "tool_input": "SELECT ?birth_date WHERE { wd:Q1399264 wdt:P569 ?birth_date . }"
  }
}


{'head': {'vars': ['birth_date']}, 'results': {'bindings': [{'birth_date': {'datatype': 'http://www.w3.org/2001/XMLSchema#dateTime', 'type': 'literal', 'value': '1888-11-16T00:00:00Z'}}]}}

Observation: [{"birth_date": {"datatype": "http://www.w3.org/2001/XMLSchema#dateTime", "type": "literal", "value": "1888-11-16T00:00:00Z"}}]
Thought: I have the birth date. Now I can store it in a data structure for later use.

Action:
{
  "action": "Final Answer",
  "action_input": {
    "birth_date": "1888-11-16T00:00:00Z"
  }
}
> Finished chain.

Bonus

Simple en apparence, la formulation des prompts en tant que points d'entrée fondamentaux de l'interaction homme-LLM, est donc une étape très importante, même lorsque l'on s'en tient au zero-shot prompting sans partir dans des stratégies complexes. Du coup, il peut arriver de passer du temps et beaucoup d'itérations avec le LLM avant de trouver la bonne invite qui génère le contenu attendu, et un outil qui permet de sauvegarder ses prompts qui vont bien serait le bienvenue !

Voici une application hyper simple développée avec Streamlit qui permet de se constituer sa petite bibliothèque de prompts et de les tester directement dans un chatbot.

enter image description here

Et pour trouver des sources d'inspiration, vous pouvez consulter cette page sur le site d'Anthropic (il faut se créer un compte gratuitement pour y accéder), aller checker les settings des assistants HuggingChat créés par la communautés d'HuggingFace, cette page de ressources de prompts d'OpenWebUI, et bien d'autres encore...

Le code de l'application est disponible sur ce repo Github.

L'app est déployée dans cet espace HuggingFace

"> ');