Docs Menu
Docs Home
/ /

LangChainGo 통합 시작하기

MongoDB Vector Search를 LangChainGo 와 통합하여 대규모 언어 모델(LLM) 애플리케이션을 빌드 하고 RAG(검색 강화 생성)를 구현 수 있습니다. 이 튜토리얼에서는 MongoDB Vector Search를 LangChainGo와 함께 사용하여 데이터에서 시맨틱 검색 수행하고 RAG 구현 빌드 방법을 보여 줍니다. 구체적으로 다음 조치를 수행합니다.

  1. 환경을 설정합니다.

  2. 사용자 지정 데이터를 MongoDB 에 저장합니다.

  3. 데이터에 MongoDB Vector Search 인덱스 생성합니다.

  4. 다음 벡터 검색 쿼리를 실행합니다.

    • 시맨틱 검색.

    • 메타데이터 사전 필터링을 통한 시맨틱 검색.

  5. MongoDB Vector Search를 사용하여 데이터에 대한 질문에 답변 RAG 를 구현합니다.

LangChainGo는 LangChain의 Go 프로그래밍 언어 구현으로, 커뮤니티 주도로 개발된 LangChain 프레임워크의 타사 포트입니다.

LangChain은 '체인'을 사용하여 LLM 애플리케이션 생성을 간소화하는 오픈 소스 프레임워크 입니다. 체인은 RAG를 포함한 다양한 AI 사용 사례에 결합할 수 있는 LangChain 전용 구성 요소입니다.

MongoDB Vector Search를 LangChain과 통합하면 MongoDB 벡터 데이터베이스로 사용하고 MongoDB Vector Search를 사용하여 데이터에서 의미적으로 유사한 문서를 검색하여 RAG 를 구현. RAG에 대해 자세히 학습 MongoDB 사용한 RAG(검색 보강 생성)를 참조하세요.

LangChainGo는 AI 애플리케이션을 위한 LLM의 오케스트레이션을 용이하게 하여 LangChain의 기능을 고 (Go) 에코시스템 에 도입합니다. 또한 개발자가 MongoDB 포함하여 선호하는 벡터 저장소 호환 데이터베이스에 연결할 수 있습니다.

이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.

이 튜토리얼을 실행하려면 먼저 환경을 설정해야 합니다. 다음 단계를 완료하여 환경을 설정하세요.

1

터미널에서 다음 명령을 실행하여 langchaingo-mongodb 라는 새 디렉토리를 만들고 프로젝트를 초기화합니다.

mkdir langchaingo-mongodb
cd langchaingo-mongodb
go mod init langchaingo-mongodb
2

다음 명령을 실행합니다.

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
3

langchaingo-mongodb 프로젝트 디렉토리 에서 .env 파일 만들고 다음 줄을 추가합니다.

OPENAI_API_KEY="<openai-api-key>"
VOYAGEAI_API_KEY="<voyage-api-key>"
MONGODB_URI="<connection-string>"

자리 표시자 값을 MongoDB cluster 의 OpenAI API 키, Voyage AI API 키 및 SRV 연결 문자열 로 바꿉니다. 연결 문자열 다음 형식을 사용해야 합니다.

mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
4

langchaingo-mongodb 프로젝트 디렉토리 에서 main.go이라는 파일 만듭니다. 튜토리얼 전체에서 이 파일 에 코드를 추가합니다.

이 섹션에서는 사용자 지정 데이터를 MongoDB 에 로드하고 MongoDB 벡터 저장소라고도 하는 벡터 데이터베이스로 인스턴스화하는 비동기 함수를 정의합니다.

1

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"
)
2

