\n\n\n\n Mi viaje construyendo IA conversacional con SDKs - AgntBox Mi viaje construyendo IA conversacional con SDKs - AgntBox \n

Mi viaje construyendo IA conversacional con SDKs

📖 12 min read2,328 wordsUpdated Mar 26, 2026

Hola a todos, Nina aquí, de vuelta de mi laboratorio digital (que, seamos honestos, es principalmente mi mesa de cocina con mucho café tibio). Hoy, quiero hablar sobre algo que ha estado resonando en mis canales de Slack y acechando mis sesiones de codificación nocturnas: el mundo a veces frustrante, a menudo brillante de los SDK de IA, específicamente cuando intentas construir un agente conversacional que realmente se sienta como una conversación, no solo un glorificado bot de preguntas frecuentes.

Mi obsesión particular últimamente ha sido con la integración de la API de Asistentes de OpenAI. Ahora, sé lo que estás pensando, “Nina, ¿API de Asistentes? ¡Eso es noticia antigua!” Y sí, no estás equivocado. Ha estado disponible durante un tiempo. Pero escúchame: no estoy aquí para darte un tutorial básico de “cómo llamar a client.beta.assistants.create()”. Todos hemos visto esos. Mi enfoque hoy es sobre un problema específico y complicado con el que me encontré y cómo luché con el SDK para lograr que hiciera lo que quería: gestionar conversaciones multi-turno y con estado utilizando herramientas personalizadas sin perder la cabeza (o el contexto de mi usuario). Piensa más allá de una simple pregunta y respuesta. Piensa en flujos de trabajo complejos, preguntas de seguimiento, y llamadas a herramientas dinámicas basadas en la intención del usuario en evolución.

El Estado de Mi Cordura: Por Qué la API de Asistentes (Aún) Importa

Antes de la API de Asistentes, construir algo con estado utilizando los modelos de OpenAI a menudo se sentía como ser un malabarista con demasiadas pelotas en el aire. Eras responsable de gestionar el historial de mensajes, diseñar prompts para el contexto, y orquestar las llamadas a herramientas todo por tu cuenta. Era… agotador. La API de Asistentes prometía quitarse mucho de esa carga de encima al gestionar hilos, mensajes, e incluso la orquestación de herramientas. Y para casos simples, lo hace maravillosamente.

Mi proyecto actual implica construir un asistente personal de finanzas “inteligente”. No solo uno que te diga tu saldo, sino uno que pueda ayudarte a planificar presupuestos, sugerir estrategias de inversión basadas en tu tolerancia al riesgo, e incluso simular escenarios financieros futuros. Esto requiere mucho ir y venir, recordar declaraciones anteriores (“Quiero ahorrar para una casa”, “Mis ingresos son X”, “¿Qué pasaría si invierto en Y?”), y, crucialmente, llamar a funciones específicas (como obtener datos de acciones en tiempo real o ejecutar un algoritmo de proyección de presupuesto) en el momento adecuado.

Mi primer intento fue un glorioso desastre. Estaba pasando todo el historial de mensajes de un lado a otro, intentando meter instrucciones complejas en el prompt del sistema, y comprobando manualmente si era necesaria una llamada a la herramienta. Funcionó, pero era frágil, lento, y una pesadilla para depurar. Fue entonces cuando decidí realmente aprovechar la API de Asistentes y su SDK.

El Abrazo del SDK: Hilos, Mensajes y el Elusivo `run`

El núcleo de la API de Asistentes es el concepto de `Hilos` y `Mensajes`. Creas un `Hilo` para una conversación, agregas `Mensajes`, y luego “ejecutas” el `Asistente` en ese `Hilo`. El `Asistente` procesa los mensajes, decide si necesita llamar a una herramienta, y genera una respuesta. Suficientemente simple, ¿verdad?

