Mở rộng AI Related Posts qua Filters trong Init Live Search

Tài liệu này hướng dẫn cách mở rộng mô-đun AI Related Posts của Init Live Search bằng hệ thống filter. Bạn sẽ học cách bổ sung nguồn ứng viên (candidate pool), thêm tín hiệu chấm điểm (signals), điều chỉnh trọng số (weights), và tinh chỉnh xếp hạng cuối cùng mà không cần sửa core. Tài liệu tập trung vào thực tiễn triển khai, hiệu năng, và khả năng bảo trì.

Mở rộng AI Related Posts qua Filters trong Init Live Search

Tổng quan kiến trúc

  • Candidate Pool: tập hợp bài viết ứng viên, mặc định gồm latest và random theo category.
  • Signals: các đặc trưng để chấm điểm như category, tag, comment, views, title bigrams, recency, time gap.
  • Weights: trọng số chuẩn hoá (tổng bằng 1) cho mỗi tín hiệu.
  • MMR Diversification: giảm trùng lặp nội dung bằng Max Marginal Relevance.
  • Cache: kết quả được cache theo algo version bằng transient.
  • Filters: điểm mở rộng chính, tất cả đều giữ nguyên nguyên tắc “prefix-consistent”.

Danh sách filter hỗ trợ

  • init_plugin_suite_live_search_ai_candidates: thêm/bớt ứng viên.
  • init_plugin_suite_live_search_ai_signals: bổ sung hoặc ghi đè tín hiệu.
  • init_plugin_suite_live_search_ai_weights: điều chỉnh trọng số.
  • init_plugin_suite_live_search_ai_score: tinh chỉnh điểm cuối cùng cho từng ứng viên.
  • init_plugin_suite_live_search_ai_selected: hậu xử lý danh sách đã chọn.
  • init_plugin_suite_live_search_ai_half_life_recency: bán rã cho độ mới.
  • init_plugin_suite_live_search_ai_half_life_gap: bán rã cho chênh lệch thời gian.
  • init_plugin_suite_live_search_ai_mmr_lambda: hệ số MMR giữa độ liên quan và đa dạng.

Ví dụ mở rộng: candidates + signals + weights

Đoạn mã dưới đây minh hoạ cách bổ sung nguồn ứng viên từ same_keyword (meta) và series (taxonomy), đồng thời thêm điểm số tương ứng và hiệu chỉnh trọng số.

// + Thêm ứng viên: cùng series (random) + cùng same_keyword (meta_query)
// + Giữ nguyên các candidate có sẵn rồi hợp nhất lại
add_filter('init_plugin_suite_live_search_ai_candidates', function($candidates, $post_id, $post_type){
    $candidates = is_array($candidates) ? $candidates : [];

    // ----- SAME_KEYWORD -----
    $same_kw_raw = function_exists('get_field')
        ? (string) get_field('same_keyword', $post_id)
        : (string) get_post_meta($post_id, 'same_keyword', true);

    if ($same_kw_raw !== '') {
        $tokens = array_filter(array_map('trim', preg_split('/[,;|]+/u', $same_kw_raw)));
        if (!empty($tokens)) {
            $meta_query = ['relation' => 'OR'];
            foreach ($tokens as $t) {
                $meta_query[] = [
                    'key'     => 'same_keyword',
                    'value'   => $t,
                    'compare' => 'LIKE',
                ];
            }
            $kw_pool = get_posts([
                'post_type'      => $post_type,
                'post_status'    => 'publish',
                'posts_per_page' => 100,
                'post__not_in'   => [$post_id],
                'fields'         => 'ids',
                'meta_query'     => $meta_query,
                'no_found_rows'  => true,
            ]);
            $candidates = array_merge($candidates, (array)$kw_pool);
        }
    }

    // ----- SERIES (nhẹ) -----
    $series_terms = wp_get_post_terms($post_id, 'series', ['fields' => 'ids']);
    if (!empty($series_terms) && !is_wp_error($series_terms)) {
        $series_pool = get_posts([
            'post_type'      => $post_type,
            'post_status'    => 'publish',
            'posts_per_page' => 30,
            'post__not_in'   => [$post_id],
            'fields'         => 'ids',
            'orderby'        => 'rand',
            'tax_query'      => [[
                'taxonomy' => 'series',
                'field'    => 'term_id',
                'terms'    => $series_terms,
            ]],
            'no_found_rows'  => true,
        ]);
        $candidates = array_merge($candidates, (array)$series_pool);
    }

    return array_values(array_unique(array_map('intval', $candidates)));
}, 10, 3);

