112 lines
3.3 KiB
PHP
112 lines
3.3 KiB
PHP
<?php
|
||
/**
|
||
* AKSRT AI SEO — AI SEO 辅助
|
||
*
|
||
* 提供 SEO 标题、描述、关键词建议。
|
||
*
|
||
* @package AKSRT
|
||
*/
|
||
|
||
defined('ABSPATH') || exit;
|
||
|
||
class AKSRT_AI_SEO {
|
||
|
||
private static $instance = null;
|
||
|
||
public static function get_instance(): self {
|
||
if (null === self::$instance) {
|
||
self::$instance = new self();
|
||
}
|
||
return self::$instance;
|
||
}
|
||
|
||
/**
|
||
* 生成 SEO 建议
|
||
*
|
||
* @param string $title 文章标题
|
||
* @param string $content 文章内容(截取前2000字)
|
||
* @return array{success: bool, data?: array, message?: string}
|
||
*/
|
||
public function suggest(string $title, string $content = ''): array {
|
||
// seo_ai_enabled 主开关
|
||
if (class_exists('AKSRT_Settings')) {
|
||
$seo = AKSRT_Settings::get_seo_settings();
|
||
if (empty($seo['seo_ai_enabled'])) {
|
||
return ['success' => false, 'message' => 'AI SEO 功能未启用'];
|
||
}
|
||
}
|
||
|
||
if (!class_exists('AKSRT_AI')) {
|
||
return ['success' => false, 'message' => 'AI 模块未加载'];
|
||
}
|
||
|
||
$ai = AKSRT_AI::get_instance();
|
||
if (!$ai->is_available()) {
|
||
return ['success' => false, 'message' => 'AI 服务未启用'];
|
||
}
|
||
|
||
// 截取内容避免 token 超限
|
||
$content = mb_substr($content, 0, 2000);
|
||
|
||
$prompt = AKSRT_AI_Prompt::get_instance()->build_seo_prompt($title, $content);
|
||
|
||
$result = $ai->generate($prompt, [
|
||
'max_tokens' => 500,
|
||
'temperature' => 0.5,
|
||
]);
|
||
|
||
if (!$result['success']) {
|
||
return $result;
|
||
}
|
||
|
||
// 解析 JSON 响应
|
||
$parsed = $this->parse_seo_response($result['content']);
|
||
|
||
return [
|
||
'success' => true,
|
||
'data' => $parsed,
|
||
];
|
||
}
|
||
|
||
/**
|
||
* 解析 SEO 响应
|
||
*/
|
||
private function parse_seo_response(string $content): array {
|
||
$defaults = [
|
||
'suggested_title' => '',
|
||
'suggested_description' => '',
|
||
'suggested_keywords' => [],
|
||
];
|
||
|
||
// 尝试 JSON 解析
|
||
$json = json_decode($content, true);
|
||
if (is_array($json)) {
|
||
return array_merge($defaults, $json);
|
||
}
|
||
|
||
// 尝试从文本中提取
|
||
$lines = explode("\n", $content);
|
||
$result = $defaults;
|
||
$current_key = '';
|
||
|
||
foreach ($lines as $line) {
|
||
$line = trim($line);
|
||
if (empty($line)) continue;
|
||
|
||
if (stripos($line, '标题') !== false || stripos($line, 'title') !== false) {
|
||
$current_key = 'suggested_title';
|
||
$result[$current_key] = trim(preg_replace('/^[^::]+[::]\s*/', '', $line));
|
||
} elseif (stripos($line, '描述') !== false || stripos($line, 'description') !== false) {
|
||
$current_key = 'suggested_description';
|
||
$result[$current_key] = trim(preg_replace('/^[^::]+[::]\s*/', '', $line));
|
||
} elseif (stripos($line, '关键词') !== false || stripos($line, 'keywords') !== false) {
|
||
$current_key = 'suggested_keywords';
|
||
$keywords_str = trim(preg_replace('/^[^::]+[::]\s*/', '', $line));
|
||
$result[$current_key] = array_map('trim', explode(',', $keywords_str));
|
||
}
|
||
}
|
||
|
||
return $result;
|
||
}
|
||
}
|