다음 코드는 이러한 작업을 수행합니다.

  • 다음을 지정하여 Atlas를 벡터 저장소로 구성합니다.

    • langchaingo_db.test 문서를 저장할 Atlas의 컬렉션으로.

    • vector_index 벡터 저장 쿼리하는 데 사용할 인덱스 로 사용됩니다.

    • text 를 원시 텍스트 콘텐츠가 포함된 필드의 이름으로 지정합니다.

    • embedding 벡터 임베딩이 포함된 필드의 이름으로.

  • 다음을 수행하여 사용자 지정 데이터를 준비합니다.

    • 각 문서에 대한 텍스트를 정의합니다.

    • LangChainGo의 mongovector 패키지를 사용하여 텍스트의 임베딩을 생성합니다. 이 패키지는 MongoDB에 문서 임베딩을 저장하고 저장된 임베딩을 검색할 수 있게 합니다.

    • 텍스트, 임베딩, 메타데이터를 포함하는 문서를 생성합니다.

  • 구성된 문서를 Atlas에 삽입한 후, 벡터 저장소를 인스턴스화합니다.

다음 코드를 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
}
3

파일 저장한 후 다음 명령을 실행 데이터를 MongoDB 에 로드합니다.

go run main.go
Connected to MongoDB Atlas.
Successfully added 3 documents to the collection.

main.go를 실행 후 Atlas 사용하는 경우 Atlas UIlangchaingo_db.test 네임스페이스 로 이동하여 벡터 임베딩을 확인할 수 있습니다.

벡터 저장 에서 벡터 검색 쿼리를 활성화 하려면 langchaingo_db.test 컬렉션 에 MongoDB Vector Search 인덱스 만듭니다.

main.go 파일 의 맨 위에 다음 가져오기를 추가합니다.

import (
// Other imports...
"fmt"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)

main() 함수 외부의 main.go 파일 에서 다음 함수를 정의합니다. 다음 함수는 MongoDB 컬렉션 에 대한 벡터 검색 인덱스 생성하고 관리 .

  1. SearchIndexExists 함수는 지정된 이름의 검색 인덱스 존재하고 쿼리 가능한지 확인합니다.

  2. CreateVectorSearchIndex 함수는 지정된 컬렉션 에 벡터 검색 인덱스 만듭니다. 이 함수는 인덱스 생성되어 쿼리할 수 있을 때까지 차단됩니다.

// 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
}

main() 함수에서 앞의 함수를 호출하여 벡터 저장 컬렉션 및 인덱스 만듭니다. 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.")
}

파일 저장한 후 다음 명령을 실행 MongoDB Vector Search 인덱스 생성합니다.

go run main.go
Checking if search index exists.
Creating vector search index...
Successfully created vector search index.

main.go를 실행한 후 클러스터의 langchaingo_db.test 컬렉션으로 이동하여 Atlas UI에서 벡터 검색 인덱스를 볼 수 있습니다.

이 섹션에서는 벡터화된 데이터에서 실행할 수 있는 다양한 쿼리를 보여줍니다. 이제 인덱스를 만들었으므로 벡터 검색 쿼리를 실행할 수 있습니다.

Basic Semantic Search 또는 Semantic Search with Filtering 탭을 선택하여 해당 코드를 확인합니다.

1

시맨틱 검색은 쿼리와 의미적으로 관련된 정보를 조회합니다. 다음 코드는 SimilaritySearch() 메서드를 사용하여 "Prevent weeds" 문자열에 대한 시맨틱 검색을 수행하고 결과를 첫 번째 문서로 제한합니다.

// 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)
2
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}]

인덱스된 필드를 컬렉션의 다른 값과 비교하는 MQL 일치 표현식을 사용하여 데이터를 사전 필터링할 수 있습니다. 필터링하려는 모든 메타데이터 필드를 filter 유형으로 인덱싱해야 합니다. 자세한 내용은 벡터 검색을 위해 필드를 인덱싱하는 방법을 참조하세요.

1

main.go 파일 에 다음 종속성을 추가합니다.

import (
// Other imports...
"github.com/tmc/langchaingo/vectorstores"
)
2

