<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:media="http://search.yahoo.com/mrss/">
  <channel>
    <title>Content Ranking · Nhân Tài Đức</title>
    <link>https://jared.lynskey.co.nz/vi/tags/content-ranking/</link>
    <description>Những câu chuyện ngắn về kỹ thuật phần mềm và lãnh đạo đội ngũ kỹ sư — performance review, tuyển dụng và mở rộng team, CI/CD, giám sát DevOps và AI agent. Viết từ Seoul.</description>
    <generator>Hugo</generator>
    <language>vi</language>
    <copyright>© 2026 Nhân Tài Đức</copyright>
    <lastBuildDate>Wed, 28 Oct 2026 00:00:00 +1300</lastBuildDate>
    <atom:link href="https://jared.lynskey.co.nz/vi/tags/content-ranking/index.xml" rel="self" type="application/rss+xml" />
    <item>
      <title>Thuật toán xếp hạng nội dung: những tín hiệu ngoài lượt xem và lượt thích</title>
      <link>https://jared.lynskey.co.nz/vi/posts/2023/2023-10-06-reinenforcement-learning/</link>
      <guid isPermaLink="true">https://jared.lynskey.co.nz/vi/posts/2023/2023-10-06-reinenforcement-learning/</guid>
      <pubDate>Fri, 06 Oct 2023 00:30:03 +0000</pubDate>
      <dc:creator>Jared Lynskey</dc:creator>
      <category>content ranking</category>
      <category>metrics</category>
      <category>SEO</category>
      <category>machine learning</category>
      <category>user engagement</category>
      <category>content strategy</category>
      <description>Thuật toán xếp hạng nội dung (content ranking) làm gì, vì sao chỉ xếp theo lượt xem và lượt thích thì bài nổi càng thêm nổi, và những tín hiệu nào đáng gộp vào một điểm số chung.</description>
      <content:encoded><![CDATA[<p>Thuật toán xếp hạng nội dung (content ranking algorithm) là thứ quyết định một bảng tin (feed), một trang kết quả tìm kiếm hay một danh sách “bài nổi bật” sẽ hiện cái gì trước, cái gì sau. Nó chấm cho mỗi nội dung một điểm số dựa trên nhiều tín hiệu (người dùng phản ứng với nội dung ra sao, bản thân nội dung thế nào, mới và liên quan đến đâu), rồi sắp xếp theo điểm số đó. Nhưng nếu chỉ xếp hạng theo lượt xem và lượt thích, bạn sẽ rơi vào vòng lặp “giàu càng thêm giàu”: bài nào nằm trên cùng thì được nhiều người thấy, nên gom thêm lượt xem và lượt thích, nên lại tiếp tục nằm trên cùng.</p>
<p>Đó chính là khuyết điểm dễ thấy của lượt xem và lượt bình chọn (upvote): chúng chỉ thưởng cho những gì vốn đã nổi. Nội dung mới thì chết đói dưới đáy bảng tin, và dần dần nền tảng biến thành một kênh chiếu lại. Ở chỗ tôi làm, xếp hạng bảng tin cho tốt là một trong những bài toán khó mà ít ai để ý, nên tôi đã dành khá nhiều thời gian nghĩ xem còn những tín hiệu nào khác đáng tin.</p>

<h3 class="relative group">Nhìn vào việc người ta làm, đừng chỉ nhìn chỗ họ bấm
    <div id="nhìn-vào-việc-người-ta-làm-đừng-chỉ-nhìn-chỗ-họ-bấm" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#nh%c3%acn-v%c3%a0o-vi%e1%bb%87c-ng%c6%b0%e1%bb%9di-ta-l%c3%a0m-%c4%91%e1%bb%abng-ch%e1%bb%89-nh%c3%acn-ch%e1%bb%97-h%e1%bb%8d-b%e1%ba%a5m" aria-label="Cọc dấu">#</a>
    </span>
    
</h3>
<p>Người dùng hiếm khi chủ động góp ý, mà có góp ý thì cũng thiên lệch; ngược lại, dữ liệu hành vi thì có rất nhiều. Thời gian ở lại trang và độ sâu cuộn cho biết người ta có đọc thật hay bỏ đi ngay sau đoạn đầu. Lượt quay lại còn tốt hơn nữa — quay lại đọc một bài là lời khen mạnh hơn bất kỳ lượt bình chọn nào. Còn thoát ra nhanh là một tín hiệu tiêu cực đáng giá: nội dung đã hứa hẹn một điều mà người đọc không nhận được.</p>

<h3 class="relative group">Tín hiệu nằm sẵn trong chính nội dung
    <div id="tín-hiệu-nằm-sẵn-trong-chính-nội-dung" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t%c3%adn-hi%e1%bb%87u-n%e1%ba%b1m-s%e1%ba%b5n-trong-ch%c3%adnh-n%e1%bb%99i-dung" aria-label="Cọc dấu">#</a>
    </span>
    
</h3>
<p>Nội dung tốt không phải lúc nào cũng gom được lượt thích ngay từ đầu, nhưng thường nó thể hiện chất lượng theo những cách khác. Tính mới là một — bài này có nói về điều gì mà nền tảng chưa từng có không? Cấu trúc là một yếu tố nữa; trình bày gọn gàng, ngữ pháp ổn và diễn đạt rõ ràng đi đôi với chất lượng nhiều hơn ta muốn thừa nhận. Lịch sử của tác giả cũng là một điểm xuất phát hợp lý, miễn là xét theo khả năng giữ chân người đọc của các bài cũ chứ không phải số lượt thích thô. Bình luận sớm cũng có ích: ba bình luận tích cực thật lòng đáng giá hơn ba mươi lượt bình chọn bấm theo phản xạ, và chỉ cần phân tích ngữ nghĩa một chút là đã có thể đánh dấu những nội dung lấp được khoảng trống mà nền tảng đang thiếu.</p>

<h3 class="relative group">Mượn tín hiệu từ bên ngoài
    <div id="mượn-tín-hiệu-từ-bên-ngoài" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#m%c6%b0%e1%bb%a3n-t%c3%adn-hi%e1%bb%87u-t%e1%bb%ab-b%c3%aan-ngo%c3%a0i" aria-label="Cọc dấu">#</a>
    </span>
    
</h3>
<p>Internet rộng lớn ngoài kia cho bạn biết cái gì đang được quan tâm ngay lúc này. Nội dung khớp với những từ khoá tìm kiếm đang lên thì đã có sẵn độ liên quan, và bài nào đăng nhanh về một chủ đề đang nóng thì xứng đáng được đẩy lên. Những gì đang rôm rả trên Twitter hay Reddit thường là bản xem trước cho những gì người dùng của bạn sẽ muốn tiếp theo — chỉ cần dự đoán một chút, bạn có thể đẩy nội dung mới phù hợp lên trước khi con sóng tới, thay vì chạy theo sau. Ô tìm kiếm ngay trên nền tảng của bạn cũng hay bị đánh giá thấp: nếu người dùng cứ tìm mãi một chủ đề, hay một bài đều đặn kéo lưu lượng truy cập (traffic) từ công cụ tìm kiếm về, thì đó là độ liên quan mà bạn chẳng cần phải đoán.</p>

<h3 class="relative group">Gộp các tín hiệu thành một điểm số
    <div id="gộp-các-tín-hiệu-thành-một-điểm-số" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#g%e1%bb%99p-c%c3%a1c-t%c3%adn-hi%e1%bb%87u-th%c3%a0nh-m%e1%bb%99t-%c4%91i%e1%bb%83m-s%e1%bb%91" aria-label="Cọc dấu">#</a>
    </span>
    
</h3>
<p>Dưới đây là ý tưởng đó viết thành vài dòng Python. Đây chỉ là ví dụ minh họa, không phải một hệ thống chạy thật trên môi trường production. Mỗi tín hiệu được chuẩn hóa (normalise) về khoảng 0 đến 1 để tín hiệu nào có con số thô lớn cũng không lấn át phần còn lại, tổng có trọng số gộp chúng thành một điểm duy nhất, và hệ số suy giảm theo thời gian (time decay) cho bài mới một cơ hội sòng phẳng để cạnh tranh với bài “hot” của tháng trước.</p>
<div class="highlight-wrapper"><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-python" data-lang="python"><span class="line"><span class="cl"><span class="n">WEIGHTS</span> <span class="o">=</span> <span class="p">{</span><span class="s2">&#34;read_depth&#34;</span><span class="p">:</span> <span class="mf">0.3</span><span class="p">,</span> <span class="s2">&#34;revisits&#34;</span><span class="p">:</span> <span class="mf">0.25</span><span class="p">,</span> <span class="s2">&#34;quality&#34;</span><span class="p">:</span> <span class="mf">0.25</span><span class="p">,</span> <span class="s2">&#34;early_comments&#34;</span><span class="p">:</span> <span class="mf">0.2</span><span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="n">HALF_LIFE_HOURS</span> <span class="o">=</span> <span class="mi">48</span>  <span class="c1"># a post loses half its score every two days</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">normalise</span><span class="p">(</span><span class="n">values</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="n">lo</span><span class="p">,</span> <span class="n">hi</span> <span class="o">=</span> <span class="nb">min</span><span class="p">(</span><span class="n">values</span><span class="p">),</span> <span class="nb">max</span><span class="p">(</span><span class="n">values</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[(</span><span class="n">v</span> <span class="o">-</span> <span class="n">lo</span><span class="p">)</span> <span class="o">/</span> <span class="p">(</span><span class="n">hi</span> <span class="o">-</span> <span class="n">lo</span><span class="p">)</span> <span class="k">if</span> <span class="n">hi</span> <span class="o">&gt;</span> <span class="n">lo</span> <span class="k">else</span> <span class="mf">0.0</span> <span class="k">for</span> <span class="n">v</span> <span class="ow">in</span> <span class="n">values</span><span class="p">]</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="k">def</span> <span class="nf">rank</span><span class="p">(</span><span class="n">items</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">    <span class="s2">&#34;&#34;&#34;items: dicts holding raw signal values plus &#34;age_hours&#34;.&#34;&#34;&#34;</span>
</span></span><span class="line"><span class="cl">    <span class="n">norm</span> <span class="o">=</span> <span class="p">{</span><span class="n">name</span><span class="p">:</span> <span class="n">normalise</span><span class="p">([</span><span class="n">item</span><span class="p">[</span><span class="n">name</span><span class="p">]</span> <span class="k">for</span> <span class="n">item</span> <span class="ow">in</span> <span class="n">items</span><span class="p">])</span> <span class="k">for</span> <span class="n">name</span> <span class="ow">in</span> <span class="n">WEIGHTS</span><span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="n">scores</span> <span class="o">=</span> <span class="p">[]</span>
</span></span><span class="line"><span class="cl">    <span class="k">for</span> <span class="n">i</span><span class="p">,</span> <span class="n">item</span> <span class="ow">in</span> <span class="nb">enumerate</span><span class="p">(</span><span class="n">items</span><span class="p">):</span>
</span></span><span class="line"><span class="cl">        <span class="n">base</span> <span class="o">=</span> <span class="nb">sum</span><span class="p">(</span><span class="n">weight</span> <span class="o">*</span> <span class="n">norm</span><span class="p">[</span><span class="n">name</span><span class="p">][</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">name</span><span class="p">,</span> <span class="n">weight</span> <span class="ow">in</span> <span class="n">WEIGHTS</span><span class="o">.</span><span class="n">items</span><span class="p">())</span>
</span></span><span class="line"><span class="cl">        <span class="n">scores</span><span class="o">.</span><span class="n">append</span><span class="p">(</span><span class="n">base</span> <span class="o">*</span> <span class="mf">0.5</span> <span class="o">**</span> <span class="p">(</span><span class="n">item</span><span class="p">[</span><span class="s2">&#34;age_hours&#34;</span><span class="p">]</span> <span class="o">/</span> <span class="n">HALF_LIFE_HOURS</span><span class="p">))</span>
</span></span><span class="line"><span class="cl">    <span class="n">order</span> <span class="o">=</span> <span class="nb">sorted</span><span class="p">(</span><span class="nb">range</span><span class="p">(</span><span class="nb">len</span><span class="p">(</span><span class="n">items</span><span class="p">)),</span> <span class="n">key</span><span class="o">=</span><span class="k">lambda</span> <span class="n">i</span><span class="p">:</span> <span class="n">scores</span><span class="p">[</span><span class="n">i</span><span class="p">],</span> <span class="n">reverse</span><span class="o">=</span><span class="kc">True</span><span class="p">)</span>
</span></span><span class="line"><span class="cl">    <span class="k">return</span> <span class="p">[</span><span class="n">items</span><span class="p">[</span><span class="n">i</span><span class="p">]</span> <span class="k">for</span> <span class="n">i</span> <span class="ow">in</span> <span class="n">order</span><span class="p">]</span></span></span></code></pre></div></div>
<p>Các trọng số chỉ là phỏng đoán cho tới khi những thử nghiệm ở phần dưới chứng minh điều ngược lại. Lượt xem và lượt thích vẫn có thể đưa vào; chỉ là chúng trở thành một đầu vào trong số nhiều đầu vào, chứ không còn là toàn bộ điểm số. Chu kỳ bán rã (half-life) quyết định bài cũ chìm xuống nhanh đến đâu, và đó là cả một chủ đề riêng: tôi đã viết kỹ về nó trong bài <a href="https://jared.lynskey.co.nz/vi/posts/2023/2023-09-27-simulating-views/" >thuật toán xếp hạng bảng tin quyết định khi nào bài cũ bị dìm xuống</a>. Các hệ thống thật thường để mô hình tự học trọng số thay vì chọn bằng tay, nhưng hình dạng thì vẫn vậy: nhiều tín hiệu đi vào, một con số đi ra, rồi sắp xếp.</p>

<h3 class="relative group">Rồi kiểm thử cho sòng phẳng
    <div id="rồi-kiểm-thử-cho-sòng-phẳng" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#r%e1%bb%93i-ki%e1%bb%83m-th%e1%bb%ad-cho-s%c3%b2ng-ph%e1%ba%b3ng" aria-label="Cọc dấu">#</a>
    </span>
    
</h3>
<p>Tín hiệu có khéo đến đâu cũng phải đem đi thử nghiệm A/B (A/B test) — cho một nhóm nhỏ người dùng xem nội dung mới rồi so mức tương tác với những bài đang giữ vị trí. Và để ý xem nội dung lan ra thế nào: lượt chia sẻ, lượt nhắc đến và lượt nhúng, cả trong lẫn ngoài nền tảng, nói lên giá trị nhiều hơn hẳn cái bộ đếm lượt xem.</p>
<p>Không chỉ số đơn lẻ nào trụ nổi khi va chạm với thực tế. Những cách xếp hạng thật sự hiệu quả đều pha trộn hành vi người dùng, chất lượng nội tại, tính thời sự và những thử nghiệm sòng phẳng, và không tín hiệu nào được quyền quyết định sau cùng.</p>
]]></content:encoded>
      <media:content url="https://jared.lynskey.co.nz/en/posts/2023/2023-10-06-reinenforcement-learning/featured.jpeg" medium="image" type="image/jpeg" />
    </item>
    <item>
      <title>Thuật toán xếp hạng bảng tin (feed ranking): khi nào bài cũ bị dìm xuống?</title>
      <link>https://jared.lynskey.co.nz/vi/posts/2023/2023-09-27-simulating-views/</link>
      <guid isPermaLink="true">https://jared.lynskey.co.nz/vi/posts/2023/2023-09-27-simulating-views/</guid>
      <pubDate>Wed, 27 Sep 2023 00:30:03 +0000</pubDate>
      <dc:creator>Jared Lynskey</dc:creator>
      <category>Content Ranking</category>
      <category>Decay</category>
      <category>Freshness</category>
      <category>Engagement</category>
      <description>Thuật toán xếp hạng bảng tin (feed ranking) dùng hàm suy giảm để cân bằng giữa độ mới và mức tương tác như thế nào — góc nhìn của một người mà công việc hằng ngày là xây dựng bảng tin nội dung.</description>
      <content:encoded><![CDATA[<p>Công việc của tôi là làm một nền tảng nội dung, nên có một câu hỏi mà người dùng không bao giờ nhìn thấy, nhưng tôi thì nghĩ tới thường xuyên hơn hầu hết mọi người: đến lúc nào thì bảng tin (feed) nên thôi hiển thị một bài viết?</p>
<p>Nội dung nào rồi cũng cũ. Người ta mở ứng dụng là để xem có gì mới, và bảng tin phải đáp ứng được điều đó. Nhưng một bài hay từ tháng trước cũng không nên biến mất chỉ vì nó không còn mới. Hàm suy giảm (decay function) là cách tiêu chuẩn để mô hình hoá sự đánh đổi này, và nó đơn giản hơn cái tên nhiều.</p>

<h4 class="relative group">Độ mới quan trọng, nhưng không phải ở đâu cũng vậy
    <div id="độ-mới-quan-trọng-nhưng-không-phải-ở-đâu-cũng-vậy" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#%c4%91%e1%bb%99-m%e1%bb%9bi-quan-tr%e1%bb%8dng-nh%c6%b0ng-kh%c3%b4ng-ph%e1%ba%a3i-%e1%bb%9f-%c4%91%c3%a2u-c%c5%a9ng-v%e1%ba%ady" aria-label="Cọc dấu">#</a>
    </span>
    
</h4>
<p>Khi mở một trang tin tức hay bảng tin mạng xã hội, thường thì bạn muốn xem cái mới nhất. Ở những lĩnh vực thay đổi nhanh như tin tức hay thị trường tài chính, nội dung tuần trước chỉ tổ chiếm chỗ, nên cách xếp hạng ở đó nghiêng hẳn về độ mới.</p>
<p>Nhưng không phải lĩnh vực nào cũng thế. Bài viết học thuật hay các bài báo chuyên sâu vẫn giữ được giá trị theo thời gian, có khi còn quý hơn. Tuổi của nội dung có ý nghĩa trong hầu hết hệ thống xếp hạng, nhưng nên cho nó nặng bao nhiêu thì hoàn toàn tuỳ vào thứ bạn đang xếp hạng.</p>

<h4 class="relative group">Mô hình hoá độ “phai” bằng suy giảm theo hàm mũ
    <div id="mô-hình-hoá-độ-phai-bằng-suy-giảm-theo-hàm-mũ" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#m%c3%b4-h%c3%acnh-ho%c3%a1-%c4%91%e1%bb%99-phai-b%e1%ba%b1ng-suy-gi%e1%ba%a3m-theo-h%c3%a0m-m%c5%a9" aria-label="Cọc dấu">#</a>
    </span>
    
</h4>
<p>Cách thường dùng để mô hình hoá độ liên quan giảm dần là suy giảm theo hàm mũ (exponential decay): nội dung càng cũ thì giá trị mà người ta cảm nhận được càng tụt nhanh. Dạng đơn giản nhất trông thế này:</p>
<p><code>relevance = e^(-decay_rate × age)</code></p>
<p>trong đó relevance là giá trị hiện tại của nội dung, decay_rate là tốc độ suy giảm, quyết định nội dung mất giá trị nhanh hay chậm, còn age là tuổi của bài, thường tính bằng số ngày kể từ lúc đăng.</p>
<p>Điều tôi thích ở mô hình này là nó chỉ có đúng một cái núm để vặn. Chỉnh tốc độ suy giảm là bạn quyết định được nội dung còn “sống” trong vài giờ, vài ngày hay vài tuần. Toàn bộ phần cấu hình chỉ có vậy.</p>

<h4 class="relative group">Tương tác giúp nội dung hay sống lâu hơn
    <div id="tương-tác-giúp-nội-dung-hay-sống-lâu-hơn" class="anchor"></div>
    
    <span
        class="absolute top-0 w-6 transition-opacity opacity-0 -start-6 not-prose group-hover:opacity-100 select-none">
        <a class="text-primary-300 dark:text-neutral-700 !no-underline" href="#t%c6%b0%c6%a1ng-t%c3%a1c-gi%c3%bap-n%e1%bb%99i-dung-hay-s%e1%bb%91ng-l%c3%a2u-h%c6%a1n" aria-label="Cọc dấu">#</a>
    </span>
    
</h4>
<p>Tuổi của bài mới chỉ là một nửa bức tranh. Lượt xem, lượt thích và bình luận cho bạn biết người ta có thật sự quan tâm hay không; bỏ qua chúng thì cách xếp hạng sẽ chôn luôn cả bài hay lẫn bài dở. Cách sửa đơn giản là nhân hai yếu tố này với nhau:</p>
<p><code>score = engagement × e^(-decay_rate × age)</code></p>
<p>Giờ thì một bài cũ nhưng tương tác mạnh vẫn có thể xếp trên một bài mới nhưng làng nhàng. Chỉ có điều, nó cần ngày càng nhiều tương tác hơn mới giữ được chỗ, vì phần suy giảm bên dưới cứ nhỏ dần đi.</p>
<p>Trong thực tế, vòng đời của một bài viết diễn ra thế này: bài mới đăng được cộng điểm vì còn mới, nên có một cơ hội công bằng để được nhìn thấy. Nếu người ta xem, thích, bình luận, điểm sẽ tăng. Ngày qua ngày, phần suy giảm gặm dần vào điểm số, và chỉ những bài giữ được tương tác đều đặn mới trụ lại gần đầu bảng tin. Nội dung mới thì được trao cơ hội; còn nội dung thật sự hay thì tự giành lấy chỗ đứng.</p>
<p>Nói thật thì mẹo chỉ có vậy. Một hàm mũ, một hệ số tương tác, một cái núm để vặn. Phần khó là chọn tốc độ suy giảm bao nhiêu cho hợp với loại nội dung của riêng bạn, và phần đó thì chỉ có thể dựa vào phán đoán.</p>
]]></content:encoded>
      <media:content url="https://jared.lynskey.co.nz/en/posts/2023/2023-09-27-simulating-views/featured.jpeg" medium="image" type="image/jpeg" />
    </item>
  </channel>
</rss>
