
Among the most challenging aspects of relational databases is the ability to work with vector data at scale. Luckily, the PostgreSQL database server supports a pgvector extension that allows you to efficiently store and query data over Machine Learning (ML) generated embeddings.
ML Embeddings contain arrays of floating point numbers that represent objects such as images, text, video, and audio. These numerical representations express objects in a high-dimensional vector space making search similarities searches possible. Below are sample real-life applications of embeddings:
This guide implements the PostgreSQL pgvector extension to run an AI-powered search application that answers customer Frequently Asked Questions (FAQs) using Python on a Ubuntu 22.04 server. You are to use sample data from the Vultr FAQ section to simulate common queries.
Before your begin:
This guide uses the OpenAI API to generate real embeddings to test the
pgvectorextensions. A free OpenAI account offers three API requests per minute and works well for this guide. In a production environment, add a payment method to your account and increase the limit
pgvector PostgreSQL ExtensionUpdate the server packages
Install the Python pip package manager
Using pip, install the Python PostgreSQL driver for Python and the OpenAI modules
Install the OpenAI modules
Install the postgresql-client package
The above command installs the PostgreSQL psql CLI tool used to access your managed database.
Using psql, log in to your Vultr Managed Database for PostgreSQL
The above command connects to your Vultr Managed Database for PostgreSQL using a connection sting. Replace the following details with your actual Vultr database credentials:
username: vultradminpassword: example-passwordhost: prod-db.vultrdb.comport: 16751Create a new sample company_db database
Switch to the database
Output:
Enable the pgvector extension on each database that requires the extension
When successful, your output should look like the one below:
Query the pg_type table to verify the availability of a new VECTOR data type
Output:
As displayed in the above output, the new vector data type is ready for use.
Create a resource_base table
The above command creates a table with the following columns:
resource_id is a PRIMARY KEY that uniquely identifies records and uses the BIGSERIAL data type.resource_description is a text-based column that stores answers to questions that customers are likely to ask in the FAQ question.embedding uses the VECTOR data type with 1536 dimensions to store embeddings for the resource_description values. Later in this guide, you are to generate embeddings using the OpenAI API.Exit the PostgreSQL console
Before developing the Python application, below is an overview of how the AI-powered search logic works:
The application accepts Linux curl commands containing sample POST requests to populate the resource_base table. For example, for the query What payment methods do you accept?, the tool expects the following resource:
When populating the table, the application queries the OpenAI API to get embeddings for the resource_description you're adding to the database.
The application adds the resource_description and vector data representing the resource in the database using the following SQL command:
insert into resource_base (resource_description, embedding) values (%s, %s)'
The application accepts a curl GET with a target FAQ the customer searches for in the database. Then, the application uses the PostgreSQL pgvector operators to perform a similarity search and returns the most relevant answer in JSON format
Based on the project's logic flow, create a separate database gateway file for the PostgreSQL database as described in the steps below.
Create a new project directory
Switch to the directory
Using a text editor such as Nano, create a new postgresql_gateway.py file
Add the following contents to the file. Replace all db_... values with your actual Vultr Managed Database for PostgreSQL details
Save and close the file
The above Python code performs the following logic:
import psycopg2 loads the Python driver that connects the Python application to the Vultr Managed Database for PostgreSQLclass PostgresqlGateway: section establishes a new module with the following methods:def __init__(self):: A constructor method that runs when you create an instance of the class. This method connects to the PostgreSQL database you created earlier.def insert_resource(self, resource_description, embedding):: Accepts the resource_description value and an embedding value from the OpenAI API, then, it uses the PostgreSQL database connection to add a new entry to the database table using the insert into resource_base (resource_description, embedding) values (%s, %s) query.def get_resources(self, embedding): Accepts an embedding generated from an HTTP GET query to search related records in the database table using the select resource_id, resource_description from resource_base ORDER BY embedding <=> (%s::vector(1536)) LIMIT 1; query. The query uses the LIMIT clause to return a single row. In production, change this value to return more rows depending on your use case<=> vector operator. The operator is suitable for finding similar documents and performing natural language searches. Other common operators you can use when designing different types of applications include:<-><->In this section, set up an application that creates embeddings during the following operations:
resource_base tableresource_base table for querying purposesCreate a central module to generate the embeddings instead of rewriting the logic on each file as described in the steps below.
Create a new embeddings_generator.py file
Add the following contents to the file. Replace the openai.api_key value with your actual OpenAPI key
Save and close the file
The above application uses the OpenAI text-embedding-ada-002 model to generate the embeddings. The model is suitable for text similarity searches. It accepts text inputs and converts them to numerical representations (embeddings).
Below is how the above Python module works:
import openai declaration loads the OpenAI module functions into the projectclass EmbeddingsGenerator: establishes one class with a single methodcreate_embedding(self, user_query): inputs raw text (user_query) and uses the OpenAI API to generate embeddings (vector data). Later, the sample application uses the embeddings to perform similarity searchesTo run the Python application, create an entry point to the application as described in the steps below
Create a new index.py file
Add the following contents to the file
Save and close the file
Below is how the application works:
import... section imports the HTTP functionalities to create a web server using the declared inbuilt Python modules. Additionally, you import the custom postgresql_gateway and embeddings_generator modules you coded earlier in the projectclass HttpHandler(http.server.SimpleHTTPRequestHandler): is a handler class for the HTTP server running on port 8080do_POST(self): processes POST requests that contain a resource you want to add to the resource_base table. This method retrieves an embedding from the OpenAI generator and passes it to the PostgreSQL database serverdo_GET(self): runs the GET method to retrieve a user's query from an HTTP request. Then, it gets an embedding of the query from the OpenAI API and passes it to the PostgreSQL database to perform a similarity searchStart the application
In a new terminal window, establish another SSH connection to your server
Populate the resource_base table using the following curl POST commands
This guide uses data samples from the Vultr FAQ section. When using a free OpenAI developer account, you must send one query every 20 seconds to avoid the rate-limiting error
Output:
Send HTTP GET queries to perform a similarity search on the PostgreSQL server. For example:
Do you charge for stopped instances?
Output:
I linked my credit card but I see a small charge on my card! What gives?
Output:
Full pricing list?
Output:
Is pricing the same in all data center locations?
Output.
What payment methods do you accept?
Output:
Based on the above results, the application returns the most relevant answer for each query to the user
After using the curl commands to send HTTP POST requests, the application populates the resource_base table with AI-generated embeddings. Access the company_db database to verify the embeddings as described in this section.
Access the PostgreSQL database server. Append the company_db database to your connection string to access the database directly
Query the resource_base table using the SQL command below. Apply the PostgreSQL SUBSTRING() and RIGHT() functions to return only the first and last few characters from the resource_description and embedding columns
Your output should look like the one below:
Create an index on the resource_base table. This is necessary when scaling your application and have more records in the table. The lists parameter in the ivfflat index sets the number of clusters that PostgreSQL creates when building the index. PostgreSQL uses the index clusters in its algorithms to find the relation between vectors. Apply the following formula when setting the list value:
For a table with less than one million rows use:
For tables with more than one million rows use:
Verify that you have a minimum of ten clusters. When records in the sample application are still few, use the minimum value of 10
In this guide, you implemented the PostgreSQL pgvector extension that generates and queries data over ML-generated embeddings. You created a sample database that stores a company knowledge base using vector data, and used the PostgreSQL cosine distance operator <=> to query data to display the most relevant results. For more information, visit the PgVector extension repository.
To implement more PostgreSQL use cases on your database, visit the following resources:
0 Comments
Be the first to comment and share your perspective with the community.