Vai al contenuto principale
Gli agenti di ricerca sono utili quando vuoi qualcosa di più di un singolo risultato di ricerca o di una risposta veloce del modello. Un buon agente di ricerca può trasformare un argomento ampio in query di ricerca, raccogliere fonti, estrarre l’evidenza importante, fare follow-up sulle lacune e scrivere un briefing con citazioni che puoi successivamente ispezionare. In questo tutorial costruiremo un agente di ricerca privato usando Python e l’API di Venice. Alla fine avrai una CLI in grado di ricercare un argomento, fare scraping di pagine pubbliche in Markdown, riassumere chunk di fonti, eseguire pass di ricerca di follow-up consapevoli delle lacune e generare un report con citazioni e artefatti JSONL locali opzionali. Ti interessa l’implementazione completa? Dai un’occhiata al repository GitHub. Prima di continuare, ti servirà una API key Venice:

Cosa costruiremo

L’implementazione di riferimento è un piccolo progetto Python con poche parti ben definite: Il flusso è il seguente: Pipeline dell'agente di ricerca privato
  1. Chiedi a Venice di generare query di ricerca diversificate per l’argomento.
  2. Cerca sul web con uno o più provider.
  3. Deduplica gli URL prima di leggerli.
  4. Usa l’endpoint di scrape di Venice per convertire ogni pagina sorgente pubblica in Markdown.
  5. Suddividi le pagine lunghe in chunk.
  6. Chiedi a Venice di estrarre l’evidenza da ogni chunk.
  7. Chiedi a Venice di trasformare l’evidenza dei chunk in source notes.
  8. Identifica lacune di ricerca e problemi di bilanciamento delle fonti prima di generare query di follow-up.
  9. Chiedi a Venice di sintetizzare il report finale con citazioni in stile footnote.
Questo è “privato” nel senso pratico che l’agente mantiene l’orchestrazione, le source notes, gli artefatti e i report finali sulla tua macchina. Venice gestisce le chiamate al modello e lo scraping tramite la sua API. L’implementazione di riferimento di default invia comunque query di ricerca a DuckDuckGo o arXiv, quindi considera la scelta del provider come parte del tuo design della privacy.

Configurare il progetto

Il progetto di riferimento usa Python 3.13 e uv, ma lo stesso codice funziona anche con un normale virtual environment. Crea un nuovo progetto:
Installa le dipendenze:
Se preferisci pip, crea un virtual environment e installa gli stessi pacchetti:
Crea un file .env per lo sviluppo locale:
Usiamo VENICE_MODEL così puoi cambiare il modello senza modificare il codice. L’implementazione di riferimento attualmente usa openai-gpt-55 come default, ma puoi sostituirlo con un altro modello di chat disponibile per il tuo account Venice.

Creare i data model

Prima di scrivere la logica dell’agente, definiremo gli oggetti che attraversano la pipeline. Questi modelli mantengono il resto del codice più facile da ragionare perché ogni fonte porta con sé la provenienza: da dove proviene, quale query l’ha trovata, quando è stata recuperata e come è stata suddivisa in chunk. Crea research_agent/models.py:
I campi importanti qui sono canonical_url, content_hash e chunks. canonical_url permette all’agente di evitare di leggere ripetutamente la stessa fonte quando i risultati di ricerca differiscono solo per parametri di tracking o frammenti. content_hash aiuta a beccare pagine duplicate anche quando vivono a URL diversi. chunks ci permette di riassumere pagine lunghe in pezzi più piccoli invece di perdere evidenza utile per i limiti di contesto. Aggiungi le funzioni helper sotto i dataclass:
Il chunking qui è volutamente semplice: chunk a carattere di dimensione fissa con overlap. È sufficiente per un demo agent di ricerca perché l’endpoint di scrape di Venice restituisce Markdown, che è di solito molto più pulito dell’HTML raw. Per ricerca in produzione su documenti tecnici lunghi, puoi migliorarlo splittando su heading, paragrafi o conteggio di token.

Costruire il client Venice

Successivamente, creeremo un piccolo client Venice. Potresti usare l’SDK Python di OpenAI per chat completions perché Venice è compatibile con OpenAI, ma l’implementazione di riferimento usa httpx direttamente così lo stesso client può chiamare l’endpoint POST /augment/scrape di Venice. Crea research_agent/venice.py:
L’helper from_env() tiene i segreti fuori dal codice sorgente. Rende anche conveniente lo sviluppo locale perché python-dotenv può caricare VENICE_API_KEY e VENICE_MODEL dal .env. Ora aggiungi le chat completions:
Per il report finale vogliamo usare lo streaming perché i report deep possono richiedere notevolmente più tempo (perché produrranno molto più testo). Questo può causare problemi di timeout per richieste in cui può servire moltissimo tempo per produrre l’output finale. Usando lo streaming possiamo eliminare questo problema e rendere la richiesta più resistente ai timeout failure:
Poi aggiungi lo scraping:
L’endpoint di scrape di Venice accetta un URL pubblicamente accessibile e restituisce la pagina come Markdown. Significa che il modello non deve parsare l’HTML raw, e i tuoi prompt di estrazione dalla fonte possono lavorare con testo più pulito. L’helper rimanente gestisce i retry e il parsing della response:
Il repo completo include anche un robusto helper _post_chat_stream() che legge eventi server-sent dalle streaming chat completions. Puoi iniziare senza streaming e poi aggiungerlo una volta che il resto del flusso di ricerca funziona.

Aggiungere i search provider

Il layer di ricerca ha due lavori: trovare URL di fonti e recuperare quegli URL tramite lo scraper di Venice. L’implementazione di riferimento usa l’endpoint HTML di DuckDuckGo per la ricerca web generale e l’Atom API di arXiv per i paper. Crea research_agent/web.py:
Ora aggiungi DuckDuckGo:
E arXiv:
La classe WebSearch coordina i provider e recupera le pagine:
L’implementazione di riferimento completa aggiunge retry, ritardi di richiesta per host ed errori più amichevoli. Vale la pena tenerli perché gli agenti di ricerca passano molto tempo a confrontarsi con pagine che bloccano l’automazione, redirezionano in modo inatteso o restituiscono errori transitori. Aggiungi i piccoli helper dei provider in fondo:

Scrivere artefatti locali

Per i workflow di ricerca, l’auditability conta. Se il report finale dice qualcosa di sorprendente, dovresti poter ispezionare quale fonte ha portato a quella conclusione. Crea research_agent/artifacts.py:
Questo scrive un oggetto JSON per riga, il che rende gli artefatti facili da appendere, ispezionare ed elaborare con tool da command-line in seguito.

Costruire l’agente di ricerca

Ora che abbiamo Venice, search, model e artefatti, possiamo costruire l’agente vero e proprio. Crea research_agent/agent.py:
Il system prompt è il guardrail comportamentale di base. Non vogliamo che il modello produca un report che suona impressionante dalla memoria. Vogliamo che usi il materiale sorgente e segnali l’incertezza quando l’evidenza è scarsa. Servono anche due dataclass finali in models.py se non li hai ancora aggiunti:
Ora definisci il ResearchAgent:
Il metodo run() coordina i pass di ricerca:
I due set seen_* sono ciò che impedisce all’agente di sprecare tempo su fonti duplicate. La dedupe per URL becca i link ripetuti. La dedupe per content hash becca mirror, post syndicati e pagine che redirezionano allo stesso contenuto finale.

Pianificare ricerche iniziali e di follow-up

La prima chiamata al modello trasforma l’argomento in query di ricerca:
Dopo ogni pass di ricerca, l’agente aggiornato fa un passo deliberato di analisi delle lacune. Guarda le note attuali, conta i cluster di fonti per dominio, chiede a Venice quale copertura manca, scrive quelle lacune negli artefatti e poi usa le query risultanti per il pass successivo. Loop di analisi delle lacune Inizia tracciando il bilanciamento delle fonti:
Questo dà all’agente un modo semplice per accorgersi di catture dei cluster di fonti. Se tutte le fonti vengono da un’unica azienda, un unico framework o un unico dominio, le query di follow-up dovrebbero deliberatamente allargare l’insieme delle fonti invece di raccoglierne altre dello stesso tipo. Ora usa quell’informazione di bilanciamento per creare ricerche di follow-up:
L’implementazione di riferimento più recente avvolge tutto questo in _gap_follow_up_queries(), che chiede a Venice di restituire sia record di lacune che query:
Quando --artifacts è abilitato, questi record vengono scritti in research_gaps.jsonl. Questo ti dà un utile audit trail del perché l’agente ha cercato una particolare query di secondo pass. Il parser dovrebbe essere indulgente. Se il modello restituisce JSON malformato, l’agente fa fallback all’argomento originale:
Questo pattern vale la pena di usarlo in tutto il codice degli agent: chiedi output strutturato, parsalo e fornisci un semplice fallback quando l’output non è utilizzabile.

Leggere e riassumere le fonti

Ora raccogliamo le source notes. L’agente cerca ogni query, recupera ogni risultato tramite Venice scrape, suddivide il Markdown in chunk e riassume l’evidenza utile.
I singoli fallimenti di search e fetch non devono fermare l’intero run. Il web pubblico è disordinato. Alcune pagine bloccano lo scraping, alcune restituiscono PDF, alcune sono down e alcune redirezionano a posti inattesi. Un agente di ricerca dovrebbe continuare a muoversi e registrare cosa è fallito. Ecco il metodo di lettura della fonte:
Per ogni chunk di fonte, chiedi a Venice un breve riassunto dell’evidenza e citazioni esatte:
Poi collassa i riassunti dei chunk in una source note:
Questa sommarizzazione in due passaggi è la parte che fa sembrare l’agente più affidabile di un semplice script “riassumi questi URL”. Il modello legge prima i chunk delle fonti, poi scrive una nota a livello di fonte a partire da quei pezzi di evidenza estratti.

Scrivere il report finale

Una volta che l’agente ha le source notes, può scrivere il report. Inizia con un report writer a singolo pass:
L’implementazione di riferimento va oltre per i report deep: chiede a Venice un’outline, redige ogni sezione del report separatamente e poi chiede un pass finale di editor per assemblare il report finito e convertire gli ID interni delle fonti in citazioni in stile footnote. Quell’approccio a fasi è utile quando vuoi output di ricerca long-form perché un unico prompt gigante spesso comprime troppo. I prompt aggiornati spingono inoltre il report verso un survey ampio e source-backed invece di una sottile guida decisionale. Se la base delle fonti è sbilanciata verso un cluster, il prompt dell’editor dice a Venice di riconoscere quello sbilanciamento ed evitare di presentarlo come rappresentativo dell’intero campo. Aggiungi gli helper di digest:
Infine, aggiungi la registrazione degli errori:
A questo punto, il loop di ricerca principale è in piedi.

Aggiungere la CLI

Ora ci serve un entry point da command-line. Crea main.py:
La CLI espone le manopole che effettivamente regolerai durante la ricerca: Ora cabla tutto insieme:
Questo ci dà una CLI di ricerca locale funzionante.

Eseguire l’agente

Esegui un rapido pass di ricerca:
Scrivi il report in un file Markdown:
Usa più fonti e più provider:
Scegli lo stile del report finale:
Usa brief per un briefing conciso source-backed, standard per un survey più completo e deep per il workflow a fasi outline/section/editor. Salva artefatti auditable:
Quando gli artefatti sono abilitati, vedrai file come:
Questi file sono utili quando vuoi capire come l’agente ha raggiunto una conclusione. Ad esempio, source_notes.jsonl mostra l’evidenza riassunta delle fonti, research_gaps.jsonl mostra perché sono state generate ricerche di follow-up ed errors.jsonl mostra le pagine che sono fallite durante search, scraping o sommarizzazione.

Note su privacy e affidabilità

Un agente di ricerca tocca diversi sistemi, quindi aiuta essere precisi su cosa va dove: Confini dei dati dell'agente di ricerca privato Se vuoi mantenere più della catena di ricerca all’interno di Venice, puoi adattare il layer del provider per chiamare l’endpoint POST /augment/search di Venice invece di interrogare DuckDuckGo direttamente. L’implementazione di riferimento usa provider pubblici leggeri così la demo resta facile da eseguire e comprendere. Per l’affidabilità, mantieni questi default conservativi:
  • Usa retry per le chiamate Venice e le richieste web.
  • Aggiungi un piccolo --request-delay se stai leggendo molte pagine dallo stesso host.
  • Limita --max-sources così argomenti ampi non corrono indefinitamente.
  • Salva --artifacts per report importanti così puoi auditare l’output finale.
  • Tratta il report come un briefing, non come verità di base. Segui le citazioni fino alla fonte originale quando l’accuratezza conta.

Testare i pezzi

Non hai bisogno di richieste web live o chiamate Venice per testare la maggior parte del sistema. Il repo di riferimento usa classi fake di Venice e fake web per testare il loop di ricerca, il comportamento di dedupe, gli artefatti e i prompt del report. Un utile primo test è la canonicalizzazione degli URL:
Poi testa che il contenuto duplicato venga saltato:
I fake rendono i test degli agent molto più veloci e meno flaky. Puoi verificare la logica di orchestrazione senza affidarti a risultati di ricerca live, condizioni di rete o output del modello.

Benchmarking

Molti provider AI hanno ora i propri workflow di deep research, quindi il repo di riferimento include un semplice benchmark contro il tool Deep Research di Perplexity. A entrambi gli agenti è stato chiesto di scrivere un report sull’architettura dei framework di AI agent, poi i report generati sono stati committati nel repo GitHub. Non è inteso essere un benchmark formale. È un modo pratico per ispezionare la struttura del report, la copertura delle fonti, la qualità delle citazioni e se l’agente si concentra troppo su un singolo cluster di fonti. È anche per questo che l’implementazione aggiornata traccia research_gaps.jsonl e il bilanciamento delle fonti prima delle ricerche di follow-up.

Estendere questo esempio

Una volta che l’agente baseline funziona, ecco alcuni modi pratici per migliorarlo:
  • Aggiungi un Venice search provider usando POST /augment/search.
  • Memorizza report e artefatti in un piccolo database SQLite invece che in file JSONL.
  • Aggiungi allowlist o blocklist di fonti per domini di ricerca affidabili.
  • Aggiungi il supporto PDF combinando Venice scrape con il parsing di documenti per fonti che non espongono HTML pulito.
  • Aggiungi un evaluation set di argomenti e tipi di fonti attesi così puoi confrontare la qualità della ricerca dopo modifiche ai prompt.
  • Aggiungi uno step di review che chieda a Venice di trovare affermazioni non supportate nel report finale prima di salvarlo.
L’upgrade più grande è di solito una migliore selezione delle fonti. La generazione di query aiuta, ma puoi anche migliorare la qualità preferendo fonti primarie, documenti di standard, doc ufficiali, paper, changelog e pagine di dataset rispetto ai riassunti a basso segnale.

Per concludere

Grazie per aver letto! Speriamo che ti abbia aiutato a costruire un agente di ricerca privato pratico con Python e l’API Venice. Il pattern utile qui non è solo “chiedi a un modello di ricercare qualcosa”. È spezzare la ricerca in passi auditable: pianifica le ricerche, raccogli le fonti, estrai l’evidenza, scrivi le source notes, fai follow-up sulle lacune e sintetizza con citazioni. Mantenendo quei passi espliciti, otteniamo un workflow di ricerca più facile da ispezionare, testare e migliorare nel tempo.