It is tedious, inconsistent, and frequently incorrect to manually tag blog posts. With AI embeddings, Laravel can automatically determine the topic of a blog post and assign the appropriate tags without the need for human intervention.

This guide demonstrates how to create a complete Laravel AI auto-tagging system using:

This is one of the most useful AI enhancements for any Laravel-based CMS or blog system.

What We're Constructing

You'll construct:

This is ideal for:

Now let's get started.

Step 1: Create Migration for Tag Embeddings

Run:

php artisan make:migration create_tag_embeddings_table

Migration:

    public function up()
    {
        Schema::create('tag_embeddings', function (Blueprint $table) {
            $table->id();
            $table->unsignedBigInteger('tag_id')->unique();
            $table->json('embedding'); // store vector
            $table->timestamps();
        });
    }

Run:

php artisan migrate

Step 2: Generate Embeddings for Tags

Create a command:

php artisan make:command GenerateTagEmbeddings

Add logic:

`public function handle()         {             $tags = Tag::all();              foreach ($tags as $tag) {                 $vector = $this->embed($tag->name);                  TagEmbedding::updateOrCreate(                     ['tag_id' => $tag->id],                     ['embedding' => json_encode($vector)]                 );                  $this->info("Embedding created for tag: {$tag->name}");             }         }          private function embed($text)         {             $client = new \GuzzleHttp\Client();              $response = $client->post("https://api.openai.com/v1/embeddings", [                 "headers" => [                     "Authorization" => "Bearer " . env('OPENAI_API_KEY'),                     "Content-Type" => "application/json",                 ],                 "json" => [                     "model" => "text-embedding-3-large",                     "input" => $text                 ]             ]);              $data = json_decode($response->getBody(), true);              return $data['data'][0]['embedding'] ?? [];         }`

Run once:

php artisan generate:tag-embeddings

Now all tags have AI meaning vectors.

Step 3: Save Embeddings for Each Post

Add to your Post model observer or event.

`$post->embedding = $this->embed($post->content);         $post->save();`

Migration for posts:

`$table->json('embedding')->nullable();`

Step 4: Matching Algorithm (Post → Tags)

Create a helper class:

`class EmbeddingHelper         {             public static function cosineSimilarity($a, $b)             {                 $dot = array_sum(array_map(fn($i, $j) => $i * $j, $a, $b));                 $magnitudeA = sqrt(array_sum(array_map(fn($i) => $i * $i, $a)));                 $magnitudeB = sqrt(array_sum(array_map(fn($i) => $i * $i, $b)));                 return $dot / ($magnitudeA * $magnitudeB);             }         }`

Step 5: Assign Tags Automatically (Queue Job)

Create job:

php artisan make:job AutoTagPost

Job logic:

`public function handle()         {             $postEmbedding = json_decode($this->post->embedding, true);              $tags = TagEmbedding::with('tag')->get();              $scores = [];             foreach ($tags as $te) {                 $sim = EmbeddingHelper::cosineSimilarity(                     $postEmbedding,                     json_decode($te->embedding, true)                 );                 $scores[$te->tag->id] = $sim;             }              arsort($scores); // highest similarity first              $best = array_slice($scores, 0, 5, true); // top 5 matches              $this->post->tags()->sync(array_keys($best));         }`

Step 6: Cron Job to Process New Posts

Add to app/Console/Kernel.php:

`protected function schedule(Schedule $schedule)         {             $schedule->command('ai:autotag-posts')->hourly();         }`

Create command:

php artisan make:command AutoTagPosts

Command logic:

`public function handle()         {             $posts = Post::whereNull('tags_assigned_at')->get();              foreach ($posts as $post) {                 AutoTagPost::dispatch($post);                 $post->update(['tags_assigned_at' => now()]);             }         }`

Now, every hour, Laravel processes all new posts and assigns AI-selected tags.

Step 7: Test the Full Flow

Useful enhancements