No desconectes todavía el Assistant que atiende producción. La ruta segura es construir Responses API en paralelo, verificar cada contrato observable y mover primero sesiones nuevas de bajo riesgo. Si una herramienta ejecuta dos veces una acción, se pierde el contexto o File Search cita el documento equivocado, el cambio debe detenerse aunque la petición devuelva 200.
Hay dos relojes distintos:
- Assistants API está programada para dejar de estar disponible el 26 de agosto de 2026.
- Los reusable prompt objects se anunciaron como deprecated el 3 de junio de 2026; esos objetos y
v1/promptsestán programados para retirarse el 30 de noviembre de 2026.
La guía oficial de migración todavía muestra Assistant → Prompt como transición, pero también avisa de la segunda retirada. Para una integración nueva y duradera, no conviene salir de un objeto que caduca para depender de otro con fecha de cierre. Lleva instrucciones y esquemas de herramientas a código versionado y pásalos en la petición a Responses.
El alcance de esta página es OpenAI Assistants API → OpenAI Responses/Conversations API. No cubre la migración desde Chat Completions, Azure OpenAI/Foundry ni endpoints de terceros compatibles con OpenAI; esos contratos requieren su propia documentación y validación.
El plan de producción en cuatro fases
La migración se controla mejor como un cambio de servicio que como una reescritura total:
- Inventariar: localizar objetos, estado, herramientas, archivos y rutas de error.
- Reconstruir: elegir propietario de configuración y estrategia de conversación; implementar Responses.
- Demostrar paridad: ejecutar el mismo conjunto de pruebas en la ruta antigua y la nueva.
- Cambiar tráfico: testers, cohorte pequeña, sesiones nuevas y, por último, retirada de la ruta anterior.
La reversión debe existir desde la primera fase. No se borran Assistants, Threads ni código antiguo hasta que termine la ventana acordada de rollback.
Fase 1: convierte un Assistant real en un contrato verificable
Elige un Assistant de producción que represente el flujo, pero que permita limitar el impacto. Documenta su configuración antes de tocar código:
- instructions y restricciones;
- modelo y parámetros;
- funciones propias y sus efectos laterales;
- File Search, archivos y vector stores;
- relación entre
thread_id, usuario y sesión; - streaming, polling, timeout y cancelación;
- formatos de salida y errores que entiende el frontend;
- diez o más conversaciones golden, incluidas negativas y casos límite.
Después rellena esta matriz. La cuarta columna evita aceptar una migración que solo “parece responder”.
| Antes | Después | Qué debe conservarse | Cómo se verifica |
|---|---|---|---|
| Assistant | instructions y tools en código | reglas, permisos, versión | mismas obligaciones y prohibiciones en golden tests |
| Thread | Conversation, cadena por response ID o historial propio | aislamiento de usuario, orden, items necesarios | un segundo turno y una reconexión recuperan solo el contexto correcto |
| Message | input y message items | role, contenido y adjuntos admitidos | pruebas separadas de texto, imagen y archivo |
| Run | Response normal, streaming o background | estados terminales y errores | completion, failure, timeout y cancel se observan |
| Run step | items de response.output | messages, reasoning, tool calls y outputs | cada tipo y call_id se puede rastrear |
| Assistant File Search | hosted file_search | vector store, filtros y citas | recupera el documento esperado y rechaza el no autorizado |
| Function tool | bucle ejecutado por la aplicación | schema, autorización, idempotencia | output vuelve con el call_id original y llega una respuesta final |
Este inventario también detecta un error de alcance: una aplicación de Azure/Foundry o un wrapper de Manychat/WOZTELL no se migra siguiendo ciegamente ejemplos de OpenAI directo. Sus SDK, superficies y contratos deben validarse en la documentación oficial del proveedor. Esta página desarrolla la ruta de OpenAI API directa.
Fase 2: reconstruye configuración y estado
Compara el mismo caso antes y después
El ejemplo compacto mantiene el mismo caso de soporte. El bloque BEFORE solo hace visibles las responsabilidades que hay que migrar; no es una recomendación para crear nuevos Assistants. El bloque AFTER lee el modelo desde OPENAI_MODEL y mueve configuración, estado y ejecución a sus nuevos propietarios.
pythonimport os from openai import OpenAI client = OpenAI() MODEL = os.environ["OPENAI_MODEL"] INSTRUCTIONS = """ Eres un agente de soporte de pedidos. No inventes estados de envío. Cuando falten datos, explica qué dato concreto se necesita. """.strip() assistant = client.beta.assistants.create( model=MODEL, instructions=INSTRUCTIONS, ) thread = client.beta.threads.create() client.beta.threads.messages.create( thread_id=thread.id, role="user", content="Resume en dos pasos cómo iniciar una devolución.", ) legacy_run = client.beta.threads.runs.create_and_poll( thread_id=thread.id, assistant_id=assistant.id, ) if legacy_run.status != "completed": raise RuntimeError(f"legacy run failed: {legacy_run.status}") # AFTER: configuración en código, estado durable en Conversation. conversation = client.conversations.create( metadata={"app_session": "migration-smoke-001"} ) response = client.responses.create( model=MODEL, conversation=conversation.id, instructions=INSTRUCTIONS, input="Resume en dos pasos cómo iniciar una devolución.", ) if response.status != "completed": raise RuntimeError(f"response failed: {response.status}") print(response.output_text)
Estos fragmentos no se ejecutaron con el project, las credenciales ni los datos del lector. Fija una versión actual del SDK y ejecútalos en staging; valida que OPENAI_MODEL admite Conversations y cada herramienta o parámetro utilizado antes de pasar a producción. Si todavía no has validado el proyecto, Billing y una primera petición, sigue antes la guía española para obtener y verificar una clave API de OpenAI.
Para sustituir un Prompt object existente:
- exporta instructions, tool schemas y formatos estructurados;
- separa secretos y valores propios de cada entorno;
- guarda la configuración en el mismo control de versiones que la aplicación;
- ejecuta el conjunto golden con Prompt ID y con configuración en código;
- elimina la dependencia del Prompt ID solo cuando la comparación pase.
Decide quién será propietario del historial
Responses ofrece tres estrategias válidas. No son alias intercambiables.
Conversation: continuidad duradera entre sesiones
Una Conversation puede contener messages, tool calls, tool outputs y otros items. Encaja cuando varios workers deben continuar la misma interacción.
pythonconversation = client.conversations.create() response = client.responses.create( model=MODEL, instructions=INSTRUCTIONS, conversation=conversation.id, input="Mi pedido P-204 figura como retrasado.", ) follow_up = client.responses.create( model=MODEL, instructions=INSTRUCTIONS, conversation=conversation.id, input="¿Qué dato necesitas para revisarlo?", )
La documentación de estado de conversación indica actualmente que los Response objects se almacenan 30 días por defecto salvo store=false, mientras que Conversations y sus items no están sujetos a ese mismo TTL. Antes de usarla, acuerda retención, borrado, aislamiento por usuario y respuesta a solicitudes de eliminación. Las políticas de organización, residencia de datos o ZDR pueden cambiar la aplicación de esta regla.
previous_response_id: cadena corta y lineal
Es útil para un flujo breve que siempre continúa desde la respuesta inmediatamente anterior. Debe volver a enviarse instructions, porque no se heredan automáticamente.
pythonfirst = client.responses.create( model=MODEL, instructions=INSTRUCTIONS, input="Explica la política de cambios en una frase.", ) second = client.responses.create( model=MODEL, instructions=INSTRUCTIONS, previous_response_id=first.id, input="¿Cuál es la excepción principal?", )
No se puede combinar previous_response_id con conversation en la misma petición. La cadena tampoco evita que los tokens anteriores vuelvan a contar como input facturado.
Historial administrado por la aplicación
Es la opción para controlar explícitamente persistencia, compactación y auditoría. La aplicación conserva entradas y todos los items de response.output, no únicamente output_text. Al usar modelos de razonamiento y herramientas, perder reasoning items o tool items puede romper la siguiente vuelta.
| Si la prioridad es... | Empieza evaluando... | Descártalo cuando... |
|---|---|---|
| continuidad entre sesiones y workers | Conversation | no se puede gobernar su retención |
| mínimo código en una cadena corta | previous_response_id | hay ramas, larga duración o control explícito |
auditoría, compactación o store=false | historial propio | no puedes reproducir fielmente todos los items |
Fase 3: demuestra que las herramientas siguen funcionando
Separa hosted/built-in, remote MCP y funciones propias
No todas las tools tienen el mismo ejecutor. La documentación oficial actual de MCP and Connectors explica que Responses conecta un remote MCP con type: "mcp" y expone items mcp_list_tools, mcp_call y, cuando corresponde, mcp_approval_request.
| Tipo de herramienta | Quién la ejecuta | Responsabilidad de la aplicación | Evidencia de aceptación |
|---|---|---|---|
Hosted/built-in (file_search, por ejemplo) | OpenAI dentro de Responses | configurar vector store, filtros, permisos, compatibilidad del modelo y evaluación | items de la tool, citas, vacío y errores coinciden con el golden set |
| Remote MCP | Responses consulta y llama tools del servidor MCP confiado | server_url, autenticación, allowed_tools, aprobaciones y revisión de datos/política del tercero | mcp_list_tools y mcp_call esperados; aprobación, rechazo y error observables |
| Custom function | código de la aplicación | validar argumentos, autorizar, ejecutar, controlar side effects, timeout, idempotencia y rondas | recibe function_call y devuelve function_call_output con el mismo call_id |
No envíes function_call_output para simular la ejecución de una remote MCP. Tampoco supongas que Responses ejecutará por sí sola una custom function de negocio.
Una función personalizada no se ejecuta sola
Responses puede devolver un function_call, pero tu aplicación debe validar argumentos, autorizar la operación, ejecutar código y devolver un function_call_output con el call_id original. La secuencia oficial de function calling admite cero, una o varias llamadas antes del texto final.
Este ejemplo conserva response.output, ejecuta todas las llamadas permitidas y limita el número de vueltas:
pythonimport json import os from openai import OpenAI client = OpenAI() MODEL = os.environ["OPENAI_MODEL"] TOOLS = [{ "type": "function", "name": "consultar_pedido", "description": "Obtiene el estado actual de un pedido autorizado.", "parameters": { "type": "object", "properties": { "pedido_id": { "type": "string", "description": "Identificador como P-204", } }, "required": ["pedido_id"], "additionalProperties": False, }, "strict": True, }] def consultar_pedido(pedido_id: str) -> dict: # Sustituir por un backend autenticado en producción. datos = {"P-204": {"estado": "en_transito", "cancelable": False}} return datos.get( pedido_id, {"estado": "no_encontrado", "cancelable": False}, ) items = [{"role": "user", "content": "¿Puedo cancelar ahora el pedido P-204?"}] for _ in range(4): response = client.responses.create( model=MODEL, instructions=( "Usa consultar_pedido para estados actuales. " "Si no existe, no inventes una respuesta." ), tools=TOOLS, input=items, ) items.extend(response.output) calls = [x for x in response.output if x.type == "function_call"] if not calls: print(response.output_text) break for call in calls: if call.name != "consultar_pedido": raise RuntimeError(f"Herramienta no permitida: {call.name}") resultado = consultar_pedido(**json.loads(call.arguments)) items.append({ "type": "function_call_output", "call_id": call.call_id, "output": json.dumps(resultado, ensure_ascii=False), }) else: raise RuntimeError("Se superó el máximo de rondas de herramientas")
En producción añade timeout, idempotency key, autorización por usuario, registro de side effects y aprobación humana para acciones sensibles. El límite de rondas impide que una llamada defectuosa se convierta en un bucle de coste indefinido.
File Search necesita una prueba de recuperación, no una prueba de transporte
file_search es una herramienta hosted de OpenAI: el servidor realiza la búsqueda, a diferencia de una función propia. La guía oficial de File Search permite pedir los resultados con include=["file_search_call.results"]; no vienen incluidos por defecto.
pythonimport os from openai import OpenAI client = OpenAI() MODEL = os.environ["OPENAI_MODEL"] VECTOR_STORE_ID = os.environ["OPENAI_VECTOR_STORE_ID"] response = client.responses.create( model=MODEL, input="Indica el plazo de devolución y cita el documento aplicable.", tools=[{ "type": "file_search", "vector_store_ids": [VECTOR_STORE_ID], }], include=["file_search_call.results"], ) for item in response.output: print(item.type) print(response.output_text)
La prueba pasa solo si:
- el vector store pertenece al project y está listo;
- aparece el documento esperado, no solo cualquier resultado;
- la respuesta enlaza citation/annotation con la fuente correcta;
- un usuario sin permiso no recupera contenido restringido;
- una pregunta sin evidencia termina en “no encontrado”, no en una regla inventada;
- la calidad queda dentro del guardrail definido frente al Assistant anterior.
No conviertas automáticamente todo Run en background
Una Response normal puede ser síncrona o streamed. background=true es una opción explícita para trabajos realmente largos. Según la guía oficial de background mode, se puede consultar una response mientras está queued o in_progress y cancelarla.
Usa background solo si el producto necesita terminar después de que el cliente deje de esperar. Para chat interactivo, streaming suele expresar mejor el progreso. Antes de decidir, prueba completion, timeout, failure, cancelación, reintento y recuperación tras reiniciar el worker.
Qué hacer con los Threads existentes
OpenAI no ofrece una herramienta automática para migrar Threads a Conversations. La estrategia recomendada es enviar chats nuevos a Responses y backfillear solo conversaciones antiguas que deban continuar.
Clasifica los Threads:
- Activos: conviértelos con prioridad y compara la primera pregunta de continuación.
- Solo consulta: mantenlos en un archivo read-only si la política lo permite; no los copies sin necesidad.
- Caducados o sin base de retención: elimina o anonimiza según la política aprobada.
La conversión debe preservar el orden y distinguir contenido de entrada y salida. Además de messages, audita attachments, tool events, metadata y contenido heredado no cubierto por el ejemplo oficial. Generar una Conversation y contar items no demuestra fidelidad; la pregunta de continuación debe recuperar los hechos correctos sin mezclar usuarios.
Fase 4: cambia tráfico con criterios de parada
Crea un dashboard de migración por caso golden, no una sola tasa global de éxito.
| Control | Evidencia mínima | Parar y revertir cuando... |
|---|---|---|
| Configuración | reglas obligatorias y prohibidas se mantienen | cambia una restricción crítica |
| Estado | segundo turno y reconexión conservan contexto | mezcla sesiones o pierde un hecho necesario |
| Funciones | nombre, argumentos, call_id y output trazables | duplica side effects o acepta una tool no permitida |
| File Search | documento y citation correctos | cita fuente equivocada o filtra contenido |
| Streaming/background | terminal states y cancelación visibles | queda trabajo huérfano o UI inconsistente |
| Calidad | golden set dentro del umbral | cae un caso crítico aunque suba el promedio |
| Latencia y coste | p50/p95 y tokens dentro de guardrails | supera el límite aprobado |
| Rollback | un flag devuelve la sesión nueva a ruta segura | requiere borrar datos o aplicar hotfix manual |
El orden de cambio recomendado es:
- testers internos con datos no sensibles;
- sesiones nuevas de un caso de bajo riesgo;
- una cohorte pequeña con observación de tools y retrieval;
- todas las sesiones nuevas;
- backfill selectivo de Threads activos;
- retirada de la ruta antigua tras cerrar la ventana de reversión.
No ejecutes dos backends con capacidad de producir el mismo side effect sin una clave de idempotencia compartida. En modo shadow, las herramientas de escritura deben estar simuladas o bloqueadas. Tampoco cambies modelo, prompt, tool schema y estrategia de estado en la misma prueba: si falla, no sabrás qué variable lo causó.
La migración termina cuando se puede contestar con evidencia a cuatro preguntas: ¿la configuración correcta llegó a cada petición?, ¿el estado pertenece al usuario correcto?, ¿cada herramienta y cita se ejecutó como estaba previsto?, ¿se puede volver atrás sin perder datos? Si una respuesta sigue siendo “no lo sabemos”, production aún debe permanecer en Assistants mientras se corrige la ruta nueva.