Aquí es donde se vuelve interesante. Mi asistente de finanzas necesita hacer cosas como:

  • Rastrear metas: “Quiero ahorrar $50,000 para un pago inicial.”
  • Reunir información: “¿Cuál es tu ingreso mensual actual?”
  • Ejecutar cálculos: “Basado en eso, ¿cuánto tiempo tomará si ahorro $1,000 al mes?”
  • Proporcionar consejos: “Considera diversificar tu portafolio con fondos indexados de bajo costo.”

Cada uno de estos a menudo requiere una llamada a una herramienta personalizada. Por ejemplo, `calculate_savings_timeline(goal_amount, monthly_income, monthly_savings)`. El desafío no es solo definir la herramienta; se trata de lograr que el Asistente:

  1. Reconozca que necesita la herramienta.
  2. Identifique todos los parámetros necesarios (incluso si están esparcidos en múltiples mensajes del usuario).
  3. Indique al usuario cuáles parámetros faltan de manera cortés.
  4. Ejecute la herramienta e incorpore los resultados.

El Problema de los Parámetros: Cuando el Asistente Pide Más

Digamos que un usuario dice, “¿Cuánto tiempo me llevará ahorrar para una casa?” Mi herramienta `calculate_savings_timeline` necesita `goal_amount`, `monthly_income`, y `monthly_savings`. El Asistente, siendo inteligente, a menudo se dará cuenta de que le falta información. El SDK, a través del objeto `run`, te dirá que `requires_action`.

Mi enfoque inicial fue simplemente esperar el estado `requires_action` y luego presentar al usuario un genérico “¿Qué información necesitas?” Esto era torpe. El usuario a menudo no sabía qué parámetros necesitaba la herramienta. Un mejor enfoque, en el que eventualmente caí, involucraba inspeccionar los `tool_calls` dentro del estado `requires_action`.


def submit_tool_outputs(client, thread_id, run_id, tool_outputs):
 return client.beta.threads.runs.submit_tool_outputs(
 thread_id=thread_id,
 run_id=run_id,
 tool_outputs=tool_outputs
 )

# ... dentro de tu bucle de conversación ...

if run.status == 'requires_action':
 tool_outputs = []
 missing_params = {}
 for tool_call in run.required_action.submit_tool_outputs.tool_calls:
 function_name = tool_call.function.name
 arguments = json.loads(tool_call.function.arguments)

 # Aquí está el truco: verificar si falta un parámetro requerido
 # Esto asume que tus herramientas están definidas con un esquema
 # Para simplificar, digamos que sabemos que 'monthly_savings' siempre es necesario
 if function_name == "calculate_savings_timeline" and "monthly_savings" not in arguments:
 missing_params['monthly_savings'] = '¿Cuánto planeas ahorrar cada mes?'
 
 # ... más verificación sofisticada de parámetros aquí ...

 if missing_params:
 # Informa al usuario qué falta
 user_prompt = "Necesito un poco más de información para ayudarte con eso. "
 for param, question in missing_params.items():
 user_prompt += f"{question} "
 return {"status": "waiting_for_user_input", "prompt": user_prompt}
 else:
 # Todos los parámetros están presentes, ejecuta la herramienta
 output = execute_tool_function(function_name, arguments) # Tu función para ejecutar la lógica real de la herramienta
 tool_outputs.append({
 "tool_call_id": tool_call.id,
 "output": json.dumps(output)
 })
 
 if tool_outputs:
 run = submit_tool_outputs(client, thread.id, run.id, tool_outputs)
 # Continúa sondeando el run hasta que se complete o requiera nueva acción
 else:
 # Este caso maneja si detectamos parámetros faltantes y pedimos al usuario
 pass

Este fragmento es una versión simplificada, pero la idea principal es interceptar `requires_action`, mirar lo que el Asistente quiere hacer, y si le faltan argumentos, solicitar al usuario específicamente por ellos. Esto hace que la conversación se sienta mucho más natural, como un humano haciendo preguntas aclaratorias, en lugar de un genérico “error.”

