ES ▾
Obtener clave de API

LangChain + LLM: Conecta una API sin censura

LangChain se conecta a cualquier endpoint compatible con OpenAI a través de su clase ChatOpenAI. Configura la base URL, la clave y el ID de modelo, y tus cadenas y agentes se ejecutan en esta API.

Actualizado

Bloques enlazados formando una cadena que alimenta un núcleo central brillante, representando pipelines de LangChain que llaman a una API

Puntos clave

  1. Usa ChatOpenAI con base_url configurado a la API y model configurado a uncensored.
  2. Las cadenas, el streaming, las llamadas a funciones y la salida JSON pasan todas por la ruta estándar de chat completions.
  3. Limita la concurrencia por lotes a 8 con una clave completa, o a 2 con una clave de prueba, para evitar errores 429.
  4. Este es un modelo alojado único, no un router, por lo que no hay cambio de modelo por proveedor.

La configuración rápida

La pregunta habitual sobre langchain + llm es si necesitas una integración especial para un endpoint de terceros. No la necesitas. El adaptador de chat de OpenAI de LangChain acepta una base URL personalizada, y esta API sigue el formato de chat completions de OpenAI, por lo que la clase estándar funciona. Instala el paquete, pasa tres valores y tendrás un objeto de modelo de chat que puedes insertar en cualquier cadena.

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)

Obtén la clave desde la página clave de API. Los nuevos usuarios pueden reclamar $0.50 de crédito de prueba con un solo clic después de registrarse, lo cual es suficiente para ejecutar todo lo de este artículo muchas veces. El ID del modelo es siempre uncensored, y las peticiones van a POST /v1/chat/completions.

Una nota sobre el nombre del sitio: a pesar de la palabra "router" en el dominio, este es un único modelo de pesos abiertos gestionado por un proveedor independiente, no un router entre proveedores ni la API oficial de ningún proveedor. No eliges entre modelos aquí. Tu código nombra un modelo, y ese es el que responde.

Mantén la clave fuera del código fuente

Codificar una clave funciona para una primera prueba y se convierte en un problema la primera vez que confirmas el archivo. Léela desde el entorno en su lugar. Las bibliotecas de cliente de OpenAI que envuelve LangChain buscan OPENAI_API_KEY y OPENAI_BASE_URL por defecto, por lo que puedes establecerlas en tu shell y construir el modelo con 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"],
)

Pasar la base URL explícitamente, como arriba, tiene una ventaja. Si otro proyecto en la misma máquina usa el servicio real de OpenAI y depende de la misma variable de entorno, tu argumento explícito evita que ambos colisionen.

Una primera cadena con una plantilla de prompt

La mayoría del trabajo real usa una plantilla de prompt, el modelo y un analizador de salida unidos con el operador pipe. Este es el estilo de LangChain Expression Language, y no necesita nada especial para un 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"}))

El modelo no tiene rechazos de contenido para uso adulto lícito, lo cual importa cuando tu cadena genera ficción, escenas de roleplay o diálogo de compañero que un modelo más cauteloso rechazaría. La única regla fija es que nunca se produce contenido sexual que involucre menores de edad. Construye tu propia validación de entrada sobre eso si tu producto tiene reglas más estrictas.

Streaming y uso

La API admite streaming sobre eventos enviados por el servidor con stream: true, y el uso se informa al final del stream. En LangChain llamas a .stream() e iteras sobre los fragmentos.

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 detalle a tener en cuenta: una petición en streaming ocupa una de tus ranuras de concurrencia hasta que termina el stream. Si tu interfaz de usuario abre un stream y el usuario se va sin cerrarlo, esa ranura permanece ocupada. Con una clave completa tienes 8 peticiones simultáneas, por lo que un puñado de streams abandonados pueden bloquear nuevas peticiones. Cancela el iterador cuando el cliente se desconecte.

Llamadas a funciones y agentes

Las llamadas a funciones son compatibles: pasas tools y opcionalmente tool_choice, el modelo responde con tool_calls (que también pueden transmitirse), y envías los resultados de vuelta como mensajes con role: "tool". En LangChain, bind_tools maneja el formato de conexión por ti.

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 un ciclo completo de agente, los ayudantes de agente de LangChain o LangGraph ejecutan este ciclo por ti: llama al modelo, ejecuta la herramienta solicitada, adjunta el mensaje de herramienta, llama de nuevo. El uso de herramientas sigue la forma estándar de OpenAI, por lo que el mismo código que escribirías para cualquier endpoint compatible funciona. Como con cualquier modelo de pesos abiertos, prueba tus esquemas de herramientas específicos. Mantén los nombres de argumentos claros, las descripciones cortas y la lista de herramientas pequeña, ya que las listas largas dan a los modelos más débiles más margen para elegir el incorrecto.

Salida estructurada con modo JSON

El modo JSON está disponible a través de response_format: {"type": "json_object"}. En LangChain puedes pasarlo como argumento de modelo y analizar el resultado tú mismo, lo que te da control sobre la validación y los reintentos.

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"])

