Pontos principais
- Use ChatOpenAI com base_url definida para a API e model definido como uncensored.
- Cadeias, streaming, chamada de ferramentas e saída JSON passam todos pelo endpoint padrão de completions de chat.
- Limite a concorrência de lotes em 8 em uma chave completa, ou 2 em uma chave de teste, para evitar erros 429.
- Este é um modelo hospedado único, não um roteador, então não há alternância de modelo por provedor.
A configuração rápida
A pergunta usual sobre langchain + llm é se você precisa de uma integração especial para um endpoint de terceiros. Você não precisa. O wrapper de chat OpenAI do LangChain aceita uma URL base personalizada, e esta API segue o formato de chat completions da OpenAI, então a classe padrão funciona. Instale o pacote, passe três valores e você terá um objeto de modelo de chat que pode ser inserido em qualquer cadeia.
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)
Obtenha a chave na página da chave de API. Novos usuários podem reivindicar $0,50 de crédito de teste lá com um clique após o sign-up, o suficiente para executar tudo neste artigo várias vezes. O ID do modelo é sempre uncensored, e as requisições vão para POST /v1/chat/completions.
Uma nota sobre o nome do site: apesar da palavra "router" no domínio, este é um único modelo de pesos abertos executado por um provedor independente, não um roteador entre provedores e nem a API oficial de nenhum fornecedor. Você não escolhe entre modelos aqui. Seu código nomeia um modelo, e é esse que responde.
Mantenha a chave fora do código-fonte
Codificar a chave funciona para um primeiro teste e se torna um problema na primeira vez que você faz commit do arquivo. Leia-a do ambiente. As bibliotecas de cliente OpenAI que o LangChain envolve procuram por OPENAI_API_KEY e OPENAI_BASE_URL por padrão, então você pode defini-las no seu shell e construir o modelo com menos argumentos.
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"],
)
Passar a URL base explicitamente, como acima, tem uma vantagem. Se outro projeto na mesma máquina usar o serviço real da OpenAI e depender da mesma variável de ambiente, seu argumento explícito evita que os dois colidam.
Uma primeira cadeia com modelo de prompt
A maioria dos trabalhos reais usa um modelo de prompt, o modelo e um analisador de saída unidos pelo operador pipe. Este é o estilo da Linguagem de Expressão LangChain, e não precisa de nada especial para um endpoint personalizado.
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"}))
O modelo não possui recusas de conteúdo para uso adulto lícito, o que importa quando sua cadeia gera ficção, cenas de roleplay ou diálogos de companheiro que um modelo mais cauteloso recusaria. A única regra fixa é que conteúdo sexual envolvendo menores de idade nunca é produzido. Construa sua própria validação de entrada sobre isso se seu produto tiver regras mais rigorosas.
Streaming e uso
A API suporta streaming sobre eventos enviados pelo servidor com stream: true, e o uso é relatado no final do stream. No LangChain, você chama .stream() e itera sobre os chunks.
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)
Um detalhe a ter em mente: uma solicitação de streaming ocupa uma de suas slots de concorrência até que o stream termine. Se sua interface abrir um stream e o usuário sair sem fechá-lo, essa slot permanece ocupada. Em uma chave completa você tem 8 requisições simultâneas, então alguns streams abandonados podem bloquear novos. Cancele o iterador quando o cliente se desconectar.
Chamada de funções e agentes
Chamada de funções é suportada: você passa tools e opcionalmente tool_choice, o modelo responde com tool_calls (que também podem ser streamed), e você envia os resultados de volta como mensagens com role: "tool". No LangChain, bind_tools cuida do formato de conexão para você.
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"]))
Para um loop de agente completo, os auxiliares de agente do LangChain ou LangGraph executam este ciclo para você: chamar o modelo, executar a ferramenta solicitada, anexar a mensagem da ferramenta, chamar novamente. O uso de ferramentas segue o formato padrão da OpenAI, então o mesmo código que você escreveria para qualquer endpoint compatível funciona. Como em qualquer modelo de pesos abertos, teste seus esquemas de ferramentas específicos. Mantenha os nomes dos argumentos simples, as descrições curtas e a lista de ferramentas pequena, já que listas longas dão mais espaço para modelos mais fracos escolherem o errado.
Saída estruturada com modo JSON
O modo JSON está disponível através de response_format: {"type": "json_object"}. No LangChain você pode passá-lo como um argumento de modelo e analisar o resultado você mesmo, o que lhe dá controle sobre validação e retentativas.
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"])
Diga a palavra "JSON" no prompt e descreva as chaves que você deseja. O modo JSON torna a saída JSON válida, mas não garante seu esquema exato, então verifique as chaves após a análise e tente novamente em caso de falha. Como solicitações falhas não são cobradas, um loop de retentativa para saída malformada custa pouco.
Concorrência, retentativas e limites de taxa
LangChain facilita a distribuição de trabalho com .batch(), e é aí que a maioria das pessoas encontra seu primeiro limite de requisições. Uma chave completa permite 8 requisições simultâneas e 300 requisições por minuto. Uma chave de teste permite 2 requisições simultâneas e 60 por minuto. Exceda e a API responde com HTTP 429. O código de erro é rate_limited quando você excede requisições por minuto, e concurrency quando muitas requisições rodam ao mesmo tempo. Em sobrecarga temporária você também recebe um 429 com um cabeçalho Retry-After: 5.
Defina a concorrência em lote você mesmo, e deixe o cliente tentar novamente com 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))
Manter-se em 6 em uma chave completa deixa margem para outras requisições da mesma conta. Em uma chave de teste use 2. Se você ainda vir erros concurrency, procure streams que nunca terminaram, já que cada um ocupa uma slot. Cada resposta carrega X-Request-Id, que é o valor para citar se entrar em contato com o suporte, e X-Balance-USD, que mostra seu crédito restante.
Chamadas assíncronas para aplicativos web
Dentro de um servidor web raramente se deseja chamadas bloqueantes. Todo modelo de chat do LangChain tem versões assíncronas de seus métodos, nomeadas ainvoke, astream e abatch, e elas funcionam contra este endpoint da mesma forma que as versões síncronas.
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())
Em um serviço assíncrono, proteja o número de chamadas em andamento com um semáforo dimensionado para sua chave, por exemplo 8 em uma chave completa. Isso mantém um endpoint ocupado do seu próprio aplicativo de preencher todas as slots e sufocar os outros. Capture exceções ao redor da chamada, registre o cabeçalho X-Request-Id quando você o tiver, e mostre ao usuário uma mensagem de nova tentativa curta em vez de um stack trace.
Dica de depuração: se uma cadeia se comportar de forma estranha, imprima as mensagens exatas que o modelo recebe. Modelos de prompt às vezes inserem espaços extras ou perdem uma variável, e o modelo só pode responder ao que foi enviado. As opções verbose e debug do LangChain, ou o rastreamento do LangSmith se você já o usar, mostram a lista final de mensagens.
O que a janela de contexto significa para cadeias
A janela de contexto é de 100.000 tokens para prompt e conclusão juntos, e max_tokens é opcional. Isso é grande o suficiente para cadeias de recuperação que empacotam muitos documentos em um único prompt, ou para uma memória de conversa que mantém um histórico longo. Também é grande o suficiente para ficar caro se você reenviar todo o histórico a cada turno sem recortar.
Um cálculo rápido. Uma etapa de recuperação que envia 12.000 tokens de entrada e recebe 800 de volta custa 0,012 x $0,15 + 0,0008 x $0,50 = $0,0018 + $0,0004 = $0,0022. Execute isso 1.000 vezes e você paga $2,20. A precificação é $0,15 por 1M tokens de entrada e $0,50 por 1M tokens de saída, sem assinatura, então você paga apenas pelo que a cadeia realmente usa. A página de precificação tem as opções de recarregar, e a documentação de início rápido lista todos os parâmetros suportados. Prompts não são usados para treinamento.
