Intermédiaire
🧠 Fondamentaux
20 XP
0 personnes ont réussi
Citer ses sources comme Perplexity
Ce qui fait la force de Perplexity par rapport a ChatGPT, c'est la transparence. Chaque affirmation est accompagnee d'une source. L'utilisateur peut verifier, approfondir, ou juger de la fiabilite. Un agent qui cite ses sources inspire confiance.
Le mecanisme est simple : quand l'agent utilise des resultats de recherche pour formuler sa reponse, il doit indiquer d'ou vient chaque information avec des references numerotees [1], [2], [3], et lister les sources a la fin.
On va construire deux fonctions :
La premiere, construire_prompt_avec_sources(question, resultats_recherche), prend la question de l'utilisateur et la liste des resultats de recherche (comme ceux retournes par rechercher_web de l'exercice 3). Elle construit un prompt qui demande au LLM de repondre en citant ses sources avec le format [1], [2], etc.
La seconde, extraire_sources_citees(reponse, resultats_recherche), prend la reponse du LLM et la liste des resultats. Elle detecte quelles sources ont ete effectivement citees dans la reponse (en cherchant les [1], [2], etc.) et renvoie un dictionnaire avec la reponse nettoyee et la liste des sources utilisees.
prompt = construire_prompt_avec_sources("Nouveautes Python 3.13 ?", resultats) Le prompt contient les sources numerotees et l'instruction de les citer
reponse = "Python 3.13 introduit le free threading [2] et d'autres ameliorations [1]." extraire_sources_citees(reponse, resultats) renvoie { "reponse": "Python 3.13 introduit le free threading [2] et d'autres ameliorations [1].", "sources": [ {"numero": 1, "titre": "Python 3.13", "url": "https://python.org/3.13"}, {"numero": 2, "titre": "What's new", "url": "https://docs.python.org"}, ] }
Tests (4/5)
Le prompt contient les sources numerotees
resultats = [
{"titre": "Source A", "url": "https://a.com", "extrait": "Contenu A"},
{"titre": "Source B", "url": "https://b.com", "extrait": "Contenu B"},
]
prompt = construire_prompt_avec_sources("Ma question ?", resultats)
assert "[1]" in prompt and "[2]" in prompt, "Les sources doivent etre numerotees"
assert "Source A" in prompt and "Source B" in prompt
assert "Ma question" in prompt
assert "https://a.com" in prompt
Extraction des sources citees
resultats = [
{"titre": "Alpha", "url": "https://alpha.com", "extrait": "..."},
{"titre": "Beta", "url": "https://beta.com", "extrait": "..."},
{"titre": "Gamma", "url": "https://gamma.com", "extrait": "..."},
]
reponse = "Selon [1], Python est genial. D'apres [3], c'est le meilleur langage."
r = extraire_sources_citees(reponse, resultats)
assert len(r["sources"]) == 2, f"Deux sources citees, pas {len(r['sources'])}"
urls = [s["url"] for s in r["sources"]]
assert "https://alpha.com" in urls and "https://gamma.com" in urls
Aucune source citee
resultats = [{"titre": "X", "url": "https://x.com", "extrait": "..."}]
reponse = "Je ne sais pas."
r = extraire_sources_citees(reponse, resultats)
assert r["sources"] == [], "Sans citation, la liste de sources doit etre vide"
assert r["reponse"] == "Je ne sais pas."
Numero de source hors limites ignore
resultats = [{"titre": "Seule", "url": "https://seule.com", "extrait": "..."}]
reponse = "Selon [1] et [5], blabla."
r = extraire_sources_citees(reponse, resultats)
assert len(r["sources"]) == 1, "Seule la source [1] existe, [5] doit etre ignoree"
assert r["sources"][0]["numero"] == 1
+ 0 tests cachés
Indices (3 disponibles)
Solution officielle
import re
def construire_prompt_avec_sources(question, resultats_recherche):
sources_text = ""
for i, r in enumerate(resultats_recherche, 1):
sources_text += f"[{i}] {r['titre']}\n"
sources_text += f"URL: {r['url']}\n"
sources_text += f"Contenu: {r['extrait']}\n\n"
prompt = f"""Reponds a la question suivante en utilisant les sources fournies.
Cite tes sources en utilisant le format [1], [2], etc. dans ton texte.
Chaque affirmation importante doit etre accompagnee de sa source.
Sources disponibles :
{sources_text}
Question : {question}"""
return prompt
def extraire_sources_citees(reponse, resultats_recherche):
numeros = set()
for match in re.finditer(r'\[(\d+)\]', reponse):
numeros.add(int(match.group(1)))
sources = []
for num in sorted(numeros):
if 1 <= num <= len(resultats_recherche):
r = resultats_recherche[num - 1]
sources.append({
"numero": num,
"titre": r["titre"],
"url": r["url"]
})
return {
"reponse": reponse,
"sources": sources
}