LangChainGo 통합 시작하기
이 페이지의 내용
Atlas Vector Search LangChainGo 와 통합하여 대규모 언어 모델(LLM) 애플리케이션을 빌드 하고 RAG(검색 강화 생성)를 구현 수 있습니다. 이 튜토리얼에서는 LangChainGo와 함께 Atlas Vector Search 사용하여 데이터에서 시맨틱 검색 수행하고 RAG 구현 빌드 방법을 보여 줍니다. 구체적으로 다음 조치를 수행합니다.
환경을 설정합니다.
Atlas에 사용자 지정 데이터를 저장합니다.
데이터에 Atlas Vector Search 검색 인덱스를 만듭니다.
다음 벡터 검색 쿼리를 실행합니다.
시맨틱 검색.
메타데이터 사전 필터링을 통한 시맨틱 검색.
Atlas Vector Search를 사용하여 RAG 를 구현하여 데이터에 대한 질문에 답변하세요.
배경
LangChainGo는 LangChain의 고 (Go) 프로그래밍 언어 구현 입니다. 이는 LangChain 프레임워크 의 커뮤니티 중심의 타사 포트입니다.
LangChain은 '체인'을 사용하여 LLM 애플리케이션 생성을 간소화하는 오픈 소스 프레임워크입니다. 체인은 RAG 를 포함한 다양한 AI 사용 사례에 결합할 수 있는 LangChain 관련 구성 요소입니다.
Atlas Vector Search LangChain과 통합하면 Atlas 벡터 데이터베이스 로 사용하고 Atlas Vector Search 사용하여 데이터에서 의미적으로 유사한 문서를 검색하여 RAG 를 구현 . RAG에 대해 자세히 학습 Atlas Vector Search 사용한 RAG(검색 보강 생성)를 참조하세요.
LangChainGo는 AI 애플리케이션을 위한 LLM의 오케스트레이션을 용이하게 하여 LangChain의 기능을 고 (Go) 에코시스템 에 도입합니다. 또한 개발자가 MongoDB 포함한 벡터 저장소를 사용하여 원하는 데이터베이스에 연결할 수 있습니다.
전제 조건
이 튜토리얼을 완료하려면 다음 조건을 충족해야 합니다.
Atlas 계정, 클러스터에서 MongoDB 버전 6.0.11, 7.0.2 또는 그 이상 실행( RC 포함). IP 주소가 Atlas 프로젝트의 액세스 목록에 포함되어 있는지 확인하세요. 자세히 알아보려면 클러스터 생성을 참조하세요.
OpenAI API 키입니다. API 요청에 사용할 수 있는 크레딧이 있는 OpenAI 계정이 있어야 합니다. OpenAI 계정 등록에 대한 자세한 내용은 OpenAI API 웹사이트를 참조하세요.
Go 프로젝트를 실행하기 위한 터미널 및 코드 편집기입니다.
환경 설정
먼저 이 튜토리얼을 진행하려면 환경을 설정하다 해야 합니다. 환경을 설정하다 하려면 다음 단계를 완료하세요.
환경 변수를 초기화합니다.
langchaingo-mongodb
프로젝트 디렉토리 에서 .env
파일 만들고 다음 줄을 추가합니다.
OPENAI_API_KEY="<api-key>" ATLAS_CONNECTION_STRING="<connection-string>"
자리 표시자 값을 Atlas cluster 의 OpenAI API 키 및 SRV 연결 문자열 로 바꿉니다. 연결 문자열 다음 형식을 사용해야 합니다.
mongodb+srv://<username>:<password>@<cluster-name>.mongodb.net/<dbname>
Atlas를 Vector Store로 사용
이 섹션에서는 사용자 지정 데이터를 Atlas 에 로드하고 Atlas 벡터 저장 라고도 하는 벡터 데이터베이스 로 인스턴스화하는 비동기 함수를 정의합니다.
다음 종속성을 가져옵니다.
main.go
파일 의 맨 위에 다음 가져오기를 추가합니다.
package main import ( "context" "fmt" "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" )
벡터 저장 세부 정보를 정의합니다.
다음 코드는 이러한 작업을 수행합니다.
다음을 지정하여 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("ATLAS_CONNECTION_STRING") if uri == "" { log.Fatal("Set your 'ATLAS_CONNECTION_STRING' 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 Atlas 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 Atlas.") // 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 Atlas 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 Atlas 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 }
고 (Go) 프로젝트 실행합니다.
파일을 저장한 후 다음 명령을 실행하여 데이터를 Atlas에 로드합니다.
go run main.go
Connected to MongoDB Atlas. Successfully added 3 documents to the collection.
팁
main.go
를 실행한 후 클러스터의 langchaingo_db.test
컬렉션으로 이동하여 Atlas UI에서 벡터 임베딩을 볼 수 있습니다.
Atlas Vector Search 인덱스 만들기
참고
Atlas Vector Search 검색 인덱스를 만들려면 Atlas 프로젝트에 대한 Project Data Access Admin
이상의 액세스 권한이 있어야 합니다.
벡터 저장소에서 벡터 검색 쿼리를 활성화하려면 langchaingo_db.test
컬렉션에 Atlas Vector Search 인덱스를 생성하세요.
main.go
파일 의 맨 위에 다음 가져오기를 추가합니다.
import ( // Other imports... "time" "go.mongodb.org/mongo-driver/v2/bson" )
main()
함수 외부의 main.go
파일 에서 다음 함수를 정의합니다. 다음 함수는 MongoDB 컬렉션 에 대한 벡터 검색 인덱스 생성하고 관리 .
SearchIndexExists
함수는 지정된 이름의 검색 인덱스 존재하고 쿼리 가능한지 확인합니다.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.") }
파일 저장한 후 다음 명령을 실행 Atlas 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 에서 벡터 검색 인덱스 볼 수 있습니다.
Vector Search 쿼리 실행
이 섹션에서는 벡터화된 데이터에 대해 실행 수 있는 다양한 쿼리를 설명합니다. 이제 인덱스 만들었으므로 main.go
파일 에 다음 종속성을 추가하여 벡터 검색 쿼리를 활성화 .
package main import ( // Other imports... "github.com/tmc/langchaingo/vectorstores" )
main()
함수에 다음 코드를 추가하여 데이터에 대해 벡터 검색 쿼리를 실행 .
메인 함수에 다음 코드를 추가하고 파일 저장합니다.
시맨틱 검색 쿼리 와 의미 있게 관련된 정보를 조회합니다. 다음 코드는 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)
다음 명령을 실행하여 쿼리를 실행합니다.
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
유형으로 필터링하도록 인덱싱해야 합니다. 자세한 내용은 벡터 검색을 위해 필드를 인덱싱하는 방법을 참조하세요.
메인 함수에 다음 코드를 추가하고 파일 저장합니다.
다음 코드에서는 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)
다음 명령을 실행하여 쿼리를 실행합니다.
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}]
데이터에 대한 질문에 답변
이 섹션에서는 Atlas Vector Search 및 LangChainGo를 사용하여 RAG를 구현 설명합니다. 이제 Atlas Vector Search 사용하여 의미적으로 유사한 문서를 조회 했으므로, 다음 코드 예시 사용하여 LLM이 Atlas Vector Search 에서 반환된 문서에 대한 질문에 답변 하도록 프롬프트를 표시합니다.
메인 함수 끝에 다음 코드를 추가하고 파일 저장합니다.
이 코드는 다음을 수행합니다.
Atlas 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.Print("Question:", question) log.Println("Generated Answer:", responseText)
다음 명령을 실행하여 파일을 실행합니다.
파일을 저장한 후 다음 명령을 실행합니다. 생성된 응답은 다를 수 있습니다.
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.
이 튜토리얼을 완료하면 Atlas Vector Search LangChainGo와 성공적으로 통합하여 RAG 애플리케이션 빌드 되었습니다. 다음을 완료했습니다:
애플리케이션 지원 데 필요한 환경 시작 및 구성
Atlas 에 사용자 지정 데이터를 저장하고 Atlas 벡터 저장 로 인스턴스화
데이터에 Atlas Vector Search 인덱스 구축하여 시맨틱 검색 기능을 지원합니다.
벡터 임베딩을 사용하여 의미적으로 관련 있는 데이터 조회
메타데이터 필터를 통합하여 향상된 검색 결과
데이터를 기반으로 질문에 의미 있는 답변을 제공하기 위해 Atlas Vector Search 사용하여 RAG 워크플로를 구현했습니다.
다음 단계
LangChainGo와 Hugging Face를 통합하는 방법을 학습 Atlas Vector Search 사용한 RAG(검색 증대 생성)를 참조하세요.
MongoDB는 다음과 같은 개발자 리소스도 제공합니다.
다음도 참조하세요.
LangChainGo, OpenAI, MongoDB 통합에 대해 자세히 학습 OpenAI 임베딩을 통해 MongoDB Atlas 벡터 스토어로 사용하기를 참조하세요.