Zum Hauptinhalt springen

Qwen-Agent: Ein Leitfaden mit Demo-Projekt

Lerne, wie du mit Qwen-Agent und Qwen3 eine Erweiterung für die Echtzeit-Zusammenfassung von Webseiten erstellen kannst.
· 12 Min. lesen

Entdecke KI

In diesem Blogpost zeige ich dir, wie du mit Qwen-Agent eine Browsererweiterung baust , die den Inhalt einer beliebigen Webseite in Echtzeit zusammenfasst. Wir zeigen dir, wie du:

  • Nutze das Qwen3-Modell von Alibaba lokal über Ollama
  • Benutze den Qwen-Agent für strukturiertes Prompting und den Einsatz von Tools
  • Baue eine Chrome-Erweiterung mit Streaming UI Updates
  • FastAPI-Backend hinzufügen, um Echtzeit-Zusammenfassung zu unterstützen

Am Ende wirst du einen offline-fähigen Zusammenfassungs-Agenten haben, der von Qwen3 angetrieben wird und der Webseiten in deinem Browser mit einem einzigen Klick liest und zusammenfasst.

Wir halten unsere Leserinnen und Leser mit The Median auf dem Laufenden, unserem kostenlosen Freitags-Newsletter, der die wichtigsten Meldungen der Woche aufschlüsselt. Melde dich an und bleibe in nur ein paar Minuten pro Woche auf dem Laufenden:

Was ist Qwen-Agent?

Qwen-Agent ist ein leichtgewichtiges Python-Framework, das von Alibaba entwickelt wurde, um leistungsstarke LLM-Anwendungen mit der Qwen3 Familie von Modellen. Sie konzentriert sich auf die Unterstützung:

  • Agenten, die Anweisungen befolgen
  • Werkzeuggebrauch und Funktionsaufruf
  • Speicher und Multi-Turn-Planung
  • Flexible Eingabeaufforderung über strukturierte Nachrichten

Im Kern vereinfacht Qwen-Agent die Entwicklung von modularen KI-Agenten mit robusten Argumentations- und Codeausführungsfunktionen. Sie enthält atomare Komponenten wie LLM-Wrapper und Tools sowie High-Level-Klassen Agent für die Orchestrierung von Arbeitsabläufen.

Qwen-Agent funktioniert sowohl mit Cloud-basierten APIs (wie Alibaba DashScope) als auch mit lokalen OpenAI-kompatiblen Laufzeiten wie vLLM und Ollama. Es macht es einfach, leistungsstarke LLM-Apps zu erstellen:

  • Schnelle Integration über FastAPI
  • Eingebautes Streaming für Frontend UIs
  • Anpassbare Funktions-Toolchains
  • Volle Offline-Privatsphäre und Leistung

Projektübersicht: Real-Time Web Summarizer Erweiterung

In diesem Abschnitt bauen wir eine Echtzeit-Zusammenfassungserweiterung auf Basis von Qwen-Agent. Dazu gehören:

  • Ein FastAPI-Backend, das Webseitentext annimmt und eine Zusammenfassung zurückgibt
  • Ein Pop-up der Chrome-Erweiterung, um sichtbare Seiteninhalte zu erfassen
  • Eine Streaming-UI als Zusammenfassung wird erstellt
  • Das Backend wird von Qwen3:1.7B betrieben und läuft lokal über Ollama

Lass uns die einzelnen Teile dieses Projekts durchgehen.

Schritt 1: Einrichten des Qwen3 Backends

In diesem Schritt werden wir alle Codedateien behandeln, die wir einrichten müssen, damit unser Backend synchron mit unserer Erweiterung arbeitet.

Schritt 1.1: Abhängigkeiten und Bibliotheken 

Beginne mit der Installation von ollama auf deinem System. Als Nächstes listest du alle Python-Abhängigkeiten auf, die für das Backend benötigt werden: requirements.txt.. So kannst du sicherstellen, dass die Umgebung auf allen Rechnern und Containern einheitlich eingerichtet ist.

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

