Engenharia

Seus fatos expiram. Seu vector store não sabe quais.

Um vector store recupera por similaridade, não por tempo, então ele entrega ao seu agent um preço, um responsável ou uma cláusula de contrato que parou de ser verdade meses atrás; memória de agent com temporal knowledge graph rastreia quando cada fato se tornou verdadeiro e quando parou de valer, para que o retrieval responda o que é verdade agora em vez do que já foi verdade um dia.

ASR

Apollo Space Research

ApolloSpace AI

· 14 min de leitura

Um prospect pergunta ao seu agent de vendas o preço de tabela de um componente. O agent responde na hora, soa seguro. O número é de janeiro. Estamos em julho. O time de compras repreçou a peça em junho, o documento novo está no mesmo index, embedado com o mesmo cuidado que o antigo, e o agent ainda escolhe o preço errado, porque nada num vector store disse a ele que a resposta de janeiro tinha expirado.

O upgrade de um vector store para memória de agent com temporal knowledge graph é um relógio: cada fact-edge sabe quando se tornou verdade e quando parou, o que é a diferença entre memória e uma pilha de fatos que foram verdade uma vez. Esse é um problema mais estreito e mais difícil do que a maioria dos times pensa que está resolvendo quando adiciona um vector database e chama isso de memória. Este post defende uma alegação: a memória de um agent não está pronta quando ele consegue recuperar um fato, está pronta quando ele consegue recuperar o fato que ainda é verdade, e isso exige representar tempo como uma parte de primeira classe dos dados, não como um efeito colateral de sorte de quando um documento por acaso foi escrito.

O conserto ingênuo: embede uma vez, recupere para sempre

A arquitetura ingênua está em todo lugar porque ela é genuinamente boa no problema para o qual foi construída. Você divide seus documentos em chunks, embeda cada chunk, e na hora da query encontra os chunks cujos embeddings estão mais próximos da pergunta. É rápido, é barato, e para conhecimento estático, um manual de produto, uma definição, uma política que nunca muda, funciona quase perfeitamente.

Ela falha no momento em que um fato tem um tempo de vida mais curto que o documento que o declara. Imagine um distribuidor cujo preço de tabela para um componente era $40 durante a primeira metade do ano e $52 depois de uma reprecificação em junho. Os dois fatos vivem no index. Os dois são bem formados, bem embedados, semanticamente sobre “preço de tabela para este componente”. A similaridade de cosseno não tem opinião sobre qual é o atual, porque similaridade é uma medida de proximidade temática, não de verdade ao longo do tempo. Se o documento de janeiro por acaso for frasiado um pouco mais perto de como a pergunta foi feita, ou foi embedado com um modelo que o pontua marginalmente mais alto, ele vence, e o agent o declara com a mesma confiança que usaria para o número correto.

Um vector store não tem eixo de tempo nenhum, o que é uma falha distinta da que um índice ficando desatualizado descreve. Aquele post é sobre representation drift, um embedding model atualiza e você re-embeda numa agenda para manter o index atualizado consigo mesmo. Este post é sobre algo que o trabalho de re-embed não consegue consertar: o fato em si expirou no mundo enquanto o texto que o descreve, e seu embedding, permaneceram idênticos byte a byte. Você pode re-embedar a nota de preço de janeiro toda noite durante um ano e ela vai continuar sendo um vetor perfeito, recém-computado, para um número que parou de ser verdade em junho.

Re-embedar conserta o index. Não conserta o fato.

Essa distinção vale a pena parar para pensar, porque é a que os times pulam. O instinto, assim que você percebe o retrieval retornando respostas desatualizadas, é tratar isso como um problema de freshness e recorrer ao conserto de freshness: re-indexar mais vezes, adicionar um boost de recência à função de ranking, decair o score dos chunks antigos ao longo do tempo.