El Elusivo Estado: Manteniendo el Rastro a Través de las Interacciones

Una de las mayores molestias era asegurarse de que si un usuario proporcionaba una pieza de información (“Mis ingresos son $5,000 al mes”), el Asistente la recordara para llamadas a herramientas posteriores dentro de la misma conversación, incluso si no se utilizaba de inmediato. La API de Asistentes maneja esto bastante bien dentro de un `Hilo` manteniendo el historial de mensajes. Sin embargo, a veces quieres guiarlo explícitamente o darle antecedentes que no son parte de un mensaje directo del usuario.

Me encontré usando `metadata` en los objetos `Thread` y `Message` más de lo que inicialmente pensaba. Por ejemplo, si el usuario indica explícitamente su tolerancia al riesgo, podría almacenar eso en la metadata del hilo. Si bien el Asistente no “lee” directamente esta metadata en su proceso de razonamiento, es invaluable para mi lógica de aplicación cuando necesito pre-poblar argumentos para llamadas a herramientas o tomar decisiones sobre qué herramientas son incluso relevantes.

Otro patrón que adopté fue inyectar mensajes “tipo sistema” en el hilo si necesitaba recordarle explícitamente al Asistente ciertos hechos derivados de una llamada a la herramienta o un proceso interno. Por ejemplo, después de que `calculate_savings_timeline` regrese con que tomará 10 años, podría agregar un mensaje como: `{“role”: “user”, “content”: “El plan de ahorro del usuario indica un plazo de 10 años para alcanzar su objetivo.”}`. Esto no es del usuario directamente, pero ayuda a reforzar el contexto para las respuestas subsiguientes del Asistente.

Manejo de la Salida de la Herramienta y Acciones Posteriores

Cuando una herramienta se ejecuta, obtienes una salida. El Asistente luego toma esta salida y genera una respuesta. Pero, ¿qué pasa si la salida de la herramienta en sí provoca un nuevo conjunto de preguntas o una nueva llamada a la herramienta? Por ejemplo, la herramienta `calculate_savings_timeline` podría devolver que el objetivo no es alcanzable con los ahorros actuales. Idealmente, el Asistente debería sugerir, “Quizás deberíamos considerar formas de aumentar tus ahorros mensuales o reducir tu objetivo.” No se trata solo de generar texto; se trata de encadenar pasos lógicos.

La API de Asistentes SDK maneja esto maravillosamente manteniendo el objeto `run` vivo. Después de que `submit_tool_outputs`, el `run` a menudo vuelve a la condición `in_progress` y luego podría generar un nuevo `requires_action` (para otra llamada de herramienta) o `completed`. Mi bucle principal para manejar el flujo de conversación se ve algo así:


def get_assistant_response(client, thread_id, assistant_id, user_message):
 client.beta.threads.messages.create(
 thread_id=thread_id,
 role="user",
 content=user_message
 )

 run = client.beta.threads.runs.create(
 thread_id=thread_id,
 assistant_id=assistant_id
 )

 while run.status in ['queued', 'in_progress', 'cancelling']:
 time.sleep(1) # Sé un buen ciudadano, no sobrecargues la API
 run = client.beta.threads.runs.retrieve(thread_id=thread_id, run_id=run.id)

 if run.status == 'completed':
 messages = client.beta.threads.messages.list(thread_id=thread_id, order="desc")
 for msg in messages.data:
 if msg.role == "assistant":
 # Encuentra el último mensaje del asistente, a menudo el primero
 return msg.content[0].text.value # Asumiendo contenido de texto
 return "Sin respuesta del asistente."

 elif run.status == 'requires_action':
 tool_outputs = []
 for tool_call in run.required_action.submit_tool_outputs.tool_calls:
 function_name = tool_call.function.name
 arguments = json.loads(tool_call.function.arguments)
 
 # Aquí es donde va tu lógica personalizada para manejar parámetros faltantes o ejecutar herramientas
 # Para este ejemplo simplificado, asumamos que todos los parámetros están presentes y los ejecutamos
 print(f"El asistente quiere llamar a {function_name} con args: {arguments}")
 
 output = execute_tool_function(function_name, arguments) # Tu lógica de ejecución de la herramienta
 tool_outputs.append({
 "tool_call_id": tool_call.id,
 "output": json.dumps(output)
 })
 
 # Enviar salidas de herramientas y continuar la ejecución
 run = client.beta.threads.runs.submit_tool_outputs(
 thread_id=thread_id,
 run_id=run.id,
 tool_outputs=tool_outputs
 )
 # Llama recursivamente a esta función para procesar la ejecución actualizada
 # Ten cuidado con la profundidad de la recursión en producción, podrías necesitar un bucle
 return get_assistant_response(client, thread_id, assistant_id, "") # Pasa un mensaje vacío
 
 elif run.status == 'failed':
 return f"La ejecución del asistente falló: {run.last_error.message}"
 
 else:
 return f"Estado de ejecución inesperado: {run.status}"

