Vai al contenuto principale

Tutorial GLM-5-Turbo: crea un agente browser in tempo reale

Impara a creare un agente AI basato su browser che cerca sul web, estrae dati di volo in tempo reale e restituisce consigli chiari usando GLM-5-Turbo.
Aggiornato 29 set 2026  · 9 min leggi

Scopri con l'IA

ChatGPTClaudePerplexity

GLM-5-Turbo è progettato specificamente per i workflow in stile agente, in cui il modello coordina un'esecuzione multi-step. È ottimizzato per comprendere istruzioni complesse, invocare strumenti in modo affidabile e produrre output strutturati che possano essere consumati dai sistemi. 

In questo tutorial costruiremo un agente browser in tempo reale che naviga attivamente il web, estrae le informazioni rilevanti e genera un riepilogo pulito per l'utente. Alla fine del tutorial, avrai un'architettura riutilizzabile che potrai estendere oltre i voli a qualsiasi attività reale di ricerca ed estrazione.

Puoi vedere il nostro progetto GLM-5 Turbo in azione nel video qui sotto:

Che cos'è GLM-5-Turbo?

GLM-5-Turbo è progettato per supportare workflow di agenti nel mondo reale. Fornisce più modalità di ragionamento, permettendo di bilanciare tra risposte più rapide e ragionamenti più profondi a seconda del compito. 

Per applicazioni interattive, supporta anche l'output in streaming, consentendo di consegnare le risposte token per token per un'esperienza d'uso più reattiva.

Uno dei suoi punti di forza è il function calling, che consente al modello di invocare strumenti esterni in modo affidabile. Il modello supporta anche output strutturati come JSON, facilitando l'integrazione con sistemi a valle e livelli UI.

Per workflow più lunghi, GLM-5-Turbo include il caching del contesto, che aiuta a mantenere le prestazioni su interazioni estese senza elaborare ripetutamente le stesse informazioni. Supporta anche l'integrazione con sistemi esterni tramite MCP (Model Context Protocol), permettendo al modello di connettersi con strumenti e fonti dati aggiuntive.

Creato per i workflow degli agenti

GLM-5-Turbo è profondamente ottimizzato per ambienti in stile agente, in particolare scenari come OpenClaw, dove i task coinvolgono più passaggi, strumenti e lunghe catene di esecuzione.

Questa ottimizzazione migliora diverse aree critiche:

  • Affidabilità dell'invocazione degli strumenti: garantisce che il modello richiami costantemente le funzioni giuste.
  • Scomposizione delle istruzioni: consente al modello di suddividere compiti complessi in passaggi più piccoli.
  • Stabilità nell'esecuzione di lunghe catene: è essenziale per agenti multi-step.

In pratica, GLM-5-Turbo è abbastanza veloce per sistemi interattivi e al contempo affidabile in scenari multi-step con chiamata agli strumenti, esattamente ciò che serve per creare agenti browser.

Progetto di esempio con GLM-5-Turbo: crea un agente browser in tempo reale

In questo tutorial creeremo un'applicazione Streamlit che si comporta come un leggero assistente di viaggio. L'utente fornisce una richiesta come “Trova i voli più economici da SFO a JFK da venerdì prossimo a lunedì”. Il sistema quindi:

  • Cerca sul web pagine di risultati di voli rilevanti
  • Apre e ispeziona tali pagine
  • Estrae informazioni utili come prezzo, durata e scali
  • Riassume le opzioni migliori in un formato facile da consultare

A livello alto, il sistema è composto da quattro componenti che lavorano insieme.

  • Il primo è il livello LLM (llm_client.py), che invia richieste di chat-completion a Z.AI o OpenRouter. 
  • Il secondo è il livello degli strumenti del browser (browser_tools.py), che espone azioni come cercare, aprire pagine ed estrarre contenuti. 
  • Il terzo è il loop dell'agente (agent.py), che collega modello e strumenti in un workflow multi-step. 
  • Infine, la UI Streamlit (app.py) fornisce un'interfaccia per interazione e visualizzazione.

