Vai al contenuto principale

Tutorial API Qwen 3.7 Max: crea un agente di produttività auto‑consapevole

Impara a creare un agente di produttività con Qwen 3.7 Max via OpenRouter, che pianifica, ricerca, scrive e si auto‑revisiona in un’app Streamlit.
· 15 min leggi

Scopri con l'IA

La maggior parte delle demo di AI assomiglia ancora a chatbot: fai una domanda, il modello risponde e l’interazione finisce lì. Ma il vero lavoro produttivo di solito non avviene in un solo passaggio. Un assistente utile deve scomporre una richiesta vaga, raccogliere contesto, confrontare opzioni, produrre un artefatto utilizzabile e poi controllare il proprio lavoro prima di consegnarlo.

Qwen 3.7 Max è un modello agentico del team Qwen, progettato per carichi di lavoro incentrati sugli agenti, tra cui coding, attività di produttività d’ufficio ed esecuzione a lungo orizzonte. È un modello agent‑first pensato non solo per rispondere bene a un singolo prompt, ma per mantenere coerenza in un flusso di lavoro più lungo che include pianificazione, uso di strumenti, scrittura e autocorrezione. 

In questo tutorial costruiremo un compatto agente di produttività usando Qwen 3.7 Max e Streamlit. L’app prende una richiesta business vaga, come preparare un’analisi competitiva dei database vettoriali, e la trasforma in un file competitive_analysis.md rifinito.

Alla fine di questo tutorial saprai come:

  • Costruire un workflow d’agente fisso in cinque turni

  • Usare enable_thinking=True e preserve_thinking=True

  • Aggiungere ricerche leggere in parallelo

  • Scrivere e ritoccare un artefatto in markdown

  • Creare una semplice UI Streamlit attorno al workflow

Il codice completo è disponibile nel repository su GitHub. 

Il progetto è volutamente piccolo, con la logica principale divisa tra agent.py per l’app Streamlit e thinking_agent.py per il workflow backend.

Se sei alle prime armi con l’AI agentica, ti consiglio di iniziare con il nostro percorso di competenze AI Agent Fundamentals.

Che cos’è Qwen 3.7 Max?

Qwen 3.7 Max è il nuovo modello di punta di Alibaba, rilasciato il 20 maggio 2026. È costruito per workflow agentici. Questo significa che, invece di essere progettato solo per chat one‑shot, è ottimizzato per attività che richiedono pianificazione, uso di strumenti, coding, automazione d’ufficio, ragionamento su contesti lunghi ed esecuzione multi‑step. 

Il team Qwen lo descrive come un modello per l’“era degli agenti”, con punti di forza tra agenti per il coding, assistenti di produttività, workflow basati su MCP e compiti autonomi di lungo periodo.

L’aspetto interessante di questo modello non è solo la performance grezza nei benchmark, ma la sua capacità di rimanere coerente lungo un task multi‑turno in cui ogni turno ha uno scopo diverso, tra cui:

  • pianificazione
  • sintesi delle evidenze
  • scrittura long‑form
  • auto‑critica
  • revisione

Su X, alcuni utenti hanno riscontrato che il modello è al livello di Opus 4.7 e GPT 5.5 per i task di coding a lungo orizzonte.

Post su X che confronta Qwen 3.7 Max con Opus 4.7

Punti di forza e benchmark di Qwen 3.7 Max

Qwen 3.7 Max si comporta molto bene nei benchmark di agent e reasoning. Nelle valutazioni come coding‑agent, ottiene 

  • 69,7 su Terminal-Bench 2.0-Terminus
  • 60,6 su SWE-bench Pro
  • 78,3 su SWE-bench Multilingual
  • 47,2 su NL2Repo
  • 53,5 su SciCode

Nei benchmark per agent generali, raggiunge 

  • 60,8 su MCP-Mark
  • 76,4 su MCP-Atlas
  • 67,2 su CoWorkBench
  • 87,0 su SpreadSheetBench-v1

Qwen 3.7 max nei benchmark

Fonte: Qwen developer blog

Per darti un’idea: tutti quei risultati sono davanti ai punteggi di DeepSeek V4 Pro; la maggior parte di poco, ma per il coding a lungo orizzonte c’è un divario piuttosto significativo tra i due (47,2 vs 35,5 in NL2Repo).

Questo ci porta a un esempio di rilievo: l’esecuzione di ottimizzazione del kernel a lungo orizzonte di Qwen 3.7 Max. Qwen 3.7 Max avrebbe girato per circa 35 ore, effettuando 1.158 chiamate a strumenti, completando 432 valutazioni di kernel e producendo un’accelerazione media geometrica di 10,0x rispetto a un’implementazione di riferimento Triton. 

Questo è importante perché mostra il modello valutato su miglioramenti iterativi basati su strumenti, non solo su domande statiche da benchmark.

In breve, Qwen 3.7 Max è una scelta solida per questa demo perché è costruito per workflow in cui il modello deve ragionare, usare strumenti, preservare il contesto e migliorare un artefatto in più turni.

Costruire un agente di produttività con preservazione del pensiero con Qwen 3.7 Max

In questo progetto, mostreremo tre idee pratiche:

  • Una richiesta vaga può essere convertita in un workflow strutturato multi‑step.
  • Gli output degli strumenti possono essere reinseriti nel processo di ragionamento invece di essere scaricati in un unico lungo prompt.
  • Un modello può rileggere e migliorare il proprio output tramite chain‑of‑thought prompting prima di consegnare l’artefatto finale all’utente.

Panoramica del progetto

Costruiremo una semplice applicazione con una casella di input e un pulsante, in cui l’utente inserisce una richiesta vaga come:

I need to prepare a competitive analysis of the top 3 vector databases
for a team presentation next week. I have no idea where to start.

Demo

Quindi, l’agente esegue un workflow fisso in cinque turni:

User prompt
Turn 1: Plan the work
Parallel research for Pinecone, Weaviate, and Qdrant
Turn 2: Summarize research
Turn 3: Build comparison matrix
Turn 4: Write competitive_analysis.md
Turn 5: Self-review and patch the markdown file
Streamlit file preview

La UI mostra ogni passaggio come una scheda di avanzamento. Man mano che il workflow si completa, le schede passano da in attesa a fatto. Alla fine, l’utente può ispezionare ogni fase: pianificazione, ricerca, sintesi, bozza, auto‑review e anteprima finale del file.

Questo rende visibile il workflow dell’agente invece di nascondere tutto dietro una singola risposta in chat.

Il repo è volutamente piccolo. I componenti principali sono:

  • thinking_agent.py: contiene la logica backend dell’agente, come chiamate al modello, prompt, preservazione del pensiero, ricerca, retry, scrittura file e auto‑review.

  • agent.py: contiene l’interfaccia Streamlit, inclusi layout della pagina, box del prompt, barra di avanzamento, schede dei passaggi, selettore dei risultati, anteprima markdown e pulsante di download.

  • requirements.txt: contiene le dipendenze runtime necessarie per la demo

Il backend è incentrato su una classe ThinkingAgent che esegue una sequenza fissa in cinque turni e restituisce record strutturati dei turni. La UI non ha bisogno di sapere come funzionano chiamate al modello, retry o fanout della ricerca. Deve solo renderizzare ogni turno quando arriva.

Il codice completo di questo tutorial è disponibile su GitHub.

Nota: Ho usato OpenRouter per accedere a Qwen 3.7 Max, ma puoi usare il modello anche tramite la Qwen API Platform ufficiale. 

Passo 1: Configurazione dell’ambiente

Prima di costruire l’agente di produttività con Qwen 3.7 Max, dobbiamo configurare un piccolo ambiente Python con Streamlit, il client compatibile con OpenAI e httpx per ricerche web leggere.

In questo tutorial useremo OpenRouter come gateway del modello, ma il backend è scritto usando il formato API compatibile con OpenAI. Significa che la stessa struttura può essere adattata in seguito ad Alibaba Cloud Model Studio o ad altri provider compatibili.

In questo passo faremo:

  • Creare un ambiente virtuale

  • Installare le dipendenze richieste

  • Impostare OPENROUTER_API_KEY o QWEN_API_KEY

  • Verificare che l’app si avvii con Streamlit

Crea e attiva un ambiente virtuale:

python3 -m venv .venv
source .venv/bin/activate

Installa le dipendenze del progetto:

pip install -r requirements.txt

Il file requirements.txt è volutamente piccolo:

httpx>=0.23.0
openai>=1.30.0
streamlit>=1.45.0

Usiamo streamlit per costruire l’interfaccia web interattiva, openai per chiamare Qwen 3.7 Max via un’API compatibile con OpenAI e httpx per recuperare risultati di ricerca live leggeri per la fase di ricerca.

Passo 2: Configura il client Qwen 3.7 Max

Il backend usa l’SDK Python di OpenAI perché OpenRouter espone un endpoint compatibile con OpenAI. Il file thinking_agent.py definisce alcuni default:

from openai import OpenAI
DEFAULT_MODEL = "qwen/qwen3.7-max"
DEFAULT_BASE_URL = "https://openrouter.ai/api/v1"
DEFAULT_REFERER = "https://qwen.ai"
DEFAULT_TITLE = "Qwen3.7-Max Productivity Agent"

Poi creiamo un client:

def build_client(
   api_key: str | None = None,
   base_url: str | None = None,
   referer: str = DEFAULT_REFERER,
   title: str = DEFAULT_TITLE,
) -> OpenAI:
   return OpenAI(
       base_url=base_url or DEFAULT_BASE_URL,
       api_key=api_key,
       default_headers={
           "HTTP-Referer": referer,
           "X-Title": title,
       },
   )

La funzione build_client() centralizza tutta la configurazione specifica del provider. Il resto dell’app non ha bisogno di sapere se la richiesta passa tramite OpenRouter, Alibaba Cloud Model Studio o un altro endpoint compatibile.

Una volta configurato il client, il backend può chiamare Qwen 3.7 Max usando il formato standard di chat completions.

Ora costruiremo il workflow principale del ThinkingAgent.

Passo 3: Workflow dell’agente in cinque turni

Ora che il client del modello è configurato, possiamo definire il workflow backend principale. Invece di chiedere a Qwen 3.7 Max di generare un report completo in un colpo solo, dividiamo il compito in cinque fasi controllate.

  • Pianificazione: Trasforma il prompt vago in un piano di lavoro strutturato.

  • Ricerca: Raccoglie e riassume evidenze da snippet esterni.

  • Sintesi: Costruisce una matrice di confronto a partire dalla ricerca.

  • Bozza: Scrive il primo report competitive_analysis.md.

  • Auto‑review: Revisiona e ritocca il report prima di restituire il file finale.

class ThinkingAgent:
   def run(
       self,
       goal: str,
       preserve_thinking: bool = True,
       data_mode: str = "live",
       on_turn: Callable[[TurnRecord], None] | None = None,
   ) -> ThinkingRunResult:
       history = [{"role": "system", "content": SYSTEM_PROMPT}]
       turns = []
       backend = self._build_search_backend(data_mode)
       turn1 = self._run_turn(
           history=history,
           turn_index=1,
           phase="planning",
           user_prompt=_planning_prompt(goal),
           preserve_thinking=preserve_thinking,
       )
       search_results = self._run_research(backend)
       turn2 = self._run_turn(
           history=history,
           turn_index=2,
           phase="research",
           user_prompt=_research_prompt(goal, search_results),
           preserve_thinking=preserve_thinking,
       )


       turn3 = self._run_turn(
           history=history,
           turn_index=3,
           phase="synthesis",
           user_prompt=_synthesis_prompt(),
           preserve_thinking=preserve_thinking,
       )
       turn4 = self._run_turn(
           history=history,
           turn_index=4,
           phase="draft",
           user_prompt=_draft_prompt(goal),
           preserve_thinking=preserve_thinking,
       )
       turn5 = self._run_turn(
           history=history,
           turn_index=5,
           phase="self_review",
           user_prompt=_self_review_prompt(draft_markdown),
           preserve_thinking=preserve_thinking,
       )

