Saltar al contenido principal

Cómo migrar de Assistants API a Responses API sin cortar producción

13 min de lecturaGuía de API

Migrar Assistants API no consiste en sustituir una URL. Este plan mantiene la ruta antigua activa mientras se reconstruyen configuración, estado, funciones, File Search y controles de producción en Responses API.

Plan de migración que mueve configuración, estado de conversación y herramientas a Responses API antes de validar, cambiar tráfico y conservar la reversión

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:

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:

  1. Inventariar: localizar objetos, estado, herramientas, archivos y rutas de error.
  2. Reconstruir: elegir propietario de configuración y estrategia de conversación; implementar Responses.
  3. Demostrar paridad: ejecutar el mismo conjunto de pruebas en la ruta antigua y la nueva.
  4. 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”.

AntesDespuésQué debe conservarseCómo se verifica
Assistantinstructions y tools en códigoreglas, permisos, versiónmismas obligaciones y prohibiciones en golden tests
ThreadConversation, cadena por response ID o historial propioaislamiento de usuario, orden, items necesariosun segundo turno y una reconexión recuperan solo el contexto correcto
Messageinput y message itemsrole, contenido y adjuntos admitidospruebas separadas de texto, imagen y archivo
RunResponse normal, streaming o backgroundestados terminales y errorescompletion, failure, timeout y cancel se observan
Run stepitems de response.outputmessages, reasoning, tool calls y outputscada tipo y call_id se puede rastrear
Assistant File Searchhosted file_searchvector store, filtros y citasrecupera el documento esperado y rechaza el no autorizado
Function toolbucle ejecutado por la aplicaciónschema, autorización, idempotenciaoutput 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.

python
import 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:

  1. exporta instructions, tool schemas y formatos estructurados;
  2. separa secretos y valores propios de cada entorno;
  3. guarda la configuración en el mismo control de versiones que la aplicación;
  4. ejecuta el conjunto golden con Prompt ID y con configuración en código;
  5. 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.

python
conversation = 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.

python
first = 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 workersConversationno se puede gobernar su retención
mínimo código en una cadena cortaprevious_response_idhay ramas, larga duración o control explícito
auditoría, compactación o store=falsehistorial propiono 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 herramientaQuién la ejecutaResponsabilidad de la aplicaciónEvidencia de aceptación
Hosted/built-in (file_search, por ejemplo)OpenAI dentro de Responsesconfigurar vector store, filtros, permisos, compatibilidad del modelo y evaluaciónitems de la tool, citas, vacío y errores coinciden con el golden set
Remote MCPResponses consulta y llama tools del servidor MCP confiadoserver_url, autenticación, allowed_tools, aprobaciones y revisión de datos/política del terceromcp_list_tools y mcp_call esperados; aprobación, rechazo y error observables
Custom functioncódigo de la aplicaciónvalidar argumentos, autorizar, ejecutar, controlar side effects, timeout, idempotencia y rondasrecibe 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:

python
import 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.

python
import 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.

ControlEvidencia mínimaParar y revertir cuando...
Configuraciónreglas obligatorias y prohibidas se mantienencambia una restricción crítica
Estadosegundo turno y reconexión conservan contextomezcla sesiones o pierde un hecho necesario
Funcionesnombre, argumentos, call_id y output trazablesduplica side effects o acepta una tool no permitida
File Searchdocumento y citation correctoscita fuente equivocada o filtra contenido
Streaming/backgroundterminal states y cancelación visiblesqueda trabajo huérfano o UI inconsistente
Calidadgolden set dentro del umbralcae un caso crítico aunque suba el promedio
Latencia y costep50/p95 y tokens dentro de guardrailssupera el límite aprobado
Rollbackun flag devuelve la sesión nueva a ruta segurarequiere borrar datos o aplicar hotfix manual

El orden de cambio recomendado es:

  1. testers internos con datos no sensibles;
  2. sesiones nuevas de un caso de bajo riesgo;
  3. una cohorte pequeña con observación de tools y retrieval;
  4. todas las sesiones nuevas;
  5. backfill selectivo de Threads activos;
  6. 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.

#OpenAI Assistants API#Responses API#Conversations API#function calling#migración API
Share: