feat: 实现统一的 AI 查询组件

- 创建 argon_ai_query_log 数据表记录所有 AI 查询
- 实现 argon_ai_query() 统一查询接口
- 记录查询时间、服务商、模型、场景、响应时间等信息
- 支持按场景区分(summary/spam_detection/keyword_extraction)
- 添加 argon_log_ai_query() 记录函数
- 添加 argon_get_ai_query_stats() 统计函数
- 修改 argon_generate_ai_summary() 使用新接口
- 所有 AI 请求统一管理和记录
This commit is contained in:
2026-01-26 12:00:51 +08:00
parent 079ba0a261
commit 7c4d1b1de7

View File

@@ -6387,6 +6387,321 @@ if (!wp_next_scheduled('argon_daily_link_check')) {
add_action('argon_daily_link_check', 'argon_scheduled_link_check');
// ==================== AI 查询组件 ====================
/**
* 创建 AI 查询记录表
*/
function argon_create_ai_query_log_table() {
global $wpdb;
$table_name = $wpdb->prefix . 'argon_ai_query_log';
$charset_collate = $wpdb->get_charset_collate();
$sql = "CREATE TABLE IF NOT EXISTS $table_name (
id bigint(20) NOT NULL AUTO_INCREMENT,
query_time datetime NOT NULL,
provider varchar(50) NOT NULL,
model varchar(100) DEFAULT NULL,
scenario varchar(50) NOT NULL,
prompt_length int(11) DEFAULT 0,
content_length int(11) DEFAULT 0,
response_length int(11) DEFAULT 0,
response_time int(11) DEFAULT 0,
status varchar(20) NOT NULL,
error_message text DEFAULT NULL,
post_id bigint(20) DEFAULT NULL,
comment_id bigint(20) DEFAULT NULL,
user_id bigint(20) DEFAULT NULL,
PRIMARY KEY (id),
KEY provider (provider),
KEY scenario (scenario),
KEY query_time (query_time),
KEY status (status)
) $charset_collate;";
require_once(ABSPATH . 'wp-admin/includes/upgrade.php');
dbDelta($sql);
}
add_action('after_switch_theme', 'argon_create_ai_query_log_table');
/**
* 记录 AI 查询
*
* @param string $provider 服务商
* @param string $model 模型
* @param string $scenario 使用场景 (summary/spam_detection/keyword_extraction)
* @param int $prompt_length 提示词长度
* @param int $content_length 内容长度
* @param int $response_length 响应长度
* @param int $response_time 响应时间(毫秒)
* @param string $status 状态 (success/error)
* @param string $error_message 错误信息
* @param array $context 上下文信息 (post_id, comment_id, user_id)
*/
function argon_log_ai_query($provider, $model, $scenario, $prompt_length, $content_length, $response_length, $response_time, $status, $error_message = '', $context = []) {
global $wpdb;
$table_name = $wpdb->prefix . 'argon_ai_query_log';
$wpdb->insert(
$table_name,
[
'query_time' => current_time('mysql'),
'provider' => $provider,
'model' => $model,
'scenario' => $scenario,
'prompt_length' => $prompt_length,
'content_length' => $content_length,
'response_length' => $response_length,
'response_time' => $response_time,
'status' => $status,
'error_message' => $error_message,
'post_id' => isset($context['post_id']) ? $context['post_id'] : null,
'comment_id' => isset($context['comment_id']) ? $context['comment_id'] : null,
'user_id' => isset($context['user_id']) ? $context['user_id'] : null
],
[
'%s', // query_time
'%s', // provider
'%s', // model
'%s', // scenario
'%d', // prompt_length
'%d', // content_length
'%d', // response_length
'%d', // response_time
'%s', // status
'%s', // error_message
'%d', // post_id
'%d', // comment_id
'%d' // user_id
]
);
}
/**
* 统一的 AI 查询接口
*
* @param string $scenario 使用场景 (summary/spam_detection/keyword_extraction)
* @param string $prompt 提示词
* @param string $content 内容
* @param array $context 上下文信息 (post_id, comment_id, user_id, provider, model)
* @return string|false 返回 AI 响应内容或 false
*/
function argon_ai_query($scenario, $prompt, $content, $context = []) {
// 获取服务商配置
$provider = isset($context['provider']) ? $context['provider'] : get_option('argon_ai_summary_provider', 'openai');
$config = argon_get_ai_provider_config($provider);
if (!$config || !isset($config['api_key'])) {
error_log("Argon AI Query Error: Provider config not found for {$provider}");
return false;
}
$api_key = $config['api_key'];
$model = isset($context['model']) ? $context['model'] : (isset($config['model']) ? $config['model'] : '');
// 记录开始时间
$start_time = microtime(true);
// 获取 post_id用于错误记录
$post_id = isset($context['post_id']) ? $context['post_id'] : 0;
// 调用对应的 API
$result = false;
$error_message = '';
try {
switch ($provider) {
case 'openai':
$result = argon_call_openai_api($api_key, $prompt, $content, $post_id);
break;
case 'anthropic':
$result = argon_call_anthropic_api($api_key, $prompt, $content, $post_id);
break;
case 'deepseek':
$result = argon_call_deepseek_api($api_key, $prompt, $content, $post_id);
break;
case 'xiaomi':
$result = argon_call_xiaomi_api($api_key, $prompt, $content, $post_id);
break;
case 'qianwen':
$result = argon_call_qianwen_api($api_key, $prompt, $content, $post_id);
break;
case 'wenxin':
$result = argon_call_wenxin_api($api_key, $prompt, $content, $post_id);
break;
case 'doubao':
$result = argon_call_doubao_api($api_key, $prompt, $content, $post_id);
break;
case 'kimi':
$result = argon_call_kimi_api($api_key, $prompt, $content, $post_id);
break;
case 'zhipu':
$result = argon_call_zhipu_api($api_key, $prompt, $content, $post_id);
break;
case 'siliconflow':
$result = argon_call_siliconflow_api($api_key, $prompt, $content, $post_id);
break;
default:
$error_message = "Unsupported provider: {$provider}";
error_log("Argon AI Query Error: {$error_message}");
}
} catch (Exception $e) {
$error_message = $e->getMessage();
error_log("Argon AI Query Exception: {$error_message}");
}
// 计算响应时间(毫秒)
$response_time = round((microtime(true) - $start_time) * 1000);
// 记录查询日志
$status = ($result !== false) ? 'success' : 'error';
if ($result === false && empty($error_message)) {
$error_message = 'API call returned false';
}
argon_log_ai_query(
$provider,
$model,
$scenario,
mb_strlen($prompt),
mb_strlen($content),
$result !== false ? mb_strlen($result) : 0,
$response_time,
$status,
$error_message,
$context
);
// 记录详细日志
if ($result !== false) {
error_log(sprintf(
'Argon AI Query Success: scenario=%s, provider=%s, model=%s, response_time=%dms, prompt_len=%d, content_len=%d, response_len=%d',
$scenario,
$provider,
$model,
$response_time,
mb_strlen($prompt),
mb_strlen($content),
mb_strlen($result)
));
} else {
error_log(sprintf(
'Argon AI Query Failed: scenario=%s, provider=%s, model=%s, response_time=%dms, error=%s',
$scenario,
$provider,
$model,
$response_time,
$error_message
));
}
return $result;
}
/**
* 获取 AI 查询统计信息
*
* @param array $filters 过滤条件 (scenario, provider, date_from, date_to)
* @return array 统计信息
*/
function argon_get_ai_query_stats($filters = []) {
global $wpdb;
$table_name = $wpdb->prefix . 'argon_ai_query_log';
$where = ['1=1'];
$params = [];
if (!empty($filters['scenario'])) {
$where[] = 'scenario = %s';
$params[] = $filters['scenario'];
}
if (!empty($filters['provider'])) {
$where[] = 'provider = %s';
$params[] = $filters['provider'];
}
if (!empty($filters['date_from'])) {
$where[] = 'query_time >= %s';
$params[] = $filters['date_from'];
}
if (!empty($filters['date_to'])) {
$where[] = 'query_time <= %s';
$params[] = $filters['date_to'];
}
$where_clause = implode(' AND ', $where);
if (!empty($params)) {
$where_clause = $wpdb->prepare($where_clause, $params);
}
// 总查询次数
$total_queries = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where_clause");
// 成功次数
$success_queries = $wpdb->get_var("SELECT COUNT(*) FROM $table_name WHERE $where_clause AND status = 'success'");
// 平均响应时间
$avg_response_time = $wpdb->get_var("SELECT AVG(response_time) FROM $table_name WHERE $where_clause AND status = 'success'");
// 按场景统计
$by_scenario = $wpdb->get_results("
SELECT scenario, COUNT(*) as count, AVG(response_time) as avg_time
FROM $table_name
WHERE $where_clause
GROUP BY scenario
", ARRAY_A);
// 按服务商统计
$by_provider = $wpdb->get_results("
SELECT provider, COUNT(*) as count, AVG(response_time) as avg_time
FROM $table_name
WHERE $where_clause
GROUP BY provider
", ARRAY_A);
return [
'total_queries' => intval($total_queries),
'success_queries' => intval($success_queries),
'error_queries' => intval($total_queries) - intval($success_queries),
'success_rate' => $total_queries > 0 ? round(($success_queries / $total_queries) * 100, 2) : 0,
'avg_response_time' => round($avg_response_time, 2),
'by_scenario' => $by_scenario,
'by_provider' => $by_provider
];
}
/**
* AJAX: 获取 AI 查询统计
*/
function argon_ajax_get_ai_query_stats() {
check_ajax_referer('argon_ai_query_stats', 'nonce');
if (!current_user_can('manage_options')) {
wp_send_json_error(__('权限不足', 'argon'));
}
$filters = [];
if (!empty($_POST['scenario'])) {
$filters['scenario'] = sanitize_text_field($_POST['scenario']);
}
if (!empty($_POST['provider'])) {
$filters['provider'] = sanitize_text_field($_POST['provider']);
}
if (!empty($_POST['date_from'])) {
$filters['date_from'] = sanitize_text_field($_POST['date_from']);
}
if (!empty($_POST['date_to'])) {
$filters['date_to'] = sanitize_text_field($_POST['date_to']);
}
$stats = argon_get_ai_query_stats($filters);
wp_send_json_success($stats);
}
add_action('wp_ajax_argon_get_ai_query_stats', 'argon_ajax_get_ai_query_stats');
// ==================== AI 文章摘要功能 ====================
/**
@@ -6686,22 +7001,6 @@ function argon_log_ai_error($provider, $error_type, $error_message, $post_id = 0
* @return string|false 摘要内容或 false
*/
function argon_generate_ai_summary($post) {
$provider = get_option('argon_ai_summary_provider', 'openai');
$config = argon_get_ai_provider_config($provider);
$api_key = $config['api_key'];
// 错误检查API 密钥
if (empty($api_key)) {
argon_log_ai_error($provider, '配置错误', 'API 密钥未配置', $post->ID);
return false;
}
// 错误检查:配置完整性
if (!isset($config['api_endpoint']) || !isset($config['model'])) {
argon_log_ai_error($provider, '配置错误', 'API 配置不完整', $post->ID, $config);
return false;
}
// 准备文章内容
$content = wp_strip_all_tags($post->post_content);
$content = preg_replace('/\s+/', ' ', $content);
@@ -6709,72 +7008,27 @@ function argon_generate_ai_summary($post) {
// 错误检查:文章内容
if (empty($content) || mb_strlen($content) < 50) {
argon_log_ai_error($provider, '内容错误', '文章内容过短至少需要50字', $post->ID, [
'content_length' => mb_strlen($content)
]);
error_log(sprintf(
'Argon AI Summary Error: 文章内容过短 (文章ID: %d, 内容长度: %d)',
$post->ID,
mb_strlen($content)
));
return false;
}
// 获取提示词
$prompt = get_option('argon_ai_summary_prompt', '你是一个专业的内容摘要助手。请仔细阅读以下文章内容用简洁、准确的语言总结文章的核心观点和主要内容。要求1) 控制在 100-150 字以内2) 突出文章的关键信息和亮点3) 使用通俗易懂的语言4) 保持客观中立的语气。');
// 记录调用信息
error_log(sprintf(
'Argon AI Summary: 开始生成摘要 (文章ID: %d, 标题: %s, 提供商: %s, 模型: %s, 内容长度: %d)',
$post->ID,
$post->post_title,
$provider,
$config['model'],
mb_strlen($content)
));
// 根据不同服务商调用 API
$result = false;
switch ($provider) {
case 'openai':
$result = argon_call_openai_api($api_key, $prompt, $content, $post->ID);
break;
case 'anthropic':
$result = argon_call_anthropic_api($api_key, $prompt, $content, $post->ID);
break;
case 'deepseek':
$result = argon_call_deepseek_api($api_key, $prompt, $content, $post->ID);
break;
case 'xiaomi':
$result = argon_call_xiaomi_api($api_key, $prompt, $content, $post->ID);
break;
case 'qianwen':
$result = argon_call_qianwen_api($api_key, $prompt, $content, $post->ID);
break;
case 'wenxin':
$result = argon_call_wenxin_api($api_key, $prompt, $content, $post->ID);
break;
case 'doubao':
$result = argon_call_doubao_api($api_key, $prompt, $content, $post->ID);
break;
case 'kimi':
$result = argon_call_kimi_api($api_key, $prompt, $content, $post->ID);
break;
case 'zhipu':
$result = argon_call_zhipu_api($api_key, $prompt, $content, $post->ID);
break;
case 'siliconflow':
$result = argon_call_siliconflow_api($api_key, $prompt, $content, $post->ID);
break;
default:
argon_log_ai_error($provider, '配置错误', '不支持的服务商', $post->ID);
return false;
}
// 使用统一的 AI 查询接口
$result = argon_ai_query('summary', $prompt, $content, [
'post_id' => $post->ID,
'user_id' => get_current_user_id()
]);
// 检查结果
if ($result === false) {
error_log('Argon AI Summary: API 调用失败 (文章ID: ' . $post->ID . ', 提供商: ' . $provider . ')');
// 错误信息已在具体的 API 调用函数中记录
error_log('Argon AI Summary: 摘要生成失败 (文章ID: ' . $post->ID . ')');
} else {
error_log(sprintf(
'Argon AI Summary: 摘要生成成功 (文章ID: %d, 摘要长度: %d字)',
$post->ID,
mb_strlen($result)
));
delete_post_meta($post->ID, '_argon_ai_summary_error');
delete_post_meta($post->ID, '_argon_ai_summary_error_time');
}