O boost de recência ajuda em exatamente um caso, quando mais novo é de forma confiável mais verdadeiro, o que é comum para um feed de notícias e falso para fatos estruturados de negócio. Um contrato renovado a uma taxa pior não torna os termos do contrato antigo menos reais, eles foram reais, por uma janela específica, e um agent negociando uma renovação precisa tanto dos termos antigos quanto da data exata em que pararam de valer. Lendo isso junto com o contrato que renovou a uma taxa pior: um sistema que só sabe “o mais novo vence” não consegue responder “o que combinamos antes disso acontecer”, frequentemente a pergunta de maior risco na sala.

O que você realmente precisa não são vetores mais frescos, é um registro de validade por fato, algo que diga que esta alegação específica, preço de tabela igual a $52, valeu a partir de 14 de junho, substituindo uma alegação anterior que valeu de 3 de janeiro a 14 de junho. Isso não é uma propriedade de um documento. É uma propriedade de um edge entre duas entidades: o componente, e seu preço.

Uma comparação de duas faixas. A faixa de cima, rotulada "o mundo", mostra uma linha do tempo com uma barra de fato para preço de tabela igual a quarenta dólares indo de janeiro a junho, depois uma segunda barra para preço de tabela igual a cinquenta e dois dólares começando em junho e continuando até hoje, com um ponto de substituição claro marcado entre elas. A faixa de baixo, rotulada "o vector store", mostra um único chunk embedado para o preço de quarenta dólares sendo casado por similaridade de cosseno com uma query recebida e devolvido como a resposta, com uma linha tracejada mostrando que a mudança de preço de junho nunca alcança o caminho de retrieval porque o store não tem eixo de tempo para representá-la.

A mecânica da memória de agent com temporal knowledge graph

Uma vez que você aceita que validade pertence ao fato, não ao documento, a estrutura natural é um grafo. Entidades viram nodes, um cliente, um componente, um contrato, um responsável pela conta. Fatos viram edges entre elas, e cada edge carrega um par de timestamps marcando a janela em que era verdade, comumente escrito como valid_from e valid_until.

A versão ingênua disso em grafo ainda falha de um jeito mais sutil: se um fato novo simplesmente sobrescreve o edge antigo, você ganha uma garantia de ordenação mas perde histórico. Você não consegue mais responder “no que o sistema acreditava em 3 de março”, e se a própria correção acabar errada, não há para onde reverter.

O conserto é um modelo bi-temporal que nunca deleta, só marca. Isso não é uma invenção nova, modelagem de dados bi-temporal é prática padrão na teoria de bancos de dados desde o trabalho de Snodgrass sobre bancos de dados temporais nos anos 1990, rastreando tanto quando algo era verdade no mundo quanto quando o sistema registrou isso. A camada de memória da ApolloSpace AI é construída para que essa disciplina rode por baixo de cada agent, não só de um deles: é o System of Record que cada agent lê antes de responder e no qual escreve de volta depois de agir, então o mesmo par valid_from/valid_until que modela o preço do componente também modela a transferência de um responsável pela conta, o termo de renovação de um contrato, o plano de um cliente, qualquer coisa que um agent de outra forma poderia assumir que ainda é verdade porque ninguém disse o contrário.

O engine Graphiti, da Zep, descrito no paper técnico da Zep sobre knowledge graphs com consciência temporal, foi uma prova precoce e influente de que essa disciplina exata pertence especificamente dentro da memória de agent, não só dentro de um livro-texto de banco de dados; vale creditar aqui o próprio nome t_valid/t_invalid que a Zep usa para o mesmo par de timestamps, como a referência da qual este design aprendeu.

O upgrade de um vector store para memória de agent com temporal knowledge graph é um relógio: cada fact-edge sabe quando se tornou verdade e quando parou, o que é a diferença entre memória e uma pilha de fatos que foram verdade uma vez. Isso é o motor inteiro descrito acima, reafirmado: não um index maior, uma janela de validade em cada edge, correndo por baixo de cada agent que a ApolloSpace AI roda.

