Accéder au contenu principal

Qwen-Agent : Un guide avec un projet de démonstration

Apprenez à utiliser Qwen-Agent et Qwen3 pour créer une extension de résumé de page Web en temps réel.
· 12 min lire

Explorez l'IA

Dans cet article de blog, je vous expliquerai comment créer une extension de navigateur basée sur Qwen-Agent qui résume le contenu de n'importe quelle page Web en temps réel. Nous vous expliquons comment :

  • Utilisez localement le modèle Qwen3 d'Alibaba via Ollama
  • Utilisez Qwen-Agent pour des messages-guides structurés et l'utilisation d'outils.
  • Créez une extension Chrome avec des mises à jour d'interface utilisateur en continu
  • Ajout d'un backend FastAPI pour la prise en charge de la synthèse en temps réel

À la fin, vous disposerez d'un agent de synthèse capable de fonctionner hors ligne, alimenté par Qwen3, qui lit et résume les pages web dans votre navigateur en un seul clic.

Nous tenons nos lecteurs informés des dernières nouveautés en matière d'IA en leur envoyant The Median, notre lettre d'information gratuite du vendredi qui analyse les principaux sujets de la semaine. Abonnez-vous et restez à la pointe de la technologie en quelques minutes par semaine :

Qu'est-ce que Qwen-Agent ?

Qwen-Agent est un framework Python léger construit par Alibaba pour développer de puissantes applications LLM en utilisant la technologie Qwen3 de modèles. Il se concentre sur le soutien :

  • Agents qui suivent les instructions
  • Utilisation d'outils et appel de fonctions
  • Mémoire et planification multi-tours
  • Des messages structurés pour vous guider de manière flexible

À la base, Qwen-Agent simplifie le développement d'agents d'intelligence artificielle modulaires dotés de solides capacités de raisonnement et d'exécution de code. Il comprend des composants atomiques tels que des wrappers LLM et des outils, ainsi que des classes de haut niveau Agent pour l'orchestration des flux de travail.

Qwen-Agent fonctionne à la fois avec des API basées sur le cloud (comme Alibaba DashScope) et des runtimes locaux compatibles avec OpenAI, comme vLLM et Ollama. Il facilite la création d'applications LLM puissantes :

  • Intégration rapide via FastAPI
  • Streaming intégré pour les interfaces utilisateur frontales
  • Chaînes de fonctions personnalisables
  • Confidentialité et performance hors ligne

Aperçu du projet : Extension du résumeur Web en temps réel

Dans cette section, nous allons créer une extension de résumé en temps réel basée sur Qwen-Agent. Il s'agit notamment de

  • Un backend FastAPI qui accepte le texte d'une page web et renvoie un résumé.
  • Une extension Chrome pop-up pour capturer le contenu visible de la page
  • Une interface utilisateur en continu est générée sous la forme d'un résumé
  • Backend alimenté par Qwen3:1.7B fonctionnant localement via Ollama

Passons en revue chaque partie de ce projet.

Étape 1 : Configuration du backend Qwen3

Dans cette étape, nous allons couvrir tous les fichiers de code que nous devons mettre en place pour que notre backend fonctionne de manière synchrone avec notre extension.

Étape 1.1 : Dépendances et bibliothèques 

Commencez par installer ollama sur votre système. Ensuite, listez toutes les dépendances Python requises pour le backend dans requirements.txt.. Cela permet d'assurer une configuration cohérente de l'environnement entre les machines et les conteneurs.

fastapi
uvicorn
python-dateutil
python-dotenv
qwen-agent[code_interpreter]
matplotlib

