Pinecone.net é uma biblioteca C# totalmente de pleno direito para o banco de dados vetorial Pinecone.
Esta é uma biblioteca comunitária que fornece suporte de primeira classe para Pinecone em C# e F#.
dotnet add package Pinecone.NET ou Install-Package Pinecone.NET
Trabalhando com índices
using Pinecone ;
// Initialize the client with your API key
using var pinecone = new PineconeClient ( "your-api-key" ) ;
// List all indexes
var indexes = await pinecone . ListIndexes ( ) ;
// Create a new index if it doesn't exist
var indexName = "myIndex" ;
if ( ! indexes . Contains ( indexName ) )
{
await pinecone . CreateServerlessIndex ( indexName , 1536 , Metric . Cosine , "aws" , "us-east-1" ) ;
}
// Get the Pinecone index by name (uses REST by default).
// The index client is thread-safe, consider caching and/or
// injecting it as a singleton into your DI container.
using var index = await pinecone . GetIndex ( indexName ) ;
// Configure an index
await pinecone . ConfigureIndex ( indexName , replicas : 2 , podType : "p2" ) ;
// Delete an index
await pinecone . DeleteIndex ( indexName ) ;Trabalhando com vetores
// Assuming you have an instance of `index`
// Create and upsert vectors
var vectors = new [ ]
{
new Vector
{
Id = "vector1" ,
Values = new float [ ] { 0.1f , 0.2f , 0.3f , .. . } ,
Metadata = new MetadataMap
{
[ "genre" ] = "horror" ,
[ "duration" ] = 120
}
}
} ;
await index . Upsert ( vectors ) ;
// Fetch vectors by IDs
var fetched = await index . Fetch ( [ "vector1" ] ) ;
// Query scored vectors by ID
var scored = await index . Query ( "vector1" , topK : 10 ) ;
// Query scored vectors by a new, previously unseen vector
var vector = new [ ] { 0.1f , 0.2f , 0.3f , .. . } ;
var scored = await index . Query ( vector , topK : 10 ) ;
// Query scored vectors by ID with metadata filter
var filter = new MetadataMap
{
[ "genre" ] = new MetadataMap
{
[ "$in" ] = new [ ] { "documentary" , "action" }
}
} ;
var scored = await index . Query ( "birds" , topK : 10 , filter ) ;
// Delete vectors by vector IDs
await index . Delete ( new [ ] { "vector1" } ) ;
// Delete vectors by metadata filter
await index . Delete ( new MetadataMap
{
[ "genre" ] = new MetadataMap
{
[ "$in" ] = new [ ] { "documentary" , "action" }
}
} ) ;
// Delete all vectors in the index
await index . DeleteAll ( ) ;Trabalhando com coleções
using Pinecone ;
// Assuming you have an instance of `PineconeClient` named `pinecone`
// List all collections
var collections = await pinecone . ListCollections ( ) ;
// Create a new collection
await pinecone . CreateCollection ( "myCollection" , "myIndex" ) ;
// Describe a collection
var details = await pinecone . DescribeCollection ( "myCollection" ) ;
// Delete a collection
await pinecone . DeleteCollection ( "myCollection" ) ; Recuperando -se de falhas em estacas paralelas em lotes
// Upsert with recovery from up to three failures on batched parallel upsert.
//
// The parallelization is done automatically by the client based on the vector
// dimension and the number of vectors to upsert. It aims to keep the individual
// request size below Pinecone's 2MiB limit with some safety margin for metadata.
// This behavior can be further controlled by calling the 'Upsert' overload with
// custom values for 'batchSize' and 'parallelism' parameters.
//
// This is not the most efficient implementation in terms of allocations in
// GC pause frequency sensitive scenarios, but is perfectly acceptable
// for pretty much all regular back-end applications.
// Assuming there is an instance of 'index' available
// Generate 25k random vectors
var vectors = Enumerable
. Range ( 0 , 25_000 )
. Select ( _ => new Vector
{
Id = Guid . NewGuid ( ) . ToString ( ) ,
Values = Enumerable
. Range ( 0 , 1536 )
. Select ( _ => Random . Shared . NextSingle ( ) )
. ToArray ( )
} )
. ToArray ( ) ;
// Specify the retry limit we are okay with
var retries = 3 ;
while ( true )
{
try
{
// Perform the upsert
await index . Upsert ( vectors ) ;
// If no exception is thrown, break out of the retry loop
break ;
}
catch ( ParallelUpsertException e ) when ( retries -- > 0 )
{
// Create a hash set to efficiently filter out the failed vectors
var filter = e . FailedBatchVectorIds . ToHashSet ( ) ;
// Filter out the failed vectors from the batch and assign them to
// the 'vectors' variable consumed by 'Upsert' operation above
vectors = vectors . Where ( v => filter . Contains ( v . Id ) ) . ToArray ( ) ;
Console . WriteLine ( $ "Retrying upsert due to error: { e . Message } " ) ;
}
} Uma abordagem semelhante pode ser usada para se recuperar de outras operações transmitidas ou em lotes.
Consulte ListOperationException , ParallelFetchException e ParallelDeleteException em vettortypes.cs.
Prefira RestTransport por padrão. Encontre a explicação detalhada para cenários específicos abaixo.
GrpcTransport é uma alternativa viável para reduzir o tráfego de rede ao trabalhar com grandes vetores em cenários de rendimento de baixa a moderada.
O Protobuf codifica vetores de uma maneira muito mais compacta; portanto, se você possui vetores de alta dimensão (1536-3072+), baixo grau de concorrência de solicitação e geralmente desperdício ou busca vetores em pequenos bacTias, vale a pena considerar GrpcTransport para o seu caso de uso.
Teoricamente, a baixa taxa de transferência de simultaneidade pode ser maior com GrpcTransport devido ao tráfego de rede reduzido, mas porque o quão trivial é simplesmente despachar várias solicitações em paralelo (e o fato de que Fetch , Upsert e Delete fazê -lo automaticamente), as vantagens dessa abordagem provavelmente serão limitadas.
No momento da redação deste artigo, a pilha HTTP/2 da Pinecone está configurada para permitir poucas ou até apenas 1 fluxo simultâneo por uma única conexão HTTP/2.
Como o HTTP/2 é obrigatório para o GRPC, isso causa uma solicitação significativa na fila em um canal GRPC em cenários de alta concorrência, resultando em uma baixa escalabilidade do GrpcTransport com baixo teto para taxa de transferência.
Os usuários que não estão cientes dessa limitação podem experimentar um aumento inesperado de latência em suas operações de consulta sob a crescente contagem de usuários por nó de aplicação e/ou muito menor do que o esperado e buscam a taxa de transferência, mesmo quando especificando manualmente um maior grau de paralelismo das operações de Upsert e Fetch .
Pinecone.NET mitiga parcialmente esse problema configurando o canal GRPC para aproveitar o balanceamento de carga do lado do cliente (DNS Records Baseado) para usar vários subcanais (atualmente existem 3 conforme o retorno pelo DNS consulta), o que deve fornecer melhor taxa de transferência do que outros clientes. Ele também permite o uso de várias conexões HTTP/2 por terminal para o SocketsHttpHandler subjacente, mas a implementação atual do GRPC parece não tirar proveito disso.
O exposto acima é que uma observação do comportamento do cliente sob teste de carga e fatores de infraestrutura adicionais podem estar em jogo, conforme indicado pelos relatórios do usuário no Fórum Comunitário da Pinecone, WRT Scalability em outras implementações.
Usuários especializados que ainda desejam usar o transporte de GRPC em cenários de alta carga podem querer explorar outros itens de ação que estão fora do escopo desta biblioteca simples e apoiada pela comunidade:
Os usuários regulares são aconselhados a usar RestTransport para cenários de alto rendimento e/ou alta concorrência, a menos que a avaliação do GrpcTransport em seu ambiente específico produza melhores resultados.
A partir de agora, RestTransport e System.Text.Json que ele usa para serialização parece ser mais eficiente em termos de memória que GrpcTransport e Protobuf . Esta não é uma limitação inerente ao GRPC, mas resultado da implementação atual do grpc-dotnet . System.Text.Json é fortemente otimizado em relação ao tráfego de alocação e resulta em uso de memória sustentado significativamente menor, tanto com carga leve quanto pesada. Dado o estado atual da implementação grpc-dotnet , não prevo que isso mude no futuro próximo. É "bom o suficiente" para a maioria das aplicações, mas a diferença de tamanho sustentada da pilha sob carga é significativa o suficiente para garantir que isso explicitamente.
Observe que Pinecone.NET já executa a construção/leitura de cópia zero de RepeatedField<float> que armazenam valores de vetor para aliviar a pressão de alocação, mas não é suficiente para compensar a vantagem de usar o System Plain System.Text.Json serialização.
As contribuições são bem -vindas! Sinta -se à vontade para abrir um problema ou um PR.