muzorix/ai-blog-engine
Composer 安装命令:
composer require muzorix/ai-blog-engine
包简介
Laravel AI blog engine: discover topics, generate articles in queued chunks, humanize and fact-check content, optimize for SEO/AEO/GEO, create FAQs and images, manage internal links and duplicates, and run the full workflow from a Filament admin panel.
README 文档
README
Fully automated, backend-only AI blog/article system for Laravel.
Discover topics, write full articles, humanize them, run fact-checking and SEO/AEO/GEO optimization, generate images and FAQs, manage internal linking and duplicate detection, and control the full lifecycle through a Filament admin panel. You bring your own frontend (Blade, Livewire, Inertia, or headless).
MIT licensed. Multi-provider throughout: Groq, Gemini, OpenAI, Claude.
Table of Contents
- What This Package Does
- Feature List
- Requirements
- Installation
- Configuration
- Admin Panel Guide
- The Article Pipeline
- Day-to-Day Usage
- Artisan Commands
- Scheduled Jobs
- Publishing Modes
- Topic Discovery
- AI Providers
- Image Providers
- Frontend Integration
- Models & Querying
- Programmatic Usage
- Database Tables
- Troubleshooting
- Package Structure
- License
What This Package Does
Most AI writing tools generate text and stop. This package treats article creation as a full lifecycle:
- Discover and score topics from RSS, News API, or manual entry
- Generate structured articles section-by-section
- Humanize, fact-check, and optimize for SEO, AEO, and GEO
- Generate FAQs, images, and internal links
- Flag duplicates and route to review (or auto-publish)
- Monitor freshness and refresh aging content
Everything is self-hosted. Your data, your pipeline, your providers.
Feature List
| Module | Description |
|---|---|
| Topic Discovery | Pluggable sources: Manual, RSS, News API — each topic scored for relevance & opportunity |
| Article Generation | Outline-first, section-by-section writing with configurable length, tone, and reading level |
| Humanization Pass | Second AI pass to reduce robotic phrasing; optional voice profile file |
| Fact Verification | Extracts claims, cross-checks against source material, flags unverified claims |
| SEO Layer | Meta title/description, slug, readability score, JSON-LD schema |
| AEO Layer | Featured-snippet answers, definition callouts, FAQPage schema |
| GEO Layer | Citation-friendly structure for AI answer engines (ChatGPT, Perplexity, etc.) |
| FAQ Generation | 4–6 auto-generated Q&A pairs per article |
| Image Generation | Featured + optional inline images; Pollinations (free) default |
| Internal Linking | Semantic similarity finds related articles; AI inserts contextual links |
| Duplicate Detection | Embedding similarity check before publish |
| Review Workflow | Human approve / reject from admin; or self-writing auto-publish mode |
| Freshness Engine | Flags old or declining-traffic articles for refresh |
| Content Calendar | AI proposes weekly topics based on content gaps |
| Analytics Loop | Weights topic sources by historical article performance |
| Filament Admin | Dashboard, Topics, Articles, Sources, Freshness Queue, AI Settings |
Requirements
- PHP 8.2+
- Laravel 11+ (tested on Laravel 13)
- Database MySQL, PostgreSQL, or SQLite
- Queue driver —
databaseorredisrecommended (QUEUE_CONNECTION=database) - At least one AI text provider API key (Groq recommended — free tier available)
- Composer with
ext-jsonandext-mbstring
Installation
1. Install the package
composer require muzorix/ai-blog-engine
For local path development (monorepo):
// composer.json "repositories": [ { "type": "path", "url": "packages/ai-blog-engine", "options": { "symlink": true } } ], "require": { "muzorix/ai-blog-engine": "@dev" }
2. Run the install command
php artisan ai-blog-engine:install --migrate
This will:
- Publish
config/ai-blog-engine.php - Publish and run database migrations
- Seed default topic sources (Manual, RSS, News API)
- Create the
public/storagesymlink (for images) - Publish Filament CSS/JS assets
3. Publish Filament assets (if admin looks unstyled)
If the admin login page has no styling, run:
php artisan filament:assets
Important: Set
APP_URLin.envto match how you access the site (http://127.0.0.1:8000vshttp://localhost:8000).
4. Configure environment variables
Copy the block below into .env and fill in your keys:
# Core ABE_MODE=review ABE_TEXT_PROVIDER=groq ABE_IMAGE_PROVIDER=pollinations # Site identity (used in prompts & schema.org) ABE_SITE_NAME=Muzorix ABE_SITE_URL=https://muzorix.com ABE_SITE_NICHE="tech news, software, AI, gadgets, and technology industry" ABE_SITE_DESCRIPTION="Tech news and insights covering software, AI, gadgets, and industry trends" ABE_AUTHOR_NAME=Muzorix ABE_ORGANIZATION_NAME=Muzorix # Generation defaults ABE_DEFAULT_LENGTH=1200 ABE_DEFAULT_TONE=professional ABE_INLINE_IMAGES=false # Publishing ABE_REQUIRE_APPROVAL=true ABE_ADMIN_PATH=abe-admin ABE_BLOG_PATH=blog # Topic sources ABE_RSS_ENABLED=true ABE_RSS_FEEDS=https://feeds.example.com/rss,https://another.com/feed ABE_NEWS_API_ENABLED=true ABE_NEWS_QUERY="technology OR software OR artificial intelligence" # API keys GROQ_API_KEY=your-groq-key NEWS_API_KEY=your-newsapi-key
5. Enable Filament panel access on your User model
Your App\Models\User must implement Filament\Models\Contracts\FilamentUser:
use Filament\Models\Contracts\FilamentUser; use Filament\Panel; class User extends Authenticatable implements FilamentUser { public function canAccessPanel(Panel $panel): bool { return true; // restrict as needed } }
6. Create an admin user
php artisan make:filament-user --panel=ai-blog-engine
7. Start the queue worker
Article generation runs as queued jobs. Start a worker:
php artisan queue:work
Or use the Laravel composer dev script if your project has it.
8. Start the scheduler (production)
Add to your server crontab:
* * * * * cd /path-to-your-project && php artisan schedule:run >> /dev/null 2>&1
The package registers these scheduled jobs automatically:
| Job | Default schedule | Purpose |
|---|---|---|
FetchTopicsJob |
Every 6 hours | Pull topics from enabled sources |
RunFreshnessScanJob |
Mondays 3 AM | Flag articles needing updates |
RunContentCalendarJob |
Sundays 4 AM | Propose new topics for the week |
PullAnalyticsJob |
Daily 5 AM | Update source weights & queue topics |
9. Open the admin panel
http://your-site.test/abe-admin
(Path is configurable via ABE_ADMIN_PATH.)
Configuration
Publish or re-publish config anytime:
php artisan vendor:publish --tag=ai-blog-engine-config --force
Key config sections
| Section | Purpose |
|---|---|
mode |
review or self_writing |
site |
Niche, description, author, organization — fed into AI prompts and schema |
text_provider |
Default provider + per-step overrides (generate, humanize, fact_check, etc.) |
image_provider |
Default image driver |
humanization |
Enable/disable pass; optional voice_profile_path text file |
fact_check |
Enable/disable; block_unverified_claims for hard blocking |
seo |
Min word count, readability target, meta length limits |
aeo |
FAQ count, snippet optimization |
geo |
Citation-friendly restructuring |
images |
Disk, directory, inline_per_h2 toggle |
duplicate_check |
Similarity threshold (0.85 default), compare last N articles |
internal_linking |
Max links per article, related article count |
freshness |
Check interval days, traffic decline threshold |
topic_discovery |
Min scores, RSS feeds, News API query |
generation |
Default length, tone, reading level |
frontend |
blog_path for canonical URLs in schema |
schedule |
Cron expressions for automated jobs |
Most settings can also be changed from AI Settings in the admin panel without editing files.
Admin Panel Guide
The Filament panel is at /abe-admin (panel ID: ai-blog-engine).
Dashboard
- Pipeline stats: queued topics, pending review, published count, freshness queue
- Recent articles table
Topics
Manage the topic pipeline:
| Action | Description |
|---|---|
| Create | Manually add a topic (auto-queued for generation) |
| Fetch Topics | Pull from RSS / News API and score with AI |
| Score | Re-run AI relevance/opportunity scoring on a topic |
| Generate | Dispatch full article pipeline for a topic |
Topic statuses: pending → scored → queued → processing → completed
Articles
Full CRUD for generated articles:
| Action | Description |
|---|---|
| Edit | Modify title, body, meta tags, status |
| Approve | Publish article (sets published_at) |
| Reject | Mark as rejected |
Review panel shows SEO score, readability, AEO score, duplicate flag, and fact-check flags.
Sources
Enable/disable and configure topic sources:
- Manual — topics entered by hand
- RSS — comma-separated feed URLs in config or
configkey-value in admin - News API — requires
NEWS_API_KEYand search query
Adjust performance_weight to boost sources that produce high-performing articles.
Freshness Queue
Articles flagged as needs_update appear here:
- Run Freshness Scan — scan all published articles
- Refresh — regenerate an updated draft through the full pipeline
AI Settings
Configure without touching .env:
- Site niche, description, author, URL
- Mode (review vs self-writing)
- Text and image providers
- Tone and target word count
- Humanization, fact-check, and inline image toggles
The Article Pipeline
When a topic is generated, it passes through these steps in order:
Topic
↓
1. ArticleGenerator — outline + section-by-section HTML body
↓
2. HumanizerPass — reduce AI tells (optional)
↓
3. FactVerifier — extract & verify claims against source
↓
4. SeoProcessor — meta tags, slug, schema.org JSON-LD
↓
5. AeoProcessor — snippet answer + definition callouts
↓
6. GeoProcessor — citation-friendly restructuring
↓
7. FaqGenerator — 4–6 FAQ pairs + FAQPage schema
↓
8. ImageGenerator — featured image (+ inline per H2 if enabled)
↓
9. InternalLinker — semantic links to related published articles
↓
10. DuplicateChecker — flag near-duplicates by embedding similarity
↓
11. Finalize — pending_review OR auto-publish
Each step logs to abe_ai_generation_logs with provider, tokens, and cost estimate.
Day-to-Day Usage
Workflow A: Fully automated (with review)
# 1. Fetch topics from RSS / News API php artisan ai-blog-engine:fetch-topics # 2. Generate articles for all queued topics php artisan ai-blog-engine:generate --queued # 3. Review in admin → Articles → Approve or Reject
Workflow B: Manual topic → article
- Go to Topics → Create
- Enter title and summary (paste source content in summary for fact-checking)
- Set status to
queued - Click Generate on the topic row (or run
php artisan ai-blog-engine:generate {id}) - Review and approve in Articles
Workflow C: Self-writing (no human step)
ABE_MODE=self_writing ABE_REQUIRE_APPROVAL=false
Articles publish automatically when fact-check and duplicate checks pass.
Workflow D: Refresh aging content
php artisan ai-blog-engine:freshness-scan
Then use Freshness Queue in admin to refresh flagged articles.
Artisan Commands
| Command | Description |
|---|---|
php artisan ai-blog-engine:install |
Publish config, migrations, seed sources, Filament assets |
php artisan ai-blog-engine:install --migrate |
Same + run migrations |
php artisan ai-blog-engine:fetch-topics |
Fetch and AI-score topics from enabled sources |
php artisan ai-blog-engine:generate {topic?} |
Generate article for one topic ID |
php artisan ai-blog-engine:generate --queued |
Generate all queued topics |
php artisan ai-blog-engine:freshness-scan |
Scan published articles for freshness flags |
php artisan make:filament-user --panel=ai-blog-engine |
Create admin login |
php artisan filament:assets |
Re-publish Filament CSS/JS (fix unstyled admin) |
Scheduled Jobs
Registered automatically when APP_ENV runs in console. Customize cron in config/ai-blog-engine.php under schedule:
'schedule' => [ 'fetch_topics' => '0 */6 * * *', // every 6 hours 'freshness_scan' => '0 3 * * 1', // Mondays 3 AM 'content_calendar' => '0 4 * * 0', // Sundays 4 AM 'analytics_pull' => '0 5 * * *', // daily 5 AM ],
Override via env: ABE_SCHEDULE_FETCH_TOPICS, ABE_SCHEDULE_FRESHNESS, etc.
Publishing Modes
| Mode | Config | Behavior |
|---|---|---|
| Review | ABE_MODE=review |
Articles land in pending_review; human approves in admin |
| Self-writing | ABE_MODE=self_writing |
Auto-publish if no blocking fact-check flags and not duplicate-flagged |
Article statuses: draft → humanizing → fact_checking → seo_processing → pending_review → published
Also: scheduled, needs_update, rejected
Topic Discovery
Manual
Create topics in admin. Best practice: paste source article text in summary or store full content in raw_payload so fact-checking can verify claims.
RSS
ABE_RSS_ENABLED=true ABE_RSS_FEEDS=https://feeds.arstechnica.com/arstechnica/index,https://www.theverge.com/rss/index.xml
Or configure feeds per-source in Sources → RSS → config key feeds as JSON array.
News API
ABE_NEWS_API_ENABLED=true NEWS_API_KEY=your-key ABE_NEWS_QUERY="technology OR artificial intelligence"
Get a free key at newsapi.org.
AI Scoring
Each topic receives:
- Relevance score (0–100) — fit to your niche
- Opportunity score (0–100) — timeliness and content gap potential
Topics below ABE_MIN_RELEVANCE (50) or ABE_MIN_OPPORTUNITY (30) are not queued.
AI Providers
Text providers
| Driver | Env key | Notes |
|---|---|---|
groq |
GROQ_API_KEY or GROQ_API_KEYS |
Free tier, fast — supports multiple keys with auto-rotation on 429 |
openai |
OPENAI_API_KEY |
GPT-4o mini default |
claude |
ANTHROPIC_API_KEY |
Claude Sonnet default |
gemini |
GEMINI_API_KEY |
Gemini 2.0 Flash default |
Use one provider for all steps:
ABE_TEXT_PROVIDER=groq GROQ_API_KEY=gsk_your_first_key
Multiple Groq keys (recommended for production / heavy usage):
When one key hits its rate limit, the package automatically switches to the next key — no job failure, no manual intervention.
GROQ_API_KEYS=gsk_key_one,gsk_key_two,gsk_key_three
You can still set GROQ_API_KEY for a single key; GROQ_API_KEYS takes precedence when set (comma-separated, no spaces required).
Keys are rotated round-robin for even load. On HTTP 429, the current key is skipped and the next is tried immediately.
Or override per pipeline step:
ABE_TEXT_PROVIDER_GENERATE=claude ABE_TEXT_PROVIDER_HUMANIZE=groq ABE_TEXT_PROVIDER_FACT_CHECK=claude
Programmatic usage
use Muzorix\AiBlogEngine\AiText\AiTextManager; $ai = app(AiTextManager::class); $response = $ai->prompt('generate', 'Write a headline about Laravel 13'); $json = $ai->json('seo', 'Return JSON with meta_title and meta_description for...');
Image Providers
| Driver | Env key | Cost |
|---|---|---|
pollinations |
none | Free |
unsplash |
UNSPLASH_ACCESS_KEY |
Free (stock photos) |
openai |
OPENAI_API_KEY |
Paid (DALL-E 3) |
stability |
STABILITY_API_KEY |
Paid |
ABE_IMAGE_PROVIDER=pollinations ABE_INLINE_IMAGES=false # true = generate image per H2 section
Images are stored on the disk configured in ABE_IMAGE_DISK (default: public) under abe-images/.
Frontend Integration
This package is backend-only. Query models from your own routes, controllers, or Livewire components.
Blade example
// routes/web.php use Muzorix\AiBlogEngine\Models\Article; Route::get('/blog', fn () => view('blog.index', [ 'articles' => Article::published() ->with(['faqs', 'images']) ->latest('published_at') ->paginate(12), ])); Route::get('/blog/{slug}', function (string $slug) { $article = Article::published() ->where('slug', $slug) ->with(['faqs', 'images', 'outboundLinks.linkedArticle']) ->firstOrFail(); return view('blog.show', compact('article')); });
{{-- resources/views/blog/show.blade.php --}} <head> <title>{{ $article->meta_title ?? $article->title }}</title> @if ($article->meta_description) <meta name="description" content="{{ $article->meta_description }}"> @endif @if ($article->schema_markup) <script type="application/ld+json">{!! $article->schema_json !!}</script> @endif </head> <h1>{{ $article->title }}</h1> {!! $article->body !!} @foreach ($article->faqs as $faq) <details> <summary>{{ $faq->question }}</summary> <p>{{ $faq->answer }}</p> </details> @endforeach
Livewire
use Livewire\Component; use Livewire\WithPagination; use Muzorix\AiBlogEngine\Models\Article; class BlogIndex extends Component { use WithPagination; public function render() { return view('livewire.blog-index', [ 'articles' => Article::published() ->latest('published_at') ->paginate(12), ]); } }
Canonical URLs
Articles expose a url attribute using ABE_SITE_URL + ABE_BLOG_PATH:
https://muzorix.com/blog/your-article-slug
Related articles
use Muzorix\AiBlogEngine\Pipeline\InternalLinker; $related = app(InternalLinker::class)->findSimilarArticles($article);
Models & Querying
| Model | Table | Purpose |
|---|---|---|
Topic |
abe_topics |
Discovered/scored topics |
TopicSource |
abe_topic_sources |
Source configuration |
Article |
abe_articles |
Generated articles |
ArticleFaq |
abe_article_faqs |
FAQ pairs |
ArticleImage |
abe_article_images |
Featured and inline images |
ArticleLink |
abe_article_links |
Internal links between articles |
FactCheckFlag |
abe_fact_check_flags |
Claim verification results |
AiGenerationLog |
abe_ai_generation_logs |
Per-step AI usage logs |
ArticleEdit |
abe_article_edits |
Human/AI edit audit trail |
ArticleAnalytic |
abe_article_analytics |
Pageview data for freshness/analytics |
Useful scopes
Article::published()->latest('published_at')->get(); Article::pendingReview()->get(); Article::needsUpdate()->get(); Topic::readyForGeneration()->get(); Topic::scheduled()->get();
Publish manually
$article->publish(); // sets status, published_at, last_updated_at
Programmatic Usage
Run the full pipeline
use Muzorix\AiBlogEngine\Facades\AiBlogEngine; use Muzorix\AiBlogEngine\Models\Topic; $topic = Topic::find(1); $article = AiBlogEngine::run($topic);
Generate with custom options
use Muzorix\AiBlogEngine\Pipeline\ArticleGenerator; $article = ArticleGenerator::make() ->provider('groq') ->targetLength(1500) ->tone('professional') ->readingLevel('grade_8') ->generate($topic);
Dispatch as background job
use Muzorix\AiBlogEngine\Jobs\GenerateArticleJob; GenerateArticleJob::dispatch($topic);
Database Tables
abe_topic_sources — source drivers and config
abe_topics — discovered topics with scores
abe_articles — article content, SEO, schema, embeddings
abe_article_faqs — FAQ Q&A pairs
abe_article_images — featured and inline images
abe_article_links — internal link graph
abe_fact_check_flags — claim verification results
abe_ai_generation_logs — AI step audit log
abe_article_edits — edit diff history
abe_article_analytics — traffic data for freshness loop
Troubleshooting
Admin panel has no CSS / looks like plain HTML
php artisan filament:assets
Hard-refresh the browser (Ctrl+Shift+R).
Admin panel CSS loads but from wrong host
Set APP_URL in .env to exactly match your browser URL:
APP_URL=http://127.0.0.1:8000
Articles stuck in processing
Ensure a queue worker is running:
php artisan queue:work
Check storage/logs/laravel.log and the abe_ai_generation_logs table.
"API key not configured" errors
Verify the correct env key for your provider (GROQ_API_KEY, etc.) and run:
php artisan config:clear
Images not displaying
php artisan storage:link
Confirm ABE_IMAGE_DISK=public and files exist in storage/app/public/abe-images/.
No topics after fetch
- Check RSS feed URLs are valid and reachable
- Verify
NEWS_API_KEYif using News API - Lower
ABE_MIN_RELEVANCE/ABE_MIN_OPPORTUNITYin config - Topics with duplicate titles from the last 30 days are skipped
Fact-check flags everything as unverified
Paste the original source content into the topic summary or ensure raw_payload.content is populated when the topic is created.
Package Structure
packages/ai-blog-engine/
├── config/ai-blog-engine.php
├── database/migrations/
├── src/
│ ├── AiBlogEngineServiceProvider.php
│ ├── AiText/Drivers/ Groq, OpenAI, Claude, Gemini
│ ├── AiImage/Drivers/ Pollinations, Unsplash, OpenAI, Stability
│ ├── TopicSources/Drivers/ Manual, RSS, News API
│ ├── Pipeline/ All processing steps + ArticlePipeline
│ ├── Jobs/ Fetch, Generate, Refresh, Calendar, Analytics
│ ├── Commands/ Install, fetch, generate, freshness-scan
│ ├── Models/
│ ├── Filament/ Admin panel, resources, widgets
│ └── Support/ Slug, embedding, readability helpers
└── README.md
License
MIT — free for personal and commercial use.
统计信息
- 总下载量: 0
- 月度下载量: 0
- 日度下载量: 0
- 收藏数: 0
- 点击次数: 1
- 依赖项目数: 0
- 推荐数: 0
其他信息
- 授权协议: MIT
- 更新时间: 2026-07-10