Due modi per inserire un LLM gratuito in uno script Python
Quando le persone cercano modelli LLM gratuiti per Python, di solito intendono una di due cose. La prima è un modello a pesi aperti che scarichi ed esegui tu stesso, usando qualcosa come Ollama, llama.cpp o la libreria transformers di Hugging Face. La seconda è un endpoint in hosting che costa così poco, o viene fornito con credito di prova sufficiente, da sembrare gratuito per un prototipo. Entrambi sono legittimi e si adattano a lavori diversi.
Eseguire localmente significa nessuna fattura per token e nessun dato che lascia la tua macchina. Significa anche che hai bisogno di abbastanza RAM o memoria GPU per il modello che scegli, aspetti i download e i modelli piccoli che entrano in un laptop rispondono peggio di quelli grandi. Un'API ospitata risolve la questione hardware, ma paghi per token una volta esaurita la prova. Questa pagina copre entrambi onestamente, poi mostra il codice Python per l'opzione ospitata su questo sito, che è compatibile con OpenAI e funziona con il pacchetto standard openai.
Modelli locali e API ospitata fianco a fianco
| Domanda | Eseguirlo da solo | API ospitata prepagata |
|---|---|---|
| Costo per token | Nessuno, ma paghi per hardware e energia | $0,15 per 1M input, $0,50 per 1M output |
| Tempo di configurazione | Installa il runtime, scarica i pesi, regola le impostazioni | Installa un pacchetto, incolla una chiave |
| Lunghezza del contesto | Limitato dalla tua memoria | 100.000 token, prompt più completamento |
| Concorrenza | Qualsiasi cosa gestisca la tua macchina | 8 richieste alla volta su una chiave completa, 2 su una chiave di prova |
| Rifiuti | Dipende dai pesi che scegli | Ottimizzato per rispondere senza filtri sui contenuti per uso adulto legale |
| Uso offline | Sì | No |
Se hai una GPU capace e il tuo lavoro è privato o offline, la soluzione locale è una buona risposta e puoi smettere di leggere qui. Se vuoi un modello più grande senza possedere la macchina, o vuoi che il tuo script si comporti allo stesso modo su un laptop, un server e un runner CI, la via ospitata è più semplice. Molti progetti usano entrambi: un piccolo modello locale durante lo sviluppo, quello ospitato in produzione, cambiando un solo URL base.
Configura l'endpoint ospitato in cinque minuti
Ti serve prima una chiave. Iscriviti con Google o con un'email e una password, poi richiedi il credito di prova di $0,50 con un clic sulla pagina della chiave API. La prova è valida per 7 giorni, una per persona e non richiede pagamenti. Poi installa l'SDK e tieni la chiave fuori dal tuo codice sorgente.
pip install openai
export NOTRACK_API_KEY="sk-your-key-here"
Il client ha bisogno di due modifiche rispetto a una configurazione OpenAI standard: l'URL base e la chiave. L'ID del modello è uncensored, ed è offerto un solo modello.
import os
from openai import OpenAI
client = OpenAI(
base_url="https://api.nsfwvideogenerator.cc/v1",
api_key=os.environ["NOTRACK_API_KEY"],
)
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "You are a concise writing assistant."},
{"role": "user", "content": "Give me three title ideas for a noir short story."},
],
temperature=0.8,
)
print(resp.choices[0].message.content)
print(resp.usage)
L'autenticazione è un header standard Authorization: Bearer, che l'SDK aggiunge per te. Esistono due metodi: POST /v1/chat/completions e GET /v1/models. Puoi verificare la connessione con client.models.list(), che restituisce l'ID del modello disponibile. L'elenco completo dei parametri è nella pagina della documentazione docs.
Risposte in streaming
Per qualsiasi cosa una persona guarda, usa lo streaming. Imposta stream=True e itera sui chunk man mano che arrivano gli eventi server-sent. L'utilizzo viene segnalato alla fine dello streaming, quindi puoi comunque registrare i conteggi dei token.
stream = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": "Write a four-line poem about rain on a tin roof."}],
stream=True,
)
for chunk in stream:
if chunk.choices and chunk.choices[0].delta.content:
print(chunk.choices[0].delta.content, end="", flush=True)
usage = getattr(chunk, "usage", None)
if usage:
print("\n", usage)
Un dettaglio coglie di sorpresa le persone: una richiesta in streaming mantiene il suo slot di concorrenza fino a quando lo streaming non finisce. Con una chiave di prova con 2 slot, due stream aperti significano che una terza chiamata riceve un 429 con il codice concurrency. Consuma sempre o chiudi lo stream.
Chiamata di funzioni e modalità JSON
L'endpoint supporta tools, tool_choice, streaming di tool_calls e risultati inviati indietro come role: "tool". Ecco un round trip completo con una funzione.
import json
tools = [{
"type": "function",
"function": {
"name": "word_count",
"description": "Count the words in a piece of text.",
"parameters": {
"type": "object",
"properties": {"text": {"type": "string"}},
"required": ["text"],
},
},
}]
messages = [{"role": "user", "content": "How many words are in: the quick brown fox jumps"}]
first = client.chat.completions.create(
model="uncensored", messages=messages, tools=tools, tool_choice="auto"
)
msg = first.choices[0].message
if msg.tool_calls:
messages.append(msg)
for call in msg.tool_calls:
args = json.loads(call.function.arguments)
result = len(args["text"].split())
messages.append({
"role": "tool",
"tool_call_id": call.id,
"content": str(result),
})
final = client.chat.completions.create(model="uncensored", messages=messages, tools=tools)
print(final.choices[0].message.content)
Analizza sempre arguments in modo difensivo; il modello lo restituisce come stringa JSON e qualsiasi modello può occasionalmente produrne uno malformato. Per l'output strutturato senza strumenti, usa la modalità JSON.
resp = client.chat.completions.create(
model="uncensored",
messages=[
{"role": "system", "content": "Reply with a JSON object with keys title and tags (list of strings)."},
{"role": "user", "content": "Describe a lighthouse keeper's diary."},
],
response_format={"type": "json_object"},
temperature=0.2,
)
data = json.loads(resp.choices[0].message.content)
print(data["title"], data["tags"])
Dici la parola JSON e nomina le chiavi nel tuo prompt, e mantieni la temperatura bassa. Avvolgi json.loads in un blocco try per il codice di produzione.
Limiti di velocità, errori e ritentativi
Una chiave completa consente 8 richieste parallele e 300 richieste al minuto. Una chiave di prova ne consente 2 e 60. Superi il limite e ottieni HTTP 429 con un codice di rate_limited (troppe al minuto) o concurrency (troppe in una volta). In caso di sovraccarico temporaneo, l'API restituisce anche 429, con un'intestazione Retry-After: 5. L'SDK ritenta alcuni errori da solo; per uno script che si espande, un piccolo wrapper esplicito è più chiaro.
import time
from concurrent.futures import ThreadPoolExecutor
from openai import RateLimitError
def ask(prompt, tries=4):
for attempt in range(tries):
try:
r = client.chat.completions.create(
model="uncensored",
messages=[{"role": "user", "content": prompt}],
max_tokens=300,
)
return r.choices[0].message.content
except RateLimitError:
time.sleep(5 * (attempt + 1))
raise RuntimeError("still rate limited")
prompts = [f"Summarize idea number {i} in one sentence." for i in range(20)]
with ThreadPoolExecutor(max_workers=2) as pool: # 8 on a full key
results = list(pool.map(ask, prompts))
Abbinare max_workers al limite di concorrenza della tua chiave previene la maggior parte dei 429 prima che accadano. Ogni risposta trasporta anche X-Request-Id, X-Balance-USD e due intestazioni di limite di richiesta, che puoi leggere tramite client.chat.completions.with_raw_response.create(...) quando ne hai bisogno per il debug o il supporto.
Come scegliere tra locale e ospitato
Salta l'ideologia e rispondi a quattro domande sul tuo progetto.
- I dati devono rimanere sulla tua macchina? Se un contratto o un'abitudine lo richiedono, esegui localmente. I prompt in hosting non vengono utilizzati per l'addestramento qui, ma viaggiano comunque sulla rete.
- Quanta memoria hai? La dimensione del modello guida la qualità. Un laptop senza GPU dedicata ti limita a modelli piccoli e le risposte lo mostreranno. Un modello in hosting ti offre uno grande con una finestra di contesto di 100.000 token, che è difficile da eguagliare a casa.
- Quante richieste invierai? Qualche centinaio a settimana è banale da entrambe le parti. Centinaia di migliaia al mese trasformano il conto in hosting in un numero reale, e un box locale inizia a sembrare attraente se rimane inattivo altrimenti. Calcola la somma: 100.000 richieste di 1.000 token di input e 500 token di output costano 100.000 x $0,0004 = $40.
- Hai bisogno che i rifiuti vengano gestiti in un certo modo? Molti modelli a pesi aperti vengono forniti con un comportamento di rifiuto integrato e altri sono ottimizzati per eliminarlo. Il modello in hosting qui è ottimizzato per rispondere senza rifiuti di contenuto per l'uso da parte di adulti legali, quindi non hai bisogno di cercare i pesi giusti.
Se le tue risposte puntano in entrambe le direzioni, inizia con l'ospitato per la prima settimana. La prova non costa nulla e imparerai di cosa hanno bisogno i tuoi prompt prima di acquistare hardware o scaricare un altro file multi-gigabyte.
Errori comuni negli script Python
La maggior parte dei problemi che le persone incontrano sono piccoli e risolvibili.
- URL di base errato. Deve terminare con
/v1. Un suffisso mancante restituisce un 404, non un messaggio utile. Stampaclient.base_urlse hai dubbi. - Chiave nel codice. Leggila da una variabile d'ambiente, come sopra, e aggiungi il tuo file
.enva.gitignore. - Nome del modello sbagliato. L'unico ID del modello è
uncensored. I nomi di altri provider non funzioneranno, quindi cambiali quando porti il codice. - Unbounded loops. Un loop di tool-calling che never ends è un bug costoso. Limitalo a, diciamo, cinque round.
- Ignorare il budget del contesto. Prompt e completamento condividono i 100.000 token. Se aggiungi ogni turno per sempre, alla fine raggiungerai il limite. Taglia o riassumi i messaggi vecchi.
- Presupporre che il JSON sia sempre valido. Anche in modalità JSON, controlla le chiavi di cui hai bisogno prima di usarle.
Quando qualcosa sembra sbagliato lato server, cita il valore X-Request-Id dalla risposta quando scrivi a support. Questo permette agli operatori di trovare la tua chiamata esatta.
Quanto costa l'opzione in hosting per il lavoro Python
Una chiamata di script tipica con 1.000 token di input e 500 token di output costa $0,00015 più $0,00025, quindi $0,0004. Il credito di prova di $0,50 copre circa 1.250 di questi. Un top-up di $20, il minimo, copre 50.000. Il credito non scade mai, le richieste fallite o rifiutate non vengono addebitate e i prompt non vengono usati per l'addestramento. I top-up vanno fino a $500 con un bonus da $50, e il pagamento è in crypto, USDT su TRC20 o USDC su Base. Vedi la pagina dei prezzi per i dettagli.
È solo testo: nessuna immagine, audio, video, embedding o fine-tuning. E l'unica regola rigida è nessun contenuto sessuale che coinvolga minorenni. Per un progetto Python che ha bisogno di generazione di testo con meno rifiuti, questa è tutta la superficie.