Hier ist eine Aufschlüsselung, wofür jede Bibliothek verwendet wird:

  • qwen-agent[code_interpreter]: Dies ist ein Kernagenten-Framework mit Tool-Unterstützung, das die Ausführung von Code und strukturiertes Denken ermöglicht.
  • fastapi und uvicorn: Diese Bibliotheken bilden zusammen das asynchrone API-Backend und bedienen deine Zusammenfassungslogik.
  • python-dotenv und python-dateutil: Diese Utility-Bibliotheken behandeln Umgebungsvariablen und die Verarbeitung von Datum und Uhrzeit (nützlich für zukünftige Skalierungen).
  • matplotlib: Es wird zum Plotten oder Anzeigen von visuellen Ausgaben verwendet (optional). 

Wenn du dieses Projekt lokal ausführen möchtest, führe den folgenden Befehl aus, um alle Abhängigkeiten zu installieren:

pip install -r requirements.txt

Schritt 1.2: DockerFile einrichten (optional)

Jetzt richten wir ein Dockerfile ein, das eine minimale Python-Umgebung mit unserer App erstellt und Port 7864 freigibt, damit die Chrome-Erweiterung Anfragen an das FastAPI-Backend senden kann.

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"]

Diese Einrichtung ist optional, wenn du dich dafür entscheidest, das Projekt lokal mit Uvicorn statt mit Docker auszuführen.

Schritt 1.3: Qwen mit Ollama dienen

Als Nächstes musst du das Qwen3-Modell mit Ollama lokal abrufen und bereitstellen. Dieser Schritt ermöglicht Offline-Inferenz, indem der LLM-Dienst auf deinem Rechner gehostet wird.

ollama pull qwen3:1.7b
ollama serve 

Schritt 1.4: Aufbau des FastAPI-Zusammenfassungsservers

Die Datei app.py definiert einen FastAPI-Server, der Zusammenfassungen von Webseiteninhalten in Echtzeit mit einem lokal laufenden Qwen3:1.7B-Modell über den Qwen-Agent streamt.

Schritt 1.4.1: Importe

Beginne mit dem Import von FastAPI, Streaming-Dienstprogrammen und Qwen-Agent-Komponenten wie Assistant, die das Modell und seine Werkzeugfähigkeiten umhüllen.

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

Schritt 1.4.2: FastAPI App Initialisierung

Als Nächstes definieren wir ein einfaches RequestData Schema, um Inhalte zu empfangen, und erstellen eine Bot-Instanz von 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."
)

Im obigen Code ist bot eine Instanz der Klasse Assistant von Qwen-Agent, die so konfiguriert ist, dass sie mit dem lokal bedienten Qwen3:1.7B-Modell über Ollama interagiert. Es nutzt das integrierte Tool code_interpreter, um den Inhalt zu durchsuchen und bei Bedarf Python-Code auszuführen. Eine benutzerdefinierte system_message steuert das Verhalten, um prägnante Webseiten-Zusammenfassungen zu erstellen.

Hinweis: Du kannst kleinere Modelle wie qwen3:0.6b oder qwen3:1.7b verwenden, um die Reaktionszeit und den Speicherverbrauch deutlich zu reduzieren. Dies ist besonders nützlich für schnellere Zusammenfassungsaufgaben ohne großen Denkaufwand.

Schritt 1.4.3: Zusammenfassende Route

Schließlich liest ein Endpunkt den eingehenden Inhalt und liefert eine Live-Zusammenfassung, die vom Modell erstellt wird. Sie verwendet eine Streaming-Ausgabe, damit der Nutzer nicht warten muss, bis die vollständige Antwort generiert wurde.

@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")

So funktioniert unser Summarizer:

  • Die Funktion empfängt die Datenanweisung POST nach dem Schema RequestData und erwartet ein Feld mit dem Namen content, das den Rohtext der Webseite enthält.
  • Eine verschachtelte stream() Generatorfunktion wird definiert, um nach und nach Nachrichten zu erzeugen. So kann Echtzeit-Feedback an das Frontend gesendet werden, bevor die endgültige Zusammenfassung fertig ist.
  • Die Nachrichtenliste enthält explizit einen Tag-Wrapper in der Benutzernachricht, um interne Überlegungen zu unterdrücken, und eine detaillierte Systemaufforderung verstärkt dieses Verhalten.
  • Der Qwen-Agent-Bot wird über bot.run(messages) aufgerufen, wobei messages eine Liste ist, die eine einzelne Benutzereingabe enthält. 
  • Die Antworten des Modells werden gestreamt und in result_list gesammelt. Der Code durchläuft dann die Liste in umgekehrter Reihenfolge, um den letzten vom LLM zurückgegebenen Inhaltsstring zu extrahieren.
  • Qwens interne Argumentationsmarker wie und werden mithilfe einer Regex aus der Ausgabe entfernt, um die endgültige Zusammenfassung zu bereinigen.
  • Wenn kein gültiger Inhalt gefunden wird, wird eine Fallback-Nachricht gesendet. Andernfalls wird die bereinigte Zusammenfassung an das Frontend gestreamt.

Schritt 2: Erstellung der Chrome-Erweiterung

In diesem Abschnitt werden alle Dateien beschrieben, die für die Einrichtung der Erweiterung benötigt werden, einschließlich der Frontend-Benutzeroberfläche, der Logik zum Extrahieren von Seiteninhalten, der Skripte für die Hintergrundkommunikation und der Konfigurationsmetadaten.

Bevor wir uns die einzelnen Skripte ansehen, hier eine einfache Aufschlüsselung, was jede Datei tut:

  • manifest.json: Dies ist eine Konfigurationsdatei, die Metadaten, Berechtigungen und Skripte für die Erweiterung definiert.
  • popup.html: Dieses Skript definiert die sichtbare Benutzeroberfläche für das Popup der Chrome-Erweiterung mit einer Schaltfläche und einem Ausgabefeld.
  • popup.js: Sie übernimmt die Frontend-Logik, erfasst den Text der aktuellen Registerkarte und streamt die zusammenfassende Antwort an die Benutzeroberfläche.
  • content.js: Es extrahiert sichtbare Webseiteninhalte, wenn sie angefordert werden, und fungiert als Inhaltsskript.
  • background.js: Dieses Skript koordiniert die Backend-Kommunikation und leitet die gestreamten Zusammenfassungen an das Popup weiter.
  • icon.png: Dies ist ein Erweiterungssymbol, das in der Symbolleiste und im Erweiterungsmanager von Chrome angezeigt wird.

Zusammen machen diese Dateien die Erweiterung interaktiv und reaktiv und ermöglichen die Kommunikation mit einem lokalen Modellserver.

Schritt 1: Metadaten der Erweiterung

Das Skript manifest.json enthält Metadaten für Chrome. Sie definiert das Verhalten der Erweiterung, die Berechtigungen, den Hintergrunddienst Worker und die Popup-Benutzeroberfläche.

{
    "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"
      }
    ]
}  

Diese Datei definiert die Konfiguration und die Fähigkeiten deiner Chrome-Erweiterung. Sie gewährt wichtige Berechtigungen wie scripting (zum Einfügen von JavaScript), activeTab (für den Zugriff auf den aktuellen Tab) und storage (zum Speichern von Benutzereinstellungen).

Das host_permissions Feld mit "<all_urls>" ermöglicht es, dass die Erweiterung auf jeder Webseite läuft. Außerdem wird die Benutzeroberfläche der Erweiterung über popup.html und icon.png eingerichtet und das Hintergrundverhalten über einen Service Worker (background.js) registriert. Schließlich wird ein Inhaltsskript (content.js) definiert, das auf allen geladenen Seiten ausgeführt wird und bei Bedarf eine Interaktion mit dem Inhalt der Webseite ermöglicht.

Schritt 2: Aufbau einer sichtbaren Schnittstelle 

Die popup.html definiert die sichtbare Schnittstelle der Erweiterung. Sie verbindet sich mit der aktiven Registerkarte, die den sichtbaren Text erfasst und an deinen Backend-Server sendet. 

<!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>

Lass uns den obigen Code Schritt für Schritt verstehen:

  1. Zunächst richten wir eine grundlegende HTML-Struktur mit einem -Tag ein, der Meta-Tags für die Zeichenkodierung, Viewport-Einstellungen und ein
Aashi Dutt

Autor

Aashi Dutt

Ich bin Google Developers Expertin für ML (Gen AI), dreifache Kaggle-Expertin und Women-Techmakers-Botschafterin mit über drei Jahren Erfahrung in der Tech-Branche. 2020 habe ich ein Health-Tech-Startup mitgegründet und absolviere derzeit einen Master in Informatik an der Georgia Tech mit Schwerpunkt Machine Learning.

Themen

Ähnlich