Display matching jobs to applicants in Job websites, recommended videos , posts, products etc. One way to do it is using Sql queries which get difficult if there are too many parameters to consider. AI comes to rescue. Simplest way to build recommendation engine is using Vector embeddings .
Here I will use Open AI to generate embeddings, we can use other LLM models as well.
- Laravel project with two models with data seeded applicants and Jobs
- Database setup with PGVector and pg laravel package, open ai php package
- jobs table embeddings col
- To Text on each model, Open AI embedding service and vector search service
- Batch embeddings
- Display Results
Setup in your OS Pgvector, https://www.postgresql.org/download/
Setup Postgres using Postgress.app
php artisan make:model Applicant -mf
php artisan make:model Job -mf
jobs
- title
- description
- requirements
- benefits
- salary
- company_name
- type (remote, hybrid, onsite)
- embeddings
$table->vector('embedding', 1536);
class CreateJobsTable extends Migration
{
/**
* Run the migrations.
*
* @return void
*/
public function up()
{
Schema::create('jobs', function (Blueprint $table) {
$table->id();
$table->string('title');
$table->text('description');
$table->text('requirements');
$table->text('benefits');
$table->string('company_name');
$table->enum('type', ['remote', 'hybrid', 'onsite']);
$table->vector('embeddings', 1536);
$table->timestamps();
});
}
/**
* Reverse the migrations.
*
* @return void
*/
public function down()
{
Schema::dropIfExists('jobs');
}
}
applicants
- full_name
- summary
- experiences
- skills
- expected_salary
- preferences (location, skill, type)
- For Laravel Pgvector laravel
- Open AI PHP SDK
composer require openai-php/client pgvector/pgvector
[[Indexing PostgresSQL Vector]]
App/Models/Job
public function toEmbeddingText(Job $job)
{
$text = <<<TEXT
Job Title : {$job->title}
Salary: {$job->salary}
Company: {$job->company_name}
Location: {$job->city}, {$job->state}
Description: {$job->salary},
Skills: {$job->skills}
TEXT;
return $text;
}
<?php
namespace App\Models;
use Illuminate\Database\Eloquent\Model;
class Applicant extends Model
{
public function toEmbeddingText(): string
{
$text = <<<TEXT
Summary: {$this->summary}
Experience:
{$this->experiences->implode('description', "\n")}
Skills:
{$this->skills->implode('name', ', ')}
Expected Salary: {$this->expected_salary}
Preferences:
Location: {$this->preferences->implode('location', ', ')}
Skills: {$this->preferences->implode('skill', ', ')}
Type: {$this->preferences->implode('type', ', ')}
TEXT;
return $text;
}
}
<?php
use OpenAI;
class EmbeddingService
{
private OpenAI\Client $openai;
public string $embeddingModel='text-embedding-3-small';
public function __construct()
{
$this->openai = OpenAI::client(config('services.openai.apikey'));
}
public function getClient()
{
return $this->openai;
}
public function generateEmbedding(string $text) : array
{
$response = $this->openai->embeddings()->create([
'model' => $this->embeddingModel,
'input' => $text,
]);
return $response->embeddings[0]->embedding;
}
}
<?php
use App\Models\Job;
use Pgvector\Laravel\Distance;
class VectorDBService
{
public function querySimilarItems(Applicant $applicant, int $topK = 5): Collection
{
if(!$applicant->embedding) {
$embedding =
$embeddingService->generateEmbedding($applicant);
$applicant->update(['embedding'=> $embedding]);
}
return Job::query()
->nearestNeighbors('embedding',
$applicant->embedding, Distance::L2)
->take($topK)
->get();
}
}
- Generate [[JsonL File for Open AI batch Embedding]]
- Upload it (JsonL file) , create a batch, save batch id to db
id
batch_id string
status
results_file_path
[Batch Status](// https://platform.openai.com/docs/guides/batch/4-checking-the-status-of-a-batch Track the status (pending, completed, failed)) 3. check if status of batch is completed, if yes download results JsonL file and process each it and bulk insert in DB
private function generateJsonLine(Job $model): string
{
$text = $model->toEmbeddingText();
$data = [
'custom_id' => (string)$model->getKey(),
'method'=> 'POST',
'url' => '/v1/embeddings',
'body' => [
'model' => 'text-embedding-3-small',
'input' => $text
],
];
return json_encode($data, JSON_UNESCAPED_UNICODE);
}