Para agentes de IA: hay un índice de documentación disponible en https://www.mongodb.com/es/docs/llms.txt — versiones en markdown de todas las páginas están disponibles agregando .md a cualquier ruta URL.
See how MongoDB 9.0 delivers up to 2x higher throughput.
MongoDB Branding Shape
Register now >
Docs Menu

memoria del SDK del motor del agente

El SDK independiente de Python para Atlas Agent Engine Memory es una memoria a largo plazo para agentes de IA, que se puede usar dentro o fuera de Atlas Agent Engine.

Proporciona a un agente un objeto, Memory, para registrar los turnos de conversación y recuperar posteriormente el contexto relevante: hechos aprendidos (semánticos), conversaciones pasadas (episódicas), conocimiento del dominio (taxonómico) y procedimientos reutilizables. El paquete se instala automáticamente y solo depende de pydantic y httpx, por lo que se integra en cualquier agente sin necesidad de incorporar la pila de la plataforma.

Los agentes implementados en la plataforma utilizan este mismo SDK. No lo construyen ellos mismos; el entorno de ejecución les proporciona un objeto agent_engine_sdk_memory.Memory, ya configurado para el transporte dentro del clúster y para la identidad de la invocación que están gestionando. Se trata de la misma clase y las mismas firmas de método; solo difiere el transporte subyacente. Por lo tanto, el código que escriba para un agente externo se ejecuta sin cambios al implementarlo en la plataforma, y ​​la información de este archivo README se aplica en ambos casos.

Hay dos cosas que difieren: de dónde proviene la identidad (ver conexión) y un puñado de llamadas que el transporte vinculado a la aplicación no puede atender y que generan MemoryNotSupportedError, que se enumeran en lo que admite cada conexión.

Si este tema es nuevo para usted, comience por comprender cómo funciona la memoria: la estructura del sistema explica la mayor parte de la API. La integración consta de tres pasos: instalación, conexión y uso.La configuración del servidor se aborda en la sección de configuración del servidor de memoria. A continuación, encontrará información de referencia sobre identidad, errores y modelos, y al final, detalles internos del paquete.

La memoria proporciona a un agente un almacén persistente de los hechos, conversaciones, procedimientos y vocabulario relevantes, y lo alimenta extrayendo información aprendida y duradera de las conversaciones que el agente mantiene con un LLM. La extracción la realiza un LLM que usted configura.

Para un agente implementado en la plataforma, no se requiere cableado. El entorno de ejecución registra cada conversación en la memoria a corto plazo mientras se ejecuta el flujo de trabajo del agente, y la promoción y extracción se realizan de forma asíncrona en segundo plano, por lo que nada bloquea una respuesta mientras se procesa un dato. Un agente que ya conoce algo persistente no tiene que esperar a que se extraiga para encontrarlo: puede escribir el dato directamente con save_semantic y las demás escrituras tipadas.

La relectura funciona en ambos sentidos. Un agente puede buscar un tipo de hecho a la vez: search_semantic para lo que ha aprendido, search_episodes para lo que sucedió antes, search_taxonomic para lo que significa un término, discover_procedures para cómo se hace algo, y manejar los resultados por sí mismo.

O bien, puede delegar todo el trabajo. build_context_from_sources toma una especificación por fuente, cada una declarando su propio modo de recuperación, filtro y recuento de candidatos, luego recupera de cada fuente según sus propios términos, clasifica y elimina duplicados de los resultados en conjunto, los recorta a un presupuesto de tokens y devuelve un único bloque de contexto para insertar en la siguiente solicitud. build_context hace lo mismo con un modo y un filtro para cada fuente; utilice el formulario por fuente cuando necesite que difieran.

La memoria se divide según la duración de las cosas y su forma.

La memoria a corto plazo (MCP) es la conversación en estado puro: un registro por turno, limitado a una sesión, escrito a medida que sucede. Es fácil de escribir y completar: todo lo dicho, en orden.

La memoria a largo plazo es lo que sobrevive a la conversación, destilada en cuatro formas porque responden a diferentes preguntas:

tipo
sostiene
respuestas

semantic

un hecho etiquetado

“¿Qué sé yo de esto?”

episodic

un episodio resumido

“¿Qué pasó antes?”

procedural

un procedimiento reutilizable

“¿Cómo hago esto?”

taxonomic

un término y su definición

“¿Qué significa esta palabra aquí?”

Los proyectos también pueden declarar tipos personalizados para los registros de dominio en los que las cuatro formas no encajan.

No se escribe la memoria a largo plazo a mano, aunque se puede. El camino normal es:

record_turn(...) you write turns as the conversation happens
│
▼ turns accumulate into a session
snapshot a contiguous run of turns, summarised
│
▼ an LLM reads the snapshot and extracts what is durable
semantic · episodic · procedural · taxonomic

La extracción es asíncrona y se ejecuta en segundo plano, por lo que record_turn sigue siendo una escritura rápida. Por lo tanto, un dato aprendido en una conversación está disponible para la siguiente, no para el siguiente turno.

La consecuencia que merece la pena tener en cuenta en el diseño: los giros recientes son visibles inmediatamente a través de la memoria a corto plazo, el conocimiento extraído aparece un poco más tarde. Pide stm cuando necesites lo que se acaba de decir.

La extracción en segundo plano depende de un servicio de incrustación, un LLM de extracción y la base de datos, y cualquiera de ellos puede fallar. Tus escrituras están aisladas de eso: record_turn tuvo éxito cuando devolvió su respuesta, independientemente de lo que ocurra después.