Il metodo run() è il ciclo di orchestrazione principale. Parte da un system prompt, crea una cronologia della conversazione, costruisce un backend di ricerca ed esegue i cinque turni uno dopo l’altro.

Ogni chiamata a _run_turn() ha una fase specifica. Questo rende il workflow più facile da eseguire il debug perché ogni chiamata al modello ha una responsabilità chiara. Il turno di pianificazione pianifica. Il turno di ricerca interpreta snippet esterni. Il turno di sintesi costruisce la struttura. Il turno di bozza scrive. Il turno di auto‑review migliora l’output.

Il passo di ricerca è posizionato tra il Turno 1 e il Turno 2 perché il modello deve prima comprendere il compito prima che le informazioni esterne vengano integrate nel workflow.

Nel passo successivo aggiungeremo la logica specifica di Qwen per la preservazione del pensiero, che aiuta il modello a mantenere continuità tra questi turni.

Passo 4: Preservare il pensiero tra i turni

Il motivo principale per cui usiamo Qwen 3.7 Max in questa demo è il supporto ai workflow con preservazione del pensiero. L’agente non tratta ogni chiamata al modello come un prompt isolato. Invece, può preservare il ragionamento dei turni precedenti e passare quel contesto ai turni successivi.

In questo passo faremo:

  • Abilitare il “pensiero” del modello.
  • Preservare il ragionamento tra i turni.
  • Accodare gli output dell’assistente alla cronologia condivisa.
response = self.client.chat.completions.create(
   model=self.config.model,
   messages=request_messages,
   extra_body={
       "enable_thinking": True,
       "preserve_thinking": preserve_thinking,
   },
)

Il flag enable_thinking=True consente a Qwen 3.7 Max di produrre contenuto di ragionamento durante la risposta. Mentre il flag preserve_thinking controlla se quel contenuto di ragionamento deve essere mantenuto nei turni successivi.

Dopo che il modello risponde, il backend estrae sia la risposta visibile sia il contenuto di ragionamento:

