MemGPT — LLMs com memória autoeditável para contexto infinito
(github.com/cpacker)- Letta é um projeto para criar IA baseada em memória avançada, capaz de aprender e se aprimorar ao longo do tempo
- As formas de uso se dividem entre o Letta Code, que executa agentes no terminal local, e a Letta API, que integra agentes a aplicações
- A CLI exige Node.js 18+; após instalar com
npm install -g @letta-ai/letta-code, executelettapara rodar, no computador local, um agente com memória - Os agentes do Letta Code ajudam com programação e tarefas que podem ser realizadas no computador, oferecem suporte a skills e subagents, e incluem skills/subagents pré-construídos para memória avançada e aprendizado contínuo
- Letta é agnóstico em relação ao modelo; o README recomenda Opus 4.5 e GPT-5.2 para melhor desempenho e aponta o model leaderboard como referência de classificação
- A Letta API serve para integrar agentes com estado a aplicações e oferece uma agents API completa, além de SDKs para Python e TypeScript
- Pacote TypeScript/Node.js:
@letta-ai/letta-client - Pacote Python:
letta-client - O exemplo mostra o fluxo de criar um agente e enviar mensagens usando
LETTA_API_KEYe uma chave da Letta API
- Pacote TypeScript/Node.js:
- O uso da Letta e dos serviços relacionados da Letta está condicionado à concordância com a política de privacidade e os termos de serviço
1 comentários
Comentários do Hacker News
Somos os autores do MemGPT. Se tiverem perguntas sobre a implementação, podemos responder
Se quiserem testar diretamente, dá para ver a edição de memória funcionando no bot do Discord no servidor do MemGPT Discord (https://discord.gg/9GEQrxmVyE). Ao conversar com o bot, é possível ver o MemGPT modificando a memória para atualizar perfis sobre o usuário e sobre si mesmo
Como tudo é open source, também dá para executar localmente a partir do código em https://github.com/cpacker/MemGPT. O repositório também tem um exemplo centrado em documentos em que você conversa com o MemGPT sobre a documentação da API do LlamaIndex
Fico curioso sobre por que processar tudo dentro de uma única janela de contexto. Quando experimentei algo parecido alguns meses atrás, paralelizei com vários agentes nas etapas de pré-processamento/pós-processamento
Por exemplo, a janela de contexto principal não sabia que memórias estavam sendo criadas ou buscadas; um pós-processador criava e armazenava memórias automaticamente a partir da conversa, e a conversa inteira também era armazenada em um banco de dados vetorial. O pré-processador injetava automaticamente memórias e contexto relevantes de acordo com a conversa, e até reescrevia o histórico para que, do ponto de vista da janela de contexto principal, aquela memória parecesse estar lá desde o início
Assim, dava para economizar bastante espaço na janela de contexto principal que seria gasto com prompts de sistema desnecessários etc.
Na abordagem implícita, o “LLM principal”, ou no caso do chat o “fio da conversa”, não sabe do gerenciamento de memória em segundo plano. Esse gerenciamento pode ficar a cargo de um “LLM de memória”, scripts baseados em regras, uma rede neural pequena etc.
A abordagem explícita é a do MemGPT: um único LLM faz tudo. Pesquisas anteriores sobre chat multissessão/de longa distância geralmente usam a abordagem implícita com um processo separado de criação de memória, e é provável que os armazenamentos de memória de chatbots de consumo também sejam majoritariamente implícitos
Acho que o gerenciamento explícito de memória exige seguir instruções complexas, então é difícil com a maioria dos LLMs públicos atuais. Estamos analisando formas de resolver isso, como fine-tuning de modelos abertos
O trade-off é exatamente o que você disse. Na abordagem implícita, não é preciso colocar todas as instruções de gerenciamento de memória no pré-prompt do LLM. A mensagem de sistema completa do MemGPT tem cerca de 1 mil tokens. Por outro lado, quando o LLM funciona corretamente, o gerenciamento explícito de memória deixa o sistema inteiro muito mais simples, eliminando o overhead de gerenciar vários modelos de LLM em threads paralelas
Vejo LLMs e processamento de linguagem natural como uma interface mais viável para dados estruturados. Quando dados são gerados em um contexto de negócios específico, extraímos os dados, criamos embeddings e construímos um banco de dados vetorial
No pós-processamento, depois que o modelo principal responde, um pós-processador gera e armazena memórias automaticamente com base na conversa. Assim, o contexto importante é salvo sem colocar esse trabalho sobre o modelo principal. Também executamos a lógica de negócios relevante como parte da solicitação e depois a realimentamos no sistema
No pré-processamento, antes de enviar uma nova entrada ao modelo principal, verificamos as memórias armazenadas, injetamos o contexto relevante e também executamos a lógica. É como se o pré-processador desse ao modelo principal uma “revisão” das conversas anteriores, preparando-o para responder de forma mais consistente e informativa
Sobre o que foi escrito nas limitações: se as variantes do Llama 2 70B, mesmo ajustadas para chamadas de função, criarem chamadas de função incorretas ou alucinarem funções fora do schema fornecido, dá para usar amostragem baseada em gramática
Pelo menos é possível garantir que as chamadas de função sejam sintaticamente corretas
[0] https://github.com/ggerganov/llama.cpp/tree/master/grammars
Por exemplo, mesmo um modelo grande ajustado com dados de chamada de função (https://huggingface.co/jondurbin/airoboros-l2-70b-2.1#agentf...) geralmente produzia JSON parseável, mas errava os argumentos ou os nomes das funções
Ao tentar gravar dados, em vez da chamada correta
working_context.appendespecificada no pré-prompt, ele produzia uma chamada totalmente não especificada comopersonal_diary.addSó pelo título, achei que fosse uma técnica de edição de memória que mudasse a memória neural do LLM, ou seja, a memória em nível de pesos durante a conversa, e não o contexto, algo como ROME [1]
Fiquei feliz em descobrir que, na verdade, era um trabalho bacana de RAG, e pretendo criar em breve minha própria versão do MemEditGPT
[1] https://arxiv.org/abs/2202.05262
Como você entendeu corretamente, o MemGPT não edita os pesos do LLM como o ROME. A “memória” de que falamos no MemGPT não é em nível de pesos, mas em nível de texto/tokens
O conceito central é dar ao LLM a capacidade de editar um scratchpad de memória de trabalho mantido dentro do contexto e de ler e escrever contexto externo por meio de funções. Um detalhe importante é que, para lidar com a limitação finita de contexto, as leituras sempre acontecem em páginas, ou seja, em chunks
O MemGPT consegue encadear funções em uma única entrada do usuário, repetindo várias leituras e escritas. Por isso, como no exemplo da documentação da API do LlamaIndex no README, ele consegue pesquisar uma grande base de documentos e reunir informações de várias fontes para retornar uma resposta
Por um tempo, suspeitei que a versão web do ChatGPT (chat.openai.com, não a API) funcionasse mais ou menos assim dentro de uma conversa. Mesmo em históricos de chat muito longos, eu tinha a sensação de que a qualidade caía gradualmente, em vez de ele esquecer tudo de repente
Claro que talvez haja muito mais pistas no contexto do que eu imaginava
De todo modo, acho bem provável que esse tipo de ideia se torne um recurso básico de todos os chatbots daqui para frente
Há perdas, e informações importantes acabam se perdendo, mas a performance pode degradar de forma relativamente suave. No MemGPT, além de todo o gerenciamento explícito de memória, também se usa resumo recursivo implícito
Se você fez qualquer tarefa minimamente interessante, a saída fica fortemente enviesada pelo prompt. Como há apenas uma única amostra — a saída/histórico anterior —, perde-se parte da informação em vez de uma distribuição de probabilidade suave, e como várias entradas podem mapear para a mesma saída, perde-se mais uma parte da informação
Mas, na prática, os prompts reais muitas vezes são a formulação mais fácil e curta que se consegue pensar para obter o resultado desejado. Então, se o LLM estima esse prompt, há uma boa chance de sua interpretação do contexto ausente estar razoavelmente correta. Ou seja, muita informação que parece ter desaparecido permanece na saída do LLM, e mesmo quando o contexto antigo é empurrado para fora, a quantidade de informação perdida de uma só vez não é tão grande
Há muitas técnicas triviais para implementar memória com perdas. Por exemplo, há o método de fazer average pooling dos tokens, como usado por sentence transformers. Não sei bem por que esse método é pouco usado para comprimir muito contexto em um prompt. Na prática, ele funciona como memória de médio prazo
Não lembro o nome, mas já existe uma linguagem de programação esotérica que executa comandos de forma instável. Se você projetar o programa com cuidado, dá para fazer uma sequência de comandos ser executada com confiabilidade de 99%, 99,9% etc.
Vinte anos depois, a mesma abordagem se popularizou sem ironia na área de infraestrutura com o nome de “engenharia do caos”
Atualização: acabei de lançar um chatbot persistente no Discord implementado sobre o MemGPT. Dá para experimentar aqui: https://discord.gg/9GEQrxmVyE
Com o código no GitHub, também dá para executar localmente a demo do chatbot e uma demo de bot de perguntas e respostas sobre documentação, em que você pode fazer perguntas ao MemGPT sobre a documentação da API
Acho que a maior limitação dos LLMs é a janela de contexto. Mesmo excelentes capacidades de raciocínio frequentemente esbarram no limite da janela de contexto em casos de uso reais
Concordo com a explicação de que resumo recursivo é uma forma simples de lidar com uma janela de contexto transbordando, mas que é inerentemente com perdas e acaba criando grandes buracos na memória do sistema
Só que o MemGPT também faz a mesma coisa e tem o mesmo problema. A diferença é apenas que, em vez de resumir tudo recursivamente, ele recupera seletivamente o histórico e o gera a cada solicitação. A ideia é legal
Mas sou cético. Essa abordagem se apoia fundamentalmente na suposição de que o contexto existente é um contexto resumível em baixa entropia e que a consulta depende apenas de parte do histórico
Isso pode valer para chats ou para “responder perguntas a partir de um enorme conjunto de documentos”. Mas em casos como geração de código, em que o contexto é denso em informações que não podem ser descartadas, como definições específicas de APIs, e em que é necessário um contexto amplo, como muitas definições de API, as duas suposições estão erradas
A estrutura e a implementação são interessantes, e a demo também é bacana, mas é uma pena que os artigos sobre resumo não reconheçam as limitações fundamentais dessa abordagem
Como você disse, a fila de conversa do MemGPT é gerenciada por resumo recursivo, como em trabalhos anteriores ou em muitas implementações de chatbots. Mas também existe uma área “fixa” de memória do LLM, legível e gravável, independente do resumo recursivo, que no artigo chamamos de “contexto de trabalho”
Portanto, o MemGPT pode acessar tanto o resumo recursivo gerado automaticamente quanto o contexto de trabalho que ele mantém ativamente atualizado
Esses dois também são separados do contexto externo do MemGPT, que é trazido para a fila de conversa por chamadas de função. Em todos os exemplos, a leitura do contexto externo não é comprimida e acontece página por página, sem resumo
Quando o resumo da fila é acionado, o MemGPT recebe uma notificação do sistema; então, se precisar preservar detalhes específicos da fila de conversa, pode escrevê-los no contexto de trabalho antes que sejam apagados ou resumidos
Nos exemplos de agentes conversacionais, o contexto de trabalho é usado para armazenar fatos essenciais sobre o usuário e o agente, mantendo uma conversa consistente. Como o LLM sempre vê o contexto de trabalho, não é preciso buscá-lo separadamente
Em perguntas e respostas sobre documentos, o contexto de trabalho pode ser usado para acompanhar a tarefa/pergunta atual e seu progresso. Em consultas complexas, isso ajuda o MemGPT a acompanhar detalhes como buscas anteriores e solicitações de páginas anteriores