En segundo plano, un paso fallido se clasifica mediante una pregunta: ¿puede cambiar la condición sin que cambie la solicitud? Los fallos ambientales (errores de red, interrupciones del proveedor, límites de velocidad, una clave API en rotación) se reintentan automáticamente con un retardo, ajustado al fallo: un fallo de red se reintenta en segundos, un problema de credenciales más lentamente, ya que las claves son rotadas por personas. Los fallos determinados por el contenido no se reintentan, porque un reintento no puede cambiar el resultado; se registran en el servidor, donde los operadores pueden verlos, en lugar de entrar en un bucle infinito.

Esto se aplica a toda la ruta de extracción, no solo a sus extremos. Un paso que falla ya no produce un resultado vacío sin más: o bien se reintenta, o bien se registra para que los operadores puedan actuar en consecuencia.

Un elemento inutilizable no implica el descarte del resto. Si una memoria no puede incrustarse —su contenido es demasiado extenso para el modelo o el proveedor la rechaza—, esa memoria se sigue almacenando y las demás del mismo lote no se ven afectadas. Lo que le falta a la memoria almacenada es un vector. En la práctica:

  • Todavía se encuentra mediante la búsqueda text y mediante hybrid; el sistema híbrido combina ambas clasificaciones, por lo que la parte de texto aún lo muestra.

  • No se encuentra mediante la búsqueda semantic, que solo compara vectores.

Esta es una buena razón para preferir hybrid como modo de recuperación predeterminado. Ya es la mejor opción para identificadores exactos y palabras poco comunes, y además significa que un recuerdo que no se pudo incrustar aún llega a usted.

Lo que esto significa para un agente:

  • Una interrupción del servicio retrasa la extracción de información; no se pierden los turnos. La memoria a corto plazo se escribe de forma síncrona y no se ve afectada: siga solicitando stm cuando necesite lo que se acaba de decir.

  • Los reintentos tienen un límite: si una interrupción de una dependencia dura más que ellos, se detiene el proceso de reintentos en lugar de entrar en un bucle indefinido, y el fallo se registra en lugar de descartarse.

  • Ninguno de estos problemas se manifiesta como un error a través del SDK. Los fallos de extracción son responsabilidad del servidor; la señal visible para el SDK son los recuerdos extraídos que aún no aparecen en la recuperación, o que aparecen en la búsqueda de texto e híbrida, pero no en la semántica.

La prosa recopilada o los propios documentos: dos cuestiones distintas.

``build_context_from_sources(...)`` es la función que se debe usar por defecto. Se le proporciona una consulta y un presupuesto de tokens; recupera los datos de los tipos de memoria especificados, clasifica los resultados, elimina los duplicados cercanos, los ajusta al presupuesto y devuelve un resultado que se puede usar directamente en una solicitud. Todo el proceso de recuperación se realiza con una sola llamada, y requiere una especificación por fuente, por lo que los modos y filtros se configuran por fuente en lugar de una sola vez para todas.

La recuperación de información puede realizarse de tres maneras. semantic compara incrustaciones y encuentra elementos con el mismo significado; text compara palabras y encuentra elementos con la misma expresión; hybrid ejecuta ambos métodos y combina las clasificaciones. Generalmente, la opción híbrida es la más adecuada por defecto: la búsqueda vectorial por sí sola no detecta identificadores exactos ni palabras poco frecuentes, mientras que la búsqueda de texto por sí sola no detecta paráfrasis.

``build_context(...)`` es el constructor más simple: la misma canalización, pero un modo y un filtro se aplican a cada fuente que lee.

``search(...)`` y las búsquedas por tipo (search_semantic, search_episodes, etc.) devuelven registros clasificados en lugar de prosa ensamblada, para cuando quieras inspeccionarlos o procesarlos tú mismo.

Cada registro lleva la identidad con la que fue escrito, y cada lectura se filtra por esa identidad en la consulta de la base de datos, en lugar de posteriormente. Los campos son: organización, usuario, proyecto, sesión y, opcionalmente, el agente, además de un parámetro de visibilidad que determina si un registro es privado para su usuario o si puede ser leído por un público más amplio.

Esto es importante por una razón práctica: una búsqueda o build_context limitada a un usuario no revelará la memoria privada de otro usuario, ya que la restricción forma parte de la consulta y no es un filtro aplicado a sus resultados. Por lo tanto, bind(...) no es una conveniencia, sino la forma de declarar el ámbito dentro del cual operan las lecturas y escrituras posteriores, y si se hace incorrectamente, se escribe la memoria de un usuario bajo la identidad de otro.

user_id es de quién es la memoria un registro. visibility es hasta dónde llega:

visibilidad
¿Quién puede leerlo?

private

solo el usuario nombrado en user_id

shared

cualquier usuario del proyecto — obsoleto, ver más abajo

org

cualquier usuario del proyecto

Ningún valor de visibilidad se extiende fuera del proyecto en el que se escribió el registro. La memoria se almacena por proyecto, por lo que la visibilidad no puede traspasar los límites de un proyecto. org es un nombre histórico y significa "no restringido a un solo usuario", no "visible para otros proyectos".

Advertencia

⚠️ ``shared`` está obsoleto y se eliminará en una versión futura. Utilice org para información destinada a trascender a un solo usuario y private para información restringida a su propietario. Dado que tanto shared como org ya tienen el mismo alcance, cambiar un registro existente de shared a org no modifica quién puede leerlo. Los registros ya escritos como shared seguirán leyéndose por ahora.

Ambos son independientes y, al leerlos, se combinan como "y",nunca como "o". Cada campo que proporcione agrega una condición de igualdad más a la consulta; cada campo que omita deja esa dimensión sin restricciones:

usted abarca la lectura por
regresas

user_id solo

todo lo que posee el usuario, en cualquier visibilidad

visibility solo

cada registro con esa visibilidad, quienquiera que sea su propietario

ambos

solo registros que coincidan con ambos: la lectura más restringida

ni