Di la palabra "JSON" en el prompt y describe las claves que deseas. El modo JSON hace que la salida sea JSON válido, pero no garantiza tu esquema exacto, así que verifica las claves después de analizar y reintenta en caso de fallo. Como las peticiones fallidas no se cobran, un bucle de reintento para salida con formato incorrecto cuesta poco.

Concurrencia, reintentos y límites de velocidad

LangChain facilita repartir el trabajo con .batch(), y ahí es donde la mayoría de la gente se topa con su primer límite de tasa. Una clave completa permite 8 peticiones concurrentes y 300 por minuto. Una clave de prueba permite 2 peticiones concurrentes y 60 por minuto. Si te pasas, la API responde con HTTP 429. El código de error es rate_limited cuando excedes las peticiones por minuto, y concurrency cuando hay demasiadas peticiones ejecutándose a la vez. En caso de sobrecarga temporal, también recibes un 429 con un encabezado Retry-After: 5.

Establece la concurrencia por lotes tú mismo, y deja que el cliente reintente con retroceso:

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))

Mantenerse en 6 con una clave completa deja margen para otras peticiones de la misma cuenta. Con una clave de prueba usa 2. Si aún ves errores concurrency, busca streams que nunca terminaron, ya que cada uno mantiene una ranura. Cada respuesta lleva X-Request-Id, que es el valor a citar si contactas con soporte, y X-Balance-USD, que muestra tu crédito restante.

Llamadas asíncronas para aplicaciones web

Dentro de un servidor web rara vez quieres llamadas bloqueantes. Cada modelo de chat de LangChain tiene gemelos asíncronos de sus métodos, nombrados ainvoke, astream y abatch, y funcionan contra este endpoint de la misma manera que las versiones sincrónicas.

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())

En un servicio asíncrono, controla el número de llamadas en curso con un semáforo dimensionado a tu clave, por ejemplo 8 con una clave completa. Eso evita que un endpoint ocupado de tu propia aplicación llene todas las ranuras y deje sin servicio a las demás. Captura excepciones alrededor de la llamada, registra el encabezado X-Request-Id cuando lo tengas, y muestra al usuario un mensaje de reintento corto en lugar de un seguimiento de pila.

Consejo de depuración: si una cadena se comporta de forma extraña, imprime los mensajes exactos que recibe el modelo. Las plantillas de prompt a veces insertan espacios en blanco adicionales o pierden una variable, y el modelo solo puede responder lo que se le envió. Los modos verbose y debug de LangChain, o el rastreo de LangSmith si ya lo usas, muestran la lista de mensajes final.

Qué significa la ventana de contexto para las cadenas

La ventana de contexto es de 100,000 tokens para prompt y completion juntos, y max_tokens es opcional. Eso es suficiente para cadenas de recuperación que meten muchos documentos en un solo prompt, o para una memoria de conversación que mantiene un historial largo. También es lo suficientemente grande como para resultar caro si reenvías un historial completo en cada turno sin recortarlo.

Un cálculo rápido. Un paso de recuperación que envía 12.000 tokens de entrada y recibe 800 de vuelta cuesta 0,012 x $0,15 + 0,0008 x $0,50 = $0,0018 + $0,0004 = $0,0022. Ejecútalo 1.000 veces y pagarás $2,20. La tarifa es de $0,15 por 1M de tokens de entrada y $0,50 por 1M de tokens de salida, sin suscripción, así que pagas solo por lo que la cadena realmente usa. La página de precios tiene las opciones de recarga, y la documentación de inicio rápido lista todos los parámetros compatibles. Los prompts no se usan para entrenamiento.

Preguntas y respuestas

¿Necesito un paquete LangChain especial para este endpoint?

No. El paquete langchain-openai es suficiente. Pasa la URL base, tu clave y el ID de modelo uncensored a ChatOpenAI.

¿Funciona con LangGraph y agentes?

Sí, siempre que usen el formato estándar de chat y llamadas a funciones de OpenAI. Se admiten llamadas a funciones, tool_calls en streaming y resultados de herramientas con role tool.

¿Puedo cambiar entre modelos dentro de una cadena?

No. Hay un único modelo, uncensored, y el servicio no es un router entre proveedores. Usa un objeto de modelo LangChain independiente si necesitas otro proveedor para otro paso.

¿Qué debo hacer con los errores 429?

Reduce la concurrencia por lotes, respeta el encabezado Retry-After cuando esté presente y asegúrate de cerrar los streams finalizados. Una clave completa permite 8 peticiones simultáneas y 300 por minuto.

¿Hay embeddings disponibles para mi almacén vectorial?

No. La API es solo de texto y no tiene un endpoint de embeddings, por lo que necesitas una fuente de embeddings separada para la recuperación.

Tu clave está a un formulario de distancia

Crea una cuenta, copia la clave, cambia la URL base. Esa es toda la configuración.

Obtener clave de API