Codice sorgente completo: https://github.com/AashiDutt/GLM5_TURBO

Passo 1: Prerequisiti

Prima di costruire l'applicazione, assicurati di avere quanto segue:

  • Python 3.9+
  • Una API key per GLM-5-Turbo (Z.AI o OpenRouter)
  • Playwright installato per l'automazione del browser

Installa le librerie necessarie:

pip install streamlit pandas python-dotenv requests playwright
playwright install chromium

Il file app.py usa Streamlit, pandas, dotenv e moduli personalizzati. Mentre browser_tools.py usa requests e Playwright. Le chiamate API vengono effettuate dentro llm_client.py usando requests.

Ora esegui il seguente comando per creare un file .env che conterrà la/le nostra/e API key:

cp .env.example .env

Seleziona l'endpoint API preferito e inserisci la API key:

.env
PROVIDER=zai
# Direct Z.AI API
ZAI_API_KEY=Your Z.AI API key
ZAI_BASE_URL=https://api.z.ai/api/paas/v4/chat/completions
ZAI_MODEL=glm-5-turbo
# OpenRouter
OPENROUTER_API_KEY=Your OpenRouter API key
OPENROUTER_BASE_URL=https://openrouter.ai/api/v1/chat/completions
OPENROUTER_MODEL=z-ai/glm-5-turbo
# Browser
HEADLESS=true
MAX_STEPS=10

Allo stesso modo, il browser Playwright viene avviato da questo costruttore in browser_tools.py:

class BrowserTools:
    def __init__(self, headless: bool = True) -> None:
        self.playwright = sync_playwright().start()
        self.browser = self.playwright.chromium.launch(headless=headless)
        self.context = self.browser.new_context(viewport={"width": 1440, "height": 1100})
        self.page = self.context.new_page()

Questo blocco inizializza un ambiente browser completamente controllato usando Playwright, che l'agente utilizza per interagire con il web. 

Avvia il motore Playwright, lancia un browser Chromium, crea una sessione isolata del browser e apre una nuova pagina in cui avvengono tutte le azioni. 

Questa configurazione consente all'agente di navigare siti reali, osservare contenuti live e prendere decisioni in base a ciò che vede.

Passo 2: Configurare il client LLM

Successivamente, configureremo il nostro LLMClient che supporta sia l'uso diretto di Z.AI sia OpenRouter con la stessa interfaccia. L'endpoint specifico del provider viene scelto nel costruttore. Poiché i prezzi sono comparabili, per questa demo puoi usare indifferentemente entrambe le API in base alle tue preferenze.

Ecco la configurazione chiave:

class LLMClient:
    def __init__(
        self,
        provider: str,
        api_key: str,
        model: str,
        app_name: str = "GLM-5 Browser Agent Demo",
        app_url: str = "http://localhost:8501",
    ) -> None:
        self.provider = provider.lower().strip()
        self.api_key = api_key
        self.model = model
        if self.provider == "zai":
            self.url = os.getenv("ZAI_BASE_URL", "https://api.z.ai/api/paas/v4/chat/completions")
        elif self.provider == "openrouter":
            self.url = os.getenv("OPENROUTER_BASE_URL", "https://openrouter.ai/api/v1/chat/completions")

Questo initializer configura il client LLM selezionando l'endpoint API appropriato (Z.AI o OpenRouter) mantenendo l'interfaccia coerente. 

Memorizza la API key, il modello e l'URL dell'endpoint, consentendo al resto del sistema di chiamare il modello senza preoccuparsi delle differenze specifiche del provider.

Quindi create_chat_completion() costruisce il payload, allega gli strumenti quando necessario e analizza eventuali chiamate a strumenti restituite. 

payload: Dict[str, Any] = {
    "model": self.model,
    "messages": messages,
    "temperature": temperature,
    "max_tokens": max_tokens,
    "stream": False,
}
if tools:
    payload["tools"] = tools
    payload["tool_choice"] = "auto"

Il payload sopra definisce la richiesta inviata al modello, inclusi il contesto della conversazione e i parametri di generazione. Allegando i tools e abilitando tool_choice="auto", si consente al modello di decidere quando invocare funzioni esterne.