Esta función `get_assistant_response`, aunque simplificada, muestra el bucle central. La clave es que `requires_action` no es un callejón sin salida. Es una oportunidad para que tu aplicación intervenga, ejecute la herramienta solicitada y luego pase los resultados de vuelta al Asistente para continuar su razonamiento. Este feedback en bucle cerrado es lo que hace posible conversaciones realmente con estado y dinámicas.

Lecciones Accionables para Tu Próximo Proyecto de Asistente AI

  1. Acepta el ciclo de vida de `run.status`: No te limites a verificar `completed`. `requires_action` es tu amigo, no un error. Construye manejadores adecuados para cada estado relevante.
  2. Inspecciona `tool_calls` en busca de parámetros faltantes: En lugar de indicaciones genéricas, profundiza en `run.required_action.submit_tool_outputs.tool_calls` para entender *exactamente* lo que el Asistente necesita. Esto eleva tu experiencia del usuario.
  3. Uso estratégico de mensajes similares a Sistema: Si la lógica interna de tu aplicación deriva nuevos hechos o necesita enfatizar cierto contexto, considera inyectar mensajes de “usuario” que representen estos hechos. El Asistente los procesará como parte del historial del hilo.
  4. Metadatos para el Estado de la Aplicación: Aunque el Asistente podría no “leer” directamente los metadatos del hilo o mensaje para su razonamiento, es un lugar poderoso para que tu aplicación almacene y recupere el estado relevante para la conversación. Piensa en preferencias de usuario, variables de sesión actuales, etc.
  5. Prueba Casos Límite para la Orquestación de Herramientas: ¿Qué pasa si una herramienta falla? ¿Qué sucede si el usuario cambia de opinión durante la invocación de la herramienta? Diseña tu `execute_tool_function` y el manejo de errores dentro del bucle `requires_action` cuidadosamente.

Construir agentes de IA conversacionales verdaderamente inteligentes no se trata solo de elegir el último modelo. Se trata de manejar hábilmente los SDK que ofrecen para gestionar las complejidades de la interacción humana. La API de Asistentes de OpenAI, con un poco de ingeniería reflexiva alrededor de su gestión de estado y orquestación de herramientas, puede realmente elevar tu experiencia conversacional más allá de simples turnos. Todavía es un viaje, y sigo aprendiendo nuevos trucos, ¡pero espero que estas ideas te ahorren algunas noches largas y cafés fríos!

Artículos Relacionados

🕒 Published:

🧰
Written by Jake Chen

Software reviewer and AI tool expert. Independently tests and benchmarks AI products. No sponsored reviews — ever.

Learn more →
Browse Topics: AI & Automation | Comparisons | Dev Tools | Infrastructure | Security & Monitoring

Related Sites

AgntmaxAgnthqBotclawAgntlog
Scroll to Top