Salut, agent en formation ! Emma ici, de retour après une autre exploration nocturne fascinante, parfois frustrante, du monde des agents AI. Si tu es comme moi, tu as probablement passé les derniers mois à entendre des murmures (ou des cris à pleins poumons) au sujet des agents AI et tu t’es dit : « D’accord, ça a l’air génial, mais aussi… qu’est-ce que c’est, vraiment, et comment je fais pour qu’un agent fasse quelque chose pour moi ? »
Eh bien, tu es au bon endroit. Aujourd’hui, nous n’allons pas seulement parler de ce que les agents AI *sont* en théorie. Nous allons retrousser nos manches et aborder une question qui tracasse beaucoup d’entre vous (et moi, jusqu’à récemment) : Comment puis-je créer un agent AI simple qui peut réellement naviguer sur internet et répondre à des questions spécifiques ?
Oublions le battage médiatique un instant. Nous allons nous concentrer sur du concret. Nous allons parler de « je peux réellement faire fonctionner ça sur mon ordinateur portable. » Parce que, soyons honnêtes, voir c’est croire, non ?
Mes Bêtises (et Éclaircies) avec un Bot Navigateur
Lorsque j’ai commencé à expérimenter l’idée d’un agent capable d’utiliser internet, j’avais de grandes visions. Je voulais qu’il recherche des sujets complexes, compare les prix sur une douzaine de sites, et soit en gros mon assistant numérique personnel. Ce que j’ai obtenu au début, c’était beaucoup d’erreurs, quelques scrapers web cassés, et un profond sentiment d’inadéquation. Mes premières tentatives étaient comme essayer d’apprendre à un tout-petit à faire fonctionner une machine complexe – beaucoup d’enthousiasme, zéro compréhension de la mécanique sous-jacente.
Le plus grand obstacle ? Faire en sorte qu’un modèle AI interagisse avec internet de manière significative et contrôlée. C’est une chose de poser une question à ChatGPT à laquelle il connaît déjà la réponse. C’est tout autre chose de lui demander d’aller chercher de nouvelles informations, de les interpréter, et ensuite d’agir en conséquence. C’est là que la partie « agent » prend tout son sens.
Après beaucoup d’essais et d’erreurs, j’ai réalisé que la clé n’était pas de construire une entité super complexe et omnisciente dès le départ. Il s’agissait de commencer petit, avec une tâche très spécifique, et de construire à partir de là. Et c’est exactement ce que nous allons faire aujourd’hui. Nous allons créer un agent navigateur basique qui peut se rendre sur un site web spécifique, extraire des informations, et faire un rapport.
Qu’est-ce Qu’un Agent AI Navigateur ?
Avant d’explorer le code, clarifions rapidement. Un agent AI, à sa base, est un programme AI conçu pour effectuer une tâche spécifique de manière autonome. Il observe son environnement (dans notre cas, internet), prend des décisions en fonction de ses objectifs, puis agit. Un agent AI *navigateur* utilise spécifiquement des capacités de navigation web dans son arsenal.
Pensez-y de cette manière : vous dites à votre agent, « Trouve-moi la météo actuelle à Londres. » Au lieu de simplement puiser dans ses connaissances internes, un agent navigateur ouvrirait (idéalement) un navigateur web, naviguerait vers un site météorologique, trouverait l’information, puis vous la transmettrait. C’est la différence entre poser une question à un bibliothécaire à laquelle il connaît déjà la réponse et lui demander d’aller trouver un livre, de lire un chapitre spécifique, puis de vous en faire un résumé.
Pour notre agent simple, nous allons utiliser quelques bibliothèques Python courantes qui rendent tout ce processus beaucoup moins intimidant.
Les Outils Dont Nous Aurons Besoin
Vous n’avez pas besoin d’un superordinateur pour cela. Juste votre configuration de développement habituelle. Voici ce que nous allons utiliser :
- Python : Notre langage de programmation de prédilection. (J’utilise 3.9, mais 3.8+ devrait faire l’affaire.)
requests: Une bibliothèque fantastique pour effectuer des requêtes HTTP (c’est-à-dire, récupérer des pages web).BeautifulSoup(bs4) : C’est notre sorcier de l’analyse web. Il nous aide à naviguer et à extraire des données de HTML.- Une API de Grand Modèle de Langue (LLM) : Nous aurons besoin d’accéder à un LLM comme les modèles GPT d’OpenAI ou Claude d’Anthropic. Pour ce tutoriel, je vais supposer que vous avez une clé API OpenAI.
Tout d’abord, préparons notre environnement. Si vous n’avez pas ces bibliothèques installées, ouvrez votre terminal ou invite de commande et exécutez :
pip install requests beautifulsoup4 openai
Et assurez-vous d’avoir votre clé API OpenAI à disposition. Vous devrez la définir comme variable d’environnement ou la passer directement dans votre script. Pour simplifier, je vais l’afficher directement, mais pour la production, les variables d’environnement sont toujours préférables.
Notre Mission : Trouver un Article Spécifique sur un Blog
Fixons un objectif concret et réalisable. La mission de notre agent sera de :
- Se rendre sur un blog spécifique (utilisons une URL de blog fictif pour l’instant, ou même agent101.net si vous vous sentez aventureux !).
- Trouver un titre de blog contenant un mot-clé spécifique (par exemple, « débutant »).
- Extraire l’URL de cet article de blog.
- Résumer le contenu de cet article de blog en utilisant un LLM.
C’est un excellent point de départ car cela combine l’interaction web avec la compréhension d’un LLM. C’est comme demander à votre assistant numérique de « trouver le dernier article sur les agents AI pour débutants sur agent101.net et me dire ce dont il s’agit. »
Étape 1 : Récupérer la Page Web
La première étape pour tout agent navigateur est de « voir » la page web. Nous allons utiliser la bibliothèque requests pour cela. Créons un script simple :
import requests
def fetch_webpage(url):
try:
response = requests.get(url)
response.raise_for_status() # Lève une HTTPError pour les mauvaises réponses (4xx ou 5xx)
return response.text
except requests.exceptions.RequestException as e:
print(f"Erreur lors de la récupération de l'URL {url} : {e}")
return None
# Utilisons une URL de test pour l'instant.
# Remplacez par une vraie URL de blog contenant des articles si vous voulez tester !
blog_url = "https://www.example-blog.com/articles" # Utilisez une vraie URL ici !
html_content = fetch_webpage(blog_url)
if html_content:
print("Contenu de la page web récupéré avec succès.")
# Nous allons traiter ce contenu à l'étape suivante
else:
print("Échec de la récupération de la page web.")
Une petite note sur les URLs : Pour que cela fonctionne, vous aurez besoin d’une vraie URL de blog. Si vous n’en avez pas sous la main, vous pouvez utiliser un site comme `http://quotes.toscrape.com/` pour tester le scraping simple, mais pour les articles de blog, trouvez un vrai blog. Même ce site, agent101.net, pourrait fonctionner si vous adaptez les sélecteurs CSS par la suite !
Étape 2 : Analyser le HTML et Trouver les Liens des Articles
Maintenant que nous avons le HTML brut, c’est comme avoir un grand livre sans index. BeautifulSoup est notre indexeur. Nous devons lui indiquer comment trouver les titres des articles et leurs liens correspondants.
C’est la partie la plus délicate car la structure HTML de chaque site web est différente. Vous devrez « inspecter » l’élément sur le site cible. Faites un clic droit sur un titre d’article sur votre blog choisi et sélectionnez « Inspecter » (ou « Inspecter l’élément »). Recherchez des modèles communs comme les balises <h2> pour les titres, ou les balises <a> pour les liens, souvent imbriquées dans une <div> avec un nom de classe spécifique.
Pour la démonstration, supposons une structure de blog commune où les titres des articles sont dans des balises <h2>, et leurs liens se trouvent dans une balise <a> juste à l’intérieur ou avant eux.
from bs4 import BeautifulSoup
def find_article_links(html_content, keyword):
soup = BeautifulSoup(html_content, 'html.parser')
article_data = []
# Il s'agit d'un exemple générique. Vous devrez ajuster les sélecteurs en fonction du site ciblé.
# Modèles courants : div avec classe 'article-item', h2 avec classe 'article-title', etc.
# Supposons que les articles se trouvent dans des éléments 'div' avec la classe 'post'
# et que les titres sont dans des balises 'h2' à l'intérieur de ces divs, avec un lien à l'intérieur.
# Exemple : Cherchez toutes les balises qui pourraient contenir des titres d'articles
# Ensuite, vérifiez s'ils sont liés.
# Une approche plus solide pourrait être de trouver d'abord les conteneurs :
article_containers = soup.find_all('div', class_='article-card') # Ajustez cette classe !
if not article_containers:
# Retour par défaut ou essayez un autre sélecteur si le premier ne fonctionne pas
article_containers = soup.find_all('article') # Un autre tag courant
for container in article_containers:
title_tag = container.find('h2') # Ou 'h3', 'a', etc.
link_tag = container.find('a', href=True) # Trouvez le premier lien à l'intérieur du conteneur
if title_tag and link_tag:
title = title_tag.get_text(strip=True)
href = link_tag['href']
# Assurez-vous que l'URL est absolue si elle est relative
if href.startswith('/'):
full_url = requests.compat.urljoin(blog_url, href)
else:
full_url = href
if keyword.lower() in title.lower():
article_data.append({'title': title, 'url': full_url})
return article_data
# ... (code précédent pour fetch_webpage) ...
if html_content:
search_keyword = "beginner" # Quel article cherchons-nous ?
found_articles = find_article_links(html_content, search_keyword)
if found_articles:
print(f"\nArticles trouvés contenant '{search_keyword}':")
for article in found_articles:
print(f"- Titre : {article['title']}\n URL : {article['url']}")
# Pour cet exemple, prenons juste le premier trouvé
target_article = found_articles[0]
else:
print(f"\nAucun article trouvé contenant '{search_keyword}'.")
target_article = None
else:
target_article = None
Personnalisation Cruciale : Les lignes comme soup.find_all('div', class_='article-card') et container.find('h2') sont des espaces réservés. Vous *devez* les adapter en fonction de la structure HTML réelle du blog que vous ciblez. C’est là qu’examiner la page web devient essentiel. Mon meilleur conseil est de commencer large (par exemple, `soup.find_all(‘a’)` pour obtenir tous les liens) puis de restreindre avec des classes ou des IDs.
Étape 3 : Extraction du Contenu de l’Article Ciblé
Une fois que nous avons l’URL de notre article cible, nous devons récupérer son contenu puis extraire le texte principal. Cela est souvent plus simple que d’analyser une page d’index, car la plupart des contenus d’articles se trouvent dans une balise de contenu principal <div> ou <article>.
# ... (code précédent) ...
import openai
import os
# Définir votre clé API OpenAI
# Meilleure pratique : os.environ.get("OPENAI_API_KEY")
# Pour cet exemple, attribution directe :
openai.api_key = "YOUR_OPENAI_API_KEY" # REMPLACEZ CECI PAR VOTRE CLÉ RÉELLE !
def extract_article_text(article_url):
article_html = fetch_webpage(article_url)
if not article_html:
return None
soup = BeautifulSoup(article_html, 'html.parser')
# Cela dépend fortement de la structure du site web.
# Modèles courants : trouver un div avec la classe 'entry-content', 'article-body', 'main-content'
# Ou juste les balises à l'intérieur de la balise principale de l'article.
# Essayons de trouver des zones de contenu commun.
content_div = soup.find('div', class_='entry-content') # Commun pour WordPress
if not content_div:
content_div = soup.find('article') # Un autre bon tag général
if not content_div:
# En dernier recours, récupérez simplement toutes les balises de paragraphe
paragraphs = soup.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
# Si nous avons trouvé un div/article de contenu spécifique, extrayez tout le texte des paragraphes
paragraphs = content_div.find_all('p')
article_text = "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
return article_text
if target_article:
print(f"\nRécupération du contenu pour : {target_article['title']}")
article_text_content = extract_article_text(target_article['url'])
if article_text_content:
# print(article_text_content[:500]) # Imprimer les 500 premiers caractères pour vérifier
print("\nTexte de l'article extrait avec succès. Passons à la summarisation.")
else:
print("Échec de l'extraction du texte de l'article.")
article_text_content = "" # Assurez-vous qu'il n'est pas None pour l'étape suivante
else:
article_text_content = ""
Encore une fois, la ligne soup.find('div', class_='entry-content') est votre cible principale pour la personnalisation. Utilisez l’inspecteur de votre navigateur !
Étape 4 : Résumé avec un LLM
Enfin, le moment où nous faisons intervenir l’intelligence artificielle ! Nous allons alimenter le texte extrait à un LLM et lui demander de faire un résumé. C’est là que “l’intelligence” de notre agent brille vraiment.
# ... (code précédent) ...
def summarize_text_with_llm(text):
if not text:
return "Aucun texte fourni pour le résumé."
# Tronquez le texte s'il est trop long pour la fenêtre de contexte du modèle
# GPT-3.5-turbo a une fenêtre de contexte de 16k, mais il est préférable de rester raisonnable.
# Pour un débutant, viser 4000-8000 tokens est sûr.
# Environ 1 token = 4 caractères pour le texte anglais.
max_tokens_for_input = 12000 # Ajustez en fonction de la capacité et des considérations de coût de votre modèle
if len(text) > max_tokens_for_input * 4: # Estimation brute en caractères
text = text[:max_tokens_for_input * 4]
print(f"Avertissement : Texte tronqué à ~{max_tokens_for_input} tokens pour le traitement LLM.")
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # Ou "gpt-4", "claude-3-opus-20240229", etc.
messages=[
{"role": "system", "content": "Vous êtes un assistant utile qui résume les articles de blog de manière concise."},
{"role": "user", "content": f"Veuillez résumer le post de blog suivant pour moi :\n\n{text}"}
],
temperature=0.7, # Contrôle de la diversité. Moins pour des résumés plus ciblés.
max_tokens=500 # Nombre maximum de tokens pour le résumé lui-même
)
return response.choices[0].message.content.strip()
except openai.APIError as e:
print(f"Erreur API OpenAI : {e}")
return "Échec en raison d'une erreur API."
except Exception as e:
print(f"Une erreur inattendue s'est produite lors du résumé : {e}")
return "Échec en raison d'une erreur inattendue."
if article_text_content:
print("\nDemande de résumé LLM...")
summary = summarize_text_with_llm(article_text_content)
print("\n--- Résumé de l'Article ---")
print(summary)
else:
print("\nImpossible de résumer : Aucun contenu d'article disponible.")
Et voilà ! Un agent d’IA basique mais fonctionnel qui peut naviguer, extraire et résumer. Ce n’est plus juste de la théorie ; c’est un bout de code concret que vous pouvez exécuter.
Mettre Tout Cela Ensemble (Script Complet)
Voici le script complet pour un copié-collé et un test faciles. N’oubliez pas de remplacer les URLs de remplacement et votre clé API OpenAI !
import requests
from bs4 import BeautifulSoup
import openai
import os
# --- Configuration ---
# Remplacez par l'URL de votre blog. Assurez-vous qu'il contient des articles !
TARGET_BLOG_URL = "https://www.example-blog.com/articles"
SEARCH_KEYWORD = "beginner" # Le mot clé à rechercher dans les titres d'articles
OPENAI_API_KEY = "YOUR_OPENAI_API_KEY" # REMPLACEZ ÇA ! Ou utilisez os.environ.get("OPENAI_API_KEY")
# Définir la clé API OpenAI
openai.api_key = OPENAI_API_KEY
# --- Fonctions auxiliaires ---
def fetch_webpage(url):
"""Récupère le contenu HTML d'une URL donnée."""
try:
response = requests.get(url, timeout=10) # Ajout d'un délai d'attente
response.raise_for_status()
return response.text
except requests.exceptions.RequestException as e:
print(f"Erreur lors de la récupération de l'URL {url} : {e}")
return None
def find_article_links(html_content, base_url, keyword):
"""Analyse le HTML pour trouver les titres d'articles et les URLs correspondants à un mot clé."""
soup = BeautifulSoup(html_content, 'html.parser')
article_data = []
# --- POINT DE PERSONNALISATION 1 : Ajustez ces sélecteurs pour votre blog cible ---
# Inspectez la page du blog pour trouver les bonnes balises et classes HTML pour les conteneurs d'articles, les titres et les liens.
article_containers = soup.find_all('div', class_='article-card') # Commun pour les cartes/prévisualisations d'articles
if not article_containers:
article_containers = soup.find_all('article') # Une autre balise commune pour les articles individuels
if not article_containers:
print("Avertissement : Impossible de trouver des balises conteneurs d'articles communes. Essai d'une recherche plus large.")
# Solution de secours : rechercher simplement tous les liens qui pourraient être des liens d'articles
all_links = soup.find_all('a', href=True)
for link in all_links:
title_text = link.get_text(strip=True)
if keyword.lower() in title_text.lower():
href = link['href']
full_url = requests.compat.urljoin(base_url, href)
article_data.append({'title': title_text, 'url': full_url})
return article_data # Retourne tôt si seules des liens larges ont été trouvés
for container in article_containers:
# Cherchez le titre et le lien dans chaque conteneur
title_tag = container.find(['h2', 'h3', 'a']) # Les titres se trouvent souvent dans h2/h3 ou avec un lien direct
link_tag = container.find('a', href=True)
if title_tag and link_tag:
title = title_tag.get_text(strip=True)
href = link_tag['href']
# Construire l'URL complète si elle est relative
full_url = requests.compat.urljoin(base_url, href)
if keyword.lower() in title.lower():
article_data.append({'title': title, 'url': full_url})
return article_data
def extract_article_text(article_url):
"""Récupère une page d'article et extrait son contenu textuel principal."""
article_html = fetch_webpage(article_url)
if not article_html:
return None
soup = BeautifulSoup(article_html, 'html.parser')
# --- POINT DE PERSONNALISATION 2 : Ajustez ces sélecteurs pour le contenu principal de l'article ---
# Recherchez la zone de contenu principal de l'article (par exemple, div avec la classe 'entry-content', 'article-body')
content_area = soup.find('div', class_='entry-content')
if not content_area:
content_area = soup.find('article', class_='main-article-content') # Un autre modèle courant
if not content_area:
content_area = soup.find('div', id='content') # Encore un autre ID courant
if content_area:
paragraphs = content_area.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
else:
# Solution de secours : obtenir toutes les balises de paragraphes si la zone de contenu spécifique n'a pas été trouvée
print(f"Avertissement : Zone de contenu spécifique non trouvée pour {article_url}. Extraction de tous les paragraphes.")
paragraphs = soup.find_all('p')
return "\n".join([p.get_text(strip=True) for p in paragraphs if p.get_text(strip=True)])
def summarize_text_with_llm(text):
"""Utilise un LLM sur le texte fourni."""
if not text or len(text.strip()) < 50: # Longueur minimale de texte pour tenter la synthèse
return "Pas assez de contenu pour la synthèse."
# Troncature simple pour rester dans les limites de la fenêtre de contexte typique
# Envisagez d'utiliser un compteur de jetons approprié pour plus de précision si nécessaire
max_chars_for_llm = 40000 # Environ 10k jetons pour GPT-3.5-turbo 16k contexte
if len(text) > max_chars_for_llm:
print(f"Avertissement : Texte tronqué de {len(text)} à {max_chars_for_llm} caractères pour le LLM.")
text = text[:max_chars_for_llm]
try:
response = openai.chat.completions.create(
model="gpt-3.5-turbo", # Vous pouvez utiliser "gpt-4" si vous avez accès et souhaitez une meilleure qualité
messages=[
{"role": "system", "content": "Vous êtes un assistant utile qui résume les articles de blog de manière concise et claire."},
{"role": "user", "content": f"Veuillez fournir un résumé concis du blog suivant, en soulignant les points clés :\n\n{text}"}
],
temperature=0.6, # Un peu moins aléatoire pour les résumés
max_tokens=600 # Max jetons pour le résumé généré
)
return response.choices[0].message.content.strip()
except openai.APIError as e:
print(f"Erreur API OpenAI : {e}")
return f"Échec en raison d'une erreur API : {e}"
except Exception as e:
print(f"Une erreur inattendue s'est produite lors de la synthèse : {e}")
return f"Échec en raison d'une erreur inattendue : {e}"
# --- Logique principale de l'agent ---
def run_browser_agent():
print(f"Démarrage de l'agent de navigation pour {TARGET_BLOG_URL} afin de trouver des articles sur '{SEARCH_KEYWORD}'...")
# Étape 1 : Récupérer la page principale du blog
blog_html = fetch_webpage(TARGET_BLOG_URL)
if not blog_html:
print("L'agent n'a pas pu récupérer la page principale du blog. Sortie.")
return
# Étape 2 : Trouver des articles correspondant au mot clé
found_articles = find_article_links(blog_html, TARGET_BLOG_URL, SEARCH_KEYWORD)
if not found_articles:
print(f"Aucun article trouvé contenant '{SEARCH_KEYWORD}' sur {TARGET_BLOG_URL}. Agent terminé.")
return
print(f"\nTrouvé {len(found_articles)} articles potentiels contenant '{SEARCH_KEYWORD}' :")
for i, article in enumerate(found_articles):
print(f"{i+1}. Titre : {article['title']}\n URL : {article['url']}")
# Pour cet exemple, traitons le premier article trouvé
target_article = found_articles[0]
print(f"\nTraitement du premier article trouvé : '{target_article['title']}' à {target_article['url']}")
# Étape 3 : Extraire le contenu de l'article cible
article_content = extract_article_text(target_article['url'])
if not article_content:
print(f"L'agent n'a pas pu extraire le contenu de {target_article['url']}. Impossible de résumer.")
return
print(f"\nContenu de l'article extrait (premiers 200 caractères) : {article_content[:200]}...")
# Étape 4 : Résumer le contenu en utilisant un LLM
print("\nDemande de synthèse de l'article au LLM...")
summary = summarize_text_with_llm(article_content)
print("\n--- Rapport final de l'agent ---")
print(f"Titre de l'article : {target_article['title']}")
print(f"URL de l'article : {target_article['url']}")
print("\nRésumé :")
print(summary)
print("\n--- Tâche de l'agent terminée ---")
if __name__ == "__main__":
run_browser_agent()
Points à Retenir pour Votre Propre Aventure d’Agent
Ce petit projet n’est que la pointe de l’iceberg, mais il vous montre les mécanismes de base. Voici ce que j’ai appris et ce que vous devriez garder à l’esprit :
- Commencez Petit, Pensez Précis : Ne tentez pas de construire Skynet dès le premier jour. Choisissez une tâche très spécifique et réalisable pour votre agent. « Trouver une recette de lasagne végétalienne » est mieux que « prépare le dîner pour moi. »
- Le Web Scraping est un Far West : Chaque site web est différent. Vous passerez du temps à inspecter des éléments dans votre navigateur pour obtenir les bons sélecteurs CSS ou XPath. C’est le travail de base, mais c’est essentiel. Les sites web changent, donc votre agent pourrait nécessiter des ajustements occasionnels.
- La Gestion des Erreurs est Votre Amie : Des choses vont mal se passer. Les sites web seront hors ligne, votre connexion internet va tomber, les appels API échoueront. Ajoutez des blocs
try-exceptpour gérer ces problèmes avec élégance. - Les LLMs sont Intelligents, mais Ont Besoin de Directives : La qualité de la sortie de votre LLM dépend fortement de votre prompt. Soyez clair, concis et dites-lui exactement ce que vous attendez. Soyez également attentif aux limites de la fenêtre de contexte et aux coûts.
- Ceci est une Fondation : Nous avons construit un agent à tour unique. Les véritables agents impliquent souvent plusieurs étapes, une logique conditionnelle (par exemple, « si je trouve ça, alors fais ça »), et de la mémoire. Mais vous avez maintenant les éléments de base.
J’espère que voir cet agent simple prendre vie démystifie un peu le concept de « d’agent AI ». Ce n’est pas de la magie ; c’est simplement une automatisation intelligente basée sur des outils existants. Allez-y, bricolez et construisez vos propres petits helpers numériques !
Bonne programmation,
Emma Walsh, agent101.net
Articles Connexes
- Comment les agents AI maîtrisent plusieurs langues facilement
- J’ai rendu mon agent AI utile (Voici comment)
- J’ai construit un agent AI en 2026 : Mon avis honnête
🕒 Published: