Questa guida pratica ed eseguibile accompagna il primo post della serie Portway. L'obiettivo: configurare un singolo modello dietro un endpoint compatibile con OpenAI sul proprio hardware, chiamarlo tramite l'SDK ufficiale OpenAI e interiorizzare il contratto stateless (senza stato). Tutto funziona localmente, a costo zero.
Cosa copre questo articolo
- Uno script
demo.pycon due blocchi:- Round-trip — una chiamata chat tramite SDK OpenAI, con stampa del contenuto e dell'oggetto
usage. - Prova stateless — la stessa domanda finale inviata come messaggio a 1 turno e come ultimo turno di una cronologia fabbricata a 5 turni; entrambi i valori
prompt_tokensvengono stampati insieme a una spiegazione del delta.
- Round-trip — una chiamata chat tramite SDK OpenAI, con stampa del contenuto e dell'oggetto
Scelta del motore su questa macchina
Mac con Apple Silicon, 48 GB di memoria unificata, Ollama già installato. Il demo utilizza l'endpoint compatibile con OpenAI di Ollama all'indirizzo http://localhost:11434/v1 e il modello gpt-oss:20b (~14 GB).
Opzioni di modello per RAM disponibile
Lo script demo funziona con qualsiasi modello servito da Ollama — basta sostituire il nome del modello in demo.py. La tabella seguente copre macchine con memoria unificata a partire da 9 GB.
| Modello | Comando di pull | Dimensione approssimativa | RAM minima | Note |
|---|---|---|---|---|
llama3.2:3b | ollama pull llama3.2:3b | ~2 GB | 8 GB | Più veloce; ideale per testare il contratto |
gemma3:4b | ollama pull gemma3:4b | ~3 GB | 8 GB | Di Google; solida nell'esecuzione di istruzioni |
mistral:7b | ollama pull mistral:7b | ~4,1 GB | 8 GB | Classico baseline a 7B |
llama3.1:8b | ollama pull llama3.1:8b | ~4,7 GB | 9 GB | Migliore qualità sotto i 10 GB |
qwen2.5:7b | ollama pull qwen2.5:7b | ~4,4 GB | 9 GB | Ottimo per istruzioni e ragionamento |
gpt-oss:20b | ollama pull gpt-oss:20b | ~14 GB | 24 GB | Utilizzato nell'output di esempio di questo post |
Su una macchina con 9 GB, sostituire gpt-oss:20b in demo.py con llama3.1:8b o qwen2.5:7b — la dimostrazione del contratto è identica.
Prerequisiti
- Ollama in esecuzione locale (
curl -s http://localhost:11434/api/tagsdeve restituire JSON) - uv installato (
uv --version) - Il modello scaricato. Questo post usa
gpt-oss:20b(richiede ~24 GB di RAM); vedi Opzioni di modello per RAM disponibile per alternative più leggere su macchine con 9 GB+.
shellollama pull llama3.2:3b
Esecuzione
Dalla root del repository:
shelluv sync # crea .venv alla root, installa dipendenze uv run --project 1-local-first python 1-local-first/demo.py
Output di esempio
Un'esecuzione reale su questa macchina (Mac classe M4, 48 GB, gpt-oss:20b tramite Ollama). I numeri differiranno con modelli più piccoli — prompt_tokens per lo stesso input rimane deterministico indipendentemente dal modello:
plaintext============================================================ Block 1 — round-trip tramite SDK OpenAI contro localhost ============================================================ content: Toronto, Vancouver, Montreal. usage: CompletionUsage(completion_tokens=43, prompt_tokens=72, total_tokens=115, ...) ============================================================ Block 2 — stessa domanda finale, 1-turn vs cronologia a 5 turni ============================================================ 1-turn response: La capitale del Canada è **Ottawa**. 5-turn response: La capitale del Canada è **Ottawa**, situata nella provincia di Ontario. 1-turn prompt_tokens: 75 5-turn prompt_tokens: 139 delta: 64 Perché esiste il delta: il server NON ha stato conversazionale tra le richieste. I prompt_tokens della chiamata a 5 turni sono superiori solo perché il client ha ri-inviato la cronologia completa nel body della richiesta. Ogni chiamata viene valutata da zero — la cronologia è responsabilità del client.
completion_tokens e il testo della risposta variano da un'esecuzione all'altra (il campionamento è non-deterministico con temperatura predefinita). prompt_tokens per lo stesso input è deterministico — 75 e 139 dovrebbero riprodursi.
Notare come la risposta a 5 turni recepisca il contesto del viaggio su strada ("situata nella provincia di Ontario") mentre la risposta a 1 turno rielabora il semplice "Driving." nel suo prompt — stesso modello, inquadratura diversa nei messaggi forniti dal client.

Il contratto stateless spiegato
Questo è il concetto più importante della serie. Ogni richiesta a un'API LLM — locale o cloud — viene valutata da zero. Il server non ha memoria dei turni precedenti. Quando invii una conversazione multi-turno, tu sei quello che ri-invia la cronologia completa nel body della richiesta. Il modello vede tutto in una volta.
L'unica "memoria" del server tra le richieste è la prefix cache (un'optimizzazione computazionale che evita di rivalutare i token già visti), mai lo stato conversazionale. La cache è invisibile — dal punto di vista del contratto API, ogni chiamata è stateless.
Comprendere questo è il fondamento per tutto ciò che segue nella serie:
- Perché la gestione delle conversazioni appartiene al client, non al server
- Perché le finestre di contesto contano per costo e latenza
- Perché lo streaming di
usagerichiede un'opt-in esplicita (stream_options.include_usage)
Definizione di completato
- Round-trip SDK OpenAI contro
localhost— il Blocco 1 stampa uncontentreale e un oggettousage. - Sa spiegare perché 5 turni vs 1 turno cambiano
prompt_tokensmentre il server non ricorda nulla — il Blocco 2 stampa entrambi i numeri e la spiegazione di un paragrafo.
Cose da notare ora
La dimensione del contesto consuma RAM/VRAM. La finestra di contesto predefinita di Ollama è conservativa per la maggior parte dei modelli; aumentarla (es. ollama run llama3.2:3b → /set parameter num_ctx 32768) costa memoria unificata. Non è stata modificata per questo post.
gpt-oss emette un canale di ragionamento (formato Harmony). Il motore applica il template; ottieni comunque un normale message.content. Il canale di ragionamento sarà segregato al gateway nel Post 3.
Nessuno streaming ancora. Il Post 5 copre la trappola dello streaming usage — devi optare tramite stream_options.include_usage, altrimenti usage è null nelle risposte in streaming.
Cosa viene dopo
Il Post 2 passa da un singolo modello all'esecuzione simultanea di più modelli e al routing delle richieste tra di essi — il primo passo verso un gateway locale reale.
La serie completa e tutto il codice demo si trovano nel repository Portway.
Local-first con Ollama
Guida pratica ed essenziale per chi vuole sperimentare con LLM locali usando l SDK OpenAI. Il contratto stateless è spiegato chiaramente attraverso l output reale. Ideale come punto di partenza per costruire un infrastruttura AI completamente locale.
Posso usare questa guida su un PC Windows o Linux?
Quale modello scegliere per iniziare con 8 GB di RAM?
Perché i prompt_tokens sono deterministici?
Cosa succede se aumento la finestra di contesto?
Posso usare modelli diversi da quelli elencati?
Fonti e riferimenti




