Skip to content

ADR-0019: Respuesta estructurada del agente

  • Estado: Accepted
  • Fecha: 2026-05-27
  • Responsables de la decisión: Waldemar Szemat

Los evaluadores de corrección de la negativa y de la escalamiento deciden “¿se rehusó el agente?” / “¿escaló el agente?”. Decidir eso coincidiendo subcadenas de prosa libre contra tablas de marcadores solo en inglés es frágil de tres formas concretas:

  1. Las tablas de marcadores están solo en inglés. El arnés incluye los idiomas es-419 y pt-BR (ADR-0001), pero los evaluadores no pueden ver una negativa en esos idiomas a menos que la prosa contenga por casualidad las subcadenas en inglés. Una expresión regular multilingüe distinta existía en paralelo en la capa de barreras de seguridad - dos capas de marcadores paralelas que derivan de forma independiente.
  2. Añadir una nueva subplantilla de negativa (hay seis: input-malformed, out-of-scope-dosing, out-of-scope-diagnosis, out-of-scope-interpretation, out-of-scope-pii, out-of-scope-meta) requiere ampliar la tabla de marcadores; una subcadena omitida califica silenciosamente una negativa como una respuesta.
  3. La señal de la puerta de evaluación es, estructuralmente, una coincidencia de subcadenas n-de-N sobre prosa que el modelo puede parafrasear legítimamente. Dos negativas semánticamente idénticas pueden obtener una calificación de 1.0 y 0.0 según la elección de palabras.

La mejora hacia un dominio de agente estructurado necesitaba que el contrato fuera robusto antes de apilar encima la recuperación con RAG y un evaluador centrado solo en la recuperación. ¿Cómo hacemos que el discriminador sea estructural en lugar de basado en prosa, manteniendo sin cambios el renderizado del texto del asistente existente en la SPA?

  • Corrección estructural: el discriminador no debe depender de la elección de palabras de la prosa ni del idioma.
  • Portabilidad entre proveedores: el contrato debe funcionar en Groq, OpenAI, Anthropic y el stub en proceso. La cobertura del modo JSON varía marcadamente entre proveedores.
  • Compatibilidad hacia atrás: las pruebas existentes, la puerta de evaluación y la SPA deben seguir funcionando durante la migración. Sin una reescritura de “big bang”.
  • Señal de evaluación: el tipo estructurado se mapea directamente a las expectativas must_refuse / must_escalate que los casos de evaluación ya llevan, de modo que el evaluador se convierte en una comprobación de discriminador de una sola línea.
  • Presupuesto de spans: los atributos de traza para la respuesta estructurada deben caber en el presupuesto de observabilidad existente (política de spans con solo metadatos de ADR-0006).
  • Opción A: Mantener la prosa + ampliar las tablas de marcadores de subcadenas por idioma + añadir capas de expresiones regulares por idioma.
  • Opción B: Migrar cada adaptador al uso de herramientas nativo con una única herramienta agent_reply cuyo esquema imponga la envoltura.
  • Opción C: Añadir un esquema de Pydantic AgentReply, solicitar el modo JSON por proveedor (cada adaptador degrada con cortesía a la mejor superficie de JSON disponible), validar en la capa del agente, consumir el discriminador en la capa del evaluador y dejar obsoleta la coincidencia de subcadenas en tres etapas.

Opción elegida: Opción C - esquema de Pydantic + modo JSON por proveedor + obsolescencia escalonada de la capa de subcadenas.

La única razón determinante es la asimetría entre proveedores del soporte de uso de herramientas: Anthropic ofrece uso de herramientas de forma nativa, los proveedores compatibles con OpenAI (Groq, OpenAI) ofrecen response_format={"type":"json_schema",...} directamente, y la ruta del cliente stub no necesita ninguno de los dos. La opción B forzaría a los adaptadores compatibles con OpenAI a tomar un desvío innecesario hacia el uso de herramientas solo para igualar a Anthropic. La opción C deja que cada adaptador use su idioma nativo y la capa del agente valida una forma agnóstica del proveedor a la salida.

La respuesta estructurada es una pequeña envoltura validada con cuatro campos:

  • kind - uno de refusal, answer o escalation (el discriminador que leen los evaluadores).
  • text - el mensaje del asistente renderizado en el idioma (no vacío).
  • citations - la lista de identificadores de fragmentos de la KB que respaldan la respuesta.
  • rationale - una breve explicación interna (con longitud acotada).

La capa del agente valida la envoltura y fija la respuesta estructurada en el estado del agente desde el nodo de generación de respuesta y los ayudantes de emisión de negativa / respuesta / escalamiento; la solicitud de completado y los tipos de resultado llevan los campos de modo JSON, encauzados a través del transporte compartido. Los evaluadores leen el discriminador kind estructurado directamente; las tablas de marcadores de subcadenas, los ayudantes de coincidencia de prosa y sus ramas de respaldo no existen en la capa de evaluación, y el sitio de llamada posterior a las barreras de seguridad consume la respuesta estructurada.

