En este artículo, aprenderá cómo implementar una canalización de poda de contexto para agentes de IA de larga duración, permitiéndoles administrar la memoria conversacional de manera eficiente a través de la similitud semántica.
Los temas que cubriremos incluyen:
Por qué el historial de conversaciones ilimitado es un problema para los agentes creados sobre grandes modelos de lenguaje y cómo se ve una estrategia de poda de contexto. Cómo utilizar modelos de incrustación de transformadores de oraciones para calcular la similitud semántica entre un mensaje actual y los turnos de conversación archivados. Cómo ensamblar una ventana de contexto eliminada del turno más reciente, los turnos pasados semánticamente relevantes y el mensaje actual.
Creación de un canal de poda de contexto para agentes de larga duración
Introducción
Los agentes de IA modernos creados sobre grandes modelos de lenguaje (LLM) están diseñados para ejecutarse de forma continua. Como resultado, su historial de conversaciones sigue creciendo indefinidamente. Pasar una historia tan completa como la ventana de contexto del LLM es la receta perfecta para costos prohibitivos de tokens, cuellos de botella de latencia y una eventual degradación del razonamiento.
La creación de una canalización de poda de contexto puede abordar este problema mediante la gestión dinámica de la memoria conversacional reciente. Este artículo describe los principios básicos para implementar una canalización de poda de contexto para agentes de larga duración.
Usamos una solución local totalmente accesible y gratuita basada en modelos de integración de código abierto en lugar de API pagas, pero puede reemplazarlas con API pagas si desea una solución más eficiente.
Estrategia de memoria propuesta
Las estrategias de memoria clásicas en los agentes se basan en una ventana deslizante que olvida la información antigua a medida que se queda atrás, incluidos detalles potencialmente críticos. Más allá de ese enfoque, es posible construir un canal selectivo y más inteligente que brinde al LLM precisamente lo que necesita como contexto.
En esencia, el contexto se puede reducir a los siguientes elementos básicos:
El mensaje actual, que contiene la solicitud o pregunta del usuario. El giro más reciente, es decir, el intercambio entrada-respuesta inmediatamente anterior, que es clave para mantener la continuidad conversacional. Las coincidencias semánticamente relevantes entre las K principales, calculadas en función de una puntuación de similitud. Estos son giros pasados estrechamente relacionados con el mensaje actual, recuperados mediante incrustaciones de vectores.
Todo lo que hay en el historial de conversaciones que queda fuera del alcance de estos tres elementos se descarta del contexto del mensaje activo, lo que ahorra cómputo y memoria.
Implementación basada en simulación
Nuestra implementación de ejemplo simula la aplicación de la estrategia antes mencionada, construyendo una ventana de poda de contexto paso a paso. Los modelos de transformadores de oraciones se utilizan para simular un proceso de larga duración junto con un historial de conversaciones simulado.
Empezamos realizando las importaciones necesarias:
importar numpy como np desde Sentence_transformers importar SentenceTransformer desde scipy.spatial.distance importar coseno
importar engordado como notario público
de transformadores de oraciones importar Transformador de oraciones
de picante.espacial.distancia importar coseno
A continuación, cargamos e inicializamos un modelo de incrustación previamente entrenado, concretamente todo MiniLM-L6-v2 de la biblioteca sentencia_transformers. Este modelo ha sido entrenado para transformar texto sin formato en vectores de incrustación que capturan características semánticas. También creamos un historial de agente simple y simulado que contiene interacciones usuario-agente (en un entorno real, esto se obtendría de una base de datos):
# Inicializar un modelo de incrustación ligero de código abierto model = SentenceTransformer(‘all-MiniLM-L6-v2’) # 1. Historial del agente simulado (generalmente obtenido de una base de datos) chat_history = [
{“role”: “user”, “content”: “My name is Alice and I work in logistics.”},
{“role”: “agent”, “content”: “Nice to meet you, Alice. How can I help with logistics?”},
{“role”: “user”, “content”: “What’s the weather like today?”},
{“role”: “agent”, “content”: “It’s sunny and 75 degrees.”},
{“role”: “user”, “content”: “I need help calculating route efficiency for my fleet.”},
{“role”: “agent”, “content”: “Route efficiency involves analyzing distance, traffic, and load weight.”},
{“role”: “user”, “content”: “Thanks, that makes sense.”},
{“role”: “agent”, “content”: “You’re welcome! Let me know if you need anything else.”}
]
# Inicializar un modelo de incrustación ligero de código abierto
modelo = Transformador de oraciones(‘todo-MiniLM-L6-v2’)
# 1. Historial del agente simulado (generalmente obtenido de una base de datos)
historial_chat = [
{“role”: “user”, “content”: “My name is Alice and I work in logistics.”},
{“role”: “agent”, “content”: “Nice to meet you, Alice. How can I help with logistics?”},
{“role”: “user”, “content”: “What’s the weather like today?”},
{“role”: “agent”, “content”: “It’s sunny and 75 degrees.”},
{“role”: “user”, “content”: “I need help calculating route efficiency for my fleet.”},
{“role”: “agent”, “content”: “Route efficiency involves analyzing distance, traffic, and load weight.”},
{“role”: “user”, “content”: “Thanks, that makes sense.”},
{“role”: “agent”, “content”: “You’re welcome! Let me know if you need anything else.”}
]
La lógica central del proceso de poda de contexto viene a continuación. Está encapsulado en una función prune_context() que recibe el mensaje actual, el historial de interacción completo y el número de turnos pasados semánticamente relevantes para recuperar, k:
def prune_context(current_prompt, historial, top_k=2): # Si el historial de conversación es demasiado corto, simplemente lo devolvemos si len(history) <= 2: devolver historial + [{"role": "user", "content": current_prompt}]# Extrayendo el turno más reciente (último par usuario/agente) turno_reciente = historial[-2:] # El resto del historial será elegible para la poda semántica archived_turns = historial[:-2]# 2. Incrustar el mensaje actual Prompt_emb = model.encode(current_prompt) # 3. Incrustar turnos archivados y calcular similitudes scoring_turns = []para entregar turnos_archivados: turn_emb = model.encode(turno["content"]) # Queremos similitud, por lo que restamos la distancia del coseno de 1 similitud = 1 - coseno(prompt_emb, turn_emb) scoring_turns.append((similitud, giro)) # 4. Ordenar por mayor similitud y dividir los giros Top-K scoring_turns.sort(key=lambda x: x[0]reverso=Verdadero) top_semantic_turns = [turn for score, turn in scored_turns[:top_k]]# Ordenar los giros semánticos cronológicamente (opcional pero recomendado para LLM) top_semantic_turns.sort(key=lambda x: archived_turns.index(x)) # 5. Armar el contexto podado final pruned_context = top_semantic_turns + Recent_turn + [{"role": "user", "content": current_prompt}]devolver contexto_podado
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
definición contexto_poda(aviso_actual, historia, top_k=2):
# Si el historial de conversación es demasiado corto, simplemente lo devolvemos
si len(historia) <= 2:
devolver historia + [{“role”: “user”, “content”: current_prompt}]
# Extrayendo el turno más reciente (último par usuario/agente)
giro_reciente = historia[–2:]
# El resto del historial será elegible para la poda semántica.
turnos_archivados = historia[:–2]
# 2. Incrustar el mensaje actual
aviso_emb = modelo.codificar(aviso_actual)
# 3. Incrustar giros archivados y similitudes informáticas
giros_puntuados = []
para doblar en turnos_archivados:
turn_emb = modelo.codificar(doblar[“content”])
# Queremos similitud, entonces restamos la distancia del coseno a 1
semejanza = 1 – coseno(aviso_emb, turn_emb)
giros_puntuados.añadir((semejanza, doblar))
# 4. Ordenar por mayor similitud y dividir los turnos Top-K
giros_puntuados.clasificar(llave=lambda incógnita: incógnita[0], contrarrestar=Verdadero)
top_semantic_turns = [turn for score, turn in scored_turns[:top_k]]
# Ordenar los giros semánticos cronológicamente (opcional pero recomendado para LLM)
top_semantic_turns.clasificar(llave=lambda incógnita: turnos_archivados.índice(incógnita))
# 5. Armar el contexto podado final
contexto_podado = top_semantic_turns + giro_reciente + [{“role”: “user”, “content”: current_prompt}]
devolver contexto_podado
El código anterior se explica en gran medida por sí mismo. Divide la lógica en un caso base (cuando el historial de conversación aún es demasiado corto, en cuyo caso todo el historial se pasa como contexto) y un caso general, en el que el proceso de poda semántica real se lleva a cabo a través de varios pasos: incrustar giros pasados, calcular similitudes de cosenos con la incrustación actual, ordenarlos de mayor a menor similitud y seleccionar los K giros pasados principales. El mensaje actual, el giro más reciente y los giros pasados semánticamente similares de top-K finalmente se ensamblan en un contexto podado.
El siguiente ejemplo ilustra cómo obtener el contexto para un nuevo mensaje en el que el usuario regresa a aspectos relacionados con la eficiencia de las rutas de la flota:
# Ejecución de simulación current_request = “¿Podemos volver a las matemáticas de la flota?” contexto_optimizado = prune_context(current_request, chat_history) # Generar el resultado print(“— VENTANA DE CONTEXTO PRUNED —“) para mensaje en contexto_optimizado: print(f”{msg[‘role’].superior()}: {mensaje[‘content’]}”)
# Ejecución de simulación
solicitud_actual = “¿Podemos volver a las matemáticas de la flota?”
contexto_optimizado = contexto_poda(solicitud_actual, historial_chat)
# Salida del resultado
imprimir(“— VENTANA DE CONTEXTO ELIMINADA —“)
para mensaje en contexto_optimizado:
imprimir(F“{mensaje[‘role’].superior()}: {mensaje[‘content’]}”)
La ventana de contexto resultante producida por nuestra estrategia de poda se muestra a continuación:
— VENTANA DE CONTEXTO ELIMINADA — USUARIO: Necesito ayuda para calcular la eficiencia de las rutas de mi flota. AGENTE: La eficiencia de la ruta implica analizar la distancia, el tráfico y el peso de la carga. USUARIO: Gracias, eso tiene sentido. AGENTE: ¡De nada! Déjame saber si necesitas algo más. USUARIO: ¿Podemos volver a las matemáticas de la flota?
—– podado CONTEXTO VENTANA —–
USUARIO: I necesidad ayuda calculador ruta eficiencia para mi flota.
AGENTE: Ruta eficiencia implica analizando distancia, tráfico, y carga peso.
USUARIO: Gracias, eso marcas sentido.
AGENTE: Tú‘re bienvenido! Dejar a mí saber si tú necesidad cualquier cosa demás.
USUARIO: Poder nosotros ir atrás a el flota matemáticas?
Tenga en cuenta que utilizamos el valor predeterminado para k, es decir, top_k=2. El último turno, que siempre está incluido en nuestro pipeline definido, consta del par de mensajes:
USUARIO: Gracias, eso tiene sentido. AGENTE: ¡De nada! Déjame saber si necesitas algo más.
USUARIO: Gracias, eso marcas sentido.
AGENTE: Tú‘re bienvenido! Dejar a mí saber si tú necesidad cualquier cosa demás.
Entonces, ¿por qué solo aparece una interacción usuario-agente adicional antes de este turno, en lugar de dos? La razón es que la estrategia top-k no opera a nivel de turno completo (es decir, un par de mensajes), sino a nivel de mensaje individual. En este caso, los dos mensajes recuperados basados en la similitud forman las dos mitades de la misma interacción, pero es igualmente posible que los dos mensajes más relevantes sean mensajes de usuario, mensajes de agente o simplemente partes no consecutivas del historial de chat.
Concluyendo
Este artículo demostró cómo implementar una canalización de poda de contexto, basada en un historial de conversación de agente simulado, que se basa en la similitud semántica para seleccionar las partes más relevantes de una conversación como contexto para el mensaje actual. Esta es una técnica importante para agentes de larga duración, que ayuda a reducir el uso de memoria y los costos de cálculo al tiempo que mejora la eficiencia general.