다음 코드에서는 SimilaritySearch() 메서드를 사용하여 문자열 "Tulip care"에 대한 시맨틱 검색을 수행합니다. 다음 매개변수를 지정합니다.

  • 1으로 반환할 문서 수

  • 점수 임계값이 0.60입니다.

필터하다 metadata.type: post 와 일치하고 점수 임계값을 포함하는 문서 반환합니다.

// 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)
3
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}]

이 섹션에서는 MongoDB Vector Search 및 LangChainGo를 사용하여 RAG를 구현 설명합니다. 이제 MongoDB Vector Search를 사용하여 의미적으로 유사한 문서를 조회 했으므로, 다음 코드 예시 사용하여 LLM이 MongoDB Vector Search에서 반환된 문서에 대한 질문에 답변 하도록 프롬프트를 표시합니다.

1

main.go 파일 의 맨 위에 다음 가져오기를 추가합니다.

import (
// Other imports...
"strings"
"github.com/tmc/langchaingo/llms/openai"
"github.com/tmc/langchaingo/chains"
"github.com/tmc/langchaingo/prompts"
"github.com/tmc/langchaingo/vectorstores"
)
2

이 코드는 다음을 수행합니다.

  • 의미론적으로 유사한 문서를 쿼리 리트리버로 MongoDB Vector Search를 인스턴스화합니다.

  • 검색된 문서를 쿼리 의 컨텍스트로 사용하도록 LLM에 지시하는 LangChainGo 프롬프트 템플릿을 정의합니다. LangChainGo는 이러한 문서를 {{.context}} 입력 변수에, 쿼리 {{.question}} 변수에 채웁니다.

  • OpenAI의 채팅 모델을 사용하여 제공된 프롬프트 템플릿에 따라 컨텍스트 인식 응답을 생성하는 체인을 구성합니다.

  • 프롬프트와 검색기를 사용하여 관련 컨텍스트를 수집하고 초보자를 위한 그림에 관한 샘플 쿼리를 체인에 보냅니다.

  • LLM의 응답과 컨텍스트로 사용된 문서를 반환하고 출력합니다.

// 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)
3

파일을 저장한 후 다음 명령을 실행합니다. 생성된 응답은 다를 수 있습니다.

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.

이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.

이 튜토리얼을 실행하려면 먼저 환경을 설정해야 합니다. 다음 단계를 완료하여 환경을 설정하세요.

1

터미널에서 다음 명령을 실행하여 langchaingo-mongodb라는 새 디렉터리를 만들고 프로젝트를 초기화합니다.

mkdir langchaingo-mongodb
cd langchaingo-mongodb
go mod init langchaingo-mongodb
2

다음 명령을 실행합니다.

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
3

langchaingo-mongodb 프로젝트 디렉토리 에서 .env 파일 만들고 다음 줄을 추가합니다.

OPENAI_API_KEY="<api-key>"
MONGODB_URI="<connection-string>"

자리 표시자 값을 MongoDB cluster 의 OpenAI API 키 및 SRV 연결 문자열 로 바꿉니다. 연결 문자열 다음 형식을 사용해야 합니다.

mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
4

langchaingo-mongodb 프로젝트 디렉토리 에서 main.go이라는 파일 만듭니다. 튜토리얼 전체에서 이 파일 에 코드를 추가합니다.

이 섹션에서는 사용자 지정 데이터를 MongoDB 에 로드하고 MongoDB 벡터 저장소라고도 하는 벡터 데이터베이스로 인스턴스화하는 비동기 함수를 정의합니다.

1

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"
)
2

