Pinecone.net es una biblioteca C# completa para la base de datos de Vector Pinecone.
Esta es una biblioteca comunitaria que proporciona soporte de primera clase para Pinecone en C# y F#.
dotnet add package Pinecone.NET o Install-Package Pinecone.NET
Trabajando con í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 ) ;Trabajando con vectores
// 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 ( ) ;Trabajando con colecciones
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" ) ; Recuperarse de fallas en un upsert paralelo por 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 } " ) ;
}
} Se puede utilizar un enfoque similar para recuperarse de otras operaciones transmitidas o lotes.
Consulte ListOperationException , ParallelFetchException y ParallelDeleteException en vectortypes.cs.
Prefiere RestTransport por defecto. Encuentre la explicación detallada de escenarios específicos a continuación.
GrpcTransport es una alternativa viable para reducir el tráfico de red cuando se trabaja con vectores grandes en escenarios de rendimiento bajo a moderado.
ProtoBuf codifica vectores de una manera mucho más compacta, por lo que si tiene vectores de alta dimensión (1536-3072+), bajo grado de consulta de solicitud y generalmente upsre o obtiene vectores en bactos pequeños, vale la pena considerar GrpcTransport para su caso de uso.
Teóricamente, el bajo rendimiento de concurrencia puede ser mayor con GrpcTransport debido al tráfico de red reducido, pero debido a que trivial es simplemente enviar múltiples solicitudes en paralelo (y el hecho de que Fetch , Upsert y Delete lo hagan automáticamente), es probable que las ventajas de este enfoque sean limitadas.
Al momento de escribir, la pila HTTP/2 de Pinecone está configurada para permitir pocos o incluso 1 flujo concurrente por conexión HTTP/2 única.
Debido a que HTTP/2 es obligatorio para GRPC, esto causa una solicitud significativa en cola sobre un canal de GRPC en escenarios de alta concurrencia, lo que resulta en una pobre capacidad de GrpcTransport con bajo techo para el rendimiento.
Los usuarios que no son conscientes de esta limitación pueden experimentar un aumento de latencia inesperado en sus operaciones de consulta bajo el recuento de usuarios en crecimiento por nodo de aplicación y/o mucho más bajo de lo esperado y el rendimiento de obtención de la obtención, incluso cuando especifica manualmente un mayor grado de paralelismo de las operaciones Upsert y Fetch .
Pinecone.NET mitiga parcialmente este problema al configurar el canal GRPC para aprovechar el equilibrio de carga del lado del cliente (basado en registros DNS) para utilizar múltiples subcanales (actualmente hay 3 como se devuelve por la consulta DNS), que se espera que proporcione un mejor rendimiento que otros clientes aún. También permite el uso de múltiples conexiones HTTP/2 por punto final para el SocketsHttpHandler subyacente, pero la implementación actual de GRPC no parece aprovechar correctamente esto.
Lo anterior es una observación del comportamiento del cliente bajo prueba de carga y podrían estar en juego factores de infraestructura adicionales como lo indican los informes de los usuarios en la escalabilidad WRT de WRT del foro comunitario de Pinecone en otras implementaciones.
Los usuarios expertos que aún desean usar el transporte de GRPC en escenarios de alta carga pueden querer explorar más elementos de acción que están fuera del alcance de esta simple biblioteca respaldada por la comunidad:
Se recomienda a los usuarios regulares que utilicen RestTransport para escenarios de alto rendimiento y/o altos escenarios de concurrencia, a menos que su evaluación de GrpcTransport en su entorno específico produzca mejores resultados.
En este momento, RestTransport y System.Text.Json utiliza para la serialización parece ser más eficiente en la memoria que GrpcTransport y Protobuf . Esta no es una limitación inherente de GRPC, sino más bien un resultado de la implementación actual de grpc-dotnet . System.Text.Json está muy optimizado con respecto al tráfico de asignación y da como resultado un uso de memoria sostenido significativamente menor bajo carga de luz y pesada. Dado el estado actual de la implementación grpc-dotnet , no anticipo que esto cambie en el futuro cercano. Es "lo suficientemente bueno" para la mayoría de las aplicaciones, pero la diferencia de tamaño del montón sostenida bajo carga es lo suficientemente significativa como para justificar esto explícitamente.
Tenga en cuenta que Pinecone.NET ya realiza una construcción/lectura de copia cero de los valores de Vector de RepeatedField<float> que almacena para aliviar la presión de asignación, pero no es suficiente compensar la ventaja de usar la serialización sencillo System.Text.Json .
¡Las contribuciones son bienvenidas! No dude en abrir un problema o un PR.