Um fato que não consegue dizer quando parou de ser verdade não é um fato em que seu agent pode confiar, é um chute vestindo um timestamp que ele não tem.

É aqui também que vale creditar o vocabulário do campo em vez de reivindicá-lo. Graph-based retrieval augmented generation, GraphRAG, é o termo do Microsoft Research para fundamentar geração num knowledge graph em vez de chunks de texto plano; o enquadramento de GraphRAG versus busca vetorial neste blog cobre por que essa disputa costuma ser mal colocada como ou-um-ou-outro. O que a ApolloSpace AI possui é a camada acima das duas ideias: o grafo bi-temporal como System of Record, o agent runtime raciocinando sobre ele como System of Intelligence, e a escrita que ele faz de volta depois de agir, uma cotação enviada, uma renovação escalada, um responsável atualizado, como o System of Action fechando o próprio loop, para que os três tipos de memória que um agent precisa se construam sobre um relógio só, em vez de três fornecedores confiando uns nos outros para concordar.

Retrieval que responde “o que é verdade agora”, não “o que já foi verdade”

O upgrade de um vector store para memória de agent com temporal knowledge graph é um relógio, e o retrieval é o momento em que esse relógio realmente é checado. O retorno aparece na hora da query. Uma query ingênua em grafo percorre cada edge conectado a uma entidade e entrega ao agent todos eles, atual e substituído juntos, e pede ao modelo para descobrir qual se aplica, o que é pedir a um language model para fazer uma contabilidade para a qual ele nunca recebeu os timestamps necessários para fazer com confiabilidade.

Esta é a query que o agent runtime da ApolloSpace AI é construído para rodar em vez de uma busca por similaridade: carimba a query com um ponto no tempo, quase sempre “agora”, e filtra na camada de banco de dados por edges cuja janela de validade contém esse instante, antes de qualquer modelo ver uma resposta candidata. Pegue o agent de vendas da cena de abertura: ele não puxa todo edge de preço e espera que o prompt empurre o modelo para o mais recente. A camada de System of Intelligence emite uma query as-of-now contra o System of Record, recebe de volta o único edge de preço atualmente válido, e a camada de System of Action responde com ele, depois escreve de volta o fato de que cotou aquilo, e quando, como seu próprio edge novo, fechando o loop em vez de deixar a cotação viver só num transcript de chat. O agent nunca vê o preço substituído, e ele é excluído por uma cláusula WHERE sobre um timestamp, não por uma heurística de ranking que poderia adivinhar errado.

Essa é a diferença entre um agent a quem foi contada a verdade e um a quem foi entregue tudo e pedido para adivinhar qual parte ainda era verdade. A evidência pública mais clara de por que vale a pena construir uma camada de memória desse jeito, não uma prova dos próprios números da ApolloSpace AI mas o caso mais forte registrado para o mecanismo, vem dos benchmarks publicados pela Zep exatamente sobre esse tipo de filtragem por ponto no tempo: no benchmark Deep Memory Retrieval, a memória em grafo com consciência temporal pontuou 94,8 por cento de acurácia com GPT-4-turbo contra um baseline de 93,4 por cento do MemGPT; no benchmark mais difícil LongMemEval, a mesma abordagem respondeu com 71,2 por cento com GPT-4o contra um baseline de full-context de 60,2 por cento, enquanto cortava a latência de resposta de cerca de 28,9 segundos para 2,58 segundos e a context window de cerca de 115.000 tokens para 1.600; o próprio retrieval do Graphiti, rodado sem uma chamada de LLM, reporta uma latência P95 de 300 milissegundos. Filtrar por validade não é só mais correto, é mais barato e mais rápido ao mesmo tempo, o mesmo formato de vitória que a camada de memória da ApolloSpace AI é construída para produzir para cada agent que a lê, não só o do benchmark.