다음 코드는 이러한 작업을 수행합니다.

  • 다음을 지정하여 Atlas를 벡터 저장소로 구성합니다.

    • langchaingo_db.test 문서를 저장할 Atlas의 컬렉션으로.

    • vector_index 벡터 저장 쿼리하는 데 사용할 인덱스 로 사용됩니다.

    • text 를 원시 텍스트 콘텐츠가 포함된 필드의 이름으로 지정합니다.

    • embedding 벡터 임베딩이 포함된 필드의 이름으로.

  • 다음을 수행하여 사용자 지정 데이터를 준비합니다.

    • 각 문서에 대한 텍스트를 정의합니다.

    • LangChainGo의 mongovector 패키지를 사용하여 텍스트의 임베딩을 생성합니다. 이 패키지는 MongoDB에 문서 임베딩을 저장하고 저장된 임베딩을 검색할 수 있게 합니다.

    • 텍스트, 임베딩, 메타데이터를 포함하는 문서를 생성합니다.

  • 구성된 문서를 Atlas에 삽입한 후, 벡터 저장소를 인스턴스화합니다.

다음 코드를 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
}
3

파일 저장한 후 다음 명령을 실행 데이터를 MongoDB 에 로드합니다.

go run main.go
Connected to MongoDB Atlas.
Successfully added 3 documents to the collection.

main.go를 실행 후 Atlas 사용하는 경우 Atlas UIlangchaingo_db.test 네임스페이스 로 이동하여 벡터 임베딩을 확인할 수 있습니다.

벡터 저장 에서 벡터 검색 쿼리를 활성화 하려면 langchaingo_db.test 컬렉션 에 MongoDB Vector Search 인덱스 만듭니다.

main.go 파일 의 맨 위에 다음 가져오기를 추가합니다.

import (
// Other imports...
"fmt"
"time"
"go.mongodb.org/mongo-driver/v2/bson"
)

main() 함수 외부의 main.go 파일 에서 다음 함수를 정의합니다. 다음 함수는 MongoDB 컬렉션 에 대한 벡터 검색 인덱스 생성하고 관리 .

  1. SearchIndexExists 함수는 지정된 이름의 검색 인덱스 존재하고 쿼리 가능한지 확인합니다.

  2. CreateVectorSearchIndex 함수는 지정된 컬렉션 에 벡터 검색 인덱스 만듭니다. 이 함수는 인덱스 생성되어 쿼리할 수 있을 때까지 차단됩니다.

// 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
}

main() 함수에서 앞의 함수를 호출하여 벡터 저장 컬렉션 및 인덱스 만듭니다. 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.")
}

파일 저장한 후 다음 명령을 실행 MongoDB Vector Search 인덱스 생성합니다.

go run main.go
Checking if search index exists.
Creating vector search index...
Successfully created vector search index.

main.go를 실행한 후 클러스터의 langchaingo_db.test 컬렉션으로 이동하여 Atlas UI에서 벡터 검색 인덱스를 볼 수 있습니다.

이 섹션에서는 벡터화된 데이터에서 실행할 수 있는 다양한 쿼리를 보여줍니다. 이제 인덱스를 만들었으므로 벡터 검색 쿼리를 실행할 수 있습니다.

Basic Semantic Search 또는 Semantic Search with Filtering 탭을 선택하여 해당 코드를 확인합니다.

1

시맨틱 검색은 쿼리와 의미적으로 관련된 정보를 조회합니다. 다음 코드는 SimilaritySearch() 메서드를 사용하여 "Prevent weeds" 문자열에 대한 시맨틱 검색을 수행하고 결과를 첫 번째 문서로 제한합니다.

// 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)
2
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}]

인덱스된 필드를 컬렉션의 다른 값과 비교하는 MQL 일치 표현식을 사용하여 데이터를 사전 필터링할 수 있습니다. 필터링하려는 모든 메타데이터 필드를 filter 유형으로 인덱싱해야 합니다. 자세한 내용은 벡터 검색을 위해 필드를 인덱싱하는 방법을 참조하세요.

1

main.go 파일 에 다음 종속성을 추가합니다.

import (
// Other imports...
"github.com/tmc/langchaingo/vectorstores"
)
2

다음 코드에서는 SimilaritySearch() 메서드를 사용하여 문자열 "Tulip care"에 대한 시맨틱 검색을 수행합니다. 다음 매개변수를 지정합니다.

  • 1으로 반환할 문서 수

  • 점수 임계값이 0.60입니다.

