En los últimos meses, he tenido la oportunidad de sumergirme en la tarea de adaptar API y sistemas de backend para el consumo de LLMS, específicamente agentes que usan el protocolo MCP. Inicialmente, esperaba que la experiencia no fuera diferente a cualquier otro proyecto de desarrollo similar que haya realizado en el pasado. Sin embargo, me fascinó descubrir que estos clientes autónomos son un nuevo tipo de criatura. En consecuencia, las API en evolución para producir el mayor valor de la interacción de agente requerido más que simplemente hacerlas accesibles.
Esta publicación es el resultado de mis experimentos y pruebas de campo, con suerte puede ser útil para otros practicantes.
El poder y la maldición de la autonomía
Los desarrolladores estamos acostumbrados a herramientas de terceros y procesos de automatización que interactúan con las API de la aplicación. Por lo tanto, nuestras interfaces han evolucionado en torno a las mejores prácticas que mejor apoyan estos casos de uso. Las API transaccionales, versionadas, impulsadas por el contrato, mental para hacer cumplir la compatibilidad hacia adelante/hacia atrás y construidas para la eficiencia. Todas estas son preocupaciones importantes que son secundarias en prioridad y, a menudo, simplemente irrelevantes al considerar al usuario autónomo.
Con los agentes como clientes, no hay necesidad de preocuparse por la compatibilidad atrasada/avanzada ya que cada sesión es apátrida y única. El modelo estudiará cómo usar herramientas cada vez que las descubra, llegando a la combinación correcta de llamadas API para lograr su objetivo. Sin embargo, por entusiasta que este agente sea, también se rendirá después de algunos intentos fallidos a menos que se les dan los incentivos y las pautas adecuadas.
Más importante aún, sin tales pistas podría tener éxito en la llamada API pero no cumplir con sus objetivos. A diferencia de las automatizaciones con guiones o desarrolladores experimentados, solo tiene la documentación de la API y las respuestas para continuar planeando cómo cumplir con sus objetivos. La naturaleza dinámica de su respuesta es tanto una bendición como una maldición, ya que estas dos fuentes también son la suma de conocimiento que puede aprovechar para ser efectivo.
API impulsadas por la conversación
Primero me di cuenta de que el agente requeriría un tipo diferente de diseño mientras solucionaba algunos casos en los que el agente no pudo llegar a los resultados deseados. Proporcioné acceso a la herramienta MCP a una API que proporciona información de uso para cualquier función de código basada en datos de rastreo. A veces parecía que el agente simplemente no lo estaba usando correctamente. Mirando más de cerca la interacción, parecía que el modelo llamaba correctamente la herramienta y por varias razones recibió una matriz vacía como respuesta. Este comportamiento sería 100% correcto para cualquier operación similar en nuestra API.
El agente, sin embargo, tuvo problemas para comprender por qué esto estaba sucediendo. Después de probar algunas variaciones simples, se rindió y decidió pasar a otras vías de exploración. Para mí, esa interacción explicó una oportunidad perdida. Nadie tuvo la culpa; Transaccionalmente, el comportamiento era correcto. Todas las pruebas relevantes pasarían, pero al medir la efectividad del uso de esta API, descubrimos que la ‘tasa de éxito’ era ridículamente baja.
La solución resultó ser simple, en lugar de devolver una respuesta vacía, decidí proporcionar un conjunto más detallado de instrucciones e ideas:
var emptyResult = new NoDataFoundResponse()
{
Message = @"There was no info found based on the criteria sent.
This could mean that the code is not called, or that it is not manually instrumented
using OTEL annotations.",
SuggestedNextSteps = @"Suggested steps:
1. Search for endpoints (http, consumers, jobs etc.) that use this function.
Endpoints are usually automatically instrumented with OTEL spans by the
libraries using them.
2. Try calling this tool using the method and class of the endpoint
itself or use the GetTraceForEndpoint tool with the endpoint route.
3. Suggest manual instrumentation for the specific method depending on the language used in the project
and the current style of instrumentation used (annotations, code etc.)"
};
En lugar de simplemente devolver los resultados al agente, estaba tratando de hacer algo que los agentes a menudo intentarán también, mantenga la conversación. Mis percepciones de las respuestas de API, por lo tanto, cambiaron. Al ser consumidos por LLMS, más allá de servir a fines funcionales, son, en esencia, un aviso inverso. Una interacción final es un callejón sin salida, sin embargo, cualquier dato que volvamos al agente le da la oportunidad de extraer otro hilo en su proceso de investigación.
Odioas, las apis ‘Elige tu propia aventura’
Pensando en la filosofía de este enfoque, me di cuenta de que había algo vagamente familiar al respecto. Hace mucho tiempo, cuando estaba dando mis primeros pasos elaborando las API modernas de REST, me presentaron el concepto de API de hipermedia y odioas: hipertexto como motor del estado de aplicación. El concepto se describió en Fielding en su publicación de blog seminal 2008 Las API REST deben estar impulsadas por hipertexto. Una oración en esa publicación me voló por completo en ese momento:
“Las transiciones de estado de aplicación deben ser impulsadas por la selección del cliente de las opciones proporcionadas por servidor que están presentes en las representaciones recibidas”
En otras palabras, el servidor puede enseñar El cliente qué hacer a continuación en lugar de simplemente enviar los datos solicitados. El ejemplo canónico es una solicitud TET simple de un recurso específico, por lo que la respuesta proporciona información sobre las acciones que el cliente puede tomar a continuación en ese recurso. Una API autodocumentadora donde el cliente no estaba obligado a saber nada sobre ella con anticipación, excepto un solo punto de entrada desde el cual surge una rama de opciones. Aquí hay un buen ejemplo de Wikipedia página:
HTTP/1.1 200 OK
{
"account": {
"account_number": 12345,
"balance": {
"currency": "usd",
"value": 100.00
},
"links": {
"deposits": "/accounts/12345/deposits",
"withdrawals": "/accounts/12345/withdrawals",
"transfers": "/accounts/12345/transfers",
"close-requests": "/accounts/12345/close-requests"
}
}
}
En ese momento, me fascinó este concepto, que me recordó lo que comúnmente se conoce como libros de ‘elige tu propia aventura’ o ‘libros de juego‘. Este género de libros, una parte inseparable de mi infancia, no simplemente transmitió una historia (o proporcionó una respuesta de API de esta metáfora), sino que también le dio al lector una clave en cuanto a cuál es el siguiente conjunto de opciones disponibles. Las API de REST de HyperMedia fueron autodocumentantes y ofrecieron a los usuarios una forma de comprender el estado de la aplicación y las operaciones disponibles para cada entidad o recurso de proceso sin tener que leer una documentación extensa.
Hipermedia sobre esteroides
Una forma de ver las API de HyperMedia es que proporcionan más contexto al usuario como parte de la respuesta. El contexto, para los agentes, lo es todo, y ciertamente no necesita detenerse con las opciones u operaciones disponibles. Una API es un punto de interacción en el que el contexto se puede transmitir y proporcionar en una forma que fomentaría una mayor interacción. ¡Echemos un vistazo a otro ejemplo!
Otra herramienta en la que estaba trabajando, permite que el modelo recupere problemas de tiempo de ejecución encontrado en el entorno de implementación, nuevamente basado en datos de observabilidad. El resultado específico para el que estaba probando una respuesta rápida fue una anomalía encontrada en el rendimiento de un punto final específico. Parece que a veces las respuestas fueron EXTREMADAMENTE Lento, ~ 70x más lento que la mediana. Proporcionar esa pieza de información al LLM fue útil, pero en última instancia no logró más que repeticiones simples de los datos proporcionados.
Como referencia, aquí está la respuesta proporcionada, así como la salida del agente:
{
"Name": "Performance Anomaly",
"Category": "Performance",
"P50":
{
"Value": 12.33,
"Unit": "ms",
"Raw": 12331700.0
},
"P95":
{
"Value": 909.62,
"Unit": "ms",
"Raw": 909625000.0
},
"SlowerByPercentage": 7376.314701136097,
"SpanInfo":
{
....
},
#more data
....
}
No hay nada funcionalmente malo con la respuesta de la API o la forma en que el usuario medió la información al usuario. El único problema es que faltan mucho contexto e ideas que podrían aprovechar la capacidad del agente para llevar esa conversación. En otras palabras, esta es una interacción tradicional de solicitud/respuesta de API, pero los agentes a través del razonamiento son capaces de mucho más. Veamos qué sucede si modificamos nuestra API para inyectar estado adicional y sugerencias para tratar de llevar la conversación hacia adelante:
{
"_recommendation":
"This asset's P95 (slowest 5%) duration is disproportionally slow
compared to the median to an excessive degree
Here are some suggested investigative next steps to get to the
root cause or correct the issue:
1. The issue includes example traces for both the P95 and median
duration, get both traces and compare them find out which asset
or assets are the ones that are abnormally slow sometimes
2. Check the performance graphs for this asset P95 and see if there
has been a change recently, if so check for pull requests
merged around that time that may be relevan tot his area
3. Check for fruther clues in the slow traces, for example maybe
ALL spans of the same type are slow at that time period indicating
a systematic issue"
"Name": "Performance Anomaly",
"Category": "Performance",
"P50":
{
...
},
#more data
Todo lo que hemos hecho es darle al modelo AI un poco más para continuar. En lugar de simplemente devolver el resultado, podemos alimentar al modelo con ideas sobre cómo usar la información proporcionada. Seguramente lo suficientemente, estas sugerencias se utilizan inmediatamente. Esta vez, el agente continúa investigando el problema llamando a otras herramientas para inspeccionar el comportamiento, comparar los rastros y comprender el linaje del problema:
Con la nueva información en su lugar, el agente se complace en continuar la exploración, examinar la línea de tiempo y sintetizar los resultados de las diversas herramientas hasta que se les ocurra nuevos datos, que de ninguna manera era parte del alcance de respuesta original:
Espera … ¿no deberían todas las API ser diseñadas así?
¡Absolutamente! Definitivamente creo que este enfoque podría beneficiar a los usuarios, desarrolladores de automatización y todos los demás, incluso si usan cerebros para razonar en lugar de modelos LLM. En esencia, una API impulsada por la conversación puede expandir el contexto más allá del ámbito de los datos y en el ámbito de las posibilidades. Abriendo más ramas de exploración para agentes y usuarios por igual y mejorando la efectividad de las API para resolver el caso de uso subyacente.
Definitivamente hay más espacio para la evolución. Por ejemplo, las sugerencias e ideas proporcionadas al cliente por la API en nuestro ejemplo eran estáticas, ¿qué pasaría si también fueran generadas por IA? Hay muchos modelos A2A diferentes por ahí, pero en algún momento, podría ser un sistema de backend y una lluvia de ideas sobre el cliente sobre lo que significan los datos y lo que se podría hacer para entenderlo mejor. ¿En cuanto al usuario? Olvídate de él, habla con su agente.