DEV Community

Cover image for Memoria de Agentes de IA: Agrega Búsqueda Semántica Sin una Vector Database
Elizabeth Fuentes L for AWS Español

Posted on

Memoria de Agentes de IA: Agrega Búsqueda Semántica Sin una Vector Database

📦 Clona y dale ⭐ a stop-ai-agents-losing-memory-sample-for-aws

La memoria del agente tiene la respuesta. El usuario hace la pregunta. Y la búsqueda no devuelve nada.

stored:   dietary_notes: "Vegetarian; severe shellfish allergy  strictly no
          crustaceans or mollusks."

asked:    "What should I avoid eating when I go out for dinner on this trip?"

keyword scan: 4 hits, answer found: False
Enter fullscreen mode Exit fullscreen mode

Cartoon: a robot librarian fails to match a semantic question with keyword scan, then retrieves the answer instantly with a vector embedding magnet: keyword scan fails, semantic search finds it

Eso es una corrida real, no un experimento mental. La pregunta no nombra ninguna clave y no comparte palabras con la nota almacenada, así que la memoria key-value del post anterior nunca la encuentra. La respuesta estuvo en el store todo el tiempo.

Esta es la línea divisoria de la búsqueda semántica: ¿conocés la clave, o solo la intención? Cuando las preguntas dejan de coincidir con claves, recuperás por significado: embebés cada memoria una vez al escribir, embebés la pregunta al consultar, y devolvés los vecinos más cercanos por similitud coseno. Este post mide dos cosas (si la búsqueda semántica encuentra lo que la búsqueda por palabras clave pierde, y qué vector store se adapta a tu deployment) usando los mismos embeddings y las mismas memorias del repo de referencia.

(Post 2 de una serie; el intro mapea todos los tipos de memoria. El código usa Strands Agents, un SDK open source; el patrón aplica a cualquier framework de agentes.)


¿Por qué la memoria key-value no encuentra la pregunta?

Porque una lectura key-value es una búsqueda que alguien diseñó de antemano, y esta pregunta no mapea a ninguna clave. El demo almacena 10 memorias sobre un viajero (datos de perfil, notas, episodios) y hace la pregunta de la cena contra tres stores. El key-value store tiene exactamente dos movimientos, y los dos fallan honestamente:

  1. Keyword scan: busca palabras de la pregunta en claves y valores. Devuelve 4 resultados, ninguno la nota de alergia, porque "avoid eating at dinner" no comparte palabras con dietary_notes ni con "shellfish". Answer found: False.
  2. Dump-all fallback: darle al modelo toda la memoria y dejar que la lea. Funciona, a un costo que crece con cada memoria que agregás. Para estas 10 memorias son 647 caracteres por pregunta; para cientos de notas son miles de tokens, en cada pregunta, para siempre.

One question hitting agent memory two ways: the keyword scan misses because no words match, vector similarity finds the allergy note by meaning

Esto no es un bug de la memoria key-value. Las búsquedas por perfil ("¿cuál es mi cabina preferida?") siguen siendo exactas, instantáneas y sin costo de embeddings, que es por eso que el post anterior las construyó así. El límite aparece solo cuando la pregunta es semántica. Esa es la señal para agregar una segunda forma de acceso, no para reemplazar la primera.


¿Cómo lo encuentra la búsqueda semántica?

Comparando significados en lugar de palabras. Cada memoria se embebe una vez al momento de escribir en un vector (aquí: Amazon Titan Text Embeddings V2, 1.024 dimensiones). Al consultar, la pregunta se embebe y el store devuelve los vecinos más cercanos por similitud coseno:

top hit: "Vegetarian; severe shellfish allergy  strictly no crustaceans
          or mollusks."  (score 0.231)
answer found: True
Enter fullscreen mode Exit fullscreen mode

Sin palabras compartidas entre la pregunta y la nota. Están cerca en significado, y el significado es lo que se indexó. Los dos backends de abajo devuelven el mismo resultado porque usan los mismos embeddings; lo que difiere es todo lo que hay alrededor de la consulta.


FAISS o Amazon S3 Vectors? Misma accuracy, diferente deployment

Los dos son embedding vector stores. Usan el mismo modelo (Titan V2), el mismo algoritmo (similitud coseno), y devuelven el mismo resultado con el mismo score. La accuracy es idéntica: no es un trade-off de calidad.

