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.
You'll construct:
This is ideal for:
Now let's get started.
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
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.
Add to your Post model observer or event.
`$post->embedding = $this->embed($post->content); $post->save();`
Migration for posts:
`$table->json('embedding')->nullable();`
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); } }`
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)); }`
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.
\



