geoflow-content-automation
Compare original and translation side by side
🇺🇸
Original
English🇨🇳
Translation
ChineseGEOFlow Content Automation
GEOFlow 内容自动化
Skill by ara.so — Daily 2026 Skills collection.
GEOFlow is an open-source PHP/PostgreSQL system for automated GEO/SEO content production. It chains model configuration, material management, task scheduling, draft review, and front-end publishing into a single pipeline. It supports any OpenAI-compatible API, runs on Docker Compose, and exposes both a REST API and a CLI.
由ara.so开发的Skill — 2026每日技能合集。
GEOFlow 是一款基于PHP/PostgreSQL的开源系统,用于自动化GEO/SEO内容生产。它将模型配置、素材管理、任务调度、草稿审核和前端发布整合为一条完整流水线。它支持所有兼容OpenAI的API,可通过Docker Compose运行,并提供REST API和CLI两种交互方式。
Installation
安装
Docker (Recommended)
Docker(推荐)
bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
cp .env.example .envbash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
cp .env.example .envEdit .env — set APP_SECRET_KEY, SITE_URL, and DB credentials
编辑.env — 设置APP_SECRET_KEY、SITE_URL和数据库凭据
vi .env
vi .env
Start web + postgres + scheduler + worker
启动web + postgres + scheduler + worker服务
docker compose --profile scheduler up -d --build
docker compose --profile scheduler up -d --build
Front-end
前端页面
Admin panel
管理面板
undefinedundefinedLocal PHP Server
本地PHP服务器
bash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
export DB_DRIVER=pgsql
export DB_HOST=127.0.0.1
export DB_PORT=5432
export DB_NAME=geo_system
export DB_USER=geo_user
export DB_PASSWORD=geo_password
export APP_SECRET_KEY=$(openssl rand -hex 32)
export SITE_URL=http://localhost:8080
php -S localhost:8080 router.php
open http://localhost:8080/geo_admin/Requirements: PHP 7.4+, extensions and , PostgreSQL instance.
pdo_pgsqlcurlbash
git clone https://github.com/yaojingang/GEOFlow.git
cd GEOFlow
export DB_DRIVER=pgsql
export DB_HOST=127.0.0.1
export DB_PORT=5432
export DB_NAME=geo_system
export DB_USER=geo_user
export DB_PASSWORD=geo_password
export APP_SECRET_KEY=$(openssl rand -hex 32)
export SITE_URL=http://localhost:8080
php -S localhost:8080 router.php
open http://localhost:8080/geo_admin/要求: PHP 7.4+,需安装和扩展,且已有PostgreSQL实例。
pdo_pgsqlcurlEnvironment Variables
环境变量
dotenv
undefineddotenv
undefined.env (copy from .env.example)
.env(从.env.example复制)
HOST_PORT=18080
SITE_URL=http://localhost:18080
APP_SECRET_KEY=$APP_SECRET_KEY # 32+ char random string — never hardcode
CRON_INTERVAL=60 # Scheduler poll interval in seconds
TZ=Asia/Shanghai
HOST_PORT=18080
SITE_URL=http://localhost:18080
APP_SECRET_KEY=$APP_SECRET_KEY # 32位以上随机字符串 — 切勿硬编码
CRON_INTERVAL=60 # 调度器轮询间隔(秒)
TZ=Asia/Shanghai
Database
数据库配置
DB_DRIVER=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_NAME=geo_system
DB_USER=geo_user
DB_PASSWORD=$DB_PASSWORD # Set via environment, not hardcoded
Generate a secure key:
```bash
openssl rand -hex 32DB_DRIVER=pgsql
DB_HOST=postgres
DB_PORT=5432
DB_NAME=geo_system
DB_USER=geo_user
DB_PASSWORD=$DB_PASSWORD # 通过环境变量设置,请勿硬编码
生成安全密钥:
```bash
openssl rand -hex 32or
或
php -r "echo bin2hex(random_bytes(32));"
---php -r "echo bin2hex(random_bytes(32));"
---Default Credentials
默认凭据
After first boot, log in at with:
/geo_admin/- Username:
admin - Password:
admin888
Change both immediately and rotate before any public deployment.
APP_SECRET_KEY首次启动后,使用以下凭据登录 :
/geo_admin/- 用户名:
admin - 密码:
admin888
请立即修改这两项凭据,并在公开部署前更新。
APP_SECRET_KEYDocker Services
Docker服务
| Service | Purpose | Profile |
|---|---|---|
| Front-end + admin HTTP | always |
| PostgreSQL database | always |
| Scans tasks, writes job queue | |
| Calls AI API, generates content | |
bash
undefined| 服务 | 用途 | 配置文件 |
|---|---|---|
| 前端+管理后台HTTP服务 | 始终运行 |
| PostgreSQL数据库 | 始终运行 |
| 扫描任务,写入任务队列 | |
| 调用AI API,生成内容 | |
bash
undefinedWeb only (no generation)
仅启动Web服务(不生成内容)
docker compose up -d
docker compose up -d
Full stack
启动完整服务栈
docker compose --profile scheduler up -d
docker compose --profile scheduler up -d
Logs
查看日志
docker compose logs -f worker
docker compose logs -f scheduler
docker compose logs -f worker
docker compose logs -f scheduler
Restart a single service
重启单个服务
docker compose restart worker
docker compose restart worker
Stop everything
停止所有服务
docker compose --profile scheduler down
---docker compose --profile scheduler down
---Content Generation Pipeline
内容生成流水线
Admin: configure model + prompts + materials
↓
Create Task (title library, model, prompt, image library, publish rules)
↓
Scheduler (bin/cron.php) → writes to job_queue table
↓
Worker (bin/worker.php) → calls AI API → generates article body
↓
Optional: insert images, build SEO meta
↓
Article enters draft → review → publish states
↓
Front-end renders article + SEO/OG tags管理员:配置模型+提示词+素材
↓
创建任务(标题库、模型、提示词、图片库、发布规则)
↓
调度器(bin/cron.php)→ 写入job_queue表
↓
Worker(bin/worker.php)→ 调用AI API → 生成文章正文
↓
可选步骤:插入图片、构建SEO元数据
↓
文章进入草稿→审核→发布状态
↓
前端渲染文章+SEO/OG标签Admin Workflow
管理工作流
1. Add an AI Model
1. 添加AI模型
Navigate to AI配置中心 → AI模型管理 and fill in:
- API Base URL (e.g. )
https://api.openai.com/v1 - Model ID (e.g. )
gpt-4o - API Key (stored encrypted; reference in your env)
$AI_API_KEY
导航至 AI配置中心 → AI模型管理 并填写以下内容:
- API基础URL(例如 )
https://api.openai.com/v1 - 模型ID(例如 )
gpt-4o - API密钥(加密存储;在环境变量中引用)
$AI_API_KEY
2. Create Materials
2. 创建素材
- 标题库 — Title pool the task draws from
- 图片库 — Image pool for article illustrations
- 知识库 — Reference knowledge injected into prompts
- 提示词模板 — Prompt templates with placeholders
- 标题库 — 任务可选用的标题池
- 图片库 — 文章配图的图片池
- 知识库 — 注入提示词的参考知识
- 提示词模板 — 带占位符的提示词模板
3. Create a Task
3. 创建任务
Navigate to 任务管理 → 新建任务:
Title Library → selects article titles
Model → which AI model to call
Prompt → which prompt template
Image Library → optional illustration set
Publish Mode → draft | auto-publish
Schedule → immediate | cron-based导航至 任务管理 → 新建任务:
标题库 → 选择文章标题来源
模型 → 调用的AI模型
提示词 → 使用的提示词模板
图片库 → 可选的配图集合
发布模式 → 草稿 | 自动发布
调度方式 → 立即执行 | 定时任务4. Monitor Execution
4. 监控执行状态
- 任务管理 — Task status, retry controls
- 文章管理 — Draft/review/published states
- 审核中心 — Review queue for human approval
- 任务管理 — 任务状态、重试控制
- 文章管理 — 草稿/审核/发布状态
- 审核中心 — 人工审核队列
CLI (bin/geoflow
)
bin/geoflowCLI(bin/geoflow
)
bin/geoflowThe CLI is the programmatic interface used by automation scripts and the companion skill .
yaojingang/yao-geo-skillsbash
undefinedCLI是自动化脚本和配套技能使用的编程接口。
yaojingang/yao-geo-skillsbash
undefinedList available commands
查看可用命令
php bin/geoflow help
php bin/geoflow help
Check task status
检查任务状态
php bin/geoflow task:status --task-id=<id>
php bin/geoflow task:status --task-id=<id>
Create a task
创建任务
php bin/geoflow task:create
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft
php bin/geoflow task:create
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft
--title-library=<id>
--model=<id>
--prompt=<id>
--publish-mode=draft
Upload an article draft
上传文章草稿
php bin/geoflow article:create
--title="Article Title"
--body="<markdown content>"
--status=draft
--title="Article Title"
--body="<markdown content>"
--status=draft
php bin/geoflow article:create
--title="Article Title"
--body="<markdown content>"
--status=draft
--title="Article Title"
--body="<markdown content>"
--status=draft
Review and publish an article
审核并发布文章
php bin/geoflow article:review --article-id=<id> --action=approve
php bin/geoflow article:publish --article-id=<id>
php bin/geoflow article:review --article-id=<id> --action=approve
php bin/geoflow article:publish --article-id=<id>
Run scheduler manually (one cycle)
手动运行调度器(单次循环)
php bin/cron.php
php bin/cron.php
Run worker manually (processes one job then exits)
手动运行Worker(处理一个任务后退出)
php bin/worker.php --once
---php bin/worker.php --once
---REST API (/api/v1
)
/api/v1REST API(/api/v1
)
/api/v1All endpoints require Bearer token authentication.
所有接口均需Bearer令牌认证。
Generate an API Token
生成API令牌
bash
php bin/api/create_token.php --name="my-integration"bash
php bin/api/create_token.php --name="my-integration"Outputs token — store in $GEOFLOW_API_TOKEN
输出令牌 — 存储到$GEOFLOW_API_TOKEN
undefinedundefinedCommon Endpoints
常用接口
bash
BASE=http://localhost:18080/api/v1
TOKEN=$GEOFLOW_API_TOKENbash
BASE=http://localhost:18080/api/v1
TOKEN=$GEOFLOW_API_TOKENList tasks
列出所有任务
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks"
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks"
Get task detail
获取任务详情
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks/<id>"
curl -H "Authorization: Bearer $TOKEN" "$BASE/tasks/<id>"
Create task
创建任务
curl -X POST
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"
curl -X POST
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"
-H "Authorization: Bearer $TOKEN"
-H "Content-Type: application/json"
-d '{ "title_library_id": 1, "model_id": 2, "prompt_id": 3, "publish_mode": "draft" }'
"$BASE/tasks"
List articles
列出所有文章
curl -H "Authorization: Bearer $TOKEN" "$BASE/articles"
curl -H "Authorization: Bearer $TOKEN" "$BASE/articles"
Publish article
发布文章
curl -X POST
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"
curl -X POST
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"
-H "Authorization: Bearer $TOKEN"
"$BASE/articles/<id>/publish"
Check job queue
查看待处理任务队列
curl -H "Authorization: Bearer $TOKEN" "$BASE/jobs?status=pending"
---curl -H "Authorization: Bearer $TOKEN" "$BASE/jobs?status=pending"
---PHP Code Examples
PHP代码示例
Connect to the Database (using GEOFlow's own layer)
连接数据库(使用GEOFlow自带的数据库层)
php
<?php
// includes/db_support.php provides the connection helper
require_once __DIR__ . '/includes/db_support.php';
$pdo = get_db_connection(); // Returns a PDO instance
// Always use prepared statements
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = :status ORDER BY created_at DESC LIMIT 10");
$stmt->execute([':status' => 'published']);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);php
<?php
// includes/db_support.php提供连接助手
require_once __DIR__ . '/includes/db_support.php';
$pdo = get_db_connection(); // 返回PDO实例
// 始终使用预处理语句
$stmt = $pdo->prepare("SELECT * FROM articles WHERE status = :status ORDER BY created_at DESC LIMIT 10");
$stmt->execute([':status' => 'published']);
$articles = $stmt->fetchAll(PDO::FETCH_ASSOC);Call the Article Service
调用文章服务
php
<?php
require_once __DIR__ . '/includes/article_service.php';
// Create a draft
$articleId = create_article([
'title' => 'My Article Title',
'body' => '## Introduction\n\nContent here...',
'status' => 'draft',
'task_id' => 42,
'seo_title' => 'My Article Title | Site Name',
'seo_desc' => 'Short description for search engines.',
]);
// Approve and publish
approve_article($articleId);
publish_article($articleId);php
<?php
require_once __DIR__ . '/includes/article_service.php';
// 创建草稿
$articleId = create_article([
'title' => 'My Article Title',
'body' => '## Introduction\n\nContent here...',
'status' => 'draft',
'task_id' => 42,
'seo_title' => 'My Article Title | Site Name',
'seo_desc' => 'Short description for search engines.',
]);
// 审核并发布
approve_article($articleId);
publish_article($articleId);Trigger the AI Engine Directly
直接触发AI引擎
php
<?php
require_once __DIR__ . '/includes/ai_engine.php';
// Execute a single task job (task_id, job_id)
$result = run_task_job(taskId: 5, jobId: 101);
if ($result['success']) {
echo "Article created: " . $result['article_id'];
} else {
echo "Error: " . $result['error'];
}php
<?php
require_once __DIR__ . '/includes/ai_engine.php';
// 执行单个任务作业(task_id, job_id)
$result = run_task_job(taskId: 5, jobId: 101);
if ($result['success']) {
echo "Article created: " . $result['article_id'];
} else {
echo "Error: " . $result['error'];
}Queue a Job Manually
手动添加任务到队列
php
<?php
require_once __DIR__ . '/includes/job_queue_service.php';
// Claim next available job (used by worker)
$job = claim_next_job();
if ($job) {
try {
// ... process the job ...
complete_job($job['id']);
} catch (Exception $e) {
fail_job($job['id'], $e->getMessage());
// Eligible jobs will be retried by scheduler
}
}
// Manually enqueue a task
enqueue_task_jobs(taskId: 5);php
<?php
require_once __DIR__ . '/includes/job_queue_service.php';
// 获取下一个可用任务(Worker使用)
$job = claim_next_job();
if ($job) {
try {
// ... 处理任务 ...
complete_job($job['id']);
} catch (Exception $e) {
fail_job($job['id'], $e->getMessage());
// 可重试任务将由调度器重新处理
}
}
// 手动将任务加入队列
enqueue_task_jobs(taskId: 5);Custom Prompt Template in PHP
PHP自定义提示词模板
php
<?php
// Prompt templates support placeholder substitution
$template = "You are an SEO writer. Write a 600-word article about: {{title}}\n\nBackground knowledge:\n{{knowledge}}";
$prompt = str_replace(
['{{title}}', '{{knowledge}}'],
[$articleTitle, $knowledgeBaseContent],
$template
);
// Pass to AI service
require_once __DIR__ . '/includes/ai_service.php';
$response = call_ai_model(modelId: 2, prompt: $prompt);
echo $response['content'];php
<?php
// 提示词模板支持占位符替换
$template = "You are an SEO writer. Write a 600-word article about: {{title}}\n\nBackground knowledge:\n{{knowledge}}";
$prompt = str_replace(
['{{title}}', '{{knowledge}}'],
[$articleTitle, $knowledgeBaseContent],
$template
);
// 传递给AI服务
require_once __DIR__ . '/includes/ai_service.php';
$response = call_ai_model(modelId: 2, prompt: $prompt);
echo $response['content'];Directory Reference
目录结构参考
GEOFlow/
├── index.php Front-end article list
├── article.php Article detail page (SEO + OG)
├── router.php Local dev router for php -S
├── admin/ All admin UI pages
├── api/v1/index.php REST API single entry point
├── bin/
│ ├── geoflow CLI entrypoint
│ ├── cron.php Scheduler (run every CRON_INTERVAL seconds)
│ └── worker.php AI generation worker (long-running)
├── includes/
│ ├── config.php Global config + constants
│ ├── database.php Front-end data access
│ ├── ai_engine.php Core generation orchestration
│ ├── ai_service.php OpenAI-compatible HTTP client
│ ├── job_queue_service.php Claim / complete / fail / retry
│ ├── task_service.php Task CRUD
│ ├── article_service.php Article lifecycle
│ └── api_auth.php Bearer token auth
└── docs/ Deployment + API + FAQ docsGEOFlow/
├── index.php 前端文章列表页
├── article.php 文章详情页(含SEO + OG标签)
├── router.php 本地开发环境php -S的路由文件
├── admin/ 所有管理后台UI页面
├── api/v1/index.php REST API统一入口
├── bin/
│ ├── geoflow CLI入口文件
│ ├── cron.php 调度器(每隔CRON_INTERVAL秒运行一次)
│ └── worker.php AI生成任务Worker(常驻进程)
├── includes/
│ ├── config.php 全局配置+常量
│ ├── database.php 前端数据访问层
│ ├── ai_engine.php 核心生成编排逻辑
│ ├── ai_service.php 兼容OpenAI的HTTP客户端
│ ├── job_queue_service.php 任务队列的认领/完成/失败/重试逻辑
│ ├── task_service.php 任务CRUD操作
│ ├── article_service.php 文章生命周期管理
│ └── api_auth.php Bearer令牌认证
└── docs/ 部署+API+FAQ文档Common Patterns
常见使用模式
Auto-publish without review
无需审核自动发布
When creating a task, set . The worker will call immediately after generation, skipping the review queue.
publish_mode = autopublish_article()创建任务时设置,Worker生成文章后会立即调用,跳过审核队列。
publish_mode = autopublish_article()Batch retry failed jobs
批量重试失败任务
bash
undefinedbash
undefinedVia CLI
通过CLI
php bin/geoflow job:retry-failed --task-id=<id>
php bin/geoflow job:retry-failed --task-id=<id>
Via database (emergency)
通过数据库(紧急情况)
psql $DATABASE_URL -c "UPDATE job_queue SET status='pending', attempts=0 WHERE status='failed' AND task_id=5;"
undefinedpsql $DATABASE_URL -c "UPDATE job_queue SET status='pending', attempts=0 WHERE status='failed' AND task_id=5;"
undefinedRun scheduler outside Docker
在Docker外运行调度器
bash
undefinedbash
undefinedAdd to crontab — runs every minute
添加到crontab — 每分钟运行一次
-
-
-
-
- cd /path/to/GEOFlow && php bin/cron.php >> /var/log/geoflow-cron.log 2>&1
-
-
-
-
-
-
-
- cd /path/to/GEOFlow && php bin/cron.php >> /var/log/geoflow-cron.log 2>&1
-
-
-
Or loop manually
或手动循环运行
while true; do php bin/cron.php; sleep 60; done
undefinedwhile true; do php bin/cron.php; sleep 60; done
undefinedDatabase maintenance
数据库维护
bash
php bin/db_maintenance.php --vacuum
php bin/db_maintenance.php --clean-old-jobs --days=30bash
php bin/db_maintenance.php --vacuum
php bin/db_maintenance.php --clean-old-jobs --days=30Troubleshooting
故障排除
| Symptom | Likely Cause | Fix |
|---|---|---|
| Worker generates nothing | Scheduler not running | Start with |
| Extension missing | Add |
| 500 on admin pages | DB not initialized | Let the web container complete its entrypoint migration on first boot |
| AI calls return 401 | Wrong API key | Re-enter key in AI模型管理; check key has no leading/trailing spaces |
| Articles stuck in draft | Auto-publish off | Set |
| Jobs pile up in queue | Worker crashed | |
| CSRF token mismatch | Session expired | Re-login to admin; ensure |
| 症状 | 可能原因 | 解决方法 |
|---|---|---|
| Worker无内容生成 | 调度器未运行 | 使用 |
找不到 | 扩展未安装 | 在 |
| 管理后台页面报500错误 | 数据库未初始化 | 等待Web容器在首次启动时完成入口脚本的数据库迁移 |
| AI调用返回401错误 | API密钥错误 | 在AI模型管理中重新输入密钥;检查密钥无首尾空格 |
| 文章一直停留在草稿状态 | 未开启自动发布 | 在任务中设置 |
| 任务队列堆积 | Worker崩溃 | 执行 |
| CSRF令牌不匹配 | 会话过期 | 重新登录管理后台;确保 |
Useful Log Commands
实用日志命令
bash
undefinedbash
undefinedAll services
查看所有服务日志
docker compose logs -f
docker compose logs -f
Only errors
仅查看错误日志
docker compose logs worker 2>&1 | grep -i error
docker compose logs worker 2>&1 | grep -i error
Scheduler activity
查看调度器活动日志
docker compose logs scheduler | tail -50
docker compose logs scheduler | tail -50
Check pending jobs in DB
查看数据库中待处理任务
docker compose exec postgres psql -U geo_user -d geo_system
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"
---docker compose exec postgres psql -U geo_user -d geo_system
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"
-c "SELECT status, COUNT(*) FROM job_queue GROUP BY status;"
---Security Checklist
安全检查清单
- Change default /
admincredentials immediatelyadmin888 - Set to a 32+ character random string via environment variable
APP_SECRET_KEY - Never commit — it is in
.envby default.gitignore - Use HTTPS in production; set to your
SITE_URLdomainhttps:// - Restrict and
/geo_admin/with network-level firewall in production/api/v1/ - Rotate API tokens periodically via
php bin/api/create_token.php
- 立即修改默认/
admin凭据admin888 - 通过环境变量设置为32位以上随机字符串
APP_SECRET_KEY - 切勿提交文件 — 默认已加入
.env.gitignore - 生产环境使用HTTPS;将设置为你的
SITE_URL域名https:// - 生产环境通过网络防火墙限制和
/geo_admin/的访问/api/v1/ - 定期通过轮换API令牌
php bin/api/create_token.php