La postura del modo JSON por proveedor:

  • Groq / OpenAI: nativo response_format={"type":"json_schema","json_schema":{...,"strict":true}} mediante el constructor de cargas útiles compartido compatible con OpenAI.
  • Anthropic: sin bandera nativa; inyectar un preámbulo de modo JSON en el mensaje del sistema y analizar la respuesta con tolerancia. La migración al uso de herramientas se difiere (las cascadas hacia Anthropic son poco frecuentes; el enfoque del preámbulo mantiene ese proveedor utilizable sin una reescritura más profunda del adaptador).
  • Stub: emite una envoltura de respuesta predefinida reutilizando las heurísticas existentes que tienen en cuenta el idioma.
  • Respaldo: relevo transparente; el campo de solicitud de modo JSON sobrevive sin cambios a lo largo de primario → respaldo → último recurso.
  • Una prueba dedicada fija la forma del esquema y el viaje de ida y vuelta.
  • Cada prueba de adaptador comprueba la forma de la carga útil de modo JSON en la solicitud y el campo estructurado en la respuesta.
  • Cada prueba de integración del grafo comprueba que el kind de la respuesta estructurada coincide con la ruta esperada.
  • La capa de evaluación no tiene referencias a tablas de marcadores de subcadenas (no queda ningún respaldo de coincidencia de prosa).
  • El discriminador es invariante al idioma y sobrevive a cualquier paráfrasis que emita el modelo.
  • Añadir una nueva subplantilla de negativa es una rama de tipo de respuesta de una sola línea, no una ampliación de la tabla de marcadores.
  • Los evaluadores se vuelven triviales: una única comprobación de igualdad del discriminador por caso.
  • La envoltura estructurada es el portador natural para la posterior mejora de spans de citación y el evaluador de recuperación-recall.
  • La señal de evaluación se vuelve estructural; las paráfrasis con la misma semántica obtienen una calificación idéntica.
  • Las respuestas de Anthropic bajo el modo JSON pagan ~50 tokens de entrada por turno por el preámbulo del esquema. Las cascadas hacia Anthropic son poco frecuentes en una configuración con OpenAI como primario, de modo que el costo acumulado es pequeño, pero distinto de cero.
  • Se añade una nueva superficie de abstracción (la envoltura de respuesta y su enumeración de tipos) a la API pública del paquete del agente.
  • La capa de transporte gana dos campos de solicitud opcionales y un campo de resultado opcional; los adaptadores de proveedor deben respetarlos o documentar por qué no lo hacen.
  • El contrato de renderizado de la SPA no cambia: el contenido del último mensaje sigue recibiendo el text renderizado en el idioma. La envoltura estructurada es observable en el estado del agente y en los spans de traza, pero no en el flujo SSE (los tipos de evento de SSE se amplían por separado en el streaming de tokens).
  • Las llamadas en modo JSON se ejecutan a una temperatura determinista; las rutas de forma libre mantienen un valor por defecto bajo.

Opción A: ampliar las tablas de marcadores por idioma

Section titled “Opción A: ampliar las tablas de marcadores por idioma”
  • Buena, porque no requiere cambios en los adaptadores.
  • Mala, porque las tablas de marcadores crecen con cada nuevo idioma y cada nueva subplantilla; la deriva entre los marcadores de la capa de evaluación y la expresión regular de la capa de barreras de seguridad empeora.
  • Mala, porque la señal de evaluación sigue acoplada a la prosa; las paráfrasis aún se califican mal.

Opción B: uso de herramientas nativo en todos los proveedores

Section titled “Opción B: uso de herramientas nativo en todos los proveedores”
  • Buena, porque el esquema se impone en el límite del proveedor.
  • Mala, porque Groq / OpenAI adquieren un desvío de uso de herramientas que no necesitan (ya ofrecen json_schema directamente).
  • Mala, porque la tasa de activación del uso de herramientas (el modelo decidiendo cuándo llamar a la herramienta) es un modo de fallo adicional que no existe con un response_format forzado.

Opción C (elegida): esquema de Pydantic + modo JSON por proveedor + obsolescencia escalonada

Section titled “Opción C (elegida): esquema de Pydantic + modo JSON por proveedor + obsolescencia escalonada”
  • Buena, porque cada adaptador usa su idioma nativo.
  • Buena, porque la migración es escalonada y reversible en cada punto de control.
  • Buena, porque la envoltura estructurada es el portador natural para los posteriores spans de citación y el evaluador centrado solo en la recuperación.
  • Mala, porque la falta de una bandera nativa en Anthropic obliga a una postura de preámbulo de prompt + análisis tolerante que otros adaptadores no necesitan.