Pular para o conteúdo principal

Qwen-Agent: Um guia com projeto de demonstração

Saiba como usar o Qwen-Agent e o Qwen3 para criar uma extensão de resumidor de páginas da Web em tempo real.
· 12 min leer

Explore com IA

Nesta publicação do blog, mostrarei a você como criar uma extensão de navegador com o Qwen-Agent que resume o conteúdo de qualquer página da Web em tempo real. Abordaremos como você pode:

  • Use o modelo Qwen3 da Alibaba localmente via Ollama
  • Usar o Qwen-Agent para estímulos estruturados e uso de ferramentas
  • Crie uma extensão do Chrome com atualizações de interface do usuário em fluxo contínuo
  • Adicionar backend FastAPI para oferecer suporte à compactação em tempo real

Ao final, você terá um agente resumidor habilitado para off-line, com a tecnologia Qwen3, que lê e resume páginas da Web em seu navegador com um único clique.

Mantemos nossos leitores atualizados sobre as últimas novidades em IA enviando o The Median, nosso boletim informativo gratuito de sexta-feira que detalha as principais histórias da semana. Inscreva-se e fique atento em apenas alguns minutos por semana:

O que é o Qwen-Agent?

Qwen-Agent é uma estrutura Python leve criada pela Alibaba para desenvolver aplicativos LLM poderosos usando o Qwen3 usando a família de modelos Qwen3. Ele se concentra no suporte:

  • Agentes de acompanhamento de instruções
  • Uso de ferramentas e chamadas de funções
  • Memória e planejamento de várias voltas
  • Solicitação flexível por meio de mensagens estruturadas

Em sua essência, o Qwen-Agent simplifica o desenvolvimento de agentes de IA modulares com raciocínio robusto e recursos de execução de código. Ele inclui componentes atômicos, como ferramentas e wrappers LLM, bem como classes Agent de alto nível para orquestrar fluxos de trabalho.

O Qwen-Agent funciona com APIs baseadas em nuvem (como o Alibaba DashScope) e tempos de execução locais compatíveis com OpenAI, como vLLM e Ollama. Com ele, você pode criar facilmente aplicativos LLM avançados:

  • Integração rápida via FastAPI
  • Fluxo integrado para UIs de front-end
  • Cadeias de ferramentas de funções personalizáveis
  • Privacidade e desempenho off-line completos

Visão geral do projeto: Extensão do Web Summarizer em tempo real

Nesta seção, criaremos uma extensão de resumidor em tempo real alimentada pelo Qwen-Agent. Isso inclui:

  • Um backend FastAPI que aceita texto de página da Web e retorna um resumo
  • Um pop-up de extensão do Chrome para capturar o conteúdo visível da página
  • Uma interface de usuário de fluxo contínuo como um resumo é gerada
  • Backend com tecnologia Qwen3:1.7B em execução local via Ollama

Vamos examinar cada parte desse projeto.

Etapa 1: Configurando o back-end do Qwen3

Nesta etapa, abordaremos todos os arquivos de código que precisamos configurar para que nosso backend funcione de forma síncrona com nossa extensão.

Etapa 1.1: Dependências e bibliotecas 

Comece instalando o ollama em seu sistema. Em seguida, liste todas as dependências do Python necessárias para o backend em requirements.txt.. Isso ajuda a garantir uma configuração consistente do ambiente entre máquinas e contêineres.

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

Aqui está um detalhamento da finalidade de cada biblioteca:

  • qwen-agent[code_interpreter]: Trata-se de uma estrutura de agente central com suporte ao uso de ferramentas, permitindo a execução de código e o raciocínio estruturado.
  • fastapi e uvicorn: Juntas, essas bibliotecas alimentam o backend da API assíncrona e atendem à lógica do resumidor.
  • python-dotenv e python-dateutil: Essas bibliotecas de utilitários lidam com variáveis de ambiente e processamento de data/hora (útil para escalonamento futuro).
  • matplotlib: É usado para plotar ou mostrar resultados visuais (opcional) 

Se você quiser executar esse projeto localmente, execute o seguinte comando para instalar todas as dependências:

pip install -r requirements.txt

Etapa 1.2: Configuração do DockerFile (opcional)

Agora, vamos configurar um Dockerfile que cria um ambiente Python mínimo com nosso aplicativo e expõe a porta 7864, permitindo que a extensão do Chrome envie solicitações ao 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"]

Essa configuração é opcional se você optar por executar o projeto localmente usando o Uvicorn em vez do Docker.

Etapa 1.3: Servindo Qwen com Ollama

Em seguida, você precisará extrair e servir o modelo Qwen3 localmente usando o Ollama. Esta etapa permite a inferência off-line ao hospedar o serviço LLM em seu computador.

ollama pull qwen3:1.7b
ollama serve 

Etapa 1.4: Criando o servidor de compactação FastAPI

O arquivo app.py define um servidor FastAPI que transmite resumos em tempo real do conteúdo de páginas da Web usando um modelo Qwen3:1.7B em execução local por meio do Qwen-Agent.

Etapa 1.4.1: Importações

Comece importando FastAPI, utilitários de streaming e componentes do Qwen-Agent, como Assistant, que envolve o modelo e suas habilidades de ferramenta.

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

