Pinecone.net est une bibliothèque C # à part entière pour la base de données Vector Pinecone.
Il s'agit d'une bibliothèque communautaire qui fournit une prise en charge de première classe pour PineCone en C # et F #.
dotnet add package Pinecone.NET ou Install-Package Pinecone.NET
Travailler avec des index
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 ) ;Travailler avec des vecteurs
// 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 ( ) ;Travailler avec des collections
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" ) ; Récupérer des échecs sur un upsert parallèle lot
// 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 } " ) ;
}
} Une approche similaire peut être utilisée pour se remettre d'autres opérations en cours d'exécution ou par lots.
Voir ListOperationException , ParallelFetchException et ParallelDeleteException dans vectortypes.cs.
Préférez RestTransport par défaut. Veuillez trouver l'explication détaillée des scénarios spécifiques ci-dessous.
GrpcTransport est une alternative viable pour réduire le trafic réseau lorsqu'il travaille avec de grands vecteurs dans des scénarios de débit faible à modéré.
Protobuf code pour les vecteurs de manière beaucoup plus compacte, donc si vous avez des vecteurs de grande dimension (1536-3072 +), un faible degré de demande concurrentiel et généralement GrpcTransport vecteurs ou de récupération des vecteurs d'utilisation dans les petites Bacthes.
Théoriquement, un faible débit de concurrence peut être plus élevé avec GrpcTransport en raison du trafic réseau réduit, mais parce que c'est trivial de simplement expédier plusieurs demandes en parallèle (et le fait que Fetch , Upsert et Delete de le faire automatiquement), les avantages de cette approche sont probablement limités.
Au moment de la rédaction du moment de la rédaction, la pile HTTP / 2 de Pinecone est configurée pour permettre à peu ou même à 1 flux simultané par connexion HTTP / 2 unique.
Étant donné que HTTP / 2 est obligatoire pour le GRPC, cela provoque une demande de demande importante sur un canal GRPC dans des scénarios de concurrence élevés, ce qui entraîne une mauvaise évolutivité du GrpcTransport avec un plafond faible pour le débit.
Les utilisateurs qui ne sont pas conscients de cette limitation peuvent subir une augmentation inattendue de la latence de leurs opérations de requête dans le nombre croissant de comptes d'utilisateurs par nœud d'application et / ou beaucoup plus bas que prévu et le débit de redressement et de récupération, même lorsqu'ils spécifiant manuellement un plus grand degré de parallélisme des opérations Upsert et Fetch .
Pinecone.NET atténue partiellement ce problème en configurant le canal GRPC pour profiter de l'équilibrage de la charge côté client (enregistrements DNS basés) pour utiliser plusieurs sous-canaux (il y en a actuellement 3 tels que renvoyés par DNS Query), qui devrait fournir un meilleur débit que les autres clients. Il permet également l'utilisation de plusieurs connexions HTTP / 2 par point de terminaison pour la SocketsHttpHandler sous-jacente, mais l'implémentation actuelle de GRPC ne semble pas en profiter correctement.
Ce qui précède est une observation du comportement du client sous les tests de charge et des facteurs d'infrastructure supplémentaires pourraient être en jeu, comme l'indiquent les rapports d'utilisateurs de l'évolutivité du forum communautaire de Pinecone dans d'autres implémentations.
Les utilisateurs experts qui souhaitent toujours utiliser le transport GRPC dans des scénarios de haute charge peuvent vouloir explorer d'autres éléments d'action qui sont hors de portée de cette simple bibliothèque soutenue par la communauté:
Il est conseillé aux utilisateurs réguliers d'utiliser RestTransport pour des scénarios de concurrence à haut débit et / ou élevés, à moins que leur évaluation de GrpcTransport dans leur environnement spécifique produise de meilleurs résultats.
À l'heure actuelle, RestTransport et System.Text.Json qu'il utilise pour la sérialisation semble être plus économe en mémoire que GrpcTransport et Protobuf . Ce n'est pas une limitation inhérente de GRPC, mais plutôt le résultat de l'implémentation actuelle de grpc-dotnet . System.Text.Json est fortement optimisé en ce qui concerne le trafic d'attribution et entraîne une utilisation soutenue significativement plus faible sous une charge légère et lourde. Compte tenu de l'état actuel de l'implémentation grpc-dotnet , je ne m'attend pas à ce que cela change dans un avenir proche. Il est "assez bon" pour la plupart des applications, mais la différence de taille de tas soutenue sous charge est suffisamment significative pour justifier de le dire explicitement.
Veuillez noter que Pinecone.NET effectue déjà une construction / lecture de copie zéro de RepeatedField<float> qui stockent les valeurs vectorielles pour atténuer la pression d'allocation, mais il ne suffit pas de compenser l'avantage de l'utilisation de System.Text.Json Serialization.
Les contributions sont les bienvenues! N'hésitez pas à ouvrir un problème ou un PR.