Observação
O acesso a essa página exige autorização. Você pode tentar entrar ou alterar diretórios.
O acesso a essa página exige autorização. Você pode tentar alterar os diretórios.
Os LLM (modelos de linguagem grande) são incríveis com a capacidade de gerar preenchimentos ou respostas de texto, com base em prompts do usuário. Assim como acontece com qualquer serviço, eles têm um custo de computação específico. Nos LLMs, isso normalmente é expresso em tokens.
Um cache semântico fornece uma maneira de usar prompts de usuário anteriores e preenchimentos de LLM para lidar com prompts de usuário semelhantes usando a busca de similaridade em vetores. Um cache semântico pode reduzir a latência e economizar os custos em seus aplicativos GenAI, pois fazer chamadas a LLMs geralmente é o serviço mais caro e de latência mais alta nesses aplicativos.
Em um cenário em que um LLM processa um prompt simples, esse custo computacional é baixo. No entanto, os LLMs não retêm nenhum contexto entre prompts e preenchimentos. É necessário enviar parte do histórico de chat para o LLM ao enviar o prompt mais recente para ser processado a fim de fornecer contexto a ele. Esse mecanismo geralmente é conhecido como uma janela de contexto e é uma janela deslizante de prompts e preenchimentos entre um usuário e um LLM. Esse texto extra aumenta o custo de computação ou os tokens necessários para processar a solicitação. Além disso, à medida que a quantidade de texto aumenta, também aumenta a latência para a geração do preenchimento.
No padrão chamado Geração de Recuperação Ampliada ou Padrão RAG, para abreviar, os dados de uma fonte externa, como um banco de dados, são enviados com o prompt do usuário e a janela de contexto para ampliar ou servir de base para o LLM. Os dados de fontes externas fornecem informações extras para gerar uma conclusão. O tamanho das cargas do padrão RAG para um LLM costuma ficar bastante grande. Não é incomum consumir milhares de tokens e aguardar muitos segundos para gerar uma conclusão. Em um mundo onde o milissegundos é importante, esperar muitos segundos geralmente é uma experiência inaceitável para o usuário. O custo também pode ficar alto em grandes volumes.
A solução normalmente usada para lidar com solicitações com altos custos computacionais e latência é empregar um cache. Esse cenário não é diferente, no entanto, há diferenças na forma como um cache semântico funciona e como ele é implementado.
Como funciona um cache semântico
Um cache tradicional que usa uma correspondência de igualdade de cadeia de caracteres para executar uma pesquisa de cache nas chaves de cache. Um cache semântico usa uma consulta de vetor nas chaves de cache, que são armazenadas como vetores. Para executar uma consulta de busca em vetores para a pesquisa do cache, o texto é convertido em uma inserção de vetor e, em seguida, usado para localizar vetores mais semelhantes nas chaves do cache.
O uso de uma consulta vetor tem algumas vantagens em relação a uma pesquisa de valor de chave. Um cache tradicional normalmente retorna apenas um único resultado para uma ocorrência no cache. Como um cache semântico usa uma consulta, ele pode, opcionalmente, retornar vários resultados ao usuário. Fornecer mais itens para escolher pode ajudar a reduzir ainda mais os custos de computação. Isso pode manter o tamanho do cache menor reduzindo o número de itens, que precisam ser gerados pelo LLM devido a erros de cache.
Pontuação de similaridade
Todas as consultas de vetor retornam o que é chamado de pontuação de similaridade que representa o quão próximos os vetores estão entre si no alto espaço dimensional. Os valores variam de 0 (sem similaridade) a 1 (correspondência exata).
Em uma consulta de vetor, a pontuação de similaridade para os resultados retornados representa o quão semelhantes são as palavras ou a intenção dos usuários em relação ao que foi passado na cláusula WHERE. Como uma consulta pode retornar vários resultados, os resultados podem ser classificados dos resultados de cache mais prováveis aos menos prováveis para um usuário escolher.
A pontuação de similaridade é usada como um filtro para uma consulta de vetor usar para limitar os resultados retornados aos itens com maior probabilidade de corresponder à intenção dos usuários. Na prática, definir o valor de pontuação de similaridade para uma consulta de vetor pode exigir um pouco de tentativa e erro. Se for muito alto, o cache preencherá rapidamente com várias respostas para perguntas semelhantes devido a erros repetidos de cache. Se for muito baixo, o cache retornará muitas respostas irrelevantes que não correspondem à intenção do usuário.
Janela de contexto
Modelos de linguagem grandes não mantêm o contexto entre solicitações. Para ter uma conversa com um LLM, você precisa manter uma janela de contexto ou histórico de chat e passá-la para o LLM a cada solicitação para que ele possa fornecer uma resposta contextualmente pertinente.
Para que um cache semântico seja eficaz, ele também precisa ter esse contexto. Em outras palavras, um cache semântico não deve usar apenas o texto de prompts individuais como chaves, ele também deve usar parte dos prompts no histórico de chat. Isso garante que o que é retornado do cache também esteja contextualmente correto, assim como seria se fosse gerado por um LLM. Se um cache não tivesse o contexto do histórico do chat, os usuários receberiam respostas inesperadas e provavelmente inaceitáveis.
Aqui está um simples exercício mental para explicar por que isso é assim. Se você perguntar pela primeira vez a um LLM: "Qual é o maior lago da América do Norte?", ele responde com "Lago Superior" com alguns fatos e números e, em seguida, armazena em cache o prompt de usuário vetorizado e o texto da resposta.
Se você perguntar: "Qual é o segundo maior?", o LLM receberá a janela de contexto do prompt anterior e a resposta com a pergunta de acompanhamento e responderá corretamente, "Lago Huron". Em seguida, armazenará em cache o segundo prompt, "Qual é o segundo maior?" e a resposta gerada, "Lago Huron".
Mais tarde, outro usuário em uma sessão diferente pergunta: "Qual é o maior estádio da América do Norte?", o LLM responde com"Estádio Michigan" com alguns fatos e números. Se esse usuário perguntar: "Qual é o segundo maior?", o cache encontrará uma correspondência exata para esse prompt e retornará, "Lago Huron", que está incorreto.
Por esse motivo, um cache semântico deve operar em uma janela de contexto. A janela de contexto já fornece as informações necessárias para um LLM gerar respostas relevantes. Isso o torna uma escolha lógica de como o cache também deve funcionar.
Implementar isso requer primeiro a vetorização da matriz de prompts da janela de contexto e do último prompt. Os vetores são usados primeiro na cláusula WHERE na consulta do vetor na chave de cache. O que é retornado é a resposta da mesma sequência de perguntas feitas anteriormente por outro usuário. À medida que a janela de contexto continuamente desliza para frente na conversa, qualquer sequência de prompts com alta similaridade é retornada pelo cache em vez de ser regenerada pelo LLM.
Manutenção
Assim como acontece com qualquer cache, é possível que ele cresça e alcance um tamanho enorme. Manter um cache semântico requer o mesmo tipo de manutenção. Há várias maneiras de manter seu tamanho, incluindo o uso de uma TTL (vida útil) para itens armazenados em cache, limitar o tamanho máximo ou limitar o número de itens no cache.
O uso de uma pontuação de similaridade também fornece um mecanismo para manter o cache leve. Se a pontuação de similaridade na cláusula WHERE para a consulta de vetor em um cache for menor, poderá aumentar o número de ocorrências de cache, reduzindo a necessidade de gerar e armazenar em cache as respostas para prompts de usuário semelhantes. No entanto, isso tem um custo do potencial de retorno de resultados menos relevantes.
Outras técnicas possíveis que usam uma TTL, mas também preservam ocorrências de cache frequentes, podem incluir manter uma pontuação de ocorrência no cache em itens e implementar um mecanismo para remover o cache baseado na pontuação de ocorrência no cache. Isso pode ser implementado usando uma operação de patch para incrementar uma propriedade de contagem de ocorrências em um item de cache. Em seguida, substituir o TTL nesse item para preservá-lo no cache se ele alcançar algum limite predeterminado de ocorrências de cache.
Também pode haver outras considerações sobre como manter um cache além da eficiência. É amplamente compreendido que o histórico de chats dos usuários pode permitir que os desenvolvedores ajustem aplicativos que usam um LLM para gerar respostas. O histórico de chats também fornece dados valiosos sobre as interações dos usuários com esses tipos de aplicativos e pode gerar insights significativos sobre sentimentos e comportamentos. O mesmo vale também para o uso do cache.
Pode ser desejável manter todo o conteúdo de um cache e usá-lo para análise posterior, e ainda garantir que os usuários vejam apenas as entradas armazenadas em cache mais recentes. Em cenários como esse, uma técnica possível pode usar um filtro extra na cláusula WHERE para a consulta de vetor no cache que filtra os itens armazenados em cache mais recentes ou os classifica com base na atualização. Por exemplo, WHERE c.lastUpdated > @someDate ORDER BY c.lastUpdated DESC
Implementar um cache semântico com o Azure Cosmos DB
Há vários exemplos que você pode usar para entender como criar seu cache semântico usando o Azure Cosmos DB.
Este exemplo de C# demonstra muitos dos conceitos necessários para criar seus próprios aplicativos do Copilot no Azure usando o Azure Cosmos DB for NoSQL. Este exemplo também vem com um Laboratório prático que orienta os usuários passo a passo nesses conceitos, incluindo como implementar um cache semântico.
Este exemplo de C# demonstra muitos dos conceitos necessários para criar seus aplicativos do Copilot no Azure usando o Azure Cosmos DB for MongoDB. Este exemplo também vem com um Laboratório prático que orienta os usuários passo a passo nesses conceitos, incluindo como implementar um cache semântico.