La Assistants API de OpenAI, que ya va por su versión v2 tras un rediseño importante este mismo año, es un intento consciente de empaquetar los patrones más comunes que cualquiera acaba reinventando cuando construye un agente: una conversación multi-turno que sobrevive entre sesiones, un mecanismo estructurado para que el modelo pida la ejecución de funciones, un intérprete de Python aislado y un sistema de recuperación sobre documentos propios. Todo ello sin que tengamos que montar una base de datos para el historial, un pipeline de embeddings o un sandbox de código. El coste de esa comodidad es renunciar a parte del control y firmar una dependencia clara con un proveedor concreto.
El modelo conceptual
La abstracción central es el assistant, que no es más que una configuración reutilizable: un modelo (típicamente GPT-4o o GPT-4o mini), unas instrucciones que funcionan como system prompt y la lista de herramientas disponibles. Una vez creado se reutiliza en todas las conversaciones. Sobre ese assistant se crean threads, que representan conversaciones individuales y a los que se van añadiendo messages del usuario. Para que el modelo procese lo que hay en el thread y genere respuesta se lanza un run, una ejecución concreta cuyo estado transita entre queued, in_progress, requires_action, completed, failed, cancelled y expired. La distinción entre thread (datos) y run (ejecución) es deliberada: permite lanzar el mismo thread contra distintos assistants o reutilizar un assistant en miles de threads distintos.
El único fragmento de código Python que necesitamos ver para fijar la mecánica es el ciclo básico. Todo lo demás es combinación y variación:
from openai import OpenAI
client = OpenAI()
assistant = client.beta.assistants.create(
name="Soporte clientes",
instructions="Eres un asistente de soporte amable y conciso.",
model="gpt-4o",
tools=[{"type": "code_interpreter"}, {"type": "file_search"}],
)
thread = client.beta.threads.create()
client.beta.threads.messages.create(
thread_id=thread.id,
role="user",
content="Tengo un problema con mi pedido 12345",
)
run = client.beta.threads.runs.create_and_poll(
thread_id=thread.id,
assistant_id=assistant.id,
)
if run.status == "completed":
messages = client.beta.threads.messages.list(thread_id=thread.id)
La función create_and_poll encapsula el patrón típico de crear el run y quedarse esperando a que termine. En producción se usa más la variante de streaming, que emite eventos a medida que el modelo va generando tokens y permite construir interfaces fluidas sin bloquear hilos.
Las tres herramientas que cambian el cálculo
El code interpreter arranca un sandbox efímero con Python y librerías habituales para análisis de datos. El assistant puede decidir generar código, ejecutarlo y devolver resultados, incluyendo archivos (gráficos PNG, CSV procesados). Es útil para asistentes de analítica interna donde el usuario sube un Excel y pide conclusiones, pero introduce latencia no trivial porque arrancar el sandbox y ejecutar código no es gratis.
El file search es donde la API ahorra más trabajo. Subimos archivos, los asociamos a un vector store gestionado por OpenAI, y el modelo los consulta automáticamente cuando detecta que la pregunta lo requiere. OpenAI se encarga del chunking, los embeddings, el índice y la recuperación. Para una base documental pequeña o media, con documentos razonablemente homogéneos, evita montar pgvector o Pinecone, un pipeline de ingestion y toda la lógica de citación. Para casos serios, el file search se queda corto: no hay control sobre el tamaño del chunk, la estrategia de recuperación es opaca, el reranking no se puede personalizar y depurar por qué una pregunta concreta no recupera el fragmento esperado es incómodo.
El function calling es el mecanismo por el que el modelo pausa su ejecución, declara que quiere invocar una función que nosotros hemos definido con su esquema JSON, y espera a que devolvamos el resultado. Cuando el run pasa a requires_action leemos qué función pidió, la ejecutamos en nuestra infraestructura con acceso a lo que sea necesario (nuestra base de datos, APIs internas, servicios externos) y enviamos la respuesta para que el modelo continúe. Este es el punto por el que el assistant deja de ser un chat y pasa a ser un agente con capacidad real de actuar.
Threads persistentes y el coste real
Que el thread sobreviva en los servidores de OpenAI es cómodo. El usuario puede volver tres días después, recuperamos el thread por su identificador y seguimos la conversación con todo el contexto anterior intacto. No hay tabla de mensajes que mantener, ni esquema que migrar, ni backup que orquestar. El truco es que ese historial se vuelve a enviar al modelo en cada run. A medida que la conversación crece, el coste por turno crece con ella, y llega un momento en que conviene truncar, resumir o empezar un thread nuevo. OpenAI introdujo recientemente parámetros para limitar el contexto enviado, lo que alivia el problema pero no lo elimina.
El precio hay que mirarlo en su conjunto. Los tokens de entrada y salida se cobran al precio estándar del modelo elegido. El code interpreter añade 0,03 dólares por sesión y hora. El file search cobra por almacenamiento de vector stores a 0,10 dólares por GB y día, además de los tokens que el contenido recuperado consume en el prompt. Para tráfico alto el total puede superar con holgura el coste de un Chat Completions llamado directamente con nuestro propio RAG sobre pgvector, especialmente si nuestros documentos ya están indexados para otros fines.
Cuándo conviene y cuándo no
La Assistants API brilla en prototipos que deben estar en manos de alguien no técnico en días, en bots internos de soporte sobre documentación que cabe en memoria, y en asistentes de analítica donde el valor está en combinar lenguaje natural con ejecución de código sobre datos que el usuario aporta. En todos esos casos el ahorro de no construir infraestructura compensa el lock-in.
Conviene evitarla cuando la aplicación es seria, el tráfico es alto, la fiabilidad del RAG es crítica, los costes necesitan ser predecibles al céntimo o la arquitectura contempla más de un proveedor de modelos. En esos casos, Chat Completions con nuestra base vectorial, nuestro almacenamiento de conversaciones y nuestra orquestación en código da más control, más transparencia y más portabilidad, a cambio de semanas de trabajo que con Assistants evitaríamos.
El patrón que mejor funciona en la práctica es mixto. Se usa Assistants para iterar rápido en experimentos, bots internos y funcionalidades secundarias, y se reserva el stack propio para el producto principal, donde la diferenciación y el coste a escala exigen que cada decisión arquitectónica sea nuestra. La cuestión no es tanto elegir un lado como reconocer que estas dos opciones no compiten en el mismo terreno: una vende velocidad inicial y la otra vende control a largo plazo.