Passo 3: Definire gli strumenti del browser

In agent.py definiamo uno schema di strumenti che il modello può chiamare. Questi includono search_web, open_url, open_search_result, extract_page, extract_flight_cards, click, type_text e wait_for_text.

Ecco un esempio di definizione di strumento:

{
    "type": "function",
    "function": {
        "name": "search_web",
        "description": "Search the public web for flight result pages or travel aggregators.",
        "parameters": {
            "type": "object",
            "properties": {
                "query": {"type": "string"},
                "max_results": {"type": "integer", "default": 8},
            },
            "required": ["query"],
        },
    },
}

Questo schema indica al modello cosa fa lo strumento, quali input si aspetta e come chiamarlo. Definendo gli strumenti in questo modo, permettiamo al modello di decidere dinamicamente quando cercare sul web invece di codificare rigidamente il flusso.

Dal lato dell'esecuzione, questi strumenti sono implementati in browser_tools.py. Per questa demo, abbiamo usato l'endpoint HTML di DuckDuckGo per memorizzare i link dei risultati correnti per la navigazione successiva.

def search_web(self, query: str, max_results: int = 8) -> Dict[str, Any]:
    url = f"https://html.duckduckgo.com/html/?q={quote_plus(query)}"
    headers = {"User-Agent": "Mozilla/5.0"}
    resp = requests.get(url, headers=headers, timeout=30)
    resp.raise_for_status()
    html = resp.text

La funzione search_web() sopra esegue una ricerca web leggera recuperando risultati HTML grezzi e analizzandoli, evitando la necessità di una search API a pagamento. Memorizza anche internamente i risultati in modo che l'agente possa navigarli nei passaggi successivi.

Ora che abbiamo definito come l'agente può interagire con il web, il prossimo passo è vedere come usa effettivamente questi strumenti per estrarre informazioni sui voli tramite la ricerca web.

Passo 4: Estrarre informazioni sui voli

Una volta che il modello trova una fonte promettente, deve ispezionarla. Possiamo usare gli strumenti del browser (dal passo 3)  per estrarre snapshot grezzi della pagina.

Passo 4.1: Apertura ed estrazione delle pagine

Useremo lo strumento open_url() per caricare la pagina web e restituire uno snapshot con titolo, URL e testo visibile.

def open_url(self, url: str) -> Dict[str, Any]:
    self.page.goto(url, wait_until="domcontentloaded", timeout=45000)
    self.page.wait_for_timeout(1200)
    return self._page_snapshot()

Per un'estrazione più ampia, useremo il seguente strumento extract_page() che restituisce lo snapshot della pagina e un insieme di link raccolti dalla pagina corrente.

def extract_page(self, max_chars: int = 7000, link_limit: int = 20) -> Dict[str, Any]:
    snapshot = self._page_snapshot(max_chars=max_chars)
    snapshot["links"] = self._collect_links(limit=link_limit)
    self.current_links = snapshot["links"]
    return snapshot

Questo è utile perché il modello non deve sempre cliccare alla cieca. A volte può ispezionare prima il testo della pagina corrente e i link disponibili, poi decidere la mossa successiva.

Passo 4.2: Estrazione euristica dei voli

Una parte chiave della demo è la funzione extract_flight_cards() che prende il testo visibile della pagina e usa pattern regex per rilevare prezzi, orari, durate e scali.

Ecco il cuore di quella logica:

money_pattern = re.compile(r"(?:\$|USD\s?)(\d{2,5})")
time_pattern = re.compile(r"\b\d{1,2}:\d{2}\s?(?:AM|PM|am|pm)?\b")
duration_pattern = re.compile(r"\b\d{1,2}\s?h\s?\d{0,2}\s?m?\b|\b\d{1,2}\s?hr\s?\d{0,2}\s?min?\b", re.I)
stop_pattern = re.compile(r"nonstop|1 stop|2 stops|3 stops", re.I)