todo en el proyecto

La tercera fila es la que sorprende. search_semantic(query, user_id="user_1", visibility="org") no significa “los recuerdos del usuario_1 más los de la organización”. Significa “los recuerdos visibles para la organización que posee el usuario_1”, que es menor que cualquiera de las restricciones por separado. No hay unión: para leer el conocimiento compartido y la memoria privada de un usuario, realice dos llamadas y combine los resultados usted mismo.

pip install agent-engine-sdk-memory
from agent_engine_sdk_memory import Memory, MemoryRequestContext

El lugar donde opera tu agente determina cómo se conecta, y la diferencia radica principalmente en quién proporciona la identidad.

Tu agente corre
tú construyes
La identidad proviene de
Ver

en la plataforma

nada: el tiempo de ejecución inyecta memory

el tiempo de ejecución, por invocación

en cualquier otro sitio

Memory(service_account_token=..., project_id=...)

su token de cuenta de servicio, más lo que usted bind

localmente, en desarrollo

Memory(base_url=...)

lo que sea que bind

La API es idéntica en las tres. El código escrito para un agente implementado externamente se ejecuta sin cambios al trasladarlo a la plataforma: se elimina la llamada al constructor y el entorno de ejecución proporciona el objeto en su lugar.

Los agentes implementados en la plataforma obtienen la identidad automáticamente, y esa es la diferencia fundamental. El entorno de ejecución ya conoce la organización, el proyecto, el usuario y la sesión de la invocación que está gestionando, por lo que los vincula automáticamente. Un agente externo solo conoce lo que implica su token de cuenta de servicio (el proyecto), por lo que debe indicar a la memoria a qué usuario y sesión pertenece cada llamada. Si esto falla, se estará escribiendo en la memoria de un usuario bajo la identidad de otro, razón por la cual la ruta externa requiere que se especifique explícitamente.

El servicio gestionado. Proporcione un token de acceso a la cuenta de servicio y el ID de su proyecto.

Genera el token con la CLI agentengine. Crea una cuenta de servicio una sola vez; el secreto del cliente se muestra solo una vez, así que guárdalo inmediatamente:

agentengine service-account create my-agent --project-id <your-project-id> --role AGENT_DEVELOPER

Luego, intercambie el ID de cliente y el secreto por un token de acceso de corta duración (1 horas) (curl solicita el secreto del cliente para que no quede registrado en el historial de la shell):

