Friday Read: Integrating Retrieval-Augmented-Generation in 5 Minutes
Learn how to work with Vector Embeddings in Supabase
In this short note, I will try to walk you through integrating Retrieval-Augmented-Generation (RAG) into any web app under 5 minutes. Retrieval-Augmented Generation is a blend of text retrieval and text generation that empowers your web app to deliver dynamic, context-aware content to its users.
Setting the Stage
Before we start with integrating Retrieval-Augmented-Generation into your web app, let’s attach the necessary packages. In this case, we need to harness the following crucial dependencies:
dotenv: For managing environment variables.
langchain/document: To work with language documents.
langchain/embeddings/openai: To utilize OpenAI embeddings.
langchain/vectorstores/supabase: For storing and retrieving vectors.
@supabase/supabase-js: To establish a connection with a Supabase database.
langchain/text_splitter: To split text into manageable chunks.
In addition to these specialised packages, we'll also make use of Node.js built-in modules, namely fs
and path
, for handling file system operations.
Here's how you can begin by importing these dependencies:
import dotenv from "dotenv";
import { Document } from "langchain/document";
import { OpenAIEmbeddings } from "langchain/embeddings/openai";
import { SupabaseVectorStore } from "langchain/vectorstores/supabase";
import { createClient } from "@supabase/supabase-js";
import { RecursiveCharacterTextSplitter } from "langchain/text_splitter";
import fs from "fs";
import path from "path";
dotenv.config({ path: `.env.local` });
Preparing Your Documents
A critical aspect of this integration process is ensuring you have some nice data to work with. For this example, let's assume you possess a set of text documents stored within a specific directory that are written in Markdown. The initial step involves loading and splitting these documents for further processing.
const fileNames = fs.readdirSync("your_documents");
const splitter = RecursiveCharacterTextSplitter.fromLanguage("markdown", {
chunkSize: 1000,
chunkOverlap: 50,
});
const langchainDocs = await Promise.all(
fileNames.map(async (fileName) => {
const filePath = path.join("blogs", fileName);
const fileContent = fs.readFileSync(filePath, "utf8");
const splitDocs = await splitter.splitText(fileContent);
return splitDocs.map((doc) => {
return new Document({
metadata: { fileName },
pageContent: doc,
});
});
})
);
In this code, we perform the following steps:
Retrieve the names of files within the "your_documents" directory.
Employ the
RecursiveCharacterTextSplitter
to segment the content into manageable chunks suitable for processing.Create
Document
objects for each chunk, complete with metadata, such as the filename.
Integration with Supabase
With your documents ready to go, the next phase of the integration journey involves incorporating them into a Supabase VectorStore. Supabase is an excellent choice for this purpose.
const auth = {
detectSessionInUrl: false,
persistSession: false,
autoRefreshToken: false,
};
const client = createClient(
process.env.SUPABASE_URL,
process.env.SUPABASE_PRIVATE_KEY,
{ auth }
);
await SupabaseVectorStore.fromDocuments(
langchainDocs.flat(),
new OpenAIEmbeddings({ openAIApiKey: process.env.OPENAI_API_KEY }),
{
client,
tableName: "documents",
}
);
In this code, we follow these steps:
Configure Supabase authentication for secure access.
Create a Supabase client to establish a connection with the database.
Utilize
SupabaseVectorStore
to store your language documents, ensuring they are readily accessible.Harness
OpenAIEmbeddings
to embed your text, preparing it for retrieval and generation.
Adding It as a Build Script
To streamline this integration process as part of your web app's build cycle, I’d recommend implementing it as a custom script within your project's package.json
file. Running this script allows you to update or add new content to your database automatically.
Here's an example of how you can include this script in your package.json
:
"scripts": {
"build-content": "node path/to/your/embeddings.js"
}
Be sure to replace "path/to/your/embdeddings.js"
with the actual path to the script containing the previously discussed code. This modification enables you to initiate the integration process by executing the command npm run build-content
.
Summary
In this concise note, I've delved into the process of seamlessly integrating Retrieval-Augmented-Generation into any web application.
We initiated by importing the essential dependencies, meticulously organised our text data, and efficiently integrated it into a Supabase database to facilitate storage and retrieval.
This approach empowers you to provide your users with dynamic, context-aware content that elevates their experience with your web application. And all of that in under 5 minutes :).