Puedes integrar MongoDB Vector Search con LangChainGo para compilar aplicaciones de grandes modelos de lenguaje (LLM) e implementar generación de recuperación aumentada (RAG). Este tutorial demuestra cómo empezar a usar MongoDB Vector Search con LangChainGo para realizar una búsqueda semántica en tus datos y compilar una implementación de RAG. Específicamente, realizas las siguientes acciones:
Configura el entorno.
Almacene datos personalizados en MongoDB.
Cree un índice de Búsqueda Vectorial de MongoDB en sus datos.
Ejecuta las siguientes consultas de búsqueda vectorial:
búsqueda semántica.
Búsqueda semántica con prefiltrado de metadatos.
Implementa RAG utilizando MongoDB búsqueda vectorial para responder preguntas sobre tus datos.
Segundo plano
LangChainGo es la implementación del lenguaje de programación Go de LangChain. Es un port de terceros impulsado por la comunidad del framework LangChain.
LangChain es un framework de código abierto que permite desarrollar aplicaciones LLM de manera sencilla usando las "cadenas". Las cadenas son componentes específicos de LangChain que puede combinar para varios casos de uso de IA, incluido RAG.
Al integrar MongoDB Vector Search con LangChain, puedes utilizar MongoDB como una base de datos vectorial y utilizar MongoDB Vector Search para implementar RAG recuperando documentos semánticamente similares de tus datos. Para obtener más información sobre RAG, consulte Generación de recuperación aumentada (RAG) con MongoDB.
LangChainGo facilita la orquestación de LLM para aplicaciones de IA, aportando las capacidades de LangChain al ecosistema Go. También permite a los desarrolladores conectarse a sus bases de datos preferidas compatibles con vector stores, incluyendo MongoDB.
Procedimiento
Requisitos previos
Para completar este tutorial, debes tener lo siguiente:
Uno de los siguientes tipos de clúster de MongoDB:
Se requiere un clúster de Atlas que ejecute MongoDB 6.0.11 versión, 7.0.2 o posterior. Asegúrese de que su dirección IP esté incluida en la lista de acceso de su proyecto de Atlas.
Una implementación local de Atlas creada usando Atlas CLI. Para obtener más información, consulta Crear una Implementación local de Atlas.
Un clúster de MongoDB Community o Enterprise con Search y Vector Search instalados.
Una llave de API de OpenAI. Debes tener una cuenta de OpenAI con créditos disponibles para las solicitudes de API. Para obtener más información sobre cómo registrar una cuenta de OpenAI, consulta el sitio web de la API de OpenAI.
Una clave de API de Voyage IA. Para crear una clave API, consulta Claves API del modelo.
Una terminal y editor de código para ejecutar su proyecto de Go.
Go instalado en tu ordenador.
Configurar el entorno
Primero debe configurar el entorno para este tutorial. Complete los siguientes pasos para configurar su entorno.
Instalar las dependencias.
Ejecute los siguientes comandos:
go get github.com/joho/godotenv go get github.com/tmc/langchaingo/chains go get github.com/tmc/langchaingo/llms go get github.com/tmc/langchaingo/prompts go get github.com/tmc/langchaingo/vectorstores/mongovector go get github.com/tmc/langchaingo/embeddings/voyageai go get go.mongodb.org/mongo-driver/v2/mongo go mod tidy
Inicializa tus variables de entorno.
En tu directorio de proyecto langchaingo-mongodb, crea un archivo .env y añade las siguientes líneas:
OPENAI_API_KEY="<openai-api-key>" VOYAGEAI_API_KEY="<voyage-api-key>" MONGODB_URI="<connection-string>"
Reemplaza los valores de marcador de posición por tu clave de API de OpenAI, clave de API de Voyage IA y la SRV cadena de conexión para tu clúster de MongoDB. Tu cadena de conexión debe utilizar el siguiente formato:
mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
Usa MongoDB como Vector Store
En esta sección, se define una función asíncrona para cargar datos personalizados en MongoDB e instanciar MongoDB como una base de datos vectorial, también llamada almacenamiento vectorial.
Importa las siguientes dependencias.
Agrega las siguientes importaciones al inicio de tu archivo main.go.
package main import ( "context" "log" "os" "github.com/joho/godotenv" "github.com/tmc/langchaingo/embeddings/voyageai" "github.com/tmc/langchaingo/schema" "github.com/tmc/langchaingo/vectorstores/mongovector" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" )
Define los detalles de la tienda de vectores.
El siguiente código realiza estas acciones:
Configura Atlas como un almacén vectorial especificando lo siguiente:
langchaingo_db.testcomo la colección en Atlas para almacenar los documentos.vector_indexcomo el índice a utilizar para consultar el almacén vectorial.textcomo el nombre del campo que contiene el contenido de texto sin formato.embeddingcomo el nombre del campo que contiene las incrustaciones vectoriales.
Prepara tus datos personalizados de la siguiente manera:
Define un texto para cada documento.
Utiliza el paquete
mongovectorde LangChainGo para generar incrustaciones para los textos. Este paquete almacena incrustaciones de documentos en MongoDB y permite búsquedas sobre incrustaciones almacenadas.Construye documentos que incluyen texto, incrustaciones y metadatos.
Incorpora los documentos construidos en Atlas e instancia el almacén de vectores.
Pega el siguiente código en tu archivo main.go:
// Defines the document structure type Document struct { PageContent string `bson:"text"` Embedding []float32 `bson:"embedding"` Metadata map[string]string `bson:"metadata"` } func main() { const ( voyageAIEmbeddingDim = 1024 similarityAlgorithm = "dotProduct" indexName = "vector_index" databaseName = "langchaingo_db" collectionName = "test" ) if err := godotenv.Load(); err != nil { log.Fatal("No .env file found") } // Loads the MongoDB URI from environment uri := os.Getenv("MONGODB_URI") if uri == "" { log.Fatal("Set your 'MONGODB_URI' environment variable in the .env file") } // Loads the API key from environment voyageApiKey := os.Getenv("VOYAGEAI_API_KEY") if voyageApiKey == "" { log.Fatal("Set your VOYAGEAI_API_KEY environment variable in the .env file") } // Connects to MongoDB cluster client, err := mongo.Connect(options.Client().ApplyURI(uri)) if err != nil { log.Fatalf("Failed to connect to server: %v", err) } defer func() { if err := client.Disconnect(context.Background()); err != nil { log.Fatalf("Error disconnecting the client: %v", err) } }() log.Println("Connected to MongoDB.") // Selects the database and collection coll := client.Database(databaseName).Collection(collectionName) // Creates an embedder client embedder, err := voyageai.NewVoyageAI( voyageai.WithModel("voyage-3-large"), ) if err != nil { log.Fatalf("Failed to create an embedder: %v", err) } // Creates a new MongoDB vector store store := mongovector.New(coll, embedder, mongovector.WithIndex(indexName), mongovector.WithPath("embeddings")) // Checks if the collection is empty, and if empty, adds documents to the MongoDB vector store if isCollectionEmpty(coll) { documents := []schema.Document{ { PageContent: "Proper tuber planting involves site selection, proper timing, and exceptional care. Choose spots with well-drained soil and adequate sun exposure. Tubers are generally planted in spring, but depending on the plant, timing varies. Always plant with the eyes facing upward at a depth two to three times the tuber's height. Ensure 4 inch spacing between small tubers, expand to 12 inches for large ones. Adequate moisture is needed, yet do not overwater. Mulching can help preserve moisture and prevent weed growth.", Metadata: map[string]any{ "author": "A", "type": "post", }, }, { PageContent: "Successful oil painting necessitates patience, proper equipment, and technique. Begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space.", Metadata: map[string]any{ "author": "B", "type": "post", }, }, { PageContent: "For a natural lawn, selection of the right grass type suitable for your climate is crucial. Balanced watering, generally 1 to 1.5 inches per week, is important; overwatering invites disease. Opt for organic fertilizers over synthetic versions to provide necessary nutrients and improve soil structure. Regular lawn aeration helps root growth and prevents soil compaction. Practice natural pest control and consider overseeding to maintain a dense sward, which naturally combats weeds and pest.", Metadata: map[string]any{ "author": "C", "type": "post", }, }, } _, err := store.AddDocuments(context.Background(), documents) if err != nil { log.Fatalf("Error adding documents: %v", err) } log.Printf("Successfully added %d documents to the collection.\n", len(documents)) } else { log.Println("Documents already exist in the collection, skipping document addition.") } } func isCollectionEmpty(coll *mongo.Collection) bool { count, err := coll.EstimatedDocumentCount(context.Background()) if err != nil { log.Fatalf("Failed to count documents in the collection: %v", err) } return count == 0 }
Ejecute su proyecto de Go.
Guarda el archivo y luego ejecuta el siguiente comando para cargar tus datos en MongoDB.
go run main.go
Connected to MongoDB Atlas. Successfully added 3 documents to the collection.
Tip
Después de ejecutar main.go, si utiliza Atlas, puede verificar sus incrustaciones vectoriales navegando hasta el namespace de langchaingo_db.test en la Interfaz de Usuario de Atlas.
Cree el índice de búsqueda vectorial de MongoDB
Para habilitar consultas de búsqueda vectorial en tu almacén vectorial, crea un índice MongoDB Vector Search en la colección langchaingo_db.test.
Añade las siguientes importaciones al principio de tu archivo main.go:
import ( // Other imports... "fmt" "time" "go.mongodb.org/mongo-driver/v2/bson" )
Define las siguientes funciones en su archivo main.go fuera de su función main(). Estas funciones crean y gestionan un índice de búsqueda vectorial para su colección de MongoDB:
La función
SearchIndexExistsverifica si existe un índice de búsqueda con el nombre especificado y si se puede consultar.La función
CreateVectorSearchIndexcrea un índice de búsqueda vectorial en la colección especificada. Esta función se bloquea hasta que se crea el índice y este puede consultarse.
// Checks if the search index exists func SearchIndexExists(ctx context.Context, coll *mongo.Collection, idx string) (bool, error) { log.Println("Checking if search index exists.") view := coll.SearchIndexes() siOpts := options.SearchIndexes().SetName(idx).SetType("vectorSearch") cursor, err := view.List(ctx, siOpts) if err != nil { return false, fmt.Errorf("failed to list search indexes: %w", err) } for cursor.Next(ctx) { index := struct { Name string `bson:"name"` Queryable bool `bson:"queryable"` }{} if err := cursor.Decode(&index); err != nil { return false, fmt.Errorf("failed to decode search index: %w", err) } if index.Name == idx && index.Queryable { return true, nil } } if err := cursor.Err(); err != nil { return false, fmt.Errorf("cursor error: %w", err) } return false, nil } // Creates a vector search index. This function blocks until the index has been // created. func CreateVectorSearchIndex( ctx context.Context, coll *mongo.Collection, idxName string, voyageAIEmbeddingDim int, similarityAlgorithm string, ) (string, error) { type vectorField struct { Type string `bson:"type,omitempty"` Path string `bson:"path,omitempty"` NumDimensions int `bson:"numDimensions,omitempty"` Similarity string `bson:"similarity,omitempty"` } fields := []vectorField{ { Type: "vector", Path: "embeddings", NumDimensions: voyageAIEmbeddingDim, Similarity: similarityAlgorithm, }, { Type: "filter", Path: "metadata.author", }, { Type: "filter", Path: "metadata.type", }, } def := struct { Fields []vectorField `bson:"fields"` }{ Fields: fields, } log.Println("Creating vector search index...") view := coll.SearchIndexes() siOpts := options.SearchIndexes().SetName(idxName).SetType("vectorSearch") searchName, err := view.CreateOne(ctx, mongo.SearchIndexModel{Definition: def, Options: siOpts}) if err != nil { return "", fmt.Errorf("failed to create the search index: %w", err) } // Awaits the creation of the index var doc bson.Raw for doc == nil { cursor, err := view.List(ctx, options.SearchIndexes().SetName(searchName)) if err != nil { return "", fmt.Errorf("failed to list search indexes: %w", err) } if !cursor.Next(ctx) { break } name := cursor.Current.Lookup("name").StringValue() queryable := cursor.Current.Lookup("queryable").Boolean() if name == searchName && queryable { doc = cursor.Current } else { time.Sleep(5 * time.Second) } } return searchName, nil }
Crea la colección de almacenamiento vectorial y el índice llamando a las funciones anteriores en tu función main(). Agrega el siguiente código al final de tu función main():
// SearchIndexExists will return true if the provided index is defined for the // collection. This operation blocks until the search completes. if ok, _ := SearchIndexExists(context.Background(), coll, indexName); !ok { // Creates the vector store collection err = client.Database(databaseName).CreateCollection(context.Background(), collectionName) if err != nil { log.Fatalf("failed to create vector store collection: %v", err) } _, err = CreateVectorSearchIndex(context.Background(), coll, indexName, voyageAIEmbeddingDim, similarityAlgorithm) if err != nil { log.Fatalf("failed to create index: %v", err) } log.Println("Successfully created vector search index.") } else { log.Println("Vector search index already exists.") }
Guarde el archivo y luego ejecute el siguiente comando para crear su índice de MongoDB Vector Search.
go run main.go
Checking if search index exists. Creating vector search index... Successfully created vector search index.
Tip
Después de ejecutar main.go, puedes ver tu índice de búsqueda vectorial en la interfaz de Atlas navegando a la colección langchaingo_db.test en tu clúster.
Ejecución de consultas de búsqueda vectorial
Esta sección demuestra varias consultas que puedes ejecutar en tus datos vectorizados. Ahora que has creado el índice, puedes ejecutar consultas de búsqueda por vectores.
Selecciona la pestaña Basic Semantic Search o Semantic Search with Filtering para ver el código correspondiente.
Agrega el siguiente código a tu función principal y guarda el archivo.
La búsqueda semántica recupera información que está significativamente relacionada con una query. El siguiente código utiliza el método SimilaritySearch() para realizar una búsqueda semántica de la cadena "Prevent weeds" y limita los resultados al primer documento.
// Performs basic semantic search docs, err := store.SimilaritySearch(context.Background(), "Prevent weeds", 1) if err != nil { fmt.Println("Error performing search:", err) } fmt.Println("Semantic Search Results:", docs)
Ejecuta el siguiente comando para ejecutar la query.
go run main.go
Semantic Search Results: [{For a natural lawn, selection of the right grass type suitable for your climate is crucial. Balanced watering, generally 1 to 1.5 inches per week, is important; overwatering invites disease. Opt for organic fertilizers over synthetic versions to provide necessary nutrients and improve soil structure. Regular lawn aeration helps root growth and prevents soil compaction. Practice natural pest control and consider overseeding to maintain a dense sward, which naturally combats weeds and pest. map[author:C type:post] 0.69752026}]
Puedes prefiltrar tus datos utilizando una expresión de coincidencia MQL que compara el campo indexado con otro valor de tu colección. Debe indexar los campos de metadatos por los que desea filtrar como tipo filter. Para obtener más información, consulte Cómo Indexar Campos para Búsqueda Vectorial.
Agrega el siguiente código a tu función principal y guarda el archivo.
El siguiente código utiliza el método SimilaritySearch() para realizar una búsqueda semántica de la string "Tulip care". Especifica los siguientes parámetros:
El número de documentos a devolver como
1.Un umbral de puntuación de
0.60.
Devuelve el documento que coincide con el filtro metadata.type: post e incluye el umbral de puntuación.
// Performs semantic search with metadata filter filter := map[string]interface{}{ "metadata.type": "post", } docs, err := store.SimilaritySearch(context.Background(), "Tulip care", 1, vectorstores.WithScoreThreshold(0.60), vectorstores.WithFilters(filter)) if err != nil { fmt.Println("Error performing search:", err) } fmt.Println("Filter Search Results:", docs)
Ejecuta el siguiente comando para ejecutar la query.
go run main.go
Filter Search Results: [{Proper tuber planting involves site selection, proper timing, and exceptional care. Choose spots with well-drained soil and adequate sun exposure. Tubers are generally planted in spring, but depending on the plant, timing varies. Always plant with the eyes facing upward at a depth two to three times the tuber's height. Ensure 4 inch spacing between small tubers, expand to 12 inches for large ones. Adequate moisture is needed, yet do not overwater. Mulching can help preserve moisture and prevent weed growth. map[author:A type:post] 0.64432365}]
Responde preguntas sobre tus datos
Esta sección demuestra una implementación de RAG utilizando MongoDB Vector Search y LangChainGo. Ahora que has usado MongoDB Vector Search para recuperar documentos semánticamente similares, usa el siguiente ejemplo de código para pedirle al LLM que responda preguntas sobre los documentos devueltos por MongoDB Vector Search.
Agrega el siguiente código al final de tu función principal y guarda el archivo.
Este código realiza lo siguiente:
Instancia MongoDB Vector Search como un recuperador para consultar documentos semánticamente similares.
Define una plantilla de prompt LangChainGo para instruir al LLM para que utilice los documentos recuperados como contexto para tu query. LangChainGo inserta estos documentos en la variable de entrada
{{.context}}y tu query en la variable{{.question}}.Construye una cadena que utiliza el modelo de chat de OpenAI para generar respuestas contextuales basadas en la plantilla de indicaciones proporcionada.
Envía una query de muestra sobre pintura para principiantes a la cadena, utilizando el prompt y el buscador para reunir el contexto relevante.
Devuelve e imprime la respuesta del LLM y los documentos utilizados como contexto.
// Implements RAG to answer questions on your data optionsVector := []vectorstores.Option{ vectorstores.WithScoreThreshold(0.60), } retriever := vectorstores.ToRetriever(&store, 1, optionsVector...) // Loads OpenAI API key from environment openaiApiKey := os.Getenv("OPENAI_API_KEY") if openaiApiKey == "" { log.Fatal("Set your OPENAI_API_KEY environment variable in the .env file") } // Creates an OpenAI LLM client llm, err := openai.New(openai.WithToken(openaiApiKey), openai.WithModel("gpt-4o"), openai.WithEmbeddingModel("voyage-3-large")) if err != nil { log.Fatalf("Failed to create an LLM client: %v", err) } prompt := prompts.NewPromptTemplate( `Answer the question based on the following context: {{.context}} Question: {{.question}}`, []string{"context", "question"}, ) llmChain := chains.NewLLMChain(llm, prompt) ctx := context.Background() const question = "How do I get started painting?" documents, err := retriever.GetRelevantDocuments(ctx, question) if err != nil { log.Fatalf("Failed to retrieve documents: %v", err) } var contextBuilder strings.Builder for i, document := range documents { contextBuilder.WriteString(fmt.Sprintf("Document %d: %s\n", i+1, document.PageContent)) } contextStr := contextBuilder.String() inputs := map[string]interface{}{ "context": contextStr, "question": question, } out, err := chains.Call(ctx, llmChain, inputs) if err != nil { log.Fatalf("Failed to run LLM chain: %v", err) } log.Println("Source documents:") for i, doc := range documents { log.Printf("Document %d: %s\n", i+1, doc.PageContent) } responseText, ok := out["text"].(string) if !ok { log.Println("Unexpected response type") return } log.Println("Question:", question) log.Println("Generated Answer:", responseText)
Ejecuta el siguiente comando para ejecutar tu archivo.
Después de guardar el archivo, ejecuta el siguiente comando. La respuesta generada puede variar.
go run main.go
Source documents: Document 1: "Successful oil painting necessitates patience, proper equipment, and technique. Begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space." Question: How do I get started painting? Generated Answer: To get started painting, you should begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space.
Requisitos previos
Para completar este tutorial, debes tener lo siguiente:
Uno de los siguientes tipos de clúster de MongoDB:
Se requiere un clúster de Atlas que ejecute MongoDB 6.0.11 versión, 7.0.2 o posterior. Asegúrese de que su dirección IP esté incluida en la lista de acceso de su proyecto de Atlas.
Una implementación local de Atlas creada usando Atlas CLI. Para obtener más información, consulta Crear una Implementación local de Atlas.
Un clúster de MongoDB Community o Enterprise con Search y Vector Search instalados.
Una llave de API de OpenAI. Debes tener una cuenta de OpenAI con créditos disponibles para las solicitudes de API. Para obtener más información sobre cómo registrar una cuenta de OpenAI, consulta el sitio web de la API de OpenAI.
Una terminal y editor de código para ejecutar su proyecto de Go.
Go instalado en tu ordenador.
Configurar el entorno
Primero debe configurar el entorno para este tutorial. Complete los siguientes pasos para configurar su entorno.
Instalar las dependencias.
Ejecute los siguientes comandos:
go get github.com/joho/godotenv go get github.com/tmc/langchaingo/chains go get github.com/tmc/langchaingo/llms go get github.com/tmc/langchaingo/prompts go get github.com/tmc/langchaingo/vectorstores/mongovector go get go.mongodb.org/mongo-driver/v2/mongo go mod tidy
Inicializa tus variables de entorno.
En tu directorio de proyecto langchaingo-mongodb, crea un archivo .env y añade las siguientes líneas:
OPENAI_API_KEY="<api-key>" MONGODB_URI="<connection-string>"
Reemplaza los valores de marcador de posición con tu clave API de OpenAI y la SRV cadena de conexión para tu clúster de MongoDB. Su cadena de conexión debe usar el siguiente formato:
mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
Usa MongoDB como Vector Store
En esta sección, se define una función asíncrona para cargar datos personalizados en MongoDB e instanciar MongoDB como una base de datos vectorial, también llamada almacenamiento vectorial.
Importa las siguientes dependencias.
Agrega las siguientes importaciones al inicio de tu archivo main.go.
package main import ( "context" "log" "os" "github.com/joho/godotenv" "github.com/tmc/langchaingo/embeddings" "github.com/tmc/langchaingo/llms/openai" "github.com/tmc/langchaingo/schema" "github.com/tmc/langchaingo/vectorstores/mongovector" "go.mongodb.org/mongo-driver/v2/mongo" "go.mongodb.org/mongo-driver/v2/mongo/options" )
Define los detalles de la tienda de vectores.
El siguiente código realiza estas acciones:
Configura Atlas como un almacén vectorial especificando lo siguiente:
langchaingo_db.testcomo la colección en Atlas para almacenar los documentos.vector_indexcomo el índice a utilizar para consultar el almacén vectorial.textcomo el nombre del campo que contiene el contenido de texto sin formato.embeddingcomo el nombre del campo que contiene las incrustaciones vectoriales.
Prepara tus datos personalizados de la siguiente manera:
Define un texto para cada documento.
Utiliza el paquete
mongovectorde LangChainGo para generar incrustaciones para los textos. Este paquete almacena incrustaciones de documentos en MongoDB y permite búsquedas sobre incrustaciones almacenadas.Construye documentos que incluyen texto, incrustaciones y metadatos.
Incorpora los documentos construidos en Atlas e instancia el almacén de vectores.
Pega el siguiente código en tu archivo main.go:
// Defines the document structure type Document struct { PageContent string `bson:"text"` Embedding []float32 `bson:"embedding"` Metadata map[string]string `bson:"metadata"` } func main() { const ( openAIEmbeddingModel = "text-embedding-3-small" openAIEmbeddingDim = 1536 similarityAlgorithm = "dotProduct" indexName = "vector_index" databaseName = "langchaingo_db" collectionName = "test" ) if err := godotenv.Load(); err != nil { log.Fatal("No .env file found") } // Loads the MongoDB URI from environment uri := os.Getenv("MONGODB_URI") if uri == "" { log.Fatal("Set your 'MONGODB_URI' environment variable in the .env file") } // Loads the API key from environment apiKey := os.Getenv("OPENAI_API_KEY") if apiKey == "" { log.Fatal("Set your OPENAI_API_KEY environment variable in the .env file") } // Connects to MongoDB client, err := mongo.Connect(options.Client().ApplyURI(uri)) if err != nil { log.Fatalf("Failed to connect to server: %v", err) } defer func() { if err := client.Disconnect(context.Background()); err != nil { log.Fatalf("Error disconnecting the client: %v", err) } }() log.Println("Connected to MongoDB.") // Selects the database and collection coll := client.Database(databaseName).Collection(collectionName) // Creates an OpenAI LLM embedder client llm, err := openai.New(openai.WithEmbeddingModel(openAIEmbeddingModel)) if err != nil { log.Fatalf("Failed to create an embedder client: %v", err) } // Creates an embedder from the embedder client embedder, err := embeddings.NewEmbedder(llm) if err != nil { log.Fatalf("Failed to create an embedder: %v", err) } // Creates a new MongoDB vector store store := mongovector.New(coll, embedder, mongovector.WithIndex(indexName), mongovector.WithPath("embeddings")) // Checks if the collection is empty, and if empty, adds documents to the MongoDB database vector store if isCollectionEmpty(coll) { documents := []schema.Document{ { PageContent: "Proper tuber planting involves site selection, proper timing, and exceptional care. Choose spots with well-drained soil and adequate sun exposure. Tubers are generally planted in spring, but depending on the plant, timing varies. Always plant with the eyes facing upward at a depth two to three times the tuber's height. Ensure 4 inch spacing between small tubers, expand to 12 inches for large ones. Adequate moisture is needed, yet do not overwater. Mulching can help preserve moisture and prevent weed growth.", Metadata: map[string]any{ "author": "A", "type": "post", }, }, { PageContent: "Successful oil painting necessitates patience, proper equipment, and technique. Begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space.", Metadata: map[string]any{ "author": "B", "type": "post", }, }, { PageContent: "For a natural lawn, selection of the right grass type suitable for your climate is crucial. Balanced watering, generally 1 to 1.5 inches per week, is important; overwatering invites disease. Opt for organic fertilizers over synthetic versions to provide necessary nutrients and improve soil structure. Regular lawn aeration helps root growth and prevents soil compaction. Practice natural pest control and consider overseeding to maintain a dense sward, which naturally combats weeds and pest.", Metadata: map[string]any{ "author": "C", "type": "post", }, }, } _, err := store.AddDocuments(context.Background(), documents) if err != nil { log.Fatalf("Error adding documents: %v", err) } log.Printf("Successfully added %d documents to the collection.\n", len(documents)) } else { log.Println("Documents already exist in the collection, skipping document addition.") } } func isCollectionEmpty(coll *mongo.Collection) bool { count, err := coll.EstimatedDocumentCount(context.Background()) if err != nil { log.Fatalf("Failed to count documents in the collection: %v", err) } return count == 0 }
Ejecute su proyecto de Go.
Guarda el archivo y luego ejecuta el siguiente comando para cargar tus datos en MongoDB.
go run main.go
Connected to MongoDB Atlas. Successfully added 3 documents to the collection.
Tip
Después de ejecutar main.go, si utiliza Atlas, puede verificar sus incrustaciones vectoriales navegando hasta el namespace de langchaingo_db.test en la Interfaz de Usuario de Atlas.
Cree el índice de búsqueda vectorial de MongoDB
Para habilitar consultas de búsqueda vectorial en tu almacén vectorial, crea un índice MongoDB Vector Search en la colección langchaingo_db.test.
Añade las siguientes importaciones al principio de tu archivo main.go:
import ( // Other imports... "fmt" "time" "go.mongodb.org/mongo-driver/v2/bson" )
Define las siguientes funciones en su archivo main.go fuera de su función main(). Estas funciones crean y gestionan un índice de búsqueda vectorial para su colección de MongoDB:
La función
SearchIndexExistsverifica si existe un índice de búsqueda con el nombre especificado y si se puede consultar.La función
CreateVectorSearchIndexcrea un índice de búsqueda vectorial en la colección especificada. Esta función se bloquea hasta que se crea el índice y este puede consultarse.
// Checks if the search index exists func SearchIndexExists(ctx context.Context, coll *mongo.Collection, idx string) (bool, error) { log.Println("Checking if search index exists.") view := coll.SearchIndexes() siOpts := options.SearchIndexes().SetName(idx).SetType("vectorSearch") cursor, err := view.List(ctx, siOpts) if err != nil { return false, fmt.Errorf("failed to list search indexes: %w", err) } for cursor.Next(ctx) { index := struct { Name string `bson:"name"` Queryable bool `bson:"queryable"` }{} if err := cursor.Decode(&index); err != nil { return false, fmt.Errorf("failed to decode search index: %w", err) } if index.Name == idx && index.Queryable { return true, nil } } if err := cursor.Err(); err != nil { return false, fmt.Errorf("cursor error: %w", err) } return false, nil } // Creates a vector search index. This function blocks until the index has been // created. func CreateVectorSearchIndex( ctx context.Context, coll *mongo.Collection, idxName string, openAIEmbeddingDim int, similarityAlgorithm string, ) (string, error) { type vectorField struct { Type string `bson:"type,omitempty"` Path string `bson:"path,omitempty"` NumDimensions int `bson:"numDimensions,omitempty"` Similarity string `bson:"similarity,omitempty"` } fields := []vectorField{ { Type: "vector", Path: "embeddings", NumDimensions: openAIEmbeddingDim, Similarity: similarityAlgorithm, }, { Type: "filter", Path: "metadata.author", }, { Type: "filter", Path: "metadata.type", }, } def := struct { Fields []vectorField `bson:"fields"` }{ Fields: fields, } log.Println("Creating vector search index...") view := coll.SearchIndexes() siOpts := options.SearchIndexes().SetName(idxName).SetType("vectorSearch") searchName, err := view.CreateOne(ctx, mongo.SearchIndexModel{Definition: def, Options: siOpts}) if err != nil { return "", fmt.Errorf("failed to create the search index: %w", err) } // Awaits the creation of the index var doc bson.Raw for doc == nil { cursor, err := view.List(ctx, options.SearchIndexes().SetName(searchName)) if err != nil { return "", fmt.Errorf("failed to list search indexes: %w", err) } if !cursor.Next(ctx) { break } name := cursor.Current.Lookup("name").StringValue() queryable := cursor.Current.Lookup("queryable").Boolean() if name == searchName && queryable { doc = cursor.Current } else { time.Sleep(5 * time.Second) } } return searchName, nil }
Crea la colección de almacenamiento vectorial y el índice llamando a las funciones anteriores en tu función main(). Agrega el siguiente código al final de tu función main():
// SearchIndexExists will return true if the provided index is defined for the // collection. This operation blocks until the search completes. if ok, _ := SearchIndexExists(context.Background(), coll, indexName); !ok { // Creates the vector store collection err = client.Database(databaseName).CreateCollection(context.Background(), collectionName) if err != nil { log.Fatalf("failed to create vector store collection: %v", err) } _, err = CreateVectorSearchIndex(context.Background(), coll, indexName, openAIEmbeddingDim, similarityAlgorithm) if err != nil { log.Fatalf("failed to create index: %v", err) } log.Println("Successfully created vector search index.") } else { log.Println("Vector search index already exists.") }
Guarde el archivo y luego ejecute el siguiente comando para crear su índice de MongoDB Vector Search.
go run main.go
Checking if search index exists. Creating vector search index... Successfully created vector search index.
Tip
Después de ejecutar main.go, puedes ver tu índice de búsqueda vectorial en la interfaz de Atlas navegando a la colección langchaingo_db.test en tu clúster.
Ejecución de consultas de búsqueda vectorial
Esta sección demuestra varias consultas que puedes ejecutar en tus datos vectorizados. Ahora que has creado el índice, puedes ejecutar consultas de búsqueda por vectores.
Selecciona la pestaña Basic Semantic Search o Semantic Search with Filtering para ver el código correspondiente.
Agrega el siguiente código a tu función principal y guarda el archivo.
La búsqueda semántica recupera información que está significativamente relacionada con una query. El siguiente código utiliza el método SimilaritySearch() para realizar una búsqueda semántica de la cadena "Prevent weeds" y limita los resultados al primer documento.
// Performs basic semantic search docs, err := store.SimilaritySearch(context.Background(), "Prevent weeds", 1) if err != nil { fmt.Println("Error performing search:", err) } fmt.Println("Semantic Search Results:", docs)
Ejecuta el siguiente comando para ejecutar la query.
go run main.go
Semantic Search Results: [{For a natural lawn, selection of the right grass type suitable for your climate is crucial. Balanced watering, generally 1 to 1.5 inches per week, is important; overwatering invites disease. Opt for organic fertilizers over synthetic versions to provide necessary nutrients and improve soil structure. Regular lawn aeration helps root growth and prevents soil compaction. Practice natural pest control and consider overseeding to maintain a dense sward, which naturally combats weeds and pest. map[author:C type:post] 0.69752026}]
Puedes prefiltrar tus datos utilizando una expresión de coincidencia MQL que compara el campo indexado con otro valor de tu colección. Debe indexar los campos de metadatos por los que desea filtrar como tipo filter. Para obtener más información, consulte Cómo Indexar Campos para Búsqueda Vectorial.
Agrega el siguiente código a tu función principal y guarda el archivo.
El siguiente código utiliza el método SimilaritySearch() para realizar una búsqueda semántica de la string "Tulip care". Especifica los siguientes parámetros:
El número de documentos a devolver como
1.Un umbral de puntuación de
0.60.
Devuelve el documento que coincide con el filtro metadata.type: post e incluye el umbral de puntuación.
// Performs semantic search with metadata filter filter := map[string]interface{}{ "metadata.type": "post", } docs, err := store.SimilaritySearch(context.Background(), "Tulip care", 1, vectorstores.WithScoreThreshold(0.60), vectorstores.WithFilters(filter)) if err != nil { fmt.Println("Error performing search:", err) } fmt.Println("Filter Search Results:", docs)
Ejecuta el siguiente comando para ejecutar la query.
go run main.go
Filter Search Results: [{Proper tuber planting involves site selection, proper timing, and exceptional care. Choose spots with well-drained soil and adequate sun exposure. Tubers are generally planted in spring, but depending on the plant, timing varies. Always plant with the eyes facing upward at a depth two to three times the tuber's height. Ensure 4 inch spacing between small tubers, expand to 12 inches for large ones. Adequate moisture is needed, yet do not overwater. Mulching can help preserve moisture and prevent weed growth. map[author:A type:post] 0.64432365}]
Responde preguntas sobre tus datos
Esta sección demuestra una implementación de RAG utilizando MongoDB Vector Search y LangChainGo. Ahora que has usado MongoDB Vector Search para recuperar documentos semánticamente similares, usa el siguiente ejemplo de código para pedirle al LLM que responda preguntas sobre los documentos devueltos por MongoDB Vector Search.
Agrega el siguiente código al final de tu función principal y guarda el archivo.
Este código realiza lo siguiente:
Instancia MongoDB Vector Search como un recuperador para consultar documentos semánticamente similares.
Define una plantilla de prompt LangChainGo para instruir al LLM para que utilice los documentos recuperados como contexto para tu query. LangChainGo inserta estos documentos en la variable de entrada
{{.context}}y tu query en la variable{{.question}}.Construye una cadena que utiliza el modelo de chat de OpenAI para generar respuestas contextuales basadas en la plantilla de indicaciones proporcionada.
Envía una query de muestra sobre pintura para principiantes a la cadena, utilizando el prompt y el buscador para reunir el contexto relevante.
Devuelve e imprime la respuesta del LLM y los documentos utilizados como contexto.
// Implements RAG to answer questions on your data optionsVector := []vectorstores.Option{ vectorstores.WithScoreThreshold(0.60), } retriever := vectorstores.ToRetriever(&store, 1, optionsVector...) prompt := prompts.NewPromptTemplate( `Answer the question based on the following context: {{.context}} Question: {{.question}}`, []string{"context", "question"}, ) llmChain := chains.NewLLMChain(llm, prompt) ctx := context.Background() const question = "How do I get started painting?" documents, err := retriever.GetRelevantDocuments(ctx, question) if err != nil { log.Fatalf("Failed to retrieve documents: %v", err) } var contextBuilder strings.Builder for i, document := range documents { contextBuilder.WriteString(fmt.Sprintf("Document %d: %s\n", i+1, document.PageContent)) } contextStr := contextBuilder.String() inputs := map[string]interface{}{ "context": contextStr, "question": question, } out, err := chains.Call(ctx, llmChain, inputs) if err != nil { log.Fatalf("Failed to run LLM chain: %v", err) } log.Println("Source documents:") for i, doc := range documents { log.Printf("Document %d: %s\n", i+1, doc.PageContent) } responseText, ok := out["text"].(string) if !ok { log.Println("Unexpected response type") return } log.Println("Question:", question) log.Println("Generated Answer:", responseText)
Ejecuta el siguiente comando para ejecutar tu archivo.
Después de guardar el archivo, ejecuta el siguiente comando. La respuesta generada puede variar.
go run main.go
Source documents: Document 1: "Successful oil painting necessitates patience, proper equipment, and technique. Begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space." Question: How do I get started painting? Generated Answer: To get started painting, you should begin with a carefully prepared, primed canvas. Sketch your composition lightly before applying paint. Use high-quality brushes and oils to create vibrant, long-lasting artworks. Remember to paint 'fat over lean,' meaning each subsequent layer should contain more oil to prevent cracking. Allow each layer to dry before applying another. Clean your brushes often and avoid solvents that might damage them. Finally, always work in a well-ventilated space.
Después de completar este tutorial, has integrado con éxito MongoDB Vector Search con LangChainGo para compilar una aplicación RAG. Has logrado lo siguiente:
Se inició y configuró el entorno necesario para soportar la aplicación
Se almacenaron los datos personalizados en MongoDB y se instanció MongoDB como un almacén vectorial
Compiló un índice de búsqueda de vectores de MongoDB en sus datos, habilitando capacidades de búsqueda semántica
Se utilizaron incrustaciones vectoriales para recuperar datos semánticamente relevantes
Resultados de búsqueda mejorados al incorporar filtros de metadatos
Implementé un flujo de trabajo de RAG usando MongoDB Vector Search para brindar respuestas significativas a preguntas basadas en sus datos
Próximos pasos
Para obtener más información sobre cómo comenzar con la búsqueda de vectores en MongoDB, consulta la Guía rápida de búsqueda de vectores en MongoDB y, a continuación, selecciona Go en el menú desplegable.
Para obtener más información sobre vector embeddings, consulta Cómo crear incrustaciones vectoriales manualmente y luego selecciona Go en el menú desplegable.
Para aprender a integrar LangChainGo y Hugging Face, consulte generación de recuperación aumentada (RAG) con MongoDB.
Para aprender a implementar RAG sin necesidad de claves o créditos API, consulta Crear una Implementación Local de RAG con MongoDB Vector Search.
MongoDB también proporciona el siguiente recurso para desarrolladores:
Tip
Para aprender más sobre cómo integrar LangChainGo, OpenAI y MongoDB, consulta Cómo usar MongoDB Atlas como un Vector Store con OpenAI Embeddings.