Punti chiave
- Usa ChatOpenAI con base_url impostato sull'API e model impostato su uncensored.
- Le catene, lo streaming, la chiamata di funzioni e l'output JSON passano tutti attraverso l'endpoint standard per le chat completions.
- Limita la concorrenza batch a 8 con una chiave completa, o 2 con una chiave di prova, per evitare errori 429.
- Questo è un modello ospitato singolo, non un router, quindi non c'è cambio di modello per fornitore.
La configurazione rapida
La solita domanda su langchain + llm è se serva un'integrazione speciale per un endpoint di terze parti. Non serve. Il wrapper di chat OpenAI di LangChain accetta un URL di base personalizzato e questa API segue il formato delle chat completions di OpenAI, quindi la classe standard funziona. Installa il pacchetto, passa tre valori e avrai un oggetto modello chat che puoi inserire in qualsiasi catena.
pip install -U langchain-openai langchain-core
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
temperature=0.7,
)
print(llm.invoke("Give me one sentence about LangChain.").content)
Ottieni la chiave dalla pagina delle chiavi API. I nuovi utenti possono richiedere $0,50 di credito di prova con un solo clic dopo la registrazione, che è sufficiente per eseguire tutto ciò che è in questo articolo molte volte. L'ID del modello è sempre uncensored e le richieste vanno a POST /v1/chat/completions.
Una nota sul nome del sito: nonostante la parola "router" nel dominio, questo è un singolo modello a pesi aperti gestito da un provider indipendente, non un router tra fornitori e non l'API ufficiale di alcun fornitore. Non scegli tra i modelli qui. Il tuo codice nomina un modello, ed è quello che risponde.
Tieni la chiave fuori dal codice sorgente
Hardcodare una chiave funziona per un primo test e diventa un problema la prima volta che fai il commit del file. Leggila dall'ambiente invece. Le librerie client di OpenAI che LangChain avvolge cercano OPENAI_API_KEY e OPENAI_BASE_URL di default, quindi puoi impostarle nel tuo shell e istanziare il modello con meno argomenti.
export OPENAI_API_KEY="YOUR_API_KEY"
export OPENAI_BASE_URL="https://api.nsfwllmrouter.com/v1"
import os
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url=os.environ.get("OPENAI_BASE_URL", "https://api.nsfwllmrouter.com/v1"),
api_key=os.environ["OPENAI_API_KEY"],
)
Passare l'URL di base esplicitamente, come sopra, ha un vantaggio. Se un altro progetto sulla stessa macchina usa il vero servizio OpenAI e si affida alla stessa variabile d'ambiente, il tuo argomento esplicito impedisce che i due entrino in collisione.
Una prima catena con un template prompt
La maggior parte dei lavori reali usa un template prompt, il modello e un parser di output uniti con l'operatore pipe. Questo è lo stile LangChain Expression Language e non ha bisogno di nulla di speciale per un endpoint personalizzato.
from langchain_core.prompts import ChatPromptTemplate
from langchain_core.output_parsers import StrOutputParser
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
prompt = ChatPromptTemplate.from_messages([
("system", "You write short story openings in the style the user asks for."),
("human", "Genre: {genre}\nSetting: {setting}\nWrite three sentences."),
])
chain = prompt | llm | StrOutputParser()
print(chain.invoke({"genre": "noir mystery", "setting": "a rainy harbor town"}))
Il modello non ha rifiuti di contenuto per l'uso adulto legale, il che conta quando la tua catena genera narrativa, scene di roleplay o dialoghi di compagnia che un modello più cauto rifiuterebbe. L'unica regola fissa è che i contenuti sessuali che coinvolgono minorenni non vengono mai prodotti. Costruisci la tua validazione degli input sopra questo se il tuo prodotto ha regole più severe.
Streaming e utilizzo
L'API supporta lo streaming tramite eventi inviati dal server con stream: true e l'utilizzo viene segnalato alla fine dello stream. In LangChain chiami .stream() e itera sui chunk.
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
for chunk in llm.stream("Write a four-line poem about debugging at night."):
print(chunk.content, end="", flush=True)
Un dettaglio da tenere a mente: una richiesta in streaming occupa uno dei tuoi slot di concorrenza fino alla fine dello stream. Se la tua UI apre uno stream e l'utente se ne va senza chiuderlo, quello slot rimane occupato. Con una chiave completa hai 8 richieste parallele, quindi un paio di stream abbandonati possono bloccarne di nuove. Annulla l'iteratore quando il client si disconnette.
Chiamata di funzioni e agenti
La chiamata di funzioni è supportata: passi tools e opzionalmente tool_choice, il modello risponde con tool_calls (che può anche essere in streaming) e tu invii i risultati indietro come messaggi con role: "tool". In LangChain, bind_tools gestisce il formato wire per te.
from langchain_core.tools import tool
from langchain_openai import ChatOpenAI
@tool
def word_count(text: str) -> int:
'Count the words in a piece of text.'
return len(text.split())
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
llm_with_tools = llm.bind_tools([word_count])
msg = llm_with_tools.invoke("How many words are in: the quick brown fox?")
for call in msg.tool_calls:
print(call["name"], call["args"], word_count.invoke(call["args"]))
Per un ciclo completo di agente, i helper degli agenti di LangChain o LangGraph eseguono questo ciclo per te: chiama il modello, esegui lo strumento richiesto, allega il messaggio dello strumento, chiama di nuovo. L'uso degli strumenti segue la forma standard di OpenAI, quindi lo stesso codice che scriveresti per qualsiasi endpoint compatibile funziona. Come con qualsiasi modello a pesi aperti, testa i tuoi schemi di strumenti specifici. Tieni i nomi degli argomenti semplici, le descrizioni brevi e l'elenco degli strumenti piccolo, poiché elenchi lunghi danno ai modelli più deboli più spazio per scegliere quello sbagliato.
Output strutturato con modalità JSON
La modalità JSON è disponibile tramite response_format: {"type": "json_object"}. In LangChain puoi passarlo come argomento del modello e analizzare il risultato da solo, il che ti dà il controllo sulla convalida e sui retry.
import json
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
model_kwargs={"response_format": {"type": "json_object"}},
)
msg = llm.invoke(
"Return a JSON object with keys title and tags (a list of three strings) "
"for an article about hiking."
)
data = json.loads(msg.content)
print(data["title"], data["tags"])
Scrivi la parola "JSON" nel prompt e descrivi le chiavi che vuoi. La modalità JSON rende l'output JSON valido, ma non garantisce il tuo schema esatto, quindi controlla le chiavi dopo l'analisi e riprova in caso di errore. Poiché le richieste fallite non vengono addebitate, un ciclo di retry per output malformati costa poco.
Concorrenza, retry e limiti di richiesta
LangChain rende facile distribuire il lavoro con .batch(), ed è lì che la maggior parte delle persone incontra il primo limite di richieste. Una chiave completa permette 8 richieste parallele e 300 richieste al minuto. Una chiave di prova permette 2 richieste parallele e 60 al minuto. Se superi il limite, l'API risponde con HTTP 429. Il codice di errore è rate_limited quando superi le richieste al minuto, e concurrency quando troppe richieste vengono eseguite contemporaneamente. In caso di sovraccarico temporaneo ricevi anche un 429 con l'intestazione Retry-After: 5.
Imposta tu la concorrenza batch e lascia che il client riprovi con backoff:
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
max_retries=4,
timeout=120,
)
inputs = [f"Summarize idea number {i} in one line." for i in range(40)]
results = llm.batch(inputs, config={"max_concurrency": 6})
print(len(results))
Rimanere a 6 su una chiave completa lascia margine per altre richieste dallo stesso account. Su una chiave di prova usa 2. Se vedi ancora errori concurrency, cerca stream che non sono mai terminati, poiché ognuno tiene uno slot. Ogni risposta porta X-Request-Id, che è il valore da citare se contatti il supporto, e X-Balance-USD, che mostra il tuo credito rimanente.
Chiamate asincrone per le app web
All'interno di un server web raramente si vogliono chiamate bloccanti. Ogni modello di chat LangChain ha versioni asincrone dei suoi metodi, nominate ainvoke, astream e abatch, e funzionano contro questo endpoint allo stesso modo delle versioni sincrone.
import asyncio
from langchain_openai import ChatOpenAI
llm = ChatOpenAI(
model="uncensored",
base_url="https://api.nsfwllmrouter.com/v1",
api_key="YOUR_API_KEY",
)
async def main():
async for chunk in llm.astream("Describe a quiet harbor at dawn in two sentences."):
print(chunk.content, end="", flush=True)
asyncio.run(main())
In un servizio asincrono, proteggi il numero di chiamate in corso con un semaforo dimensionato alla tua chiave, ad esempio 8 su una chiave completa. Questo impedisce a un endpoint occupato della tua app di riempire tutti gli slot e affamare gli altri. Cattura le eccezioni intorno alla chiamata, registra l'intestazione X-Request-Id quando ce l'hai e mostra all'utente un breve messaggio di retry invece di uno stack trace.
Suggerimento di debug: se una catena si comporta in modo strano, stampa i messaggi esatti che il modello riceve. I template prompt a volte inseriscono spazi bianchi extra o perdono una variabile, e il modello può rispondere solo a ciò che gli è stato inviato. Le modalità verbose e debug di LangChain, o il tracing di LangSmith se lo usi già, mostrano l'elenco finale dei messaggi.
Cosa significa la finestra di contesto per le catene
La finestra di contesto è di 100.000 token per prompt e completion insieme, e max_tokens è opzionale. È abbastanza grande per le catene di retrieval che inseriscono molti documenti in un unico prompt, o per una memoria di conversazione che mantiene una lunga cronologia. È anche abbastanza grande da diventare costosa se invii di nuovo l'intera cronologia a ogni turno senza tagliarla.
Un rapido calcolo. Un passaggio di recupero che invia 12.000 token di input e ne riceve 800 costa 0,012 x $0,15 + 0,0008 x $0,50 = $0,0018 + $0,0004 = $0,0022. Esegui questo 1.000 volte e paghi $2,20. I prezzi sono $0,15 per 1M di token di input e $0,50 per 1M di token di output, senza abbonamento, quindi paghi solo per ciò che la catena utilizza effettivamente. La pagina dei prezzi ha le opzioni di ricarica e la documentazione quickstart elenca ogni parametro supportato. I prompt non sono usati per l'addestramento.