message = response.choices[0].message
assistant_content = _normalize_text(getattr(message, "content", ""))
reasoning_content = _extract_reasoning_content(message)
The visible answer is what the app can show to the user. The reasoning content is used internally to help the next turn stay aligned with the earlier plan.
Then the backend appends the assistant message back into the conversation history:
```python
assistant_message = {
   "role": "assistant",
   "content": assistant_content,
}
if preserve_thinking and reasoning_content:
   assistant_message["reasoning_content"] = reasoning_content
history.append(assistant_message)

Se il turno di pianificazione decide che il report finale dovrebbe confrontare i database vettoriali in base a modello di deployment, scalabilità, esperienza sviluppatore, ricerca ibrida e prontezza enterprise, i turni successivi possono continuare a usare quei criteri. Senza questo, ogni turno potrebbe andare fuori rotta. La sintesi potrebbe scegliere criteri diversi dalla pianificazione, o l’auto‑review potrebbe valutare il report rispetto a un obiettivo differente.

Aggiungeremo ora il livello di ricerca che fornisce all’agente contesto esterno prima che scriva il report.

Passo 5: Aggiungi ricerca in parallelo con dati di fallback

Prima che il modello scriva un’analisi competitiva, ha bisogno di contesto esterno. In questa demo, manteniamo l’ambito della ricerca fisso confrontando tre database vettoriali: Pinecone, Weaviate e Qdrant.

In questo passo faremo:

  • Definire i database target.
  • Eseguire chiamate di ricerca in parallelo.
  • Ripiegare su dati di fixture deterministici se la ricerca live fallisce.
DATABASES = ("Pinecone", "Weaviate", "Qdrant")

L’elenco dei target è volutamente fisso. Questo mantiene la demo stabile e più facile da spiegare. In seguito puoi estendere il progetto in modo che il modello scelga dinamicamente i target di confronto.

Successivamente, il backend avvia le chiamate di ricerca in parallelo:

from concurrent.futures import ThreadPoolExecutor, as_completed
with ThreadPoolExecutor(max_workers=3) as executor:
   future_map = {
       executor.submit(backend.search, database, query): database
       for database, query in queries.items()
   }
   for future in as_completed(future_map):
       database = future_map[future]
       results[database] = future.result()

Il ThreadPoolExecutor avvia una ricerca per database. È più veloce dell’esecuzione sequenziale perché le ricerche su Pinecone, Weaviate e Qdrant possono avvenire in parallelo.

try:
   results[database] = future.result()
except Exception as exc:
   fallback = FixtureSearchBackend().search(database, queries[database])
   fallback.warning = (
       f"Live search failed for {database} ({exc}). "
       "Fallback demo research was used."
   )
   results[database] = fallback

Se la ricerca live funziona, l’agente usa snippet aggiornati. Se fallisce, l’app completa comunque il workflow usando snippet di ricerca deterministici incorporati. Il principio di design importante è la degradazione graduale. L’agente non dovrebbe andare in crash solo perché una dipendenza esterna fallisce.

Nel passo successivo passeremo questi risultati di ricerca a Qwen 3.7 Max così da sintetizzare una matrice di confronto.

Passo 6: Genera la sintesi e la matrice di confronto

Una volta che l’agente ha gli snippet di ricerca, il compito successivo è la sintesi. Questo passo trasforma informazioni sparse in un confronto strutturato che può supportare il report finale.

In questo passo faremo:

  • Inviare il contesto di ricerca a Qwen 3.7 Max.
  • Chiedere al modello di creare un confronto strutturato.
  • Renderizzare poi quell’output nell’interfaccia Streamlit.
turn3 = self._run_turn(
   history=history,
   turn_index=3,
   phase="synthesis",
   user_prompt=_synthesis_prompt(),
   preserve_thinking=preserve_thinking,
)

Quando questo codice gira, la cronologia della conversazione contiene già la richiesta originale dell’utente, l’output della pianificazione, i riassunti della ricerca e, facoltativamente, il pensiero preservato dei turni precedenti.

Questo significa che il prompt di sintesi non deve ripetere l’intero compito. Può concentrarsi su un lavoro, cioè convertire la ricerca in una matrice di confronto pronta per la decisione.

Una buona sintesi dovrebbe confrontare le opzioni lungo dimensioni come:

  • Modello di deployment
  • Scalabilità
  • Esperienza sviluppatore
  • Supporto alla ricerca ibrida
  • Prontezza enterprise
  • Casi d’uso ideali

Inoltre, useremo questa struttura sintetizzata per scrivere il primo report Markdown.

Passo 7: Scrivi il report markdown

Dopo pianificazione, ricerca e sintesi, l’agente ha contesto sufficiente per scrivere il primo deliverable completo. In questa demo, il deliverable è un file markdown chiamato competitive_analysis.md.

In questo passo faremo:

  • Chiedere a Qwen 3.7 Max di scrivere la prima bozza del report.
  • Rimuovere eventuali fence di codice markdown non necessarie.
  • Salvare la bozza su disco.
  • Registrare l’azione di scrittura file come evento di uno strumento.
turn4 = self._run_turn(
   history=history,
   turn_index=4,
   phase="draft",
   user_prompt=_draft_prompt(goal),
   preserve_thinking=preserve_thinking,
)

Il turno di bozza usa tutto il contesto precedente, inclusi l’obiettivo originale dell’utente, il piano, la ricerca e la matrice di sintesi.

La risposta del modello viene quindi ripulita e scritta su disco:

draft_markdown = _strip_markdown_fences(turn4.assistant_content)
report_path = run_output_dir / "competitive_analysis.md"
report_path.write_text(draft_markdown)

L’helper _strip_markdown_fences() è utile perché i modelli avvolgono spesso l’output markdown tra triple backtick. Va bene in una risposta in chat, ma non è ideale quando si scrive direttamente su un file .md.

Il backend registra anche l’azione di scrittura:

turn4.tool_events.append(
   ToolEvent(
       name="write_file",
       status="completed",
       input_payload={"path": str(report_path)},
       output_payload={
           "description": "Initial draft written to disk.",
       },
   )
)

Questo evento aiuta la UI a mostrare che il backend ha creato un file reale, non solo un’altra risposta testuale. A questo punto, l’app ha già un report utilizzabile. Ma aggiungiamo un altro turno del modello per migliorare la qualità prima di mostrare l’artefatto finale.

Nel passo successivo chiederemo a Qwen 3.7 Max di revisionare e ritoccare la propria bozza.

Passo 8: Auto‑review e patch del report

L’ultimo turno del modello è una fase di auto‑review. Invece di consegnare subito la prima bozza all’utente, l’agente rilegge il report, lo verifica rispetto all’obiettivo originale e scrive una versione migliorata.

In questo passo faremo:

  • Inviare la bozza markdown di nuovo al modello.
  • Chiedere al modello di identificare i punti deboli.
  • Generare una versione rivista.
  • Sovrascrivere il report con la versione finale migliorata.
turn5 = self._run_turn(
   history=history,
   turn_index=5,
   phase="self_review",
   user_prompt=_self_review_prompt(draft_markdown),
   preserve_thinking=preserve_thinking,
)

Il prompt di auto‑review chiede al modello di controllare aspetti pratici, come se la raccomandazione sia chiara, se i criteri di confronto siano coerenti e se il report sia utile per una presentazione di team.

L’output rivisto viene scritto sullo stesso file:

final_markdown = _strip_markdown_fences(turn5.assistant_content)
report_path.write_text(final_markdown)

L’agente può anche registrare l’evento di scrittura finale:

turn5.tool_events.append(
   ToolEvent(
       name="write_file",
       status="completed",
       input_payload={"path": str(report_path)},
       output_payload={
           "description": "Self-reviewed final report written to disk.",
       },
   )
)

Questo passaggio trasforma l’app da un semplice generatore a un piccolo workflow editoriale. L’output che l’utente vede non è la prima bozza; è la versione revisionata e ritoccata.

Ora collegheremo questo workflow backend all’app Streamlit.

Passo 9: Costruisci l’interfaccia di avanzamento in Streamlit

Passiamo ora dalla logica backend all’interfaccia utente. L’app Streamlit dovrebbe permettere all’utente di inserire un prompt, eseguire il workflow e osservare il completamento di ciascuna fase.

Nota: questo codice appartiene al file agent.py perché controlla ciò che l’utente vede e clicca.

In questo passo faremo:

  • Configurare la pagina Streamlit.

  • Creare l’input del prompt.

  • Aggiungere un pulsante di esecuzione.

  • Chiamare la funzione ThinkingAgent.run().

  • Aggiornare la UI al completamento dei turni.

def render_streamlit_app() -> None:
   st.set_page_config(
       page_title="Qwen3.7-Max Demo Agent",
       page_icon=":material/psychology:",
       layout="wide",
       initial_sidebar_state="collapsed",
   )

L’app usa un layout ampio perché la UI include schede dei passaggi, indicatori di avanzamento e pannelli dei risultati.

L’area di input è semplice:

goal = st.text_area(
   "Prompt",
   value=DEFAULT_GOAL,
   height=120,
   label_visibility="collapsed",
)

run_clicked = st.button("Run Demo", use_container_width=True)

All’utente serve solo fornire il compito. L’app non chiede di configurare temperatura, modalità di ricerca, ID modello o impostazioni di ragionamento. Questi dettagli sono gestiti dal backend.

Quando l’utente clicca il pulsante, agent.py crea l’agente backend e lo esegue:

agent = ThinkingAgent(config=config)

result = agent.run(
   goal=goal,
   preserve_thinking=True,
   data_mode="live",
   on_turn=on_turn,
)

Qui i due file si incontrano. agent.py attiva il workflow, ma thinking_agent.py possiede il comportamento reale dell’agente. La callback della UI aggiorna la pagina dopo ogni turno:

def on_turn(turn: TurnRecord) -> None:
   partial_turns.append(turn)

   progress_placeholder.progress(
       len(partial_turns) / len(phase_order),
       text=f"Completed {_phase_label(turn.phase)}",
   )

   with steps_placeholder.container():
       _render_step_cards(partial_turns, result_ready=False)

La callback riceve un TurnRecord completato e aggiorna la barra di avanzamento e le schede dei passaggi. Durante l’esecuzione, le schede fungono da indicatori di stato. Dopo il completamento, diventano viste dei risultati cliccabili.

Ora renderemo il report markdown finale e lo esporremo come file scaricabile.

Passo 10: Anteprima e download del report finale

Dopo che il backend ha finito, la UI deve mostrare il report finale. Il backend ha già creato il file markdown, quindi agent.py deve solo leggerlo, renderizzarlo ed esporre un pulsante di download.

In questo passo faremo:

  • Verificare se il file del report esiste.
  • Leggere il contenuto markdown finale.
  • Mostrare un pulsante di download.
  • Renderizzare il markdown nell’app Streamlit
def _render_file_preview(report_path: str) -> None:
   path = Path(report_path)

   if not path.exists():
       st.info("No file was written yet.")
       return
   content = path.read_text()
   st.caption(f"Created file: {path.name}")
   st.download_button(
       "Download competitive_analysis.md",
       data=content,
       file_name=path.name,
       use_container_width=True,
   )
   st.markdown(content)

La funzione inizia controllando se il file esiste. Questo impedisce all’app di andare in crash se il backend si è interrotto prima di scrivere il report. Quindi legge il contenuto markdown e crea un pulsante di download. Infine, renderizza il markdown direttamente nella pagina Streamlit.

Questa è una chiara separazione di responsabilità in cui:

  • thinking_agent.py scrive il report

  • agent.py mostra l’anteprima e consente il download del report

Nel passo successivo aggiungeremo una gestione degli errori di base in modo che la demo si comporti meglio quando i percorsi del provider falliscono o si verificano limitazioni di rate.

Passo 11: Gestione errori e rate limit

I provider di modelli possono andare in timeout, restituire errori temporanei o applicare rate limit alle richieste. Se esponiamo quegli errori grezzi direttamente agli utenti, l’app dà l’idea di essere incompleta.

Questo passo è diviso tra entrambi i file. La logica di retry appartiene a thinking_agent.py perché fa parte della chiamata al modello nel backend. Il messaggio di errore appartiene a agent.py perché controlla ciò che l’utente vede.

In questo passo faremo:

  • Ritentare i fallimenti temporanei del provider nel backend.
  • Rilevare errori simili a rate limit.
  • Mostrare un messaggio più chiaro nella UI di Streamlit
delays = (1.0, 2.0)

for attempt_index in range(len(delays) + 1):
   try:
       return self.client.chat.completions.create(...)
   except Exception as exc:
       if not _is_retryable_provider_error(exc):
           raise

       if attempt_index >= len(delays):
           raise

       time.sleep(delays[attempt_index])

Il backend prova la chiamata al modello, intercetta i fallimenti ritentabili, attende brevemente e riprova. I brevi ritardi bastano a compensare un’instabilità temporanea del provider senza far attendere troppo l’utente.

def _friendly_rate_limit_message(exc: Any) -> str:
   return (
       "Qwen 3.7 Max is temporarily rate-limited upstream. "
       "Please wait 30 to 60 seconds and try again."
   )

Questo mantiene JSON grezzi del provider o stack trace fuori dalla UI. L’utente riceve una spiegazione chiara e un passo successivo pratico.

Con questo passo finale, possiamo ora eseguire l’app completa e testare il workflow end‑to‑end.

Passo 12: Esegui la demo

Ora che il workflow backend e l’interfaccia Streamlit sono collegati, possiamo eseguire l’app completa.

In questo passo faremo:

  • Avviare l’app Streamlit.
  • Inserire il prompt di produttività predefinito.
  • Eseguire il workflow in cinque fasi.
  • Vedere l’anteprima e scaricare il report markdown finale

Avvia l’app:

streamlit run agent.py

Usa il prompt predefinito:

I need to prepare a competitive analysis of the top 3 vector databases
for a team presentation next week. I have no idea where to start.

Quando clicchi Run Demo, l’app attraverserà l’intero workflow:

  • Pianificazione: Crea un piano strutturato.

  • Ricerca: Raccoglie snippet per Pinecone, Weaviate e Qdrant.

  • Sintesi: Costruisce una matrice di confronto.

  • Bozza: Scrive il primo report markdown.

  • Auto‑review: Ritocca il report.

  • Anteprima: Mostra il file finale competitive_analysis.md.

Il report finale è salvato in:

output/showcase_runs/<timestamp>/with_preserve/competitive_analysis.md

Alla fine, dovresti vedere l’anteprima del report e un pulsante di download nell’interfaccia Streamlit.

Conclusione

In questo tutorial abbiamo costruito un compatto agente di produttività con Qwen 3.7 Max che esegue un workflow fisso e interpretabile: dalla pianificazione, alla ricerca, alla sintesi, alla scrittura, fino all’auto‑review. Lungo il percorso mostra perché preservare il ragionamento tra i turni può essere utile per task agentici long‑form.

La versione attuale usa solo Streamlit, chiamate a modelli compatibili con OpenAI, un layer di ricerca leggero e output in Markdown. Questo la rende facile da studiare, modificare e riutilizzare nei tuoi esperimenti.

Da qui ci sono molte estensioni naturali:

  • Lasciare al modello la scelta dei target di confronto invece di usare un elenco fisso

  • aggiungere esecuzioni affiancate “con preserve_thinking” vs “senza preserve_thinking”

  • Esporre nella UI uso di token e stime di costo

  • generare una scaletta di slide o un grafico insieme al report markdown

  • Sostituire lo scraping DuckDuckGo con una API di ricerca più robusta

Se vuoi andare oltre un workflow fisso e imparare a costruire applicazioni AI pronte per la produzione, dall’ingegneria dei prompt al RAG fino ai sistemi agentici, il nostro percorso di competenze AI Engineering with LangChain è un buon prossimo passo.

Domande frequenti sul tutorial API Qwen 3.7 Max

Quando dovrei usare preserve_thinking=True con Qwen 3.7 Max?

preserve_thinking=True aiuta i turni successivi a rimanere allineati con le decisioni prese in precedenza nei workflow multi‑turno. Nel nostro esempio, se il turno di pianificazione sceglie criteri specifici di confronto, i turni di sintesi e auto‑review possono continuare a usare lo stesso ragionamento invece di deviare.

Qwen 3.7 Max è solo per agenti di coding?

No. Il coding è uno dei suoi principali punti di forza, ma il modello è pensato anche per agenti di produttività, automazione d’ufficio, workflow basati su MCP, attività multilingue, ragionamento su contesti lunghi e uso di strumenti a lungo orizzonte.

L’app demo usa davvero la ricerca sul web?

Sì, il percorso predefinito attuale prova lo scraping live di DuckDuckGo. Se fallisce, ripiega su ricerche di fixture incorporate.

Il modello crea sub‑agent nella nostra app demo?

No. L’app usa una sola sessione di modello. La parte “parallela” avviene nel livello di ricerca, dove il backend lancia tre ricerche web in concorrenza e alimenta i risultati nella stessa sessione del modello.

Perché separiamo agent.py e thinking_agent.py?

agent.py mantiene leggibile la UI di Streamlit. thinking_agent.py contiene il workflow backend, la costruzione dei prompt, i retry e la logica di ricerca, così che il comportamento dell’agente possa essere testato in modo indipendente dall’interfaccia.

Aashi Dutt

Autore

Aashi Dutt

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

Correlato