Menu fechado

Como Manter a Memória Persistente em LLMs sem Fine-Tuning

LLMs

Introdução ao Desenvolvimento de Memória Persistente em LLMs

Os LLMs (Modelos de Linguagem de Grande Escala) são conhecidos por sua capacidade de gerar respostas inteligentes e personalizadas. No entanto, eles têm um problema fundamental: são stateless por padrão. Isso significa que eles apenas conhecem o que é enviado em cada prompt individual e não têm memória de longo prazo. Quando um usuário envia uma solicitação, o sistema esquece tudo o que foi discutido anteriormente, a menos que seja armazenado em algum lugar.

Este problema de memória afeta a experiência do usuário, tornando difícil para o sistema entender o contexto e a evolução da conversa ao longo do tempo. Além disso, isso pode levar a erros e respostas inconsistentes.

Para superar isso, é possível adicionar memória persistente aos LLMs sem fine-tuning, utilizando tecnologias como Node.js, OpenAI API, Redis e armazenamento de vetores. Isso permite que o sistema lembre-se de interações passadas, entenda objetivos a longo prazo e recupere contexto relevante.

Neste artigo, vamos explorar como criar um sistema de memória persistente para LLMs utilizando uma abordagem prática e produtiva.

Arquitetura de Memória Persistente para LLMs

Componentes da Arquitetura

A arquitetura de memória persistente para LLMs é composta por quatro componentes principais: Redis, um armazenamento de vetores, uma camada de construção de contexto e o LLM em si.

Redis

O Redis é usado para armazenar a memória estruturada dos usuários. Isso inclui informações como objetivos, preferências e histórico de conversas. O código abaixo mostra como criar um cliente Redis e armazenar e recuperar a memória de um usuário:


import { createClient } from "redis";

const redis = createClient({
  url: process.env.REDIS_URL
});

await redis.connect();

export async function getUserMemory(userId) {
  const data = await redis.get(`user:${userId}:memory`);
  return data ? JSON.parse(data) : {};
}

export async function updateUserMemory(userId, memory) {
  await redis.set(`user:${userId}:memory`, JSON.stringify(memory));
}

Armazenamento de Vetores

O armazenamento de vetores é usado para armazenar as representações semânticas das conversas e outros dados relevantes. Isso permite que o sistema recupere informações relevantes e construa contexto a partir delas. O código abaixo mostra como criar um armazenamento de vetores e armazenar e recuperar as representações semânticas de um usuário:


import OpenAI from "openai";

const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });

export async function embedText(text) {
  const response = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: text
  });
  return response.data[0].embedding;
}

export async function storeSemanticMemory(userId, memory) {
  await vectorDB.put(`user:${userId}:semantic-memory`, JSON.stringify(memory));
}

export async function getSemanticMemory(userId) {
  const data = await vectorDB.get(`user:${userId}:semantic-memory`);
  return data ? JSON.parse(data) : {};
}

Camada de Construção de Contexto

A camada de construção de contexto é responsável por combinar as informações armazenadas em Redis e no armazenamento de vetores para criar um contexto relevante para o LLM. O código abaixo mostra como criar uma camada de construção de contexto e construir um contexto a partir das informações armazenadas:


function buildPrompt(userMemory, semanticMemories, userInput) {
  return `
    Você é um assistente de inteligência artificial específico para domínio.
    Perfil do Usuário:
    ${JSON.stringify(userMemory, null, 2)}
    Contexto Relevante:
    ${semanticMemories.join("\n")}
    Pergunta Atual:
    ${userInput}
    Forneça uma resposta consistente e consciente do contexto.
  `;
}

LLM

O LLM é o coração da arquitetura, responsável por gerar respostas a partir do contexto construído. O código abaixo mostra como chamar o LLM e gerar uma resposta a partir do contexto construído:


const completion = await openai.chat.completions.create({
  model: "gpt-4o-mini",
  messages: [
    { role: "system", content: systemPrompt }
  ]
});

Esses componentes trabalham juntos para fornecer memória persistente ao LLM, permitindo que ele gere respostas mais relevantes e consistentes.

Implementação Passo a Passo da Memória Persistente

Passo 1: Configuração do Redis

Para implementar a memória persistente, precisamos configurar o Redis como o armazenamento estruturado de memória. Isso envolve instalar as dependências necessárias e configurar o Redis para armazenar os dados.


// memory.js
import { createClient } from "redis";
const redis = createClient({
  url: process.env.REDIS_URL
});
await redis.connect();
export async function getUserMemory(userId) {
  const data = await redis.get(`user:${userId}:memory`);
  return data ? JSON.parse(data) : {};
}
export async function updateUserMemory(userId, memory) {
  await redis.set(`user:${userId}:memory`, JSON.stringify(memory));
}

Passo 2: Criação de um Armazenamento de Vetores

Além do Redis, precisamos criar um armazenamento de vetores para armazenar as informações semânticas. Isso envolve escolher uma ferramenta de armazenamento de vetores, como Pinecone, Weaviate ou Supabase, e configurá-la para armazenar os dados.


// vector-store.js
import OpenAI from "openai";
const openai = new OpenAI({ apiKey: process.env.OPENAI_API_KEY });
export async function embedText(text) {
  const response = await openai.embeddings.create({
    model: "text-embedding-3-small",
    input: text
  });
  return response.data[0].embedding;
}

Passo 3: Construção de uma Camada de Contexto

Agora que temos o Redis configurado e o armazenamento de vetores criado, precisamos construir uma camada de contexto para combinar as informações estruturadas e semânticas. Isso envolve criar uma função que combine as informações e construa um prompt de sistema para o modelo LLM.