Um diagrama de fact-edge. Um node de entidade para uma empresa se conecta a dois nodes de preço mutuamente exclusivos: um edge tracejado e acinzentado rotulado preço de tabela, válido de 3 de janeiro a 14 de junho, agora marcado como inválido, e um edge sólido e destacado rotulado preço de tabela, válido a partir de 14 de junho sem data de término, marcado como atualmente válido. Um node de query rotulado "a partir de: hoje" envia uma seta sólida selecionando só o edge atualmente válido, e uma seta tracejada para o edge substituído mostrando que ele é visto, considerado, e explicitamente pulado em vez de deletado.

O que isso custa, honestamente

Nada disso é de graça, e fingir o contrário é o jeito mais rápido de perder a confiança de quem está lendo. Um grafo temporal exige mais da ingestão do que um pipeline de embedar-e-esquecer. Cada fato novo tem que ser checado contra edges existentes em busca de contradição antes de poder ser escrito, o que no design do Graphiti significa uma passada de busca semântica e de grafo, e frequentemente uma chamada de modelo, em cada escrita, não só em cada leitura. Isso é write-amplification: pagar um custo na hora da ingestão para não pagar um custo muito maior e silencioso na hora da resposta.

Também tem um custo de lock-in. Uma vez que a memória vive em entidades e edges com timestamp em vez de embeddings planos, você não consegue trocar por qualquer vector database que tenha o melhor benchmark neste trimestre, o grafo é o substrato agora. E um grafo é mais visível operacionalmente que um vector store, contradições têm que ser resolvidas por alguma política, a maioria dos sistemas assume por padrão confiar na evidência mais nova, e essa política precisa estar certa, porque ela está fazendo um trabalho de julgamento que um score de similaridade costumava fazer implicitamente e mal.

A troca vale a pena mesmo assim, porque o custo alternativo é invisível até ficar caro: um agent que cita com confiança um fato morto para um cliente real, sem registro de quando ele ficou errado, um modo de falha que pertence ao design da camada de memória desde o dia um, não como um patch aparafusado depois que a primeira cotação errada chega a alguém que importava.

A virada

Por baixo dos timestamps e dos schemas de edge, isso é sobre algo muito mais humano que design de banco de dados. Todo time tem um colega cuja informação é tecnicamente precisa e praticamente perigosa, aquele que ainda cita o org chart do ano passado, os termos do fornecedor antes da renegociação. Eles não estão mentindo. Só nunca atualizaram o relógio na cabeça, e todo mundo aprendeu, silenciosamente, a checar de novo qualquer coisa que eles dizem antes de repetir.

Um agent com um vector store e nenhuma camada temporal é esse colega, em escala, respondendo com confiança total toda vez. O conserto nunca foi fazer ele soar menos seguro. O upgrade de um vector store para memória de agent com temporal knowledge graph é um relógio, e esse é o mesmo upgrade de que o colega excessivamente confiante também precisa: não um jeito de soar menos seguro, mas um jeito de saber, estruturalmente, que parte do que ele lembra tem data de validade, do mesmo jeito que uma pessoa que lê notícias aprende quais das suas crenças precisam ser revisitadas e quais ainda valem.


Esse é o padrão que vale a pena construir a camada de memória de um agent para alcançar: fatos como entidades e edges com timestamp, não embeddings planos, para que um agent responda com o que é verdade hoje e ainda consiga mostrar o trabalho para o que já foi verdade e quando isso mudou. É uma extensão da mesma ideia por trás de dar aos agents contexto completo da sua operação — contexto só é útil com um relógio anexado. Acerte isso, e o prospect perguntando sobre aquele componente ainda recebe uma resposta instantânea, só que agora com o número de junho, com a data de julho anexada ao porquê.

A ApolloSpace AI cuida da operação repetitiva da sua empresa pro seu time não precisar.

Entre na lista de espera: acesso antecipado, preço de usuário fundador e um lugar na primeira fila enquanto a gente constrói.

Entrar na lista de espera