Docs Menu

LangChainGo 통합 시작하기

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

  1. 환경을 설정합니다.

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

  3. 데이터에 Atlas Vector Search 검색 인덱스를 만듭니다.

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

    • 시맨틱 검색.

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

  5. 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 프로젝트를 실행하기 위한 터미널 및 코드 편집기입니다.

  • 컴퓨터에 고 (Go) 설치합니다.

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

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
3

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>
4

langchaingo-mongodb 프로젝트 디렉토리 에서 main.go 라는 파일 만든 다음, 다음 코드를 복사하여 파일 에 붙여넣습니다. 튜토리얼 전체에서 이 파일 에 코드를 추가합니다.

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

1

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

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

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

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

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

    • 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
}
3

파일을 저장한 후 다음 명령을 실행하여 데이터를 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 프로젝트에 대한 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 컬렉션 에 대한 벡터 검색 인덱스 생성하고 관리 .

  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.")
}

파일 저장한 후 다음 명령을 실행 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 에서 벡터 검색 인덱스 볼 수 있습니다.

이 섹션에서는 벡터화된 데이터에 대해 실행 수 있는 다양한 쿼리를 설명합니다. 이제 인덱스 만들었으므로 main.go 파일 에 다음 종속성을 추가하여 벡터 검색 쿼리를 활성화 .

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

main() 함수에 다음 코드를 추가하여 데이터에 대해 벡터 검색 쿼리를 실행 .

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

다음 코드에서는 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)
2
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 에서 반환된 문서에 대한 질문에 답변 하도록 프롬프트를 표시합니다.

1

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

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

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

  • 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)
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.

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

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

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

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

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

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

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

LangChainGo와 Hugging Face를 통합하는 방법을 학습 Atlas Vector Search 사용한 RAG(검색 증대 생성)를 참조하세요.

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

다음도 참조하세요.