add_filter('init_plugin_suite_live_search_ai_signals', function($signals, $post_id, $candidate_id){
    // SAME_KEYWORD
    $get_kw = function($pid){
        $raw = function_exists('get_field') ? (string) get_field('same_keyword', $pid)
                                            : (string) get_post_meta($pid, 'same_keyword', true);
        return $raw !== '' ? array_filter(array_map('trim', preg_split('/[,;|]+/u', $raw))) : [];
    };
    $src = $get_kw($post_id);
    $dst = $get_kw($candidate_id);

    $kw_score = 0.0;
    if ($src && $dst) {
        $inter = array_intersect($src, $dst);
        $kw_score = count($inter) / max(count($src), 1);
    }
    $signals['same_keyword'] = $kw_score;

    // SERIES (binary, nhưng nhẹ)
    $src_series = wp_get_post_terms($post_id, 'series', ['fields' => 'ids']);
    $dst_series = wp_get_post_terms($candidate_id, 'series', ['fields' => 'ids']);
    $signals['series'] = (!empty(array_intersect($src_series, $dst_series))) ? 1.0 : 0.0;

    return $signals;
}, 10, 3);

add_filter('init_plugin_suite_live_search_ai_weights', function($weights){
    $weights['tag']            = 0.25;
    $weights['series']         = 0.20;
    $weights['title_bigrams']  = 0.15;
    $weights['same_keyword']   = 0.15;
    $weights['category']       = 0.08;
    $weights['views']          = 0.07;
    $weights['comment']        = 0.05;
    $weights['freshness']      = 0.05;
    return $weights;
}, 10, 1);

Hướng dẫn từng bước

  1. Cài đặt hoặc mở theme/plugin nơi bạn muốn thêm filter.
  2. Sao chép đoạn mã ví dụ vào file PHP phù hợp (ví dụ: một module mở rộng hoặc functions.php).
  3. Đảm bảo taxonomy series và meta same_keyword tồn tại và có dữ liệu.
  4. Tinh chỉnh giới hạn posts_per_page để cân bằng giữa chất lượng và hiệu năng.
  5. Điều chỉnh trọng số trong filter ..._ai_weights để đạt thứ hạng mong muốn.

Hiệu năng và cache

  • Prime caches: core đã gọi _prime_post_cachesupdate_object_term_cache để tránh N+1.
  • Giới hạn pool: chọn ngưỡng hợp lý cho candidate pool từ meta/tax để không nặng DB.
  • TTL: kết quả cuối cùng được cache bởi transient với TTL có thể chỉnh bằng filter ..._ai_cache_ttl.
  • Algo version: thay đổi logic lớn hãy tăng $algo_ver để invalidation mượt.

Tinh chỉnh xếp hạng nâng cao

  • Dùng init_plugin_suite_live_search_ai_score để can thiệp điểm sau khi tính, ví dụ đẩy nhẹ bài có conversion cao.
  • Dùng init_plugin_suite_live_search_ai_selected để loại trừ các bài trùng chủ đề đặc biệt ở bước cuối.
  • Điều chỉnh init_plugin_suite_live_search_ai_mmr_lambda để cân bằng giữa độ liên quan và đa dạng.

Kiểm thử và đo lường

  • Tạo bộ test gồm các bài có cùng series, cùng keyword, khác category để quan sát thứ hạng.
  • So sánh CTR/Time on Page trước và sau khi bật filter tùy chỉnh.
  • Giám sát log/monitoring cho số lượng candidate và thời gian xử lý trung bình.

Các lưu ý triển khai

  • Luôn kiểm tra quyền tồn tại của taxonomy/meta trước khi truy vấn.
  • Ưu tiên trả về ID và đặt no_found_rows để giảm overhead truy vấn.
  • Loại trừ $post_id khỏi tập ứng viên và chỉ giữ bài publish.
  • Chuẩn hoá và loại bỏ trùng lặp bằng array_uniqueintval.

Kết luận

Với hệ thống filter mở và cache thân thiện, AI Related Posts của Init Live Search cho phép bạn mở rộng logic liên quan linh hoạt mà vẫn giữ hiệu năng cao. Hãy bắt đầu bằng ví dụ trong mục này, sau đó tinh chỉnh tín hiệu và trọng số để phù hợp nội dung của bạn.

Bình luận


  • Không có bình luận.

Init Toolbox

Nhấn Ctrl + \ trên máy tính, hoặc vuốt sang trái ở bất kỳ đâu trên mobile.

Đăng nhập





Đang tải...