Questi pattern regex agiscono come rilevatori di segnali comuni relativi ai voli. Il money_pattern cattura prezzi in formati come $299 o USD 299, mentre il time_pattern identifica orari di partenza e arrivo. 

Allo stesso modo, il duration_pattern estrae la durata del viaggio e il stop_pattern rileva se un volo è diretto o con scali. Puoi creare pattern simili per qualsiasi caso d'uso.

Una volta definiti questi segnali, la funzione scansiona piccoli blocchi di testo e cerca combinazioni significative. Quando trova una corrispondenza, costruisce una scheda di volo strutturata:

card = {
    "snippet": joined[:350],
    "price_usd": int(price_match.group(1)),
    "times": times[:2],
    "duration": duration.group(0) if duration else None,
    "stops": stops.group(0) if stops else None,
}

Il blocco di codice sopra converte testo grezzo in una rappresentazione strutturata di un'opzione di volo. Estrae il prezzo, limita le voci orarie alle più rilevanti e gestisce i campi mancanti. Questo preserva il contesto originale, utile per il debugging.

Con l'estrazione in atto, l'agente ora ha un modo per convertire contenuti di pagina non strutturati in dati utilizzabili. Successivamente vedremo come questi dati fluiscono attraverso il loop dell'agente per guidare decisioni multi-step.

Passo 5: Loop dell'agente

Il comportamento effettivo dell'agente risiede nella classe BrowserAgent (dentro agent.py). Questa classe invia al modello il prompt e lo schema degli strumenti, verifica se il modello vuole chiamare uno strumento, esegue quello strumento, aggiunge il risultato della chiamata alla conversazione e continua fino a produrre una risposta finale.

Ecco il loop principale:

for step in range(1, self.max_steps + 1):
    assistant = self.llm.create_chat_completion(
        messages=messages,
        tools=TOOLS,
        temperature=0.2,
        max_tokens=2200,
        enable_thinking=False,
    )
    if assistant.tool_calls:
        ...
        result = self.tool_runner(tc.name, tc.arguments)
        ...
        continue
    final_content = assistant.content.strip()
    if final_content:
        yield {"type": "final", "step": step, "content": final_content}
        return

Questo loop è il cuore della demo. 

A ogni passaggio, il modello riceve lo stato corrente della conversazione e decide cosa fare dopo. Se restituisce chiamate a strumenti, queste vengono eseguite usando tool_runner e i risultati vengono reinseriti nel loop per l'iterazione successiva. 

Questo processo continua finché il modello non produce una risposta finale invece di chiamare uno strumento.

Il loop applica anche un limite massimo di passaggi per evitare esecuzioni infinite e mantiene il processo controllato ed efficiente.

Passo 6: Collegare gli strumenti all'app

In app.py mappiamo i nomi degli strumenti richiesti dal modello ai metodi reali del browser. Questa mappatura è implementata con la funzione make_tool_runner():

def make_tool_runner(browser: BrowserTools):
    def run(name: str, args: Dict[str, Any]):
        if name == "search_web":
            return browser.search_web(**args)
        if name == "open_url":
            return browser.open_url(**args)
        if name == "open_search_result":
            return browser.open_search_result(**args)
        if name == "extract_page":
            return browser.extract_page(**args)
        if name == "extract_flight_cards":
            return browser.extract_flight_cards(**args)

Questa funzione agisce da dispatcher tra il modello e il browser. Il modello restituisce solo il nome dello strumento e gli argomenti, e run() traduce il tutto in una chiamata di funzione sull'istanza BrowserTools. 

Ora che l'agente può chiamare ed eseguire gli strumenti con successo, il passo successivo è costruire l'interfaccia utente che unisce tutto.

Passo 7: Costruire la UI con Streamlit

La UI Streamlit trasforma l'agente browser in un'applicazione interattiva. Raccoglie i dettagli del viaggio dall'utente, avvia l'agente, esegue lo streaming dei passaggi intermedi e infine mostra un riepilogo insieme a una tabella strutturata delle migliori opzioni di volo. Questo flusso UI completo è implementato in app.py.

st.set_page_config(page_title="GLM-5 Browser Agent", page_icon="", layout="wide")
st.title("GLM-5-Turbo Real-Time Browser Agent")
st.subheader("Trip Request")
origin = st.text_input("Origin", value="SFO")
destination = st.text_input("Destination", value="JFK")
depart_date = st.date_input("Depart date")
return_date = st.date_input("Return date")
adults = st.number_input("Adults", min_value=1, max_value=9, value=1)
natural_request = st.text_area(
    "Optional natural language request",
    value="Find the cheapest reasonable flight options and summarize the best ones.",
    height=120,
)
user_query = (
    f"Find flight options from {origin} to {destination} for {adults} adult(s). "
    f"Departure date: {depart_date}. Return date: {return_date}. "
    f"User preference: {natural_request}"
)
run = st.button("Run browser agent", type="primary", use_container_width=True)
if run:
    if not api_key:
        st.error("Please enter an API key in the sidebar.")
        st.stop()
    llm = LLMClient(provider=provider, api_key=api_key, model=model)
    event_box = st.container(border=True)
    result_box = st.container(border=True)
    all_events = []
    with managed_browser(headless=headless) as browser:
        agent = BrowserAgent(llm=llm, tool_runner=make_tool_runner(browser), max_steps=max_steps)
for event in agent.stream(user_query):
    all_events.append(event)
    with event_box:
        if event["type"] == "status":
            st.write(f"**Step {event['step']}** — {event['content']}")
        elif event["type"] == "tool_call":
            with st.expander(f"Step {event['step']} • Tool call: {event['tool']}", expanded=False):
                st.json(event["arguments"])
        elif event["type"] == "tool_result":
            with st.expander(f"Step {event['step']} • Tool result: {event['tool']}", expanded=False):
                st.json(event["result"])
        elif event["type"] == "final":
            st.success("Agent finished.")
final_event = next((e for e in reversed(all_events) if e["type"] == "final"), None)
final_text = final_event["content"] if final_event else ""
with result_box:
    st.subheader("Final Answer")
    parsed = parse_final_json(final_text)
    if parsed:
        st.markdown(render_final_markdown(parsed))
        options_df = build_options_dataframe(parsed)
        if not options_df.empty:
            st.subheader("Top options table")
            st.dataframe(
                options_df,
                use_container_width=True,
                column_config={
                    "Link": st.column_config.LinkColumn("Link", display_text="Open source"),
                },
            )
    else:
        st.markdown(final_text)

Ecco come la UI di Streamlit mette insieme tutti i componenti:

  • Le chiamate st.set_page_config() e st.title() definiscono il layout complessivo e l'intestazione dell'app, mentre i widget di input come st.text_input(), st.date_input(), st.number_input() e st.text_area() raccolgono i dettagli del viaggio. 
  • Quando l'utente clicca il pulsante Run browser agent, l'app verifica innanzitutto che sia disponibile una API key, quindi inizializza LLMClient. Crea poi contenitori separati per i log intermedi dell'agente e per i risultati finali, e apre una sessione gestita del browser Playwright. All'interno di quella sessione, BrowserAgent inizia a inviare eventi in streaming e la UI mostra ogni aggiornamento di stato, chiamata a strumento e risultato dello strumento.
  • Quando l'agente termina, l'app estrae l'evento finale, analizza il JSON restituito e lo rende. Il riepilogo è mostrato tramite render_final_markdown(), mentre build_options_dataframe() converte le opzioni di volo strutturate in una tabella con link alle fonti cliccabili.

Completato questo passo, puoi salvare tutto come app.py e avviare l'app con:

streamlit run app.py

Questo progetto funziona bene come demo reale perché usa risultati di ricerca del browser effettivi e chiamate agli strumenti. Allo stesso tempo, è ancora un prototipo. I siti di voli sono dinamici, le pagine possono essere rumorose e alcune piattaforme usano protezioni anti-bot. 

L'attuale logica di estrazione è volutamente euristica, il che mantiene il sistema semplice e facile da comprendere.