Etapa 1.4.2: Inicialização do aplicativo FastAPI

Em seguida, definimos um esquema simples de RequestData para receber conteúdo e criamos uma instância de bot de 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."
)

No código acima, o bot é uma instância da classe Assistant do Qwen-Agent, configurada para interagir com o modelo Qwen3:1.7B servido localmente por meio do Ollama. Ele usa a ferramenta integrada code_interpreter para analisar o conteúdo e executar o código Python, se necessário. Um system_message personalizado orienta seu comportamento para gerar resumos concisos de páginas da Web.

Observação: Você pode usar modelos menores, como qwen3:0.6b ou qwen3:1.7b, para reduzir significativamente o tempo de resposta e o uso da memória. Isso é especialmente útil para tarefas de resumo mais rápidas sem muita sobrecarga de raciocínio.

Etapa 1.4.3: Rota do resumidor

Por fim, um endpoint lê o conteúdo de entrada e produz um texto de resumo em tempo real à medida que é gerado pelo modelo. Ele usa saída de streaming para que o usuário não precise esperar que a resposta completa seja gerada.

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

Veja como nosso resumidor funciona:

  • A função recebe a instrução de dados POST usando o esquema RequestData, esperando um campo chamado content que contém o texto bruto da página da Web.
  • Uma função geradora stream() aninhada é definida para produzir mensagens progressivamente. Isso permite que o feedback em tempo real seja enviado de volta ao front-end antes que o resumo final seja concluído.
  • A lista de mensagens inclui explicitamente um wrapper de tag na mensagem do usuário para suprimir o raciocínio interno, e um prompt detalhado do sistema reforça esse comportamento.
  • O bot Qwen-Agent é chamado usando bot.run(messages), onde messages é uma lista que contém uma única mensagem de entrada do usuário. 
  • As respostas do modelo são transmitidas e coletadas em result_list. Em seguida, o código percorre a lista de forma inversa para extrair a string de conteúdo mais recente retornada pelo LLM.
  • Os marcadores de raciocínio interno do Qwen, como e , são removidos da saída usando um regex para limpar o resumo final.
  • Se nenhum conteúdo válido for encontrado, uma mensagem de fallback será enviada. Caso contrário, o resumo limpo é transmitido para o frontend.

Etapa 2: Criando a extensão do Chrome

Esta seção apresenta todos os arquivos necessários para configurar a extensão, inclusive a interface do usuário de front-end, a lógica para extrair o conteúdo da página, os scripts de comunicação em segundo plano e os metadados de configuração.

Antes de examinarmos os scripts individuais, aqui está um detalhamento simples do que cada arquivo faz:

  • manifest.json: Esse é um arquivo de configuração que define metadados, permissões e scripts de extensão.
  • popup.html: Esse script define a interface do usuário visível para o pop-up da extensão do Chrome com um botão e um painel de saída.
  • popup.js: Isso lida com a lógica de front-end e captura o texto da guia atual e transmite a resposta resumida para a interface do usuário.
  • content.js: Ele extrai o conteúdo visível da página da Web quando solicitado, atuando como o script de conteúdo.
  • background.js: Esse script coordena a comunicação de back-end e retransmite os resumos transmitidos para o pop-up.
  • icon.png: Esse é um ícone de extensão exibido na barra de ferramentas e no gerenciador de extensões do Chrome.

Juntos, esses arquivos tornam a extensão interativa, reativa e capaz de se comunicar com um servidor de modelo local.

Etapa 1: Metadados de extensão

O script manifest.json contém metadados para o Chrome. Ele define o comportamento da extensão, as permissões, o trabalhador do serviço em segundo plano e a interface do usuário pop-up.

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

Esse arquivo define a configuração e os recursos de sua extensão do Chrome. Ele concede permissões essenciais, como scripting (para injetar JavaScript), activeTab (para acessar a guia atual) e storage (para salvar as preferências do usuário).

O campo host_permissions com "<all_urls>" permite que a extensão seja executada em qualquer página da Web. Ele também configura a interface do usuário da extensão por meio de popup.html e icon.png e registra o comportamento em segundo plano usando um service worker (background.js). Por fim, ele define um script de conteúdo (content.js) a ser executado em todas as páginas carregadas, permitindo a interação com o conteúdo da página da Web quando necessário.

Etapa 2: Criando uma interface visível 

O site popup.html define a interface visível da extensão. Ele se conecta à guia ativa que captura o texto visível e o envia ao seu servidor de back-end. 

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

Vamos entender o código acima passo a passo:

  1. Primeiro, configuramos uma estrutura HTML básica com uma tag que inclui metatags para codificação de caracteres, configurações de viewport e uma tag
Aashi Dutt

Autor

Aashi Dutt

Sou Especialista Google Developers em ML (Gen AI), tricampeã no Kaggle e Embaixadora Women Techmakers, com mais de três anos de experiência na área de tecnologia. Cofundei uma startup de saúde em 2020 e atualmente faço um mestrado em ciência da computação na Georgia Tech, com foco em aprendizado de máquina.

Temas

Relacionado

Tutorial

Guia para iniciantes no uso da API do ChatGPT

Tutorial

Guia de Introdução ao Ajuste Fino de LLMs