Store Encuentra la respuesta Similarity score
Key-value (keyword scan) No — keyword miss
FAISS — Facebook AI Similarity Search, índice in-process de Meta 0.231
Amazon S3 Vectors (managed cloud) 0.231

La diferencia está en el deployment. FAISS es una librería in-process: cero infraestructura, un pip install, corre local al proceso. Provee persistencia en disco via faiss.write_index / faiss.read_index. En este demo el índice no se persiste y se reconstruye desde cero cada corrida. S3 Vectors es un servicio managed de AWS: el índice vive en un bucket en la nube, accesible desde cualquier proceso con credenciales AWS, sin cluster que administrar ni escalar.

Semantic search flow: embedding the question takes ~510 ms for both backends, then FAISS queries in 0.09 ms (in-process, index rebuilt each run in this demo) and S3 Vectors in 195 ms (cloud index, always available)

El demo usa las mismas credenciales AWS para los dos: embeddings de Titan via Bedrock y S3 Vectors via boto3; el mismo setup de aws configure los alimenta a los dos, por eso no requiere configuración adicional dentro de un workflow de Strands Agents. El demo auto-provisiona el bucket y el índice en la primera corrida: create_vector_bucketcreate_index (1.024 dims, coseno) → put_vectors / query_vectors.

El costo que comparten los dos backends: embeber la pregunta cuesta ~510 ms con Titan V2. El tiempo de query del índice (0.09 ms para FAISS, 195 ms para S3 Vectors) es secundario a eso. Planificá el embedding call en cualquier path sensible a latencia, independientemente del vector store que uses.


¿Necesitás una vector database?

Depende del patrón de queries. AWS posiciona S3 Vectors como "ideal para workloads con queries menos frecuentes", que describe exactamente la memoria de un agente: un agente consulta las memorias de un usuario unas pocas veces por conversación, no miles de veces por segundo.

FAISS Amazon S3 Vectors Vector database dedicada
Tipo Librería in-process AWS vector storage Motor de base de datos completo
Ejemplos OpenSearch, Qdrant, Weaviate, Milvus, pgvector, Chroma
Accuracy semántica ✅ igual ✅ igual ✅ igual
Infraestructura Ninguna — pip install Ninguna — fully managed Self-hosted o managed
Máx. vectores Memoria del proceso Hasta 2 mil millones por índice Depende del deployment
Query latency ~0.09 ms ~100–200 ms Sub-10 ms a alto QPS
Hybrid search ✅ la mayoría lo soporta
Mejor para Prototipo / agente local Agente cloud, queries infrecuentes Alto QPS, filtros avanzados, producción

La decisión:

Necesitás Usá Por qué
Datos bajo claves conocidas (perfil, preferencias) Key-value (post 1) Exacto e instantáneo; no pagués ~510 ms de embedding para un lookup
Búsqueda semántica, local / prototipo FAISS Cero infraestructura, pip install, in-process
Búsqueda semántica, cloud / queries infrecuentes S3 Vectors AWS vector storage managed, latencia subsegundo, hasta 2 mil millones de vectores
Alto QPS, hybrid search o filtros avanzados Vector DB dedicada OpenSearch, Qdrant, Weaviate, Milvus, pgvector, Chroma
Preguntas multi-hop sobre relaciones Graph (próximo post) La búsqueda semántica encuentra piezas; no puede seguir aristas entre ellas

Lo que este demo no cubre: FAISS y S3 Vectors son storage backends. Almacenan vectores y recuperan por similitud. Construir qué recordar (extraer hechos específicos de conversaciones, deduplicación, memoria estructurada entre sesiones) lo manejan servicios de memoria managed como Amazon Bedrock AgentCore Memory. Esa técnica es el tema de un próximo post de esta serie.


¿Cómo elige el agente entre key lookup y búsqueda semántica?

Por los docstrings de las herramientas, solo. El último test del demo conecta las dos herramientas de recall a un agente de Strands:

@tool
def recall_by_key(key: str) -> str:
    """Recall a memory when the question maps to a known identifier.
    Use when the user asks about a stored field: "my preferred cabin",
    "my home airport"..."""

@tool
def recall_semantic(question: str, top_k: int = 3) -> str:
    """Recall memories by meaning when no key is obvious.
    Use for open questions: "what should I avoid eating on this trip?"..."""
Enter fullscreen mode Exit fullscreen mode