Ancora più importante, questa configurazione non è limitata ai voli. Con pochi ritocchi, principalmente aggiornando i prompt e la logica di estrazione, puoi riutilizzare la stessa architettura per creare agenti per shopping, ricerca, aggregazione di notizie o qualsiasi attività che richieda di navigare e riassumere informazioni live.

Conclusione

In questo tutorial abbiamo costruito un agente browser in tempo reale con GLM-5-Turbo, Playwright e Streamlit. Il modello funge da pianificatore, gli strumenti del browser costituiscono il livello di esecuzione e la UI trasforma i risultati strutturati in un formato facile da usare.

La lezione più importante è che il valore non deriva dal solo modello. Nasce dalla combinazione del modello con un livello di strumenti controllato e un loop dell'agente. 

Tuttavia, ci sono diverse estensioni possibili per questo progetto. Puoi migliorare l'estrazione aggiungendo parser specifici per sito, estendere il sistema a hotel o itinerari, o integrare preferenze dell'utente come voli diretti o compagnie aeree preferite. Questi aspetti possono aiutarti a trasformare questa demo in una vera estensione del browser.

GLM-5-Turbo FAQ

Ho bisogno di una GPU per eseguire questo progetto con GLM-5-Turbo?

No. Questo progetto non richiede una GPU perché il modello è accessibile tramite API (Z.AI o OpenRouter). Tutta l'elaborazione pesante avviene sull'infrastruttura del provider, mentre la tua macchina locale gestisce solo la UI e l'automazione del browser.

Perché usare un agente browser invece di una flight API?

Le API sono più affidabili e strutturate, ma sono limitate a dati predefiniti e spesso richiedono accesso a pagamento. Un agente browser, invece, può:

  • esplorare dinamicamente più fonti
  • adattarsi a query differenti
  • funzionare senza API pre-integrate

Tuttavia, per sistemi in produzione, un approccio ibrido (agente e API) è di solito la scelta migliore.

Perché l'agente a volte fallisce su alcuni siti?

Molti siti di viaggio utilizzano:

  • rendering dinamico via JavaScript
  • protezioni anti-bot
  • layout che cambiano frequentemente

Poiché questa demo si basa su automazione generica del browser ed estrazione euristica, potrebbe non funzionare in modo coerente su tutti i siti.

Perché abbiamo evitato di restituire JSON come output finale?

Il JSON è utile internamente per debugging, elaborazioni strutturate, ranking e filtraggio. Tuttavia, gli utenti finali preferiscono riepiloghi leggibili. Per questo il sistema converte gli output strutturati in un formato facile da consultare.


Aashi Dutt's photo
Author
Aashi Dutt
LinkedIn
Twitter

Sono una Google Developers Expert in ML (Gen AI), una Kaggle 3x Expert e una Women Techmakers Ambassador con oltre 3 anni di esperienza nel tech. Ho co-fondato una startup health-tech nel 2020 e sto conseguendo un master in informatica al Georgia Tech, con specializzazione in machine learning.

Argomenti
Intelligenza artificiale
AI Agents
Correlato

blog

I 15 migliori server MCP remoti che ogni AI builder dovrebbe conoscere nel 2026

Scopri i 15 migliori server MCP remoti che stanno trasformando lo sviluppo AI nel 2026. Scopri come migliorano automazione, ragionamento, sicurezza e velocità dei workflow.
Abid Ali Awan's photo

Abid Ali Awan

15 min

blog

Tokenizzazione nel NLP: come funziona, sfide e casi d'uso

Guida al preprocessing NLP nel machine learning. Copriamo spaCy, i transformer di Hugging Face e come funziona la tokenizzazione in casi d'uso reali.
Abid Ali Awan's photo

Abid Ali Awan

10 min

blog

Che cos'è Snowflake? Guida per principianti alla piattaforma dati cloud

Esplora le basi di Snowflake, la piattaforma dati cloud. Scopri la sua architettura, le sue funzionalità e come integrarla nelle tue pipeline di dati.
Tim Lu's photo

Tim Lu

12 min

Mostra AltroMostra Altro