Genkit, almayla artırılmış üretim (RAG) akışları oluşturmanıza yardımcı olan soyutlamalar ve ilgili araçlarla entegrasyon sağlayan eklentiler sağlar.
RAG nedir?
Almayla artırılmış üretim, harici bilgi kaynaklarını LLM yanıtlarına dahil etmek için kullanılan bir tekniktir. LLM'ler genellikle geniş bir materyal grubuyla eğitilse de pratik kullanımları genellikle belirli alan bilgisi gerektirir. Bu nedenle, LLM'leri özelleştirmeniz önemlidir (ör. müşterilerin şirketinizin ürünleriyle ilgili sorularını yanıtlamak için bir LLM kullanmak isteyebilirsiniz).
Bir çözüm, daha spesifik veriler kullanarak modelde ince ayar yapmaktır. Ancak bu, hem işlem maliyeti hem de yeterli eğitim verisi hazırlamak için gereken çaba açısından pahalı olabilir.
Buna karşılık RAG, modele aktarılan istemlere harici veri kaynaklarını dahil ederek çalışır. Örneğin, "Bart ile Lisa'nın ilişkisi nedir?" isteminde bazı alakalı bilgiler eklenerek istemin genişletilebileceğini ("artırıldığını") düşünebilirsiniz. Böylece, "Homer ve Marge'ın çocukları Bart, Lisa ve Maggie'dir. Bartu ile Lisa'nın ilişkisi nedir?"
Bu yaklaşımın birkaç avantajı vardır:
- Modeli yeniden eğitmeniz gerekmediği için daha uygun maliyetli olabilir.
- Veri kaynağınızı sürekli olarak güncelleyebilirsiniz. LLM, güncellenen bilgileri hemen kullanabilir.
- Artık LLM'nizin yanıtlarında referanslar gösterebilirsiniz.
Öte yandan, RAG kullanmak doğal olarak daha uzun istemler anlamına gelir ve bazı LLM API hizmetleri gönderdiğiniz her giriş jetonu için ücret alır. Son olarak, uygulamalarınız için maliyet dengelerini değerlendirmeniz gerekir.
RAG çok geniş bir alandır ve en yüksek kaliteli RAG'ye ulaşmak için birçok farklı teknik kullanılır. Temel Genkit çerçevesi, RAG'yi gerçekleştirmenize yardımcı olmak için iki temel soyutlama sunar:
- Dizine ekleyenler: "Dizin"e belge ekler.
- Yerleştirenler: Dokümanları vektör temsiline dönüştürür.
- Alıcı: Bir sorgu verildiğinde "dizin"den doküman alır.
Genkit, "dizin"in ne olduğu veya belgelerden tam olarak nasıl alındığı konusunda herhangi bir görüş belirtmediği için bu tanımlar kasıtlı olarak geniş tutulmuştur. Genkit yalnızca bir Document
biçimi sağlar ve geri alma veya dizine ekleme uygulama sağlayıcısı tarafından tanımlanan diğer her şey.
Dizine ekleyenler
Dizin, belirli bir sorgu verildiğinde ilgili belgeleri hızlı bir şekilde alabileceğiniz şekilde belgelerinizi takip etmekten sorumludur. Bu işlem genellikle, belgeleriniz için dizin oluşturan ve yerleşik olarak adlandırılan çok boyutlu vektörleri kullanan bir vektör veritabanı kullanılarak gerçekleştirilir. Metin yerleştirme, bir metin pasajında ifade edilen kavramları (şeffaf olmayan bir şekilde) temsil eder. Bunlar, özel amaçlı makine öğrenimi modelleri kullanılarak oluşturulur. Bir vektör veritabanı, metni yerleştirmesini kullanarak dizine ekleyerek kavramsal olarak alakalı metinleri gruplandırabilir ve yeni bir metin dizesiyle (sorgu) ilgili belgeleri alabilir.
Oluşturma amacıyla doküman almadan önce dokümanları doküman dizine eklemeniz gerekir. Tipik bir besleme akışı aşağıdakileri yapar:
İstemlerinizi geliştirmek için yalnızca alakalı bölümlerin kullanılması amacıyla büyük belgeleri daha küçük belgelere bölün (bu işleme "bölümlere ayırma" denir). Bu, birçok LLM'nin sınırlı bir bağlam penceresine sahip olması nedeniyle gereklidir. Bu da istemle birlikte dokümanların tamamını eklemeyi pratik hale getirmez.
Genkit, yerleşik parçalara ayırma kitaplıkları sağlamaz. Bununla birlikte, Genkit ile uyumlu açık kaynak kitaplıklar mevcuttur.
Her bir parça için yerleştirilmiş öğeler oluşturun. Kullandığınız veritabanına bağlı olarak bunu bir yerleştirme oluşturma modeliyle açıkça yapabilir veya veritabanı tarafından sağlanan yerleştirme oluşturucuyu kullanabilirsiniz.
Metin parçasını ve dizini veritabanına ekleyin.
Veri kaynağınız kararlıysa besleme akışınızı seyrek olarak veya yalnızca bir kez çalıştırabilirsiniz. Öte yandan, sık sık değişen verilerle çalışıyorsanız besleme akışını sürekli olarak çalıştırabilirsiniz (ör. bir Cloud Firestore tetikleyicisinde, bir doküman her güncellendiğinde).
Yerleştirenler
Yerleştirici, içerik (metin, resim, ses vb.) alan ve orijinal içeriğin anlamsal anlamını kodlayan sayısal bir vektör oluşturan bir işlevdir. Yukarıda da belirtildiği gibi, dizine ekleme işleminin bir parçası olarak yerleştiricilerden yararlanılır. Ancak bu modeller, dizin olmadan yerleşik oluşturmak için bağımsız olarak da kullanılabilir.
Retriever
Retriever, her türlü belge alma işlemiyle ilgili mantığı kapsayan bir kavramdır. En popüler getirme işlemleri genellikle vektör depolarından getirmeyi içerir. Ancak Genkit'te, veri döndüren herhangi bir işlev veri alıcı olabilir.
Getirici oluşturmak için sağlanan uygulamalardan birini kullanabilir veya kendi getiricinizi oluşturabilirsiniz.
Desteklenen dizine ekleyenler, veri toplayıcılar ve yerleştiriciler
Genkit, eklenti sistemi aracılığıyla dizine ekleme ve alma desteği sağlar. Aşağıdaki eklentiler resmi olarak desteklenir:
- Pinecone bulut vektör veritabanı
Ayrıca Genkit, veritabanı yapılandırmanıza ve şemanıza göre özelleştirebileceğiniz önceden tanımlanmış kod şablonları aracılığıyla aşağıdaki vektör depolarını destekler:
pgvector
ile PostgreSQL
Model yerleştirme desteği aşağıdaki eklentiler aracılığıyla sağlanır:
Eklenti | Modeller |
---|---|
Google Üretken Yapay Zeka | Metin yerleştirme |
RAG akışı tanımlama
Aşağıdaki örneklerde, restoran menüsü PDF dokümanlarından oluşan bir koleksiyonu bir vektör veritabanına nasıl besleyebileceğiniz ve hangi yiyeceklerin mevcut olduğunu belirleyen bir akışta kullanmak üzere nasıl alabileceğiniz gösterilmektedir.
Bağımlıları yükleme
Bu örnekte, langchaingo
'daki textsplitter
kitaplığını ve ledongthuc/pdf
PDF ayrıştırma kitaplığını kullanacağız:
go get github.com/tmc/langchaingo/textsplitter
go get github.com/ledongthuc/pdf
Dizine Eklemeyi Tanımlama
Aşağıdaki örnekte, PDF dokümanlarından oluşan bir koleksiyonu besleyecek ve bunları yerel bir vektör veritabanında saklayacak bir dizine ekleme aracının nasıl oluşturulacağı gösterilmektedir.
Basit test ve prototipleme için Genkit'in hazır olarak sunduğu yerel dosya tabanlı vektör benzerliği alma aracını kullanır. Bu kodu üretimde kullanmayın.
Dizinleyiciyi oluşturma
// Import Genkit's file-based vector retriever, (Don't use in production.)
import "github.com/firebase/genkit/go/plugins/localvec"
// Vertex AI provides the text-embedding-004 embedder model.
import "github.com/firebase/genkit/go/plugins/vertexai"
ctx := context.Background()
g, err := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.VertexAI{}))
if err != nil {
log.Fatal(err)
}
if err = localvec.Init(); err != nil {
log.Fatal(err)
}
menuPDFIndexer, _, err := localvec.DefineIndexerAndRetriever(g, "menuQA",
localvec.Config{Embedder: googlegenai.VertexAIEmbedder(g, "text-embedding-004")})
if err != nil {
log.Fatal(err)
}
Bölme yapılandırması oluşturma
Bu örnekte, dokümanları vektörleştirilebilecek segmentlere ayırmak için basit bir metin ayırıcı sağlayan textsplitter
kitaplığı kullanılır.
Aşağıdaki tanım, parçalara ayırma işlevini 20 karakterlik parçalar arasında 20 karakterlik çakışma olacak şekilde 200 karakterlik doküman segmentleri döndürecek şekilde yapılandırır.
splitter := textsplitter.NewRecursiveCharacter(
textsplitter.WithChunkSize(200),
textsplitter.WithChunkOverlap(20),
)
Bu kitaplıkla ilgili daha fazla parçalara ayırma seçeneğini langchaingo
dokümanlarında bulabilirsiniz.
Dizine ekleme akışınızı tanımlama
genkit.DefineFlow(
g, "indexMenu",
func(ctx context.Context, path string) (any, error) {
// Extract plain text from the PDF. Wrap the logic in Run so it
// appears as a step in your traces.
pdfText, err := genkit.Run(ctx, "extract", func() (string, error) {
return readPDF(path)
})
if err != nil {
return nil, err
}
// Split the text into chunks. Wrap the logic in Run so it appears as a
// step in your traces.
docs, err := genkit.Run(ctx, "chunk", func() ([]*ai.Document, error) {
chunks, err := splitter.SplitText(pdfText)
if err != nil {
return nil, err
}
var docs []*ai.Document
for _, chunk := range chunks {
docs = append(docs, ai.DocumentFromText(chunk, nil))
}
return docs, nil
})
if err != nil {
return nil, err
}
// Add chunks to the index.
err = ai.Index(ctx, menuPDFIndexer, ai.WithDocs(docs...))
return nil, err
},
)
// Helper function to extract plain text from a PDF. Excerpted from
// https://github.com/ledongthuc/pdf
func readPDF(path string) (string, error) {
f, r, err := pdf.Open(path)
if f != nil {
defer f.Close()
}
if err != nil {
return "", err
}
reader, err := r.GetPlainText()
if err != nil {
return "", err
}
bytes, err := io.ReadAll(reader)
if err != nil {
return "", err
}
return string(bytes), nil
}
Dizine ekleme akışını çalıştırma
genkit flow:run indexMenu "'menu.pdf'"
indexMenu
akışı çalıştırıldıktan sonra vektör veritabanı dokümanlarla doldurulur ve getirme adımlarına sahip Genkit akışlarında kullanılmaya hazır hale gelir.
Alma işlemi içeren bir akış tanımlama
Aşağıdaki örnekte, bir RAG akışında retriever'ı nasıl kullanabileceğiniz gösterilmektedir. Dizine ekleme örneğinde olduğu gibi bu örnekte de Genkit'in dosya tabanlı vektör alıcısını kullanılır. Bu alıcıyı üretimde kullanmamanız önerilir.
ctx := context.Background()
g, err := genkit.Init(ctx, genkit.WithPlugins(&googlegenai.VertexAI{}))
if err != nil {
log.Fatal(err)
}
if err = localvec.Init(); err != nil {
log.Fatal(err)
}
model := googlegenai.VertexAIModel(g, "gemini-1.5-flash")
_, menuPdfRetriever, err := localvec.DefineIndexerAndRetriever(
g, "menuQA", localvec.Config{Embedder: googlegenai.VertexAIEmbedder(g, "text-embedding-004")},
)
if err != nil {
log.Fatal(err)
}
genkit.DefineFlow(
g, "menuQA",
func(ctx context.Context, question string) (string, error) {
// Retrieve text relevant to the user's question.
resp, err := ai.Retrieve(ctx, menuPdfRetriever, ai.WithTextDocs(question))
if err != nil {
return "", err
}
// Call Generate, including the menu information in your prompt.
return genkit.GenerateText(ctx, g,
ai.WithModelName("googleai/gemini-2.0-flash"),
ai.WithDocs(resp.Documents),
ai.WithSystem(`
You are acting as a helpful AI assistant that can answer questions about the
food available on the menu at Genkit Grub Pub.
Use only the context provided to answer the question. If you don't know, do not
make up an answer. Do not add or change items on the menu.`)
ai.WithPrompt(question),
})
Kendi dizine ekleme ve alma işlemlerinizi yazma
Kendi alıcınızı da oluşturabilirsiniz. Bu seçenek, dokümanlarınız Genkit'te desteklenmeyen bir doküman deposunda yönetiliyorsa (ör. MySQL, Google Drive vb.) kullanışlıdır. Genkit SDK'sı, doküman getirme işlemi için özel kod sağlamanıza olanak tanıyan esnek yöntemler sunar.
Ayrıca, Genkit'teki mevcut alıcılara dayalı özel alıcıları tanımlayabilir ve bunlara gelişmiş RAG teknikleri (yeniden sıralama veya istem genişletme gibi) uygulayabilirsiniz.
Örneğin, kullanmak istediğiniz özel bir yeniden sıralama işleviniz olduğunu varsayalım. Aşağıdaki örnekte, işlevinizi daha önce tanımlanan menü alıcısına uygulayan özel bir alıcı tanımlanmaktadır:
type CustomMenuRetrieverOptions struct {
K int
PreRerankK int
}
advancedMenuRetriever := genkit.DefineRetriever(
g, "custom", "advancedMenuRetriever",
func(ctx context.Context, req *ai.RetrieverRequest) (*ai.RetrieverResponse, error) {
// Handle options passed using our custom type.
opts, _ := req.Options.(CustomMenuRetrieverOptions)
// Set fields to default values when either the field was undefined
// or when req.Options is not a CustomMenuRetrieverOptions.
if opts.K == 0 {
opts.K = 3
}
if opts.PreRerankK == 0 {
opts.PreRerankK = 10
}
// Call the retriever as in the simple case.
resp, err := ai.Retrieve(ctx, menuPDFRetriever,
ai.WithDocs(req.Query),
ai.WithConfig(ocalvec.RetrieverOptions{K: opts.PreRerankK}),
)
if err != nil {
return nil, err
}
// Re-rank the returned documents using your custom function.
rerankedDocs := rerank(response.Documents)
response.Documents = rerankedDocs[:opts.K]
return response, nil
},
)