is_enabled()) { return [ 'success' => false, 'message' => __('AI SEO 功能未启用', 'aksrt'), ]; } if (!class_exists('AKSRT_AI') || !class_exists('AKSRT_AI_Prompt')) { return [ 'success' => false, 'message' => __('AI 模块未加载', 'aksrt'), ]; } $ai = AKSRT_AI::get_instance(); if (!$ai->is_available()) { return [ 'success' => false, 'message' => __('AI 服务未启用或未配置', 'aksrt'), ]; } $type = $type === 'term' ? 'term' : 'post'; if ($id <= 0) { return [ 'success' => false, 'message' => __('请先保存后再生成 SEO', 'aksrt'), ]; } $context = $type === 'term' ? $this->build_term_context($id) : $this->build_post_context($id); if (is_wp_error($context)) { return [ 'success' => false, 'message' => $context->get_error_message(), ]; } $prompt = AKSRT_AI_Prompt::get_instance(); $title = $this->run_field( $ai, $context, $prompt->get_seo_title_system_instruction(), 'title' ); $keywords = $this->run_field( $ai, $context, $prompt->get_seo_keywords_system_instruction(), 'keywords' ); $description = $this->run_field( $ai, $context, $prompt->get_seo_description_system_instruction(), 'description' ); return [ 'success' => true, 'data' => [ 'title' => $title, 'keywords' => $keywords, 'description' => $description, ], ]; } /** * 草稿场景:用传入 title/content 生成(兼容旧 /ai/seo-suggest)。 */ public function suggest(string $title, string $content = ''): array { if (!$this->is_enabled()) { return ['success' => false, 'message' => __('AI SEO 功能未启用', 'aksrt')]; } if (!class_exists('AKSRT_AI') || !class_exists('AKSRT_AI_Prompt')) { return ['success' => false, 'message' => __('AI 模块未加载', 'aksrt')]; } $ai = AKSRT_AI::get_instance(); if (!$ai->is_available()) { return ['success' => false, 'message' => __('AI 服务未启用', 'aksrt')]; } $title = trim($title); $content = trim(wp_strip_all_tags($content)); if ($title === '' && $content === '') { return [ 'success' => false, 'message' => __('无可用内容,请先填写标题或正文', 'aksrt'), ]; } $parts = []; if ($title !== '') { $parts[] = '标题: ' . $title; } if ($content !== '') { $content = wp_trim_words($content, 1500, ''); $parts[] = "正文:\n" . $content; } $context = implode("\n\n", $parts); $prompt = AKSRT_AI_Prompt::get_instance(); $title_out = $this->run_field($ai, $context, $prompt->get_seo_title_system_instruction(), 'title'); $kw_out = $this->run_field($ai, $context, $prompt->get_seo_keywords_system_instruction(), 'keywords'); $desc_out = $this->run_field($ai, $context, $prompt->get_seo_description_system_instruction(), 'description'); $keywords = []; if (!empty($kw_out['content'])) { $keywords = array_values(array_filter(array_map('trim', explode(',', $kw_out['content'])))); } return [ 'success' => true, 'data' => [ 'suggested_title' => (string) ($title_out['content'] ?? ''), 'suggested_description' => (string) ($desc_out['content'] ?? ''), 'suggested_keywords' => $keywords, 'fields' => [ 'title' => $title_out, 'keywords' => $kw_out, 'description' => $desc_out, ], ], ]; } /** * @return string|WP_Error */ public function build_post_context(int $post_id, int $word_limit = 1500) { $post = get_post($post_id); if (!$post) { return new WP_Error('no_post', __('内容不存在', 'aksrt')); } $title = trim((string) $post->post_title); $excerpt = trim((string) $post->post_excerpt); $content = wp_strip_all_tags((string) $post->post_content); $content = trim(preg_replace('/\s+/u', ' ', $content) ?? $content); $content = wp_trim_words($content, max(50, $word_limit), ''); if ($title === '' && $content === '' && $excerpt === '') { return new WP_Error('empty_content', __('无可用内容,请先填写标题、摘要或正文并保存', 'aksrt')); } $parts = []; if ($title !== '') { $parts[] = '标题: ' . $title; } if ($excerpt !== '') { $parts[] = '摘要: ' . $excerpt; } if ($content !== '') { $parts[] = "正文:\n" . $content; } return implode("\n\n", $parts); } /** * @return string|WP_Error */ public function build_term_context(int $term_id) { $term = get_term($term_id); if (!$term || is_wp_error($term)) { return new WP_Error('no_term', __('分类/标签不存在', 'aksrt')); } $parts = []; if ($term->name !== '') { $parts[] = '标题: ' . $term->name; } if ($term->description !== '') { $parts[] = '描述: ' . wp_strip_all_tags((string) $term->description); } if (empty($parts)) { return new WP_Error('empty_term', __('没有可用的标题或描述', 'aksrt')); } return implode("\n\n", $parts); } /** * @return array{content?:string,error?:string} */ private function run_field(AKSRT_AI $ai, string $context, string $system, string $field): array { $result = $ai->generate_text($context, [ 'system_instruction' => $system, 'max_tokens' => $field === 'description' ? 400 : 300, 'temperature' => 0.5, ]); if (empty($result['success'])) { return [ 'error' => (string) ($result['message'] ?? __('生成失败', 'aksrt')), ]; } $raw = (string) ($result['content'] ?? ''); return [ 'content' => $this->post_process_field($field, $raw), ]; } private function post_process_field(string $field, string $raw): string { $raw = trim($raw); // Strip common wrappers $raw = preg_replace('/^["\'“”]+|["\'“”]+$/u', '', $raw) ?? $raw; $raw = trim($raw); if ($field === 'title') { $title = sanitize_text_field($raw); $seo = class_exists('AKSRT_Settings') ? AKSRT_Settings::get_seo_settings() : []; $conn = trim((string) ($seo['seo_connector'] ?? '-')) ?: '-'; $site = trim((string) get_bloginfo('name')); if ($site !== '' && $title !== '' && mb_stripos($title, $site) === false) { $title = $title . ' ' . $conn . ' ' . $site; } return $title; } if ($field === 'keywords') { $raw = str_replace(['、', ',', ';', ';', "\n", "\r"], ',', $raw); $parts = array_map('trim', explode(',', $raw)); $parts = array_values(array_unique(array_filter($parts, static function ($p) { return $p !== ''; }))); $parts = array_map('sanitize_text_field', $parts); return implode(',', array_slice($parts, 0, 12)); } // description $raw = preg_replace('/\s+/u', ' ', $raw) ?? $raw; return sanitize_text_field(trim($raw)); } }