필터하다 metadata.type: post 와 일치하고 점수 임계값을 포함하는 문서 반환합니다.

// 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)
3
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}]

이 섹션에서는 MongoDB Vector Search 및 LangChainGo를 사용하여 RAG를 구현 설명합니다. 이제 MongoDB Vector Search를 사용하여 의미적으로 유사한 문서를 조회 했으므로, 다음 코드 예시 사용하여 LLM이 MongoDB Vector Search에서 반환된 문서에 대한 질문에 답변 하도록 프롬프트를 표시합니다.

1

main.go 파일 의 맨 위에 다음 가져오기를 추가합니다.

import (
// Other imports...
"strings"
"github.com/tmc/langchaingo/chains"
"github.com/tmc/langchaingo/prompts"
"github.com/tmc/langchaingo/vectorstores"
)
2

이 코드는 다음을 수행합니다.

  • 의미론적으로 유사한 문서를 쿼리 리트리버로 MongoDB Vector Search를 인스턴스화합니다.

  • 검색된 문서를 쿼리 의 컨텍스트로 사용하도록 LLM에 지시하는 LangChainGo 프롬프트 템플릿을 정의합니다. LangChainGo는 이러한 문서를 {{.context}} 입력 변수에, 쿼리 {{.question}} 변수에 채웁니다.

  • OpenAI의 채팅 모델을 사용하여 제공된 프롬프트 템플릿에 따라 컨텍스트 인식 응답을 생성하는 체인을 구성합니다.

  • 프롬프트와 검색기를 사용하여 관련 컨텍스트를 수집하고 초보자를 위한 그림에 관한 샘플 쿼리를 체인에 보냅니다.

  • LLM의 응답과 컨텍스트로 사용된 문서를 반환하고 출력합니다.

// 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)
3

파일을 저장한 후 다음 명령을 실행합니다. 생성된 응답은 다를 수 있습니다.

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.

이 튜토리얼을 완료했다면 MongoDB Vector Search와 LangChainGo를 성공적으로 통합하여 RAG 애플리케이션 빌드 되었습니다. 다음을 완료했습니다:

  • 애플리케이션 지원 데 필요한 환경 시작 및 구성

  • 사용자 지정 데이터를 MongoDB 에 저장하고 MongoDB 벡터 저장 로 인스턴스화

  • 데이터에 MongoDB Vector Search 인덱스 구축하여 시맨틱 검색 기능을 지원합니다.

  • 벡터 임베딩을 사용하여 의미적으로 관련성 있는 데이터 조회

  • 메타데이터 필터를 통합하여 검색 결과 향상

  • 데이터를 기반으로 질문에 의미 있는 답변을 제공하기 위해 MongoDB Vector Search를 사용하여 RAG 워크플로를 구현했습니다.

  • MongoDB Vector Search를 시작하는 방법에 대해 자세히 학습하려면 MongoDB Vector Search 빠른 시작을 참조한 다음 드롭다운 메뉴에서 Go 를 선택합니다.

  • 벡터 임베딩에 대해 자세히 알아보려면 벡터 임베딩 생성 방법을 참조하고 드롭다운 메뉴에서 Go를 선택하세요.

  • LangChainGo와 Hugging Face를 통합하는 방법을 학습하려면 MongoDB 사용한 RAG(검색 보강 생성)를 참조하세요.

  • API 키나 크레딧 없이 RAG를 구현 방법을학습 MongoDB Vector Search를 사용하여 로컬 RAG 구현 구축하기를 참조하세요.

MongoDB는 다음과 같은 개발자 리소스도 제공합니다.

LangChainGo, OpenAI, MongoDB 통합에 대해 자세히 학습하려면 OpenAI 임베딩을 통해 MongoDB Atlas 벡터 스토어로 사용하기를 참조하세요.

돌아가기

LangChain JS/TS

이 페이지의 내용