Con la pregunta de la cena, el agente llama recall_semantic; con "¿cuál es mi cabina preferida?", llama recall_by_key. Sin lógica de routing, sin prompt engineering. La frase when to use this al inicio de cada docstring es lo que el modelo lee para decidir. Escribila descuidadamente y el agente paga la latencia del embedding en lookups de perfil.


¿Cómo le pedís a un asistente de código que construya esto?

La calidad de la implementación de búsqueda semántica que construya tu asistente depende de las decisiones que nombres en el prompt. Sin nombrarlas, por defecto embebera todo y consultará un índice único. Estas cinco instrucciones codifican lo que este post midió:

  1. "Agregá búsqueda semántica solo para preguntas que no mapean a claves; mantené los datos de perfil en key-value state." De lo contrario, el asistente embebe cada query, incluyendo lookups exactos que ya tienen una clave conocida.
  2. "Embebé cada memoria una vez, al momento de escribir; solo la pregunta se embebe al momento de consultar." Los asistentes tienden a re-embeber todo el store por query.
  3. "Usá una sola función de embedding para almacenamiento y consultas, y especificá el modelo y las dimensiones." Embedders distintos producen scores de similitud silenciosamente incorrectos.
  4. "Dame dos herramientas de recall con docstrings de 'cuándo usar': por clave y por significado." El agente rutea por pregunta desde esas frases; sin código de routing.
  5. "Hacé el deployment explícito: índice in-process para un prototipo local, vector storage managed para un deployment en cloud, y probalo con un test de cliente nuevo que siga viendo todos los vectores."

El repo de referencia implementa y mide los cinco. Correlo para ver cada decisión en acción.


¿Cómo corrés el demo?

git clone https://github.com/elizabethfuentes12/stop-ai-agents-losing-memory-sample-for-aws
cd stop-ai-agents-losing-memory-sample-for-aws/02-vector-memory-demo
uv venv && uv pip install -r requirements.txt
uv run python test_vector_memory.py
Enter fullscreen mode Exit fullscreen mode

Necesitás credenciales AWS (aws configure) para los embeddings de Titan y S3 Vectors. El demo crea el vector bucket y el índice automáticamente si no existen. OPENAI_API_KEY solo se necesita para la conversación del agente en el notebook (o cambiá una línea por Amazon Bedrock); las mediciones de retrieval corren sin ningún LLM.


FAQ

¿Una vector database es lo mismo que la memoria de un agente de IA?
No. Una vector database es un posible backend para un tipo de memoria (recuperación por significado). La memoria del agente es el sistema completo: key-value state, vector o graph storage, reglas de selección e higiene. Muchos agentes en producción necesitan retrieval vectorial sin una vector database.

¿Puedo usar una vector database como memoria de agente?
Sí, para las memorias que consultarás por significado. Pero primero ruteá los datos con clave conocida (preferencias, configuraciones) a key-value storage: un lookup directo no cuesta nada, mientras que cada vector query paga el embedding call de la pregunta (~510 ms con Titan V2) antes de tocar el índice.

¿Cuándo necesito algo más que S3 Vectors?
Cuando cambia el patrón de queries. Las vector databases dedicadas como OpenSearch, Qdrant, Weaviate, Milvus, pgvector y Chroma están diseñadas para alto QPS, hybrid search, agregaciones y filtros avanzados. S3 Vectors es purpose-built para queries infrecuentes: maneja hasta 2 mil millones de vectores por índice con latencia subsegundo, que cubre workloads de memoria de agente mucho más allá del prototipo.

¿Cuál es la latencia real end-to-end?
FAISS: 0.09 ms de query al índice + ~510 ms de embedding = ~0.5 s total. S3 Vectors: 195 ms de query al índice + ~510 ms de embedding = ~0.7 s total. El índice rara vez es tu cuello de botella; el embedding call sí lo es.

¿Por qué mi búsqueda semántica devuelve memorias incorrectas?
Las causas más comunes: el store y las consultas usan modelos o dimensiones de embedding distintos, las memorias se embebieron con texto desactualizado, o datos de perfil con clave contaminaron el índice. Usá un solo embedder para todo, embebé al escribir, y mantené los datos de perfil fuera del vector store.


Recursos


Gracias!

🇻🇪 Dev.to Linkedin GitHub Twitter Instagram Youtube


Top comments (0)