Voici un aperçu de l'utilisation de chaque bibliothèque :

  • qwen-agent[code_interpreter]: Il s'agit d'un cadre d'agent de base avec un support d'utilisation d'outils, permettant l'exécution de code et le raisonnement structuré.
  • fastapi et uvicorn: Ces bibliothèques alimentent ensemble le backend de l'API asynchrone et servent votre logique de résumé.
  • python-dotenv et python-dateutil: Ces bibliothèques utilitaires gèrent les variables d'environnement et le traitement de la date et de l'heure (utile pour la mise à l'échelle future).
  • matplotlib: Il est utilisé pour le graphique ou l'affichage de sorties visuelles (facultatif). 

Si vous souhaitez exécuter ce projet localement, exécutez la commande suivante pour installer toutes les dépendances :

pip install -r requirements.txt

Étape 1.2 : Configuration de DockerFile (optionnel)

Maintenant, configurons un Dockerfile qui crée un environnement Python minimal avec notre app et expose le port 7864, permettant à l'extension Chrome d'envoyer des requêtes au backend FastAPI.

FROM python:3.10

WORKDIR /app
COPY . .

RUN pip install --no-cache-dir -r requirements.txt

EXPOSE 7864

CMD ["uvicorn", "app:app", "--host", "0.0.0.0", "--port", "7864"]

Cette configuration est optionnelle si vous choisissez d'exécuter le projet localement en utilisant Uvicorn au lieu de Docker.

Étape 1.3 : Servir Qwen avec Ollama

Ensuite, vous devrez extraire et servir le modèle Qwen3 localement à l'aide d'Ollama. Cette étape permet l'inférence hors ligne en hébergeant le service LLM sur votre machine.

ollama pull qwen3:1.7b
ollama serve 

Étape 1.4 : Construction du serveur de compression FastAPI

Le fichier app.py définit un serveur FastAPI qui diffuse en temps réel des résumés du contenu des pages web à l'aide d'un modèle Qwen3:1.7B exécuté localement via Qwen-Agent.

Étape 1.4.1 : Importations

Commencez par importer FastAPI, les utilitaires de diffusion en continu et les composants Qwen-Agent tels que Assistant, qui englobe le modèle et ses capacités d'outil.

import re
from fastapi import FastAPI, Request
from fastapi.responses import StreamingResponse
from pydantic import BaseModel
from typing import List, Dict, Any
from qwen_agent.agents import Assistant
import logging

Étape 1.4.2 : Initialisation de l'application FastAPI

Ensuite, nous définissons un schéma simple RequestData pour recevoir du contenu, et nous créons une instance de bot Assistant. 

app = FastAPI()
class RequestData(BaseModel):
    content: str
bot = Assistant(
    llm={
        "model": "qwen3:1.7b",
        "model_server": "http://localhost:11434/v1",
        "api_key": "EMPTY"
    },
    function_list=["code_interpreter"],
    system_message="You are a summarization assistant. Directly output the cleaned summary of the given text without any reasoning, self-talk, thoughts, or internal planning steps. Do not include phrases like 'I think', 'maybe', 'let's', 'the user wants', or anything not part of the final summary. Your output must look like it was written by an editor, not a model."
)

Dans le code ci-dessus, bot est une instance de la classe Assistant de Qwen-Agent, configurée pour interagir avec le modèle Qwen3:1.7B desservi localement via Ollama. Il utilise l'outil intégré code_interpreter pour analyser le contenu et exécuter le code Python si nécessaire. Un site system_message personnalisé guide son comportement pour générer des résumés concis de pages web.

Note : Vous pouvez utiliser des modèles plus petits comme qwen3:0.6b ou qwen3:1.7b pour réduire considérablement le temps de réponse et l'utilisation de la mémoire. Ceci est particulièrement utile pour les tâches de résumé plus rapides sans trop de frais de raisonnement.

Étape 1.4.3 : Itinéraire de synthèse

Enfin, un point de terminaison lit le contenu entrant et produit un résumé en direct au fur et à mesure qu'il est généré par le modèle. Il utilise une sortie en continu afin que l'utilisateur n'ait pas à attendre que la réponse complète soit générée.

@app.post("/summarize_stream_status")
async def summarize_stream_status(data: RequestData):
    user_input = data.content
    def stream():
        try:
            yield "🔍 Reading content on website...\n"
            print(" Received text:", user_input[:200])
            messages = [
            {"role": "system", "content": "You are a summarization assistant. Directly output the cleaned summary of the given text without any reasoning, self-talk, thoughts, or internal planning steps. Do not include phrases like 'I think', 'maybe', 'let's', 'the user wants', or anything not part of the final summary. Your output must look like it was written by an editor, not a model."},
            {"role": "user", "content": "<nothink>\nSummarize the following text clearly and concisely. Do not include any internal thoughts, planning, or reasoning. Just return the final summary:\n\n" + user_input + "\n</nothink>"}
            ]
            yield "🧠 Generating summary...\n"
            result = bot.run(messages)
            result_list = list(result)
            print(" Raw result:", result_list)
            # Extract summary
            last_content = None
            for item in reversed(result_list):
                if isinstance(item, list):
                    for subitem in reversed(item):
                        if isinstance(subitem, dict) and "content" in subitem:
                            last_content = subitem["content"]
                            break
                if last_content:
                    break
            if not last_content:
                yield " No valid summary found.\n"
                return
            summary = re.sub(r"</?think>", "", last_content)
            summary = re.sub(
                r"(?s)^.*?(Summary:|Here's a summary|The key points are|Your tutorial|This tutorial|To summarize|Final summary:)", 
                "", 
                summary, 
                flags=re.IGNORECASE
            )
            summary = re.sub(r"\n{3,}", "\n\n", summary)
            summary = summary.strip()
            yield "\n📄 Summary:\n" + summary + "\n"
        except Exception as e:
            print(" Error:", e)
            yield f"\n Error: {str(e)}\n"
    return StreamingResponse(stream(), media_type="text/plain")

Voici comment fonctionne notre résumé :

  • La fonction reçoit l'instruction de données POST en utilisant le schéma RequestData et attend un champ nommé content qui contient le texte brut de la page web.
  • Une fonction génératrice imbriquée stream() est définie pour produire progressivement des messages. Cela permet de renvoyer un retour d'information en temps réel au frontend avant que le résumé final ne soit terminé.
  • La liste des messages inclut explicitement une balise dans le message de l'utilisateur pour supprimer le raisonnement interne, et une invite détaillée du système renforce ce comportement.
  • Le bot Qwen-Agent est invoqué à l'aide de bot.run(messages), où messages est une liste contenant un seul message d'entrée de l'utilisateur. 
  • Les réponses du modèle sont diffusées et collectées sur le site result_list. Le code parcourt ensuite la liste en sens inverse pour extraire la chaîne de contenu la plus récente renvoyée par le LLM.
  • Les marqueurs de raisonnement interne de Qwen tels que et sont supprimés de la sortie à l'aide d'une expression rationnelle afin de nettoyer le résumé final.
  • Si aucun contenu valide n'est trouvé, un message de repli est envoyé. Dans le cas contraire, le résumé nettoyé est transmis au frontend.

Étape 2 : Création de l'extension Chrome

Cette section passe en revue tous les fichiers nécessaires à la configuration de l'extension, y compris l'interface utilisateur frontale, la logique d'extraction du contenu des pages, les scripts de communication en arrière-plan et les métadonnées de configuration.

Avant d'examiner les différents scripts, voici une description simple de ce que fait chaque fichier :

  • manifest.json: Il s'agit d'un fichier de configuration qui définit les métadonnées, les autorisations et les scripts de l'extension.
  • popup.html: Ce script définit l'interface utilisateur visible pour la fenêtre contextuelle de l'extension Chrome avec un bouton et un panneau de sortie.
  • popup.js: Il gère la logique frontale, capture le texte de l'onglet en cours et transmet la réponse du résumé à l'interface utilisateur.
  • content.js: Il extrait le contenu visible de la page web lorsqu'il est demandé, agissant comme un script de contenu.
  • background.js: Ce script coordonne la communication avec le backend et transmet les résumés en continu à la fenêtre contextuelle.
  • icon.png: Il s'agit d'une icône d'extension affichée dans la barre d'outils et le gestionnaire d'extensions de Chrome.

Ensemble, ces fichiers rendent l'extension interactive, réactive et capable de communiquer avec un serveur de modèle local.

Étape 1 : Métadonnées d'extension

Le script manifest.json contient des métadonnées pour Chrome. Il définit le comportement de l'extension, les autorisations, le service d'arrière-plan et l'interface utilisateur.

{
    "manifest_version": 3,
    "name": "Web Summarizer",
    "version": "1.0",
    "description": "Summarize the content of the current page using a local LLM agent.",
    "permissions": [
      "scripting",
      "activeTab",
      "storage"
    ],
    "host_permissions": [
      "<all_urls>"
    ],
    "action": {
      "default_popup": "popup.html",
      "default_icon": "icon.png"
    },
    "background": {
      "service_worker": "background.js"
    },
    "content_scripts": [
      {
        "matches": ["<all_urls>"],
        "js": ["content.js"],
        "run_at": "document_idle"
      }
    ]
}  

Ce fichier définit la configuration et les capacités de votre extension Chrome. Il accorde des autorisations essentielles telles que scripting (pour injecter du JavaScript), activeTab (pour accéder à l'onglet actuel) et storage (pour enregistrer les préférences de l'utilisateur).

Le champ host_permissions avec "<all_urls>" permet à l'extension de fonctionner sur n'importe quelle page web. Il met également en place l'interface utilisateur de l'extension par le biais de popup.html et icon.png, et enregistre le comportement en arrière-plan à l'aide d'un agent de service (background.js). Enfin, il définit un script de contenu (content.js) à exécuter sur toutes les pages chargées, ce qui permet d'interagir avec le contenu de la page web en cas de besoin.

Étape 2 : Construire une interface visible 

Le site popup.html définit l'interface visible de l'extension. Il se connecte à l'onglet actif qui capture le texte visible et l'envoie à votre serveur dorsal. 

<!DOCTYPE html>
<html>
<head>
  <meta charset="UTF-8">
  <meta http-equiv="X-UA-Compatible" content="IE=edge">
  <meta name="viewport" content="width=device-width, initial-scale=1.0">
  <title>Web Summarizer Assistant</title>
  <style>
    body {
      width: 500px;
      height: 600px;
      background-color: aliceblue;
      
      
      
    }
    .container {
      display: flex;
      flex-direction: column;
      align-items: center;
      gap: 15px;
    }
    button {
      border: none;
      border-radius: 5px;
      background-
      color: white;
      
      
      cursor: pointer;
    }
    button:hover {
      background-
    }
    pre {
      background-
      border: 1px solid #ccc;
      border-radius: 5px;
      
      width: 90%;
      height: 350px;
      overflow: auto;
    }
    input[type="text"] {
      
      width: 90%;
      border-radius: 5px;
      border: 1px solid #ccc;
    }
  </style>
</head>
<body>
  <div class="container">
    <h2>Summarize Current Page</h2>
    <button id="summarizeBtn">Summarize</button>
    <pre id="output">Summary will appear here...</pre>
  </div>
  <script src="popup.js"></script>
</body>
</html>

Comprenons le code ci-dessus étape par étape :

  1. Tout d'abord, nous mettons en place une structure HTML de base avec une balise qui comprend des balises méta pour l'encodage des caractères, les paramètres d'affichage et une balise
Aashi Dutt

Auteur

Aashi Dutt

Je suis experte Google Developers en ML (Gen AI), triple experte Kaggle et ambassadrice Women Techmakers, avec plus de trois ans d’expérience dans la tech. J’ai cofondé une startup dans le domaine de la santé en 2020 et je poursuis actuellement un master en informatique à Georgia Tech, avec une spécialisation en apprentissage automatique.

Sujets

Contenus associés