Caché semántico para agentes de LLM con Redis: reduce costos y latencia
Cómo el caché semántico reduce los costos y la latencia de los LLM: embeddings, umbrales, Redis y RedisVL, ajuste de la precisión, y almacenamiento en caché de subpreguntas dentro de los flujos de trabajo de los agentes.
Fuentes: Semantic Caching for AI Agents: How to Make Your LLM Apps Faster and Cheaper
Las llamadas a un LLM potente cuestan dinero real y son lentas. Cualquiera que haya visto cómo aumenta la factura de una API al colocar un agente delante de usuarios reales conoce esa tensión: los modelos que dan las mejores respuestas son los más caros y lentos. Los modelos del nivel de GPT-4 pueden costar varios dólares por mil solicitudes, y los flujos de trabajo basados en agentes empeoran la situación, ya que una consulta de usuario puede generar de 8 a 20 llamadas al modelo.
El caché semántico aborda directamente ese costo, pero la mayoría de los equipos lo utilizan insuficientemente. Es fácil de implementar con Redis.
Por qué los cachés comunes fallan
Un caché regular considera dos solicitudes iguales solo si sus bytes coinciden. Eso funciona para consultas a bases de datos y solicitudes HTTP, pero falla con el lenguaje natural. Imagine a un agente de soporte que recibe estas solicitudes de tres usuarios diferentes:
- Un cliente pregunta cómo solicitar un reembolso
- Otro exige que le devuelvan su dinero
- Un tercero quiere saber qué establece la política de reembolsos
Un caché a nivel de byte detecta tres entradas no relacionadas y paga por tres llamadas al LLM. Una persona diría que los tres quieren la misma respuesta. Un caché semántico funciona a partir del significado en lugar de los caracteres.
El mecanismo en cuatro pasos
- Incrustar la consulta recibida, convirtiéndola en un vector de alta dimensión con un modelo de incrustación.
- Comparar ese vector con cada entrada almacenada, utilizando la distancia de coseno o una medida similar.
- Si la entrada más cercana está dentro del umbral de distancia que hayas configurado, considerarla como un acierto y devolver inmediatamente la respuesta almacenada.
- Si nada está lo suficientemente cerca, enviar la consulta a través de tu pipeline RAG o al LLM, y luego almacenar la pregunta y la respuesta para la próxima vez.
Las preguntas reformuladas terminan compartiendo las mismas respuestas. A medida que el caché se llena, una menor proporción del tráfico llega al modelo.
Una versión mínima desde cero
Antes de utilizar una biblioteca en producción, es útil conocer sus componentes internos. Comience con las importaciones:
from sentence_transformers import SentenceTransformer
import numpy as np
Luego incluya un pequeño conjunto de Preguntas Frecuentes y escriba una búsqueda de vecino más cercano:
model = SentenceTransformer("all-mpnet-base-v2")# Embed your FAQ dataset
faq_questions = ["How do I get a refund?", "Where is my order?", ...]
faq_embeddings = model.encode(faq_questions)def cosine_distance(a, b):
return 1 - np.dot(a, b) / (np.linalg.norm(a) * np.linalg.norm(b))def check_cache(query: str, threshold: float = 0.3):
query_embedding = model.encode(query)
distances = [cosine_distance(query_embedding, e) for e in faq_embeddings]
best_idx = np.argmin(distances)
best_distance = distances[best_idx]
if best_distance < threshold:
return faq_answers[best_idx] # Cache hit
return None # Cache miss
Prueba una pregunta de prueba sobre cuántos días tarda en llegar un reembolso y la búsqueda devuelve como entrada más cercana la FAQ almacenada sobre cómo obtener un reembolso, con una distancia de coseno de aproximadamente 0.33. No se encontró coincidencia solo por palabras en común; los vectores simplemente están cerca uno del otro.
Ejecutándolo en Redis
La versión de prueba no tiene persistencia, ni vencimiento, ni separación entre usuarios, ni búsqueda de vectores optimizada. Para cualquier cosa orientada al cliente, se necesita una base de datos real. Redis, junto con el SDK de código abierto RedisVL, proporciona todas estas funcionalidades listas para usar.
from redisvl.extensions.cache.llm import SemanticCache
from redisvl.utils.vectorize import HFTextVectorizer
Elige un modelo de embedding diseñado específicamente para esta tarea y luego crea la caché:
# Load a cache-optimized embedding model
vectorizer = HFTextVectorizer("redis/langcache-embed-v1")# Create the cache
cache = SemanticCache(
name="customer_support_cache",
vectorizer=vectorizer,
redis_client=redis_client,
distance_threshold=0.3
)# Set TTL (time to live) — keeps cache fresh
cache.set_ttl(86400) # 24 hours
El modelo langcache-embed-v1 es un modelo de embedding de pesos abiertos ajustado específicamente para el caché semántico, por lo que tiende a superar a los modelos de embedding de uso general en esta tarea.
Tres funcionalidades listas para producción vienen con la ruta de Redis:
- TTL. Las entradas caducan por sí solas, de modo que las respuestas obsoletas no permanecen vigentes a medida que el producto cambia.
- Namespacing. Los equipos, usuarios o inquilinos pueden tener cada uno su propio caché aislado dentro de una misma instancia de Redis.
- Búsqueda vectorial optimizada. La indexación HNSW permite búsquedas de vecinos más cercanos con baja latencia, incluso a gran escala.
Cuatro cifras a vigilar
Construir el caché es la parte fácil. También es necesario saber si está funcionando correctamente.
Tasa de acierto es la proporción de consultas que encuentran una coincidencia dentro del umbral, y determina el ahorro que se obtiene. Con una tasa de acierto del 30% en un sistema que realiza 10,000 llamadas a LLM al día, 3,000 llamadas se eliminan.
Precisión indica qué fracción de los aciertos devolvieron la respuesta correcta. Un caché que ofrece respuestas incorrectas con certeza es peor que ninguno.
Recuperación muestra qué fracción de las consultas que deberían haber tenido éxito realmente lo lograron, dado que existía una entrada equivalente. Una baja recuperación impide obtener el ahorro correspondiente.
Mejora de la latencia compara los dos métodos. Las pruebas de rendimiento indican que las respuestas del caché tardan aproximadamente de 2 a 5 ms, frente a 300 a 500 ms en una llamada típica a GPT-4, es decir, unas 160 veces más rápidas. Lo que realmente se siente depende de la frecuencia con la que se realizan las consultas, como lo muestra esta fórmula:
With-Cache Latency = (avg_llm_latency × (1 - hit_rate)) + (avg_cache_latency × hit_rate)
Con un 30% de aciertos, se puede esperar que la latencia general disminuya entre un 25 y un 30%. Una vez que la caché alcance un 60 a un 75% de uso, las mejoras son significativas.
Tratar la caché como un clasificador
Cada búsqueda termina con uno de cuatro resultados:
| La caché devuelve un acierto | La caché devuelve un error | |
|---|---|---|
| Debería haber sido un acierto | Positivo verdadero | Negativo falso |
| Debería haber sido un error | Positivo falso | Negativo verdadero |
Los falsos positivos son los más peligrosos: dos preguntas que se parecen pero significan cosas diferentes, por lo que la caché devuelve una respuesta incorrecta. Una pregunta sobre reembolsos tras cambiar de opinión y otra sobre el proceso general de reembolso pueden quedar cerca una de la otra en el espacio de embedding, pero cada una merece su propia respuesta. Es en estos casos donde la caché debe ser muy cuidadosa, ya que la respuesta que devuelve suena completamente plausible.
Mejorar la precisión
Regular el umbral. El umbral de distancia es la configuración más influyente. Un valor más bajo aumenta la precisión y disminuye la capacidad de recuperación, mientras que uno más alto hace lo contrario. Pruebe una serie de valores en un conjunto de pruebas representativo y elija aquel que maximice el F1, que es la media armónica de la precisión y la capacidad de recuperación. Ese enfoque elevó la precisión del 79 % al 90 % manteniendo una tasa de acierto del 25 %.
Reclasificar con un cross-encoder. Los modelos de embedding estándar codifican cada oración por separado. Un cross-encoder lee la consulta y un candidato juntos, lo que permite detectar matices que los bi-encoders pasan por alto. Tomemos una oración sobre un banco que eleva las tasas de interés y otra sobre un río que se inunda cerca del banco tras fuertes lluvias: la palabra compartida “banco” acerca bastante sus embeddings, pero un cross-encoder percibe que no están relacionados. En los experimentos, la reclasificación elevó la precisión al 94%. Los cross-encoders no pueden generar vectores buscables, por lo que funcionan como una segunda etapa sobre los candidatos top-K que obtiene el modelo de embedding.
Pide a un LLM pequeño que haga la evaluación. Para lograr la máxima precisión, utiliza un modelo económico como GPT-4o-mini para validar los casos dudosos con una solicitud sencilla: ¿son semánticamente equivalentes estas dos preguntas? Responde en milisegundos por una fracción del costo de una llamada completa. Las pruebas mostraron un 100% de precisión, aunque con un menor índice de recuperación, ya que el evaluador a veces rechaza coincidencias válidas. Si eso es aceptable depende del caso de uso. En atención al cliente, una respuesta almacenada ligeramente incorrecta puede ser peor que una llamada nueva.
Primero, realice una coincidencia difusa. Antes de calcular cualquier representación vectorial, ejecute una comparación por distancia de edición entre la consulta y las entradas almacenadas. Si difiere en dos o tres caracteres intercambiados, es casi con certeza un error tipográfico de la misma pregunta, por lo que concédale una coincidencia directa. Esto omite por completo el paso de la representación vectorial, logrando una latencia cercana a cero. Con umbrales de similitud altos, se alcanzó un 100% de precisión y un 94% de recuperación en conjuntos de prueba repletos de errores tipográficos.
La ventaja aún mayor: el caché dentro de los agentes
Cachear solo la respuesta final a una pregunta del usuario es un modelo mental incompleto. Los agentes funcionan de manera diferente. Un flujo típico para una pregunta de soporte compleja se ve así:
- Divida la consulta en 3 a 5 subpreguntas.
- Recupere los documentos relevantes de una base de conocimientos.
- Pida a un LLM que sintetice una respuesta.
- Evalúe la calidad de la respuesta e itere si es necesario.
Cada subpregunta representa una oportunidad para utilizar la caché. Si el agente ya ha respondido a “¿Cuáles son los límites de tasa de su API?”, no es necesario que vuelva a preguntarle al modelo cuando esa subpregunta aparezca en una solicitud compleja de otro usuario.
Se probó un agente de investigación avanzada basado en LangGraph en tres escenarios:
| Escenario | Tasa de éxito | Llamadas a LLM | Latencia |
|---|---|---|---|
| Usuario nuevo, caché fría | 25% | 8 | aproximadamente 20 s |
| Usuario recurrente, caché caliente | 75% | 4 | aproximadamente 13 s |
| Revisión previa a la compra | 75% | 6 | aproximadamente 18 s |
En el tercer escenario, tres de cada cuatro subpreguntas provinieron del caché. Las llamadas al modelo disminuyeron de 8 a entre 4 y 6, y la latencia de extremo a extremo se redujo en aproximadamente un tercio. La calidad de las respuestas se mantuvo igual, con respuestas personalizadas y precisas en ambos casos.
El efecto es acumulativo. Cada ejecución exitosa se agrega al caché, y los usuarios posteriores con preguntas similares se benefician de todo lo almacenado anteriormente, por lo que los ahorros aumentan a medida que el sistema maneja cientos o miles de consultas.
Leer más
- Por qué los agentes de análisis de IA necesitan una capa semántica en lugar de SQL en bruto — La conversión de texto a SQL en tablas de almacén en bruto inventa métricas e ignora las reglas de acceso. Vea cómo una capa semántica como Cube, junto con MCP, proporciona a los agentes un significado empresarial estructurado.
- Tu primer agente LangGraph: un asistente de fútbol con herramientas y enrutamiento — Tutorial paso a paso de LangGraph: estado compartido, herramientas, un nodo de planificación, aristas condicionales y un resumen en markdown, desarrollado alrededor de un asistente ficticio de estadísticas de fútbol.
- Cómo funciona la IA moderna: explicación de LLMs, técnicas de prompt, RAG, agentes y MCP — Una explicación en lenguaje sencillo de la tecnología de IA: cómo los LLMs predicen tokens, cuatro técnicas de prompt, RAG con embeddings, el ciclo de agentes ReAct y qué hace MCP.
- Un agente de programación local en Python: la herramienta Ollama que escribe y ejecuta código — Crea un pequeño bucle de agente con Ollama y llama3.1:8b que escribe archivos en Python, los ejecuta y corrige errores, sin necesidad de claves API ni costos en la nube.
- Depuración de fallos silenciosos en RAG: recuperación, fragmentación, filtros y evaluaciones — Las respuestas de RAG pueden parecer correctas pero seguir estando equivocadas. Un plan de acción orientado a Python para rastrear la recuperación de datos, la fragmentación, los filtros de metadatos, las evaluaciones, la búsqueda híbrida y el anclaje en información real.
- Búsqueda semántica en PostgreSQL con pgvector y Node.js, sin necesidad de LLM — Crea búsquedas basadas en el significado dentro de PostgreSQL utilizando pgvector, embeddings de MiniLM y Node.js: tokens, fragmentos, vectores, un índice IVFFlat, operadores de distancia y cómo leer las puntuaciones.