AI From U AI FROM U.COM

Blog / Guías

LangChain sobre una API compatible con OpenAI

Los dos campos que ChatOpenAI necesita para este endpoint, por qué invoke, stream y las herramientas funcionan sin cambios, y cómo la caché abarata un agente.

LangChain no tiene por qué saber que está hablando con nosotros. Su modelo de chat de OpenAI lee la base URL de su propio constructor, y este gateway responde en el formato de cable de OpenAI, así que una cadena escrita contra OpenAI funciona aquí en cuanto cambian dos argumentos. Nada de lo que hay abajo es un fork de langchain-openai, una capa alrededor ni una copia parcheada: es el mismo paquete que ya tienes, configurado.

Los dos campos que importan

ChatOpenAI acepta una lista larga de argumentos y casi todos se quedan donde están. Dos no — la dirección a la que envía y la clave con la que envía:

import os
from langchain_openai import ChatOpenAI

llm = ChatOpenAI(
    model="terra",
    base_url="https://aifromu.com/v1",
    api_key=os.environ["AIFROMU_API_KEY"],
)

print(llm.invoke("Name three uses for a cache read.").content)

Esa base URL la imprime esta página, no está escrita a mano en ella. Es la dirección que este despliegue sirve de verdad, así que el fragmento funciona contra el host en el que lo estés leyendo y no contra un nombre de máquina que alguien compiló dentro de un ejemplo hace tiempo. La clave sale del entorno por la razón de siempre: una clave escrita en un constructor es una clave que ya está en un repositorio, y rotarla después significa editar código en vez de cambiar una variable.

Todo lo demás del objeto es de la librería. invoke devuelve el mensaje terminado y .content es el texto; una lista de mensajes vale donde valía una cadena suelta; ChatPromptTemplate | llm | StrOutputParser() se compone exactamente igual que antes. Nada de eso nos toca a nosotros cambiarlo, que es el argumento entero a favor de servir el formato de cable de otro en lugar de inventarse uno propio.

Streaming, herramientas y salida estructurada

llm.stream("...") te entrega la respuesta a trozos, según el modelo la va escribiendo. Los server-sent events atraviesan este gateway hasta tu proceso, así que el bucle que escribes alrededor es el de la propia librería:

for chunk in llm.stream("Explain a cache read in two sentences."):
    print(chunk.content, end="", flush=True)

Úsalo para cualquier cosa larga. Una generación que tarda minutos tiene que poner algo en el cable antes de que alguien cierre una conexión ociosa en algún punto entre tu proceso y el modelo, y el streaming es lo que lo pone — la guía de streaming explica dónde está exactamente ese límite y qué hacer con él.

Las llamadas a herramientas y la salida estructurada no tienen sección propia aquí, y esa es justamente la gracia. bind_tools y with_structured_output construyen los mismos cuerpos de petición que construían siempre, el gateway sirve el formato de cable de OpenAI, y las respuestas vuelven con la forma que LangChain ya sabe interpretar. No hay nada que portar ni nada que esquivar.

Qué modelo nombrar

model= acepta uno de los cuatro alias, elegido por lo que pide el trabajo y no por de quién es el modelo: astra para razonamiento de frontera sobre una ventana de 1M tokens, sol para razonamiento profundo y código difícil, terra para el trabajo cotidiano de asistente — RAG, agentes, herramientas y programación, que es el del fragmento de arriba — y luna para borradores rápidos, clasificación y chat de alto volumen.

Una cadena que ya envía un nombre de modelo al estilo de OpenAI no hay que editarla llamada por llamada. Esos nombres se aceptan en el cable y se leen como la clase de peso a la que pertenecen: un mini o un nano es la llamada barata y de alto volumen de alguien y se sirve en luna, un nombre gpt-6 es la frontera de este año y se sirve en astra, y la serie 4 corriente queda entre medias, en terra. La respuesta devuelve la misma cadena que enviaste, así que tu cliente sigue reconociéndola como petición suya, y un nombre que no encaja en ninguna regla se rechaza en vez de adivinarse — adivinar es facturar mal en las dos direcciones a la vez, y ninguna de las dos se ve hasta que alguien lee el libro.

Cadenas que iteran, y la lectura de caché

Un agente es un bucle, y un bucle reenvía su prefijo. Cada vuelta de un agent executor vuelve a subir por el cable el system prompt, las definiciones de las herramientas y la transcripción acumulada; una cadena de RAG que pone las mismas instrucciones delante de cada pasaje recuperado hace lo mismo a menor escala. Ese prefijo, y no la respuesta, es el grueso de lo que paga una cadena larga.

Y es también la parte barata de reenviar. Un token releído de un prefijo en caché se cobra a una décima parte de la tarifa de entrada de su propio modelo, así que un system prompt que se mantiene idéntico byte a byte de una vuelta a la siguiente es lo que abarata el bucle de un agente. Ordena los mensajes en consecuencia: primero lo que no cambia nunca, y después los documentos recuperados y el turno del usuario.

Nada de esto hay que creérselo. El portal detalla cada petición, así que el ahorro aparece donde puedes verlo — vuelta a vuelta, contra la ejecución que lo gastó.

Un token releído de un prefijo en caché se cobra a una décima parte de la tarifa de entrada de su propio modelo, así que un system prompt que se mantiene idéntico byte a byte de una vuelta a la siguiente es lo que abarata el bucle de un agente.

Traducción automática, pendiente de revisión por un hablante nativo. La versión en inglés es la de referencia.