ACCESS_TOKEN=$(curl --fail-with-body --silent --show-error --user <client-id> --data grant_type=client_credentials https://agentengine.mongodb.com/api/v1/oauth/token | jq -er .access_token)
memory = Memory(service_account_token="<your-access-token>", project_id="<your-project-id>")
Entrada
Vuelve a
notas

service_account_token

AGENTIC_MEMORY_SERVICE_ACCOUNT_TOKEN

Token de acceso a la cuenta de servicio, enviado como credencial de portador. Se vuelve a generar cuando caduca.

project_id

AGENTIC_MEMORY_PROJECT_ID

El proyecto requiere leer y escribir. Es necesario para el servicio alojado.

base_url

AGENTIC_MEMORY_BASE_URL

Opcional. Permite modificar el host para configurar una pila que no sea de producción.

La plataforma vincula tus credenciales a su proyecto, por lo que un project_id que no coincida será rechazado. Un service_account_token en blanco generará un ValueError.

api_key (y AGENTIC_MEMORY_API_KEY) siguen siendo aceptados como un alias obsoleto y emiten un DeprecationWarning; pasar tanto la entrada nueva como la antigua genera ValueError. Las claves de API del proyecto ya no se pueden generar a través de HTTP, por lo que las nuevas integraciones deben usar un token de cuenta de servicio.

Señala un backend que ejecutes tú mismo, como por ejemplo el stack agentengine dev up que se inicia.

memory = Memory(base_url="http://localhost:8080")
Entrada
Vuelve a
notas

base_url

AGENTIC_MEMORY_BASE_URL

La URL de tu backend.

service_account_token

AGENTIC_MEMORY_SERVICE_ACCOUNT_TOKEN

Opcional. Omítalo para el desarrollo local. (api_key / AGENTIC_MEMORY_API_KEY son alias obsoletos).

Deje project_id vacío para el desarrollo local.

Cuando su agente se ejecuta en Atlas Agent Engine, no necesita construir ni conectar Memory en absoluto; la plataforma inyecta una instancia lista y preconfigurada, con la identidad, el tenencia y el transporte ya vinculados. El código de la aplicación no pasa ninguna de las entradas anteriores; utiliza el identificador que le proporciona el entorno de ejecución. La identidad (usuario, sesión, organización, proyecto) se resuelve a partir del contexto de ejecución ambiental, por lo que las operaciones se pueden llamar directamente:

# `memory` is supplied by the platform runtime — do not construct it.
memory.record_turn(role="user", content="I'm allergic to penicillin.")
context = memory.build_context(query="What medications should I avoid?")
# bind(...) is still available to scope a call chain to a specific identity.

La ruta vinculada a la aplicación tiene algunas brechas de capacidad (llamada a herramienta / metadatos de cambio de modelo y algunas lecturas de estilo de lista); consulte la columna Vinculada a la aplicación en Qué admite cada conexión y `docs/capability-matrix.md `__.

Cada entrada también recurre a una variable de entorno AGENTIC_MEMORY_* cuando se omite el argumento. Un argumento explícito siempre tiene prioridad. Memory() sin argumentos lee los tres del entorno.

project_id decide a dónde van las llamadas. La autenticación nunca lo hace.

  • Establece project_id y el SDK llamará a las rutas de tu proyecto, /api/v1/projects/{project_id}/memory/*.

  • Déjelo vacío y el SDK llamará directamente al backend, /api/v1/memory/*.

Si project_id está configurado pero el backend no tiene una ruta coincidente, como un backend local, la llamada genera `MemoryRouteNotFoundError <#errors>`__ con una sugerencia para desconfigurarlo. Lo contrario también es cierto. Diríjase al servicio alojado sin project_id y la llamada 404s con una sugerencia para configurarlo.

La autenticación y el enrutamiento son independientes, por lo que puedes pasar un service_account_token con project_id vacío. El SDK envía entonces las llamadas autenticadas directamente al backend en base_url, a través de las rutas directas, omitiendo la ruta del proyecto. Esto resulta ideal para un motor de orquestación (OE) alojado al que se accede directamente.

Las capacidades dependen del enrutamiento de la llamada, no de la autenticación.

Operación
Alojado (proyecto_id establecido)
Directo (project_id vacío)
Vinculado a la aplicación

record_turn, build_context

✓

✓

✓

build_context_from_sources

✓

✓

✓

record_turn con metadatos de llamada a la herramienta/modelo

✓

✓

✗

search_episodes (y search sobre episódico)

✓

✓

✓

search_semantic, discover_procedures

✓

✓

✓

search_taxonomic

✓

✓

✓

CRUD específico de tipo (save_*, get_*, list_*)

✓

✓

✓ con huecos

tipo personalizado save / retrieve

✓ (restringido por bandera)

✗ sin contexto de ejecución

✓

Una llamada no compatible genera `MemoryNotSupportedError <#errors>`__ — antes de cualquier solicitud de red para brechas conocidas por backend, o después de la respuesta para brechas de capacidad que solo la plataforma puede informar (ver Errores) — nunca un fallo silencioso o un error HTTP sin procesar. La referencia completa por backend, incluidas las brechas limitadas a la aplicación en CRUD específicos del tipo, se encuentra en `docs/capability-matrix.md `__.

Los agentes vinculados a la aplicación son la excepción a todo lo anterior. Dentro de un agente de plataforma implementado, la plataforma inyecta un runtime listo, por lo que el código de la aplicación no pasa ninguna de estas entradas.

Un recorrido completo: construir la memoria, vincular una identidad conversacional, registrar un turno y recuperar el contexto.

from agent_engine_sdk_memory import Memory, MemoryRequestContext
memory = Memory(service_account_token="<your-access-token>", project_id="<your-project-id>")
# Scope every call to a user and conversation.
session = memory.bind(MemoryRequestContext(user_id="user_1", session_id="thread_123"))
# Record what happened.
session.record_turn(role="user", content="I'm allergic to penicillin.")
session.record_turn(role="assistant", content="Noted — I'll avoid it.")
# Later, pull back the relevant context for a new prompt. Include "stm"
# to surface the turns just recorded (the default is episodic, semantic).
# max_tokens is an optional gross context-construction budget.
context = session.build_context(
query="What medications should I avoid?",
enabled_sources={"stm", "episodic", "semantic"},
max_tokens=2048,
)
# context is a ContextResponse — inject its content into the next prompt.

bind(ctx) Devuelve un nuevo identificador con ámbito limitado a ctx sin modificar el original, de modo que un Memory puede atender a muchos usuarios y sesiones simultáneamente.

``build_context`` son fuentes predeterminadas. Si se omite enabled_sources, se utilizan por defecto episodic y semantic; stm, taxonomic y procedural requieren una configuración explícita.

``max_tokens``. Presupuesto bruto opcional positivo para la construcción del contexto. No es un costo de obtención ni un tamaño de salida prometido: después de la recuperación y la clasificación, el servidor resta una reserva de formato de token 500, luego selecciona codiciosamente bloques de memoria completos que caben en el resto. Los valores positivos iguales o inferiores a 500 no dejan presupuesto para memorias. Los valores superiores a 500 aún pueden generar un contexto vacío cuando ningún bloque cabe. metadata.token_count informa solo la salida formateada y excluye la reserva. Omita max_tokens para mantener el comportamiento anterior.

``format_style`` y ``include_memories``. build_context y build_context_from_sources aceptan dos opciones de modelado de respuesta. format_style ("openai", "claude" o "jinja2"; la enumeración FormatStyle se exporta para anotaciones de tipo) selecciona el formato de formatted_context, y los valores no válidos generan ValueError localmente. Si se omite, el servidor infiere el formato a partir de su modelo configurado. Una advertencia: el servidor actualmente vuelve a inferir cuando el valor explícito coincide con su tipo de modelo predeterminado, por lo que un "openai" explícito se respeta literalmente solo en servidores con un modelo de la familia OpenAI configurado; "claude" y "jinja2" siempre se respetan. include_memories=True completa el selected_memories de la respuesta con la lista MemoryChunk posterior al presupuesto, para que pueda inspeccionar exactamente qué recuerdos se seleccionaron. Ambos requieren una conexión HTTP alojada o directa; el modo vinculado a la aplicación genera MemoryNotSupportedError cuando se establece cualquiera de ellos.

Contexto por fuente — ``build_context_from_sources``. Donde build_context aplica un filtro y recuperación semántica a cada fuente, este método permite que cada fuente declare su propia recuperación mode (text, semantic o hybrid), metadata_filter y top_k a través de un SourceSpec. Los resultados se fusionan y se eliminan los duplicados entre las fuentes, opcionalmente se reordenan por una relevancia rerank, luego se formatean y se presupuestan como build_context. metadata.ranking_strategy y metadata.source_outcomes informan cómo se produjo el orden final y cómo le fue a cada fuente. sources debe ser no vacío y puede listar cada fuente como máximo una vez, y el top_k de cada fuente debe estar entre 1 y 200; session_id solo es necesario cuando se incluye la fuente stm.

La búsqueda híbrida suele ser la opción predeterminada adecuada para fuentes que contienen texto: la búsqueda vectorial por sí sola no detecta identificadores exactos ni palabras poco frecuentes, mientras que la búsqueda de texto por sí sola no detecta paráfrasis. mode se establece por defecto en semantic cuando una especificación lo omite.

Declaración de metadatos filtrables. Un metadata_filter solo puede nombrar metadatos que su proyecto haya declarado, ya que el filtro se aplica dentro del índice de búsqueda en lugar de sobre sus resultados. Declare los atributos y las ramas del índice que deben servirlos en project-config.yaml:

metadata_partition_key: # Filterable metadata attributes (max 10)
- name: tier
type: string # string | number | boolean | date
- name: confidence
type: number
metadata_partition_index:
semantic: [both] # both legs, so a hybrid source can filter
short_term: [both] # stm is searchable only once listed here
short_term:
embed_on_write: true # required to give stm a vector leg

Nota

Requiere memory-server 0.0.81 o posterior. Las claves de partición solo se respetan en un entorno de ejecución que las admita, y el servidor de memoria lee su configuración al inicio; almacenar una configuración no la aplica. Después de editar project-config.yaml, cárguelo con agentengine memory configure y luego aplique el entorno de ejecución a la imagen actual:

agentengine memory apply --upgrade

--upgrade Cambia el entorno de ejecución a la imagen que tu entorno tiene asignada actualmente, que es como se obtiene un servidor de memoria más reciente; sin esto, el entorno de ejecución mantiene la imagen en la que ya se encuentra. agentengine memory apply --dry-run informa sobre la imagen en uso si deseas verificarla antes de realizar cualquier cambio, y --wait se bloquea hasta que el entorno de ejecución indique que está listo.

Cuatro consecuencias que conviene conocer antes de escribir un filtro:

  • La clave del filtro es la ruta de índice cualificada, no el nombre declarado. Un atributo declarado como tier se indexa en metadata.tier, y eso es lo que debe indicar el filtro; no se añade ningún prefijo.

  • Una clave solo se puede filtrar por las piernas que usted indique. metadata_partition_index asigna una fuente a [vector], [text] o [both], y una fuente hybrid necesita [both]: un filtro que su rama de texto no puede servir se rechaza antes de que se ejecute la consulta en lugar de devolver silenciosamente menos.

  • Una clave incorrecta genera un error. A diferencia de la clave principal metadata_filter de build_context, las claves por origen se validan con respecto al conjunto declarado. Una ruta no declarada o mal escrita genera una excepción MemoryBadRequestError cuyo mensaje enumera las rutas aceptadas, por lo que un error tipográfico provoca un fallo en la llamada en lugar de devolver silenciosamente un conjunto de resultados reducido.

  • La memoria a corto plazo no tiene índice propio. Es filtrable —y buscable mediante este método— solo una vez que el proyecto lista short_term en metadata_partition_index. Hasta entonces, una fuente stm falla en lugar de no devolver nada: la búsqueda falla y la fuente se informa con un error en metadata.source_outcomes. Si stm fue la única fuente solicitada, todas las fuentes han fallado y la llamada devuelve 503 en lugar de un contexto vacío, porque un resultado vacío sería indistinguible de una búsqueda correcta sobre un corpus vacío. Darle una rama vectorial ([vector] o [both]) también requiere short_term.embed_on_write: true, y la configuración se rechaza sin ella, porque un índice vectorial sobre giros que nunca se incrustan sería un peso muerto. La rama de texto no necesita incrustaciones.

La declaración type establece la semántica de coincidencia. Una clave string se indexa como un token, por lo que la coincidencia es de valor completo y distingue entre mayúsculas y minúsculas: "gold" no coincide ni con Gold ni con gold-tier. Los operadores de rango requieren una clave number o date.

Cláusulas admitidas: igualdad; $gt / $gte / $lt / $lte, con límites combinados que se fusionan en un solo rango; $in / $nin para conjuntos; $ne; $exists; y $and / $or / $nor para composición.

agent_id es filtrable sin ser declarado. Los campos de tenencia org_id, user_id, project_id, session_id, visibility, deleted, is_latest y has_embedding están reservados para el control de acceso y no pueden ser filtrados por los llamantes.

Ensamblándolo. Con la configuración anterior:

from agent_engine_sdk_memory import SourceSpec
context = session.build_context_from_sources(
query="what did we decide about the refund policy?",
sources=[
# Exact match on a string key, and a range on a numeric one.
SourceSpec(
source=MemorySource.SEMANTIC,
mode=RetrievalMode.HYBRID,
metadata_filter={
"metadata.tier": "gold",
"metadata.confidence": {"$gt": 0.8},
},
top_k=20,
),
# Sources without a filter are unrestricted.
SourceSpec(source=MemorySource.EPISODIC, mode=RetrievalMode.HYBRID, top_k=5),
SourceSpec(source=MemorySource.STM, mode=RetrievalMode.TEXT, top_k=10),
],
rerank=True,
)

Este método llega al backend en los tres modos de conexión. En la ruta del proyecto alojado (project_id configurada), Gateway actúa como proxy al mismo controlador por origen, estampando org/project desde la sesión autenticada; en la ruta directa (project_id vacía), el proxy OE la reenvía; en el entorno de ejecución en la plataforma (vinculado a la aplicación), la plataforma estampa la tenencia y lleva la respuesta completa de vuelta a través de la ejecución duradera.

Más allá de record_turn y build_context, el objeto Memory expone:

  • Búsqueda: search(query, sources=[...]) se expande a través de los tipos de memoria y devuelve uno clasificado como list[MemoryChunk]; search_semantic, search_episodes, search_taxonomic y discover_procedures apuntan a un solo tipo.

  • Escrituras y lecturas específicas de tipo (donde la conexión admite CRUD): save_semantic / get_semantic, save_episode / list_episodes, save_taxonomic / get_taxonomic_term / list_domains y save_procedure / get_procedure.

  • Tipos dememoria personalizados: save(memory_type, content, tags=...) y retrieve(memory_type, query, tags=..., top_k=...) operan sobre tipos declarados en la configuración de memoria del proyecto. Se rechazan los nombres de tipos integrados; utilice los métodos dedicados mencionados anteriormente. Estas llamadas no contienen campos de identidad; la plataforma imprime org, project y user de la solicitud. En una plataforma que no sirve rutas de tipo personalizado, o en una donde la función está deshabilitada, la llamada genera `MemoryNotSupportedError <#errors>`__.

Un breve ejemplo utilizando el límite session mencionado anteriormente: escribir un hecho, leerlo de nuevo por etiqueta y buscar entre tipos:

# Save a semantic fact (user_id is inherited from the bound session).
session.save_semantic(text="Prefers window seats on flights.", label="seat-preference")
# Read it straight back by label.
fact = session.get_semantic("seat-preference")
# Search across memory types; returns one ranked list[MemoryChunk].
hits = session.search("travel preferences", sources=["semantic", "episodic"], top_k=5)

Memory También es un gestor de contexto; with Memory(service_account_token=...) as memory: libera el transporte subyacente al salir.

El SDK lee y escribe en la memoria; no configura el servidor. Los ajustes (incrustaciones, el LLM de extracción, los tipos de memoria que se extraen, los metadatos filtrables) se encuentran en el bloque memory: del project-config.yaml de tu proyecto y se aplican mediante la CLI. Solo se carga ese subdocumento; el resto del archivo se ignora.

agentengine memory configure # store the memory: block for this project
agentengine memory apply --wait # roll the memory server so it takes effect

El almacenamiento no se está aplicando. configure Guarda la configuración y la plataforma la entrega automáticamente al servidor de memoria de tu proyecto, pero el servidor solo lee la configuración al iniciar. Hasta que un pod se reinicia, la nueva configuración permanece en disco sin leer. agentengine memory apply realiza ese reinicio y, si el proyecto aún no tiene uno, primero aprovisiona el entorno de ejecución de memoria.

agentengine memory apply --upgrade Además, traslada el entorno de ejecución a la imagen del servidor de memoria que su entorno tiene actualmente asignada, que es como se obtiene un servidor más reciente. Sin --upgrade, la imagen permanece sin cambios. agentengine memory status informa del estado del entorno de ejecución en cualquier momento; --wait en apply se bloquea hasta que esté listo.

Algunas partes de la configuración son prácticamente de escritura única, así que decídalas antes de empezar a escribir en la memoria:

  • Claves de partición de metadatos: los atributos filtrables declarados. Una vez declarada, no se puede eliminar una clave ni cambiar su tipo.

  • Declaraciones de tipos de memoria personalizados: una vez que se acepta un tipo, su colección y conjunto de etiquetas no se pueden editar ni eliminar a través de la API de configuración; en su lugar, declare un nuevo nombre de tipo.

  • Losíndices de búsqueda se aprovisionan solo si no existen previamente. Agregar una clave de partición después de que el servidor se haya iniciado no reconstruye el índice existente; se registra la discrepancia y, en lugar de ignorarse silenciosamente, se produce un error al aplicar un filtro a la nueva clave en la base de datos. Recrear el índice es una operación manual.

El nivel de registro, el modelo de lógica descriptiva (LLM) de extracción y los tipos de extracción habilitados pueden modificarse y reaplicarse. Cambiar el modelo de incrustación o la dimensión requiere migrar y volver a incrustar las memorias existentes; los cambios de dimensión también requieren reconstruir los índices de búsqueda vectorial.

Las operaciones de memoria están delimitadas por user_id, agent_id y session_id, y se llevan a cabo en un MemoryRequestContext. Para cada llamada, cada campo se resuelve a través de tres niveles, con la mayor precedencia primero:

  1. Argumento de llamada: un valor que se pasa directamente al método (por ejemplo, search_semantic(query, user_id="user_2")).

  2. Contexto vinculado: el MemoryRequestContext pasado a bind(...).

  3. Contexto de ejecución: identidad ambiental proporcionada por el entorno de ejecución, utilizada por la ruta vinculada a la aplicación.

Los valores en blanco o solo espacios en blanco se consideran no establecidos en cada nivel; agent_id siempre es opcional. La identidad se valida en el lado del cliente solo para las escrituras específicas del tipo: save_semantic, save_taxonomic y save_procedure requieren un user_id, y save_episode requiere tanto user_id como session_id; cada uno genera MemoryIdentityError cuando el campo no se puede resolver. Las operaciones del flujo de trabajo (record_turn, build_context, las búsquedas) y las lecturas get_* / list_* no imponen la identidad localmente; reenvían lo que se resuelva al backend, que puede rechazar la solicitud como un error de transporte.

``session_id`` tiene un alcance por operación. Identifica una conversación, por lo que se hereda (de bind/tiempo de ejecución) solo para E/S de conversación: record_turn y la rama de memoria a corto plazo de build_context. La búsqueda episódica y semántica no la hereda: search_episodes, list_episodes y la rama episódica de search() resuelven session_id solo a partir del argumento de llamada. La memoria episódica se almacena sin alcance de sesión (los episodios consolidados llevan session_id: null), por lo que una sesión vinculada filtraría silenciosamente toda la memoria duradera y devolvería una lista vacía sin error. Pase session_id= explícitamente en la llamada de búsqueda cuando desee una lectura episódica con alcance de sesión. (Esto alinea el SDK con la ruta de la plataforma agent-engine-sdk-langgraph / TenantRuntime, que ya requiere un session_id explícito en la búsqueda episódica). user_id y agent_id no se ven afectados y siguen heredando de bind/tiempo de ejecución en las lecturas.

Cada escritura requiere un visibility. El valor predeterminado difiere según el tipo de memoria, ya que los tipos se utilizan de manera diferente:

guardar
visibilidad predeterminada

save_semantic, save_episode, save_procedure

private

save_taxonomic

org

La memoria taxonómica se establece por defecto en org porque el vocabulario de un dominio se comparte por definición; un término y su significado rara vez son información privada de un solo usuario. Todo lo demás se establece por defecto en private, por lo que un dato que guardes estará restringido al usuario del que se aprendió, a menos que indiques lo contrario.

record_turn es la excepción: no toma ni visibility ni user_id. Un turno de conversación siempre se escribe bajo el usuario y la sesión vinculados, por lo que bind(...) antes de grabar los turnos; no hay una forma por llamada de establecer el usuario en un turno.

# Private to user_1 — the default.
session.save_semantic(text="Prefers window seats.", label="seat-preference")
# Readable across the whole organization.
session.save_semantic(
text="Refunds over $500 need manager approval.",
label="refund-policy",
visibility="org",
)

Las lecturas dejan visibility sin definir, por lo que solo están limitadas por la identidad que se resuelve, normalmente la vinculada user_id. Ese es el valor predeterminado adecuado para la personalización: se obtiene todo lo que posee el usuario, con cualquier nivel de visibilidad.

Para alcanzar el conocimiento compartido se pasa una visibilidad, y aquí entra en juego la regla. Un identificador vinculado a user_id="user_1" sigue contribuyendo a que user_id, por lo que esto solo lee los registros visibles para la organización que posee el usuario_1:

session = memory.bind(MemoryRequestContext(user_id="user_1", session_id="thread_123"))
session.search_semantic("refund policy", visibility="org") # user_1 AND org

Pasar user_id=None no lo amplía. Un None o un argumento en blanco se considera "no proporcionado" en cada nivel, por lo que se aplica el valor vinculado. Leer todos los usuarios con un identificador que no tenga un límite user_id:

# The original handle is unbound, so it carries no user_id.
memory.search_semantic("refund policy", visibility="org")
# Or bind only the parts you want.
thread = memory.bind(MemoryRequestContext(session_id="thread_123"))
thread.search_semantic("refund policy", visibility="org")

Así, un asistente que necesita tanto el historial del usuario como el conocimiento compartido del equipo realiza dos lecturas y las combina:

personal = session.search_semantic(query, top_k=5) # user_1, any visibility
shared = memory.search_semantic(query, visibility="org", top_k=5) # org-wide, any owner

Los errores se dividen en dos familias. Los errores del lado del cliente son una subclase de ValueError y generalmente se generan antes de cualquier llamada de red:

  • MemoryClientError — base para errores de uso.

  • MemoryIdentityError — No se pudo resolver un campo de identidad obligatorio. Subclases de ValueError directamente (es una clase hermana de MemoryClientError, por lo que no es capturada por except MemoryClientError).

  • MemoryNotSupportedError — la operación no está disponible para la conexión activa (ver Conectar). El mensaje nombra la operación y el motivo. Para los métodos de tipo personalizado (save / retrieve) también puede generarse después de la llamada HTTP, cuando la respuesta muestra que la plataforma carece de la capacidad: un 404/405 simple (plataforma demasiado antigua para servir la ruta) o el 400 estructurado de la puerta de enlace informa que los tipos de memoria personalizados están deshabilitados en la implementación. Un 404 estructurado de tipo desconocido es un error de solicitud, no una falta de capacidad, y genera MemoryBadRequestError.

Los errores de transporte provienen de MemoryAPIError y llevan el status HTTP y el cuerpo de la respuesta:

  • MemoryAuthError — Falló la autenticación o autorización (401/403).

  • MemoryBadRequestError — la solicitud fue rechazada (un 4xx distinto de autenticado o no aprovisionado).

  • MemoryRouteNotFoundError — una solicitud de bucle principal 404, por lo que es probable que la forma de la ruta no coincida con el backend. El mensaje da una pista direccional para establecer o eliminar project_id. Subclases MemoryBadRequestError.

  • MemoryNotProvisionedError — El entorno de ejecución de memoria del proyecto aún no está disponible.

  • MemoryServerError — El backend generó un error (5xx) o devolvió un cuerpo no analizable o inesperado.

  • MemoryConnectionError — No se pudo acceder al servidor.

Los tipos de solicitud y respuesta se exportan desde la raíz del paquete y se reexportan desde agent_engine_sdk_memory.models. La recuperación devuelve MemoryChunk y ContextResponse; escribe resultados con tipos de retorno como WriteTurnResult, CreateSemanticResult y CreateEpisodicResult. SearchSource enumera los tipos que se pueden buscar. La creación del contexto se configura con argumentos de palabra clave en Memory.build_context (por ejemplo, enabled_sources y max_tokens), no con un modelo de configuración independiente. Importe estos desde agent_engine_sdk_memory, no desde agent_engine_sdk; los modelos solían estar allí y se han movido aquí.

record_turn toma un diccionario opcional metadata, y la recuperación a corto plazo lo devuelve en su propio espacio en el fragmento: chunk.metadata["metadata"]. Tus claves se mantienen separadas de los campos de turno de la plataforma (session_id, role, turn_seq, …) en lugar de mezclarse con ellos, por lo que una clave tuya nunca entra en conflicto con una de las suyas; puedes nombrar una clave role o session_id y leer tu propio valor.

Dos limitaciones que la seguridad contra colisiones no cubre. Los valores deben sobrevivir al viaje de ida y vuelta, por lo que deben ser serializables en JSON y tener un tamaño razonable. Además, para que una clave sea filtrable, debe poder declararse como una clave de partición de metadatos: cada segmento separado por puntos debe coincidir con ^[a-z][a-z0-9_]{0,63}$, se permite como máximo un punto y se reservan algunos nombres (org_id, project_id, user_id, agent_id, content, embedding, embedding_model_id, created_at). Una clave que no cumpla con este formato —Tier, $foo, a.b.c— se almacena y se devuelve, pero no se puede filtrar.

Un diccionario vacío se trata como si no hubiera metadatos: metadata={} registra el turno sin una ranura metadata en el fragmento, así que léalo con chunk.metadata.get("metadata", {}) si el llamador no hubiera proporcionado ninguna. La memoria semántica se comporta de forma idéntica, lo que permite que una ruta de lectura cubra ambas. Episódico, taxonómico y procedimental se comportan de la misma manera: el diccionario que escribes se devuelve bajo chunk.metadata["metadata"], y uno vacío significa que no hay ninguna ranura. Episódico es la única excepción: un filtro de compatibilidad descarta todo el diccionario, incluidas las claves no relacionadas, si resulta que contiene citation_score, llm_confidence_score y combined_score juntos, ya que esa forma también coincide con un blob interno previo a la migración. La recuperación también puede llevar chunk.metadata["contextual_metadata"]; ese es el artefacto de extracción de la propia plataforma, no tus datos, y no forma parte de ningún contrato del que debas depender.

La memoria a corto plazo está limitada a una sesión, por lo que la escritura y la lectura deben nombrar la misma: vincularla una sola vez y pasarla:

from agent_engine_sdk_memory import (
Memory,
MemoryRequestContext,
MemorySource,
RetrievalMode,
SourceSpec,
)
session_id = "session-42"
memory = Memory(base_url="http://localhost:8080").bind(
MemoryRequestContext(user_id="user-1", session_id=session_id)
)
memory.record_turn(
role="user",
content="The engagement is green and the rollout continues.",
metadata={"engagement_id": "eng-alpha", "tier": 2},
)
context = memory.build_context_from_sources(
query="what is the engagement status?",
sources=[
SourceSpec(
source=MemorySource.STM,
mode=RetrievalMode.HYBRID,
metadata_filter={"metadata.engagement_id": "eng-alpha"},
)
],
session_id=session_id,
include_memories=True,
)
for chunk in context.selected_memories or []:
print(chunk.metadata["metadata"]["engagement_id"])

Atlas Search indexa la escritura de forma asíncrona, por lo que una lectura realizada inmediatamente después puede que aún no vea el turno.

Una diferencia ortográfica que conviene tener en cuenta: se lee el nombre sin formato dentro del diccionario anidado, chunk.metadata["metadata"]["engagement_id"], pero se filtra por la ruta del índice cualificado, {"metadata.engagement_id": ...}. Ambos reconocen el anidamiento: el filtro se dirige al documento almacenado, donde el diccionario reside realmente bajo metadata.

El filtrado por nombre simple se rechaza con un error que enumera las rutas aceptadas, por lo que ese error le da la respuesta.

Primero se deben declarar los metadatos filtrables para el proyecto: engagement_id debe aparecer debajo de metadata_partition_key, y short_term debe figurar en metadata_partition_index. Consulte la sección "Declaración de metadatos filtrables" en "Uso".

Las secciones siguientes describen cómo está construido el paquete. No son necesarias para usarlo.

agent_engine_sdk_memory/
├── memory.py # Memory — the public, transport-free facade
├── protocol.py # MemoryRequestContext, MemoryRuntime + MemoryCrudClient seams
├── identity.py # resolve_identity — call arg > bind ctx > runtime ctx
├── errors.py # MemoryIdentityError + the typed transport-error family
├── models.py # Pydantic models for memory requests/responses
├── validation.py # require_positive_max_tokens — public build_context guard
├── _transport.py # _HttpTransport — shared retry / error-mapping / lifecycle
├── _http_runtime.py # _HttpMemoryRuntime — the workflow ops + per-backend profiles
├── _direct_crud.py # empty-tenancy MemoryCrudClient over the shared transport
├── _wire.py # custom-type request-body builders shared by the CRUD clients
├── _tag_syntax.py # client-side custom-type name + tag-syntax checks
├── _client.py # MemoryClient — internal HTTP client for the Memory Server API
└── _denylist.py # do-not-add dependency denylist (see below)

Memory es libre de transporte: delega las operaciones del flujo de trabajo (record_turn, build_context, las búsquedas, discover_procedures) a un MemoryRuntime inyectado, y el CRUD específico del tipo a un MemoryCrudClient inyectado. La superficie pública se congela mediante una prueba de instantánea en tests/test_package_contract.py; MemoryClient permanece interna. La tenencia por llamada (org_id, project_id a nivel de cuerpo nunca aparece en una firma pública; el único project_id en la superficie pública es el selector de forma de ruta del constructor opcional, que va en la URL, no en un cuerpo de solicitud.

La restricción pydantic + httpx solamente se aplica, no es una aspiración. _denylist.py enumera las distribuciones conocidas de gran tamaño y los nombres de importación (langchain, fastapi, pymongo, …), y tests/test_package_contract.py falla si la importación del paquete incluye alguno de ellos en sys.modules.

agent-engine-runner-shared Declara este paquete como una dependencia del espacio de trabajo y construye el MemoryClient interno en agent_engine_runner_shared/memory.py, pasando su búsqueda de contexto de ejecución como una función invocable execution_id_provider para que los encabezados de ID de ejecución por solicitud funcionen sin que este paquete importe ningún código de plataforma.

Califique esta página