Alma ile artırılmış oluşturma (RAG)

Firebase Genkit, almayla geliştirilmiş nesil oluşturmanıza yardımcı olan soyutlamalar sunar (RAG) akışlarının yanı sıra ilgili araçlarla entegrasyon sağlayan eklentiler de sunar.

RAG nedir?

Alma ile artırılmış oluşturma, harici veya bağlantılı verileri toplamak için kullanılan bir bilgi kaynaklarını LLM'nin yanıtlarına dönüştürme. Proje hedeflerinizin Çünkü LLM'ler genellikle birçok farklı alanda eğitilirken LLM'lerin pratik kullanımı için genellikle belirli alan bilgisi (ör. Örneğin, müşterilerin deneyimlerine yanıt vermek için şu konularla ilgili sorular: .

Çözümlerden biri, daha ayrıntılı veriler kullanarak modelde ince ayar yapmaktır. Ancak bu hem işlem maliyeti hem de gereken çaba açısından pahalı olabilir. ve yeterli eğitim verisi hazırlamak için bu belgeyi kullanın.

Buna karşılık RAG, harici veri kaynaklarını aktarılma süresini gösterir. Mesela Arkadaş Bitkiler projesinin "What is Bart to Lisa?" (Bart'ın Leyla ile ilişkisi nedir?) genişletilebilir ("genişletilmiş") olabilir alakalı bilgiler bulunuyor ve "Homer ve Marge'ın çocuklarının adı Bart, Leyla ve Maggie. Bart'ın ilişkisi nedir? Leyla'ya mı göndermek istiyorsun?"

Bu yaklaşımın çeşitli avantajları vardır:

  • Modeli yeniden eğitmeniz gerekmeyeceğinden bu yöntem daha uygun maliyetli olabilir.
  • Veri kaynağınızı sürekli olarak güncelleyebilirsiniz. Böylece LLM hemen bilgileri nasıl kullanacağınızı öğreneceksiniz.
  • Artık LLM'nizin yanıtlarında referanslara atıfta bulunabilirsiniz.

Diğer yandan, RAG kullanımı doğal olarak daha uzun istemler ve bazı LLM API'leri anlamına gelir. hizmetleri, gönderdiğiniz her giriş jetonu için ücret alır. Sonuç olarak, satın alma süreci dengesini sağlayabilirsiniz.

RAG çok geniş bir alandır ve bunu başarmak için kullanılan birçok farklı RAG kalitesini artırırsınız. Temel Genkit çerçevesi, aşağıdakileri yapmak için iki ana soyutlama sunar: size yardımcı olacak:

  • Dizine ekleyenler: Bir "dizine" doküman ekleyin.
  • Katıştırıcılar: Dokümanları vektörel temsile dönüştürür
  • Toplayıcılar: Belirli bir sorguda "dizinden" doküman alma.

Genkit hakkında herhangi bir fikriniz olmadığı için bu tanımlar kasıtlı olarak geniş kapsamlıdır. "dizin" nedir nasıl alındığı veya tam olarak nasıl alındığı. Yalnızca Genkit bir Document biçimi sağlar ve diğer her şey, alıcı veya dizine ekleme aracı uygulama sağlayıcısıdır.

Dizine ekleyenler

Dizin, dokümanlarınızın kaydını tutmaktan sorumlu olur. Belirli bir sorgu verildiğinde ilgili dokümanları hızla alabilirsiniz. Bu, en çok genellikle belgelerinizi dizine eklemek için kullanılan bir vektör veritabanı çok boyutlu vektörlere yerleştiriyoruz. Metin yerleştirme (opak olarak) bir metin parçasıyla ifade edilen kavramları temsil eder. Bunlar üretilir makine öğrenimi modellerini kullanır. Yerleştirme kullanılarak metin dizine eklendiğinde, bir vektör veritabanı, kavramsal olarak ilişkili metinleri kümeleyip dokümanlar yeni bir metin dizesiyle (sorgu) ilişkilidir.

Belgeleri oluşturma amacıyla alabilmek için önce doküman dizininize alabilirsiniz. Tipik bir besleme akışı takip etmek için:

  1. Büyük dokümanları daha küçük dokümanlara bölün, böylece yalnızca alakalı dokümanlar bazı bölümler, istemlerinizi genişletmek için kullanılır ("parçalama"). Bu gerekli çünkü birçok LLM'nin sınırlı bir bağlam penceresi olduğundan bu dili bir istemle tüm dokümanları dahil edebilirsiniz.

    Genkit, yerleşik öbekleme kitaplıkları sağlamaz; ancak her halükarda Genkit ile uyumlu kaynak kitaplıklar var.

  2. Her parça için yerleştirmeler 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 kullanabilir.

  3. Metin parçasını ve dizinini veritabanına ekleyin.

Çalışıyorsanız besleme akışınızı nadiren veya yalnızca bir kez çalıştırabilirsiniz. istikrarlı bir veri kaynağına sahip olmalıdır. Öte yandan verilere dayalı sürekli değişen bir şey varsa besleme akışını ( (ör. Cloud Firestore tetikleyicisinde) bir belge güncellendiğinde.

Yerleştirme

Yerleştirme, içeriği (metin, görsel, ses vb.) alıp orijinal içeriğin anlamsal anlamını kodlayan sayısal bir vektör oluşturan bir işlevdir. Yukarıda belirtildiği gibi yerleştiriciler, dizine ekleme sürecinin bir parçası olarak kullanılır. Bununla birlikte, dizin içermeyen yerleştirmeler oluşturmak için bağımsız olarak da kullanılabilirler.

Toplayıcılar

Retriever, herhangi bir belgenin türüne ilişkin mantığı içeren bir kavramdır. içerir. En popüler alma durumları genellikle vektör depoları gibi Genkit'te bir retriever veri döndüren herhangi bir fonksiyon olabilir.

Bir retriever oluşturmak için, sağlanan uygulamalardan birini veya kendinizinkini oluşturun.

Desteklenen dizinleyiciler, alıcılar ve yerleştiriciler

Genkit, eklenti sistemi üzerinden dizinleyici ve retriever desteği sağlar. İlgili içeriği oluşturmak için kullanılan şu eklentiler resmi olarak desteklenmektedir:

Ayrıca Genkit, önceden tanımlanmış fonksiyonlar aracılığıyla aşağıdaki vektör depolarını da destekler. kod şablonlarını kullanarak veritabanı yapılandırmanız ve şema:

Model yerleştirme desteği aşağıdaki eklentiler aracılığıyla sağlanır:

Eklenti Modeller
Google Üretken Yapay Zeka Gecko metni yerleştirme
Google Vertex Yapay Zeka Gecko metni yerleştirme

RAG Akışı Tanımlama

Aşağıdaki örneklerde, restoran menüsüyle ilgili PDF dokümanları koleksiyonunu nasıl aktarabileceğiniz gösterilmektedir bunları bir vektör veritabanına aktarma ve hangi gıda öğelerinin mevcut olduğunu belirleyen bir akışta kullanmak üzere alma.

Bağımlıları yükleme

Bu örnekte langchaingo ve langchaingo adresindeki textsplitter kitaplığını kullanacağız. ledongthuc/pdf PDF ayrıştırma kitaplığı:

go get github.com/tmc/langchaingo/textsplitter
go get github.com/ledongthuc/pdf

Dizine Ekleleyici Tanımlama

Aşağıdaki örnekte, PDF dokümanları koleksiyonunu aktarmak için dizinleyicinin nasıl oluşturulacağı gösterilmektedir yerel bir vektör veritabanında saklamanız gerekir.

Yerel dosya tabanlı Vektör benzerliği alicisini kullanır. Genkit'in, basit test ve prototip oluşturma için kullanıma hazır sunduğu verilere sahip olursunuz ( üretimde kullanılması)

Dizine ekleyici 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()

err := vertexai.Init(ctx, &vertexai.Config{})
if err != nil {
    log.Fatal(err)
}
err = localvec.Init()
if err != nil {
    log.Fatal(err)
}

menuPDFIndexer, _, err := localvec.DefineIndexerAndRetriever(
    "menuQA",
    localvec.Config{
        Embedder: vertexai.Embedder("text-embedding-004"),
    },
)
if err != nil {
    log.Fatal(err)
}

Parçalama yapılandırması oluşturma

Bu örnekte, basit bir metin sağlayan textsplitter kitaplığı kullanılmaktadır ayırıcıyı kullanır.

Aşağıdaki tanım, dokümanı döndürmek için parçalama işlevini yapılandırır 20 karakterlik kısımlar arasında çakışma olacak şekilde 200 karakterlik segmentler.

splitter := textsplitter.NewRecursiveCharacter(
    textsplitter.WithChunkSize(200),
    textsplitter.WithChunkOverlap(20),
)

Bu kitaplık için daha fazla parçalama seçeneğini şurada bulabilirsiniz: langchaingo dokümanları.

Dizine ekleme akışınızı tanımlayın

genkit.DefineFlow(
    "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 = menuPDFIndexer.Index(ctx, &ai.IndexerRequest{Documents: 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ışını çalıştırdıktan sonra, vektör veritabanı satın alma adımlarıyla Genkit akışlarında kullanıma hazır hale getirebilirsiniz.

Alma ile bir akış tanımlama

Aşağıdaki örnek, RAG akışında bir retriever'ı nasıl kullanabileceğinizi gösterir. Beğenme örneğin, bu örnekte Genkit'in dosya tabanlı vector retriever'ını kullanır. ve üretimde kullanmamanız gerekir.

    ctx := context.Background()

    err := vertexai.Init(ctx, &vertexai.Config{})
    if err != nil {
        log.Fatal(err)
    }
    err = localvec.Init()
    if err != nil {
        log.Fatal(err)
    }

    model := vertexai.Model("gemini-1.5-pro")

    _, menuPdfRetriever, err := localvec.DefineIndexerAndRetriever(
        "menuQA",
        localvec.Config{
            Embedder: vertexai.Embedder("text-embedding-004"),
        },
    )
    if err != nil {
        log.Fatal(err)
    }

    genkit.DefineFlow(
        "menuQA",
        func(ctx context.Context, question string) (string, error) {
            // Retrieve text relevant to the user's question.
            docs, err := menuPdfRetriever.Retrieve(ctx, &ai.RetrieverRequest{
                Document: ai.DocumentFromText(question, nil),
            })
            if err != nil {
                return "", err
            }

            // Construct a system message containing the menu excerpts you just
            // retrieved.
            menuInfo := ai.NewSystemTextMessage("Here's the menu context:")
            for _, doc := range docs.Documents {
                menuInfo.Content = append(menuInfo.Content, doc.Content...)
            }

            // Call Generate, including the menu information in your prompt.
            resp, err := model.Generate(ctx, &ai.GenerateRequest{
                Messages: []*ai.Message{
                    ai.NewSystemTextMessage(`
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.`),
                    menuInfo,
                    ai.NewUserTextMessage(question),
                },
            }, nil)
            if err != nil {
                return "", err
            }

            return resp.Text()
        })

Kendi dizinleyici ve alıcılarınızı yazma

Kendi retriever'ınızı da oluşturabilirsiniz. Bu, Dokümanlar, Genkit'te desteklenmeyen bir doküman deposunda yönetiliyor (ör. MySQL, Google Drive vb.). Genkit SDK'sı esnek yöntemler sunar. Dokümanları almak için özel kod sağlarsınız.

Ayrıca, mevcut retriever'lar üzerinde derleme yapan custom retriever'lar da tanımlayabilirsiniz. ve gelişmiş RAG teknikleri (ör. yeniden sıralama veya istem uzantısı) görürsünüz.

Örneğin, kullanmak istediğiniz özel bir yeniden sıralama işleviniz olduğunu varsayalım. İlgili içeriği oluşturmak için kullanılan aşağıdaki örnekte, işlevinizi menü retriever'ın bir önceki halidir:

type CustomMenuRetrieverOptions struct {
    K          int
    PreRerankK int
}
advancedMenuRetriever := ai.DefineRetriever(
    "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.
        response, err := menuPDFRetriever.Retrieve(ctx, &ai.RetrieverRequest{
            Document: req.Document,
            Options:  localvec.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
    },
)