// context-builder.js
function buildPrompt(userMemory, semanticMemories, userInput) {
  return `
  You are a domain-specific AI assistant.
  User Profile:
  ${JSON.stringify(userMemory, null, 2)}
  Relevant Past Context:
  ${semanticMemories.join("\n")}
  Current Question:
  ${userInput}
  Provide a consistent and context-aware response.
  `;
}

Passo 4: Atualização da Memória de Forma Inteligente

Finalmente, precisamos atualizar a memória de forma inteligente após gerar uma resposta. Isso envolve criar uma função que atualize a memória com as informações novas e significativas, sem armazenar tudo.


// memory-updater.js
function updateMemoryFromConversation(memory, userInput, response) {
  if (userInput.includes("pivot")) {
    memory.summary = "User pivoted business direction.";
  }
  return memory;
}

Desafios e Soluções em Sistemas Reais

Deriva de Memória

A deriva de memória ocorre quando os objetivos antigos permanecem para sempre, enquanto os usuários mudam de direção. Isso pode acontecer porque o sistema não adapta-se ao tempo. Para resolver isso, podemos usar pesos temporais para dar mais importância às informações mais recentes. Além disso, podemos realizar um resumo periódico para eliminar informações obsoletas.




Sobrecarga de Contexto

A sobrecarga de contexto ocorre quando o sistema recupera muito contexto relevante, aumentando o custo de tokens e reduzindo a precisão. Para evitar isso, podemos limitar a recuperação semântica e usar camadas de resumo para simplificar as informações.

Colapso de Identidade

O colapso de identidade ocorre quando o sistema prompt muda muito frequentemente, resultando em respostas inconsistentes. Para evitar isso, podemos manter um sistema prompt de identidade estável e tratar a memória como uma extensão, não como uma substituição.

Soluções

Para resolver esses desafios, podemos adotar as seguintes soluções:

* Utilizar pesos temporais para dar mais importância às informações mais recentes.
* Realizar um resumo periódico para eliminar informações obsoletas.
* Limitar a recuperação semântica para evitar sobrecarga de contexto.
* Usar camadas de resumo para simplificar as informações.
* Manter um sistema prompt de identidade estável.


// Exemplo de uso de pesos temporais
function updateMemory(memory, newInfo, timestamp) {
  // Dê mais importância às informações mais recentes
  memory = Object.assign(memory, newInfo);
  memory.timestamp = timestamp;
  return memory;
}

// Exemplo de resumo periódico
function summarizeMemory(memory, threshold) {
  // Elimine informações obsoletas
  const recentInfo = Object.keys(memory).filter(key => memory[key].timestamp > threshold);
  return recentInfo.reduce((acc, key) => ({ ...acc, [key]: memory[key] }), {});
}

// Exemplo de limitação da recuperação semântica
function retrieveSemanticMemory(memory, limit) {
  // Recupere apenas as informações mais relevantes
  const relevantInfo = Object.keys(memory).slice(0, limit);
  return relevantInfo.map(key => ({ type: key, content: memory[key] }));
}

// Exemplo de uso de camadas de resumo
function summarizeSemanticMemory(memory, threshold) {
  // Simplifique as informações
  const summarizedMemory = memory.map(item => ({
    type: item.type,
    content: item.content.substring(0, threshold)
  }));
  return summarizedMemory;
}

Conclusão e Próximos Passos 🚀

A memória persistente é crucial para a construção de sistemas de LLMs confiáveis e eficazes. Ao implementar uma arquitetura de memória persistente, os desenvolvedores podem criar sistemas que lembram das interações passadas, entendem os objetivos a longo prazo dos usuários e recuperam o contexto relevante. Sem fine-tuning, é possível alcançar a memória persistente com uma abordagem prática e produtiva.

Importância da Memória Persistente

A memória persistente permite que os sistemas de LLMs:

* Lembrem das interações passadas dos usuários
* Entendam os objetivos a longo prazo dos usuários
* Recuperem o contexto relevante para fornecer respostas mais precisas

Implementação da Memória Persistente

Para implementar a memória persistente, os desenvolvedores podem seguir os passos descritos anteriormente:

* Armazenar a memória estruturada no Redis
* Adicionar memória semântica com um armazenamento de vetores
* Construir um contexto assembler para combinar a memória estruturada e semântica
* Atualizar a memória inteligentemente após gerar uma resposta

Desafios e Soluções

Os desenvolvedores podem enfrentar desafios como a deriva da memória, a sobrecarga de contexto e a colapso de identidade. Para superar esses desafios, é possível usar técnicas como a ponderação temporal da memória, a resumo periódico e a manutenção de um sistema de identidade estável.

Próximos Passos

Para os desenvolvedores que desejam implementar a memória persistente em seus sistemas de LLMs, é importante:

* Compartilhar suas experiências e desafios ao implementar a memória persistente
* Discutir as melhores práticas e soluções para superar os desafios
* Continuar a aprender e melhorar a implementação da memória persistente


// Exemplo de código para atualizar a memória inteligentemente
function updateMemoryFromConversation(memory, userInput, response) {
  if (userInput.includes("pivot")) {
    memory.summary = "User pivoted business direction.";
  }
  return memory;
}

Fonte de Referência: dev.to.
Curadoria e Adaptação: Redação Yassutaro Developers.



Redação YTI&W-News

Redação Developers | Yassutaro TI & Web

Notícias do universo do Desenvolvimento Web, dicas e tutoriais para Webmasters.

Deixe um comentário

O seu endereço de e-mail não será publicado. Campos obrigatórios são marcados com *

Publicado em:Desenvolvimento de Software,Desenvolvimento Web
Fale Conosco
×

Inscreva-se em nossa Newsletter!


Receba nossos lançamentos e artigos em primera mão!