Contents
- 1. The Starting Point of Intelligent Blog Recommendations
- 2 Data Extraction
- 3. Logic for Generating the Recommendation List
- 4. Basic Construction and Presentation Design of the Recommendation List System
- 5. WordPress Runtime Recommendation System Implementation
- 6. Summary: A Recommendation System Based on Pre-computed Indexes
- 7. Postscript: Why I didn't choose the readily available "Related Articles" plugin
1. The Starting Point of Intelligent Blog Recommendations
This is the second article in the series (see:Adding a Semantic Index to Your Blog (Part 2): Implementation of JSON Structure and Script GenerationFinally, we have generated the key data using a Python script. semantic-index.json The document. And the next, most natural question is:我们该如何利用这份已经整理好的内容关系,让博客中的文章真正开始形成连接?
After careful consideration, I believe the most needed function for blogs right now is... “"Recommended List"”:它能让读者在浏览文章时,顺着推荐列表继续探索更多相关主题,从而提升阅读体验,同时也让博客中的内容能够被更充分地发现与利用。
而在当前这个场景下,我真正关注的其实并不是“推荐算法”本身,而是:如何利用现有的数据结构,把已经存在的内容关联真正转化为可运行的推荐能力。
这里最核心的前提,就是上一篇文章中生成的 semantic-index.json 文件。因此,这一篇文章将重点围绕:如何从现有 JSON 数据出发,将其中已经整理好的文章关系,进一步转化为实际可用的推荐系统。
整个目标并不复杂:尽量避免引入额外运行时计算,在保持结构轻量的前提下,实现一个真正能够体现文章内容关联的推荐系统。
The following sections will be the focus of this article:
- How to start from the existing
semantic-index.json中提取并整理推荐系统所需的数据结构,并最终生成用于运行时查询的文章索引文件article-index.json - How to run based on WordPress
article-index.jsonofrelatedThis mechanism establishes a lightweight mapping between article IDs and display data (title/url), and constructs the final recommendation results. - 如何将推荐列表功能真正接入 WordPress 页面,并完成最终的前端展示
整个过程的重点,并不是重新计算文章之间的关系,而是:如何将已经整理好的内容关联,转化为一个真正可运行、可展示、可维护的轻量推荐系统。
2 Data Extraction
2.1 JSON File Overview
In the previous chapter, we clarified the functional goals to be achieved in step 1: "From the existing..." semantic-index.json We will extract and organize the data that the recommendation system truly needs, and generate an article index structure suitable for WordPress. Before we begin processing, let's quickly review... semantic-index.json The basic structure of a file.
Each article is in semantic-index.json It generally contains the following fields:
id: A unique identifier for articles, used for quick searching and mapping.titleArticle title, used for displaying and generating the recommendation list.urlArticle link, for use in the recommendation list.summaryArticle summaries, which can be used for brief displays of recommendation lists.keywordsArticle keywords can help generate semantic relevance.relatedThe recommended list of related articles (which can be empty or partially filled) is an important basis for generating the recommendation list.
After understanding semantic-index.json After establishing the basic structure, the next step is...Read data from the file into the programThis is so that we can process and generate the recommendation list in subsequent steps.
Depending on the use case, the methods for reading JSON may vary slightly. Here we provide two common methods: Python and WordPress/PHP.
2.2 Data Reading and Processing
2.2.1 Python Example
If you are using Python to process JSON files locally or on a server, you can do the following:
Import json # Read the structured data file semantic-index.json with open('semantic-index.json', 'r', encoding='utf-8') as f: articles = json.load(f) # Output the first two records to verify the correctness of the data structure for article in articles[:2]: print(article['id'], article['title'], article['url'])
json.load() It will parse the JSON file into a list and dictionary structure in Python, with each article corresponding to a dictionary object. The read operation itself is very straightforward, and its main purpose is:
- Verify that the data structure is correct.
- Confirm that the fields (id / title / url / related) are complete.
- Provides a basic data source for the subsequent index generation stage.
2.2.2 WordPress / PHP Examples
If you want to read JSON files directly in WordPress, you can use the following method:
<?php
// 读取本地 JSON 文件
json_file = get_template_directory() . '/semantic-index.json';json_data = file_get_contents(json_file); // Convert JSON to a PHP arrayarticles = json_decode(json_data, true); // View the data of the first two articles for (i = 0; i<2;i++) { echo articles[i]['id'] . ' - ' . articles[i]['title'] . ' - ' . articles[i]['url'] . '
'; } ?>
file_get_contents()Used to read local file contentjson_decode($json_data, true)It will convert JSON into a PHP associative array.- Then it can also be based on
$articlesOrganize and process the fields of the array.
2.2.3 Final Method Selection
Although WordPress/PHP can read directly semantic-index.json While it completes basic data parsing, from the perspective of the overall design goals of the recommendation system, the final data processing stage is not suitable to be completed in the runtime environment. Therefore, in terms of implementation, I chose to entrust the core data processing flow to Python.
The reason is not complicated in essence: for recommendation systems, the truly valuable work is concentrated in the data preprocessing stage, including field extraction, structure reorganization, and article relationship construction. This type of operation is essentially "offline data processing," and Python has a clearer and more efficient expressive ability in handling JSON data, dictionary mapping, and batch structure transformation.
In contrast, PHP's role in the WordPress ecosystem is more focused on the runtime environment. Its core responsibility is page rendering and content output, rather than handling complex data structure construction tasks. Placing data processing logic in the PHP runtime not only increases the burden during page generation but also blurs the boundaries between the data layer and the presentation layer.
Therefore, in the overall implementation scheme of this paper, the data processing stage is uniformly handled by Python, while WordPress/PHP only serves as the final consumer, responsible for reading the already constructed index data and rendering the page.
It is important to emphasize that, regardless of whether Python or PHP is used, the content handled in the current 2.2 phase is essentially just...Raw JSON data input layerThe goal of this stage is not to build the final recommendation system structure, but to complete the reading and verification of the basic data to ensure that the data source for subsequent processing is reliable.
The structured index actually used in WordPress runtime (article-index.jsonThis layer will be generated uniformly in the subsequent construction phase (2.4). This layer will assume the responsibility of "runtime data entry" and is the data core of the entire recommendation system, no longer belonging to the scope of raw data processing.
2.3 Data Preparation and Structuring
Successfully read semantic-index.json Afterwards, we obtained a complete set of original article data. However, structurally, this data still remained in a "raw list form," leaning more towards content storage than a structured representation oriented towards an indexing system.
If we directly proceed to the subsequent index building stage based on this original structure, it will bring an implicit problem: although the data is complete, it lacks consistent access semantics. Although the fields of each article exist, they are still "loosely organized" at the structural level.
Therefore, upon entering article-index.json Before building the index, a lightweight structural adjustment needs to be performed. The purpose of this process is not to change the data itself, but to make each piece of data structurally consistent, thereby providing a stable and predictable input source for subsequent index building.
At this stage, the processing logic is actually very restrained: no new data relationships are introduced, and no indexing operations are performed; the original data is simply reorganized into a standardized set of structures.
In this process, each article is remapped to a unified data structure, with its core fields remaining unchanged, undergoing only minimal and necessary extraction and normalization processing, for example... id,title,url and used for subsequent relational expressions related Field.
The corresponding processing logic can be expressed using a very straightforward Python code:
cleaned_articles = [] for article in json_data: cleaned_articles.append({ 'id': article['id'], 'title': article['title'], 'url': article['url'], 'related': article.get('related', []) })
The logic itself is not complicated; all it does is ensure that all articles have a consistent structural form before moving on to the next stage.
It's important to emphasize that the data structure at this stage remains a "list structure," without the introduction of any indexing or search optimizations. In other words, it's still a linear dataset, only cleaner and more consistent in structure.
[ { 'id': 'article_id_1', 'title': 'article title 1', 'url': '/article-1/', 'related': ['article_id_5', 'article_id_9'] }, { 'id': 'article_id_2', 'title': 'article title 2', 'url': '/article-2/', 'related': [] } ]
The significance of this structure lies not in performance or query efficiency, but in providing a semantically consistent data starting point for the subsequent index building phase. Only when all articles have a unified data representation format can the necessary runtime data be stably generated. article-index.json.
Therefore, the more accurate positioning of this stage is not "processing data" but "constraining data form"—what it does is to converge the raw data from a loose collection of content into a structurally unified intermediate data layer, providing conditions for subsequent indexing processing.
2.4 Building a runtime article index
After completing the previous stage of data processing, the next step is quite clear: to further solidify the structured article data into an index file that can be used directly at runtime.
The key point at this stage is not "reprocessing data," but "determining the final form of the data." In other words, at this moment, the data begins to shift from an "intermediate structure" to a "runtime asset," and its use changes from being a processing object to an index resource that is directly consumed.
In the current design, this final form is uniformly defined as article-index.jsonIt no longer carries any data processing logic, nor does it participate in any structural evolution process; it simply exists as a static, stable data carrier that can be read across environments.
From a system perspective, the significance of this index file is very clear: it is the sole data entry point that WordPress relies on during runtime. Therefore, it must be completely free from the constraints of runtime logic to ensure that its structure remains unchanged after generation.
Under this design, WordPress's responsibilities are strictly narrowed down. It is only responsible for reading the index, locating data based on the post ID, and completing the final display based on that, without participating in any form of data construction or relational calculation.
The final generated article-index.json Structurally, it is represented as a mapping table with article IDs as the core key. Each entry contains complete basic information about the article and its predefined relationships, such as title, link, and related list:
{ "article_id_1": { "title": "WordPress Cache Optimization Practices", "url": "/wordpress-cache/", "related": [ "article_5", "article_9" ] }, "article_id_2": { "title": "Cloudflare Tunnel Usage Log", "url": "/cloudflare-tunnel/", "related": [ "article_1", "article_7" ] } }
Structurally, this file is no longer a "data set," but a runtime index graph that has been flattened. The relationships between all the articles no longer exist in the form of calculations, but are solidified in a static mapping manner.
The entire index generation process can be understood as a one-way data convergence process: the original semantic data is processed, compressed, and transformed into a stable ID mapping structure, ultimately outputting a JSON file that can be directly consumed at runtime.
semantic-index.json (raw semantic data) ↓ Structure organization (2.3) ↓ Indexing transformation ↓ article-index.json (runtime data assets) ↓ WordPress read-only consumption
The most critical change in this process is not "generating a file", but the redefinition of the system boundary: the data construction phase ends here, and all subsequent runtime behaviors can only be carried out on the established structure.
Therefore, it can be said that from this moment on, the structure of the entire recommendation system is completely fixed. All subsequent logic no longer changes the data itself, but only reads and maps based on this index structure.
3. Logic for Generating the Recommendation List
3.1 Core Ideas of Recommendation Logic
In the previous chapter, we completed the runtime index file. article-index.json The next problem to solve is quite straightforward: how to quickly generate a corresponding recommendation list based on the current article at runtime.
At this stage, the recommendation logic itself is no longer complex, because the relationships between articles are not dynamically derived at runtime, but are "fixed" into the data structure during the preceding index building process. In other words, the recommendation system no longer needs to consider "whether articles are related"; it only needs to accept the given fact—the relevance of each article... related The field itself already defines its recommended objects.
Therefore, starting from the current article ID, the core action of the recommendation logic is actually very simple: first locate the article... article-index.json The corresponding article node in the text, and then read it. related The list is then mapped back to the complete information of the articles (such as title and URL), and finally assembled into a recommendation result that can be directly used by the front end.
To put it more intuitively, this process is not essentially "computation," but rather a standard data unfolding: starting from a known node, performing a finite unfolding operation along predefined relational edges.
current_article_id ↓ article-index.json lookup ↓ Get related ID list ↓ ID → article data mapping ↓ Generate recommendation list
Because of this structure, the recommender system no longer possesses any "algorithmic complexity" at this layer: it does not involve similarity calculation, ranking strategies, or real-time judgment by semantic models. All these factors that could potentially affect recommendation quality have been moved to the data generation stage, and only one stable and controllable execution path is retained at runtime.
From an engineering perspective, this design is very friendly to content systems like WordPress. It avoids complex database-level joins and completely bypasses runtime computational overhead, reducing the entire process to pure in-memory data lookup and structure mapping. In other words, the runtime cost of the recommendation system is almost equivalent to one array read plus one simple traversal.
More importantly, in this model, the upper limit of recommendation quality is no longer determined by runtime, but entirely by the pre-stage evaluation. related How the field is constructed. Once related Once generated and solidified, all that can be done at runtime is to faithfully "reproduce" this relational structure, rather than reprocessing or optimizing it.
Therefore, it can be said that the recommender system at this stage has undergone a fundamental change: it is no longer a "computation system" but a typical "data unfolding system", with the core of all logic converging on one action - to fully unfold the results from the defined relations.
3.2 Generation of the Recommendation List
Once the recommendation logic is clear, generating the recommendation list at runtime becomes very straightforward: since it has already been built in the previous stages... article-index.jsonTherefore, in the WordPress execution environment, there is no longer a need to process any raw data, nor is it necessary to perform structural transformations or relational calculations.
From the overall process perspective, the runtime actually does only one thing: based on the current article ID, it finds the corresponding node from the pre-built index and then reads the relevant data. related Create a list and map these IDs back to specific article information.
In other words, the generation of the recommendation list is no longer a "building process" but a "data restoration process"—the system simply unfolds the relationship structure that was determined in the offline stage into content that is visible to the user.
In WordPress, this logic can usually be encapsulated into a very lightweight function whose core purpose is not to implement an algorithm, but to complete a standard data mapping:
function get_related_posts(current_article_id,limit = 5) {
json_path = get_template_directory() . '/cache/article-index.json';articles_index = json_decode( file_get_contents(json_path), true ); if (!isset(articles_index[current_article_id])) { return []; }related_ids = articles_index[current_article_id]['related'] ?? [];
recommendations = []; foreach (related_ids as related_id) { if (!isset(articles_index[related_id])) { continue; }article = articles_index[related_id];
recommendations[] = [ 'title' =>article['title'], 'url' => article['url'] ]; if (count(recommendations) >= limit) { break; } } returnrecommendations;
The final output data structure is also very simple; it is essentially a standard list of articles.
[ [ 'title' => 'Cloudflare Tunnel Usage Log', 'url' => '/cloudflare-tunnel/' ], [ 'title' => 'WordPress Caching Optimization Practices', 'url' => '/wordpress-cache/' ] ]
At this point, the recommender system's responsibilities at the runtime level have completely converged: it no longer builds relationships or calculates relevance, but simply presents the already determined results stably.
4. Basic Construction and Presentation Design of the Recommendation List System
4.1 Preparation of Operating Environment
Before integrating the WordPress recommendation list, you need to ensure that your system has the basic operating conditions. This "environment preparation" does not involve any recommendation logic or data structure design; it simply ensures that the offline build phase and runtime execution environment are working correctly, thus providing a foundation for subsequent steps.
From an overall architectural perspective, the core focus at this stage is not "whether the function is implemented", but "whether the execution conditions are met".
1. It is necessary to confirm whether the Python runtime environment is available.
Since the initial data building process relies on Python, this environment serves as the foundational execution platform for the entire system's offline phase.
In practical use, it is only necessary to ensure that the server or local environment can execute Python normally, for example, by verifying through a simple version check:
python3 --version
If the version number is returned correctly, it means that the Python environment is basically usable.
2. It is necessary to confirm whether the preceding data file has been successfully generated.
In the current system, the core output during the offline phase is semantic-index.json The file contains the source of semantic relationships between articles and is used as data input for the subsequent index building stage.
Therefore, before proceeding to the next stage, it is necessary to ensure that the file already exists and meets the following basic conditions:
- The file has been successfully generated.
- JSON structure is complete and parseable
- The encoding format is UTF-8.
relatedThe field data exists and the structure is correct.
The status of this file directly determines whether the entire recommendation system has a computable basic input.
3. It is necessary to confirm that the WordPress theme has basic modification capabilities.
Since the recommendation list display needs to be integrated at the theme level in subsequent stages, it is necessary to be able to access and modify relevant files in the theme, for example:
functions.phpsingle.phpOr the corresponding article template file
If you are using a sub-theme structure, it is recommended to make all changes in the sub-themes to avoid overwriting issues caused by main theme updates.
When all three conditions mentioned above are met, the basic operating environment of the entire system can be considered ready. The significance of this stage lies in confirming that offline data generation capabilities and runtime execution capabilities are integrated, but no indexing structure or recommendation logic itself has yet been implemented.
4.2 Generate runtime article index files
After completing the preliminary semantic data preparation and structural design, the next step is the most crucial step in the entire recommendation system: generating article index files that can be directly used by WordPress at runtime.
The essence of this stage is not "reprocessing data", but rather solidifying the previously determined structured results into a stable runtime data asset, transforming it from a "processing object" into a "directly consumable data entry point".
In other words, this step marks the formal transition of the entire system from the "data construction phase" to the "runtime consumption phase." At the implementation level, this process is typically accomplished through a separate Python script, for example:
build_article_index.py
The script has a very single function: to... semantic-index.json The existing structured relationships are reorganized into an index structure centered on article IDs, and output as a runtime file.
The core processing logic is as follows:
import json json_file = 'semantic-index.json' output_file = 'article-index.json' with open(json_file, 'r', encoding='utf-8') as f: json_data = json.load(f) articles_index = {} for article in json_data: article_id = article['id'] articles_index[article_id] = { 'title': article['title'], 'url': article['url'], 'related': [ item['id'] if isinstance(item, dict) else item for item in article.get('related', []) ] } with open(output_file, 'w', encoding='utf-8') as f: json.dump(articles_index, f, ensure_ascii=False, indent=2) print("article-index.json has been generated")
Logically speaking, this process itself is not complicated; it simply involves a structural transformation.
- Input: Semantic layer data (semantic-index.json)
- Output: Runtime index (article-index.json)
- Intermediate process: Field extraction + ID mapping + Structure solidification
At this point, the most crucial data preparation stage of the entire recommendation system is complete. From a system perspective, the completion of this step means:
– Semantic relational data has been solidified
– Runtime index has been generated
– WordPress will only need to perform data reading and mapping afterwards.
4.3 Location of the Recommendation List on the Article Page
Before we actually start implementing the recommendation list, there is another question that is easily overlooked but is actually very important: where should the recommendation list be placed on the article page?
From a technical implementation perspective, this may seem like a simple page layout issue, but from the perspective of reading experience and content organization, this step can directly affect the final performance of the entire recommendation system.
For many traditional blogs, related articles are usually placed in the sidebar, at the bottom of the page, after the comment section, or randomly inserted into the main text area. However, most of these methods fall under the "traffic distribution" approach, and their core goal is often to increase click-through rates rather than helping readers establish structural relationships between content.
The recommendation system implemented in this paper is not designed for traditional "popular recommendations," but rather to provide readers with truly valuable reading paths based on the semantic connections of the current article. This means that the recommendation list must depend on the context of the current article; therefore, its most appropriate location is not the sidebar, but rather:After the main text ends.
However, in my current blog, there is already a fixed module after the main text of the article – “Content Structure Tips”:

The purpose of this module is to explain the position of the current article from the perspective of the overall knowledge system, as well as the possible thematic directions it may be related to. From a reading logic perspective, it belongs to the "structural cognition layer".
The recommended list, however, plays a different role. It doesn't explain the structure, but rather translates it into a concrete path for the next reading step. Therefore, the relationship between the two in the reading journey can be abstracted as follows:
Main text ↓ Content structure hints (structural understanding layer) ↓ Related article recommendation list (path extension layer) ↓ Comments section
In this sequence, "content structure hints" provide abstract knowledge location, while the recommendation list provides specific article jump links. The key significance of this design is that it prevents the recommendation system from existing as an isolated module, but rather embeds it into the entire reading logic chain, forming continuous cognitive and behavioral guidance.
At the same time, this order also has a very practical advantage: the recommendation list will not interfere with the reading process of the main text, and will only appear after the user has finished reading the current content, thus avoiding the continuous distraction of attention caused by traditional sidebar recommendations.
From a system design perspective, this layout actually defines the "triggering timing" of the recommendation system—it is not a distracting element that exists continuously during the reading process, but an extended module that is activated only after the reading is completed.
Once this positional relationship is established, the subsequent question becomes very clear: how to ensure that during WordPress runtime... article-index.json The associated data is mapped to a recommendation list and embedded into the article content stream.
4.4 Theoretical Implementation Model for Recommendation List Generation
If we abstract away the preceding construction process, what actually happens in the runtime phase of the entire recommendation system is very simple: it is not "computing recommendations", but rather "performing a constrained data expansion".
The key point here is that the recommendation logic does not occur during WordPress runtime, but is pre-defined in... semantic-index.json → article-index.json This offline phase is complete. Therefore, the runtime environment is not a problem requiring reasoning, but a data problem that has already been structured and defined.
From a model perspective, WordPress does only three very specific things when a post page loads: identify the current post, locate the index node, and expand the related collection. However, these three steps are not an algorithmic process, but rather closer to a "mapping chain".
The current article ID here doesn't act as an "input variable," but rather as a "locating anchor." Its sole purpose is to find the corresponding node within a pre-built index space. Once this node is successfully located, subsequent actions become completely unpredictable.
Because article-index.json In this context, the relationships between articles have been predefined as a static structure, for example:
article_id → { title, url, related[] }
Therefore, the so-called "recommendation generation" at runtime is essentially just... related The set of IDs pointed to by this field is expanded and mapped back to the displayable information structure. This process is more like a "structure restoration" than a "content computation".
The truly significant change here is that the system's complexity is completely brought forward. The runtime no longer participates in any semantic judgments, similarity calculations, or ranking decisions; it simply faithfully executes a predefined structural mapping relationship.
Therefore, the core of this model lies not in "recommendation algorithm design," but in "whether the data has been structured into expandable relationships." Once this is true, the runtime logic naturally degenerates into a very lightweight reading process, such as simple index access and array traversal in PHP:
$related_ids = $article_index[$current_post_id]['related'] ?? [];
What this line of code actually represents is no longer "recommendation logic," but rather "the entry point for expanding relationships."
Theoretically speaking, the essence of this design is a typicalPrecomputed relation modelIt migrates the problems that originally belonged to the runtime to the build phase, thereby gaining runtime simplicity and determinism.
Therefore, when we look back at the entire recommendation system, we find that its true premise is not the implementation capabilities of WordPress or PHP, but a more fundamental assumption: the relationships between articles can be fully determined offline and stably preserved in a structured form.
Once this assumption holds true, the recommender system itself is no longer a "system" but more like an "expander"—it is only responsible for presenting the existing structure again.
5. WordPress Runtime Recommendation System Implementation
5.1 The essence of this solution: only two code snippets
If we take all the content from the previous chapters into account, this recommendation system is actually not as complex as you might imagine in terms of implementation. In fact, you could say it's been deliberately compressed into a very "restrained" form—the entire system only involves two pieces of code that actually do the work.
The first section is the offline build script. build_article_index.pyresponsible for semantic-index.json Convert to runtime usable article-index.jsonThe second part is the PHP logic on the WordPress side, which is responsible for reading this index when the article page loads and generating the final recommendation list.
In addition, there is no database JOIN, no real-time similarity calculation, and no runtime semantic reasoning process.
From an implementation perspective, this structure is essentially doing something very simple: the recommendation system no longer participates in the calculation, but is only responsible for displaying the results.
5.2 build_article_index.py
The complete code is as follows:
Import json # Semantic-index.json file path # This file is generated by the preceding Python script and contains article semantic relationship data json_file = 'semantic-index.json' # Output runtime index file (JSON version) # WordPress will directly read this file at runtime (the path needs to be modified according to the actual environment) output_file = '/docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json' # Read raw semantic data # The input structure is a list, each item representing the semantic information of an article with open(json_file, 'r', encoding='utf-8') as f: json_data = json.load(f) # Initialize runtime index structure # The final structure is a dict: { article_id: article_data } articles_index = {} # Build runtime index structure (using article_id as the key) for article in json_data: # Unique identifier for the current article article_id = article['id'] # Construct a standardized article index structure # Note: The related field is uniformly converted to an ID list articles_index[article_id] = { 'title': article['title'], 'url': article['url'], # related is compatible with two structures: # 1. dict (containing an id field) # 2. Directly string/int id # Finally uniformly converted to string ID list 'related': [ item['id'] if isinstance(item, dict) else item for item in article.get('related', []) ] } # Write to the runtime index file (JSON) # ensure_ascii=False: Preserve Chinese characters # indent=2: Improve readability (facilitating debugging) with open(output_file, 'w', `encoding='utf-8') as f: json.dump( articles_index, f, ensure_ascii=False, indent=2 ) #` The build complete message is printed ("article-index.json has been generated").
Once the script is saved, you can directly execute the generation process:
python3 build_article_index.py
After execution, if everything is normal, the terminal will output a result similar to the following:

At this point, you can see the final generated runtime index file in the target directory:

After the index file is generated, it is usually quickly validated on two levels to ensure that the data meets expectations in both "content format" and "structural legitimacy".
First, you can confirm whether the index structure has been generated as expected by directly viewing the file content. Taking my blog's Argon theme as an example:
head -20 /docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json
If the output clearly shows a structure with the article ID as the key, and contains... title,url and related If fields like these are present, it means the index data has been correctly generated and is available at runtime.

After confirming that the data structure appears normal, a more rigorous structure check can be performed using a JSON parsing tool, for example:
python3 -m json.tool /docker/wordpress/html/wp-content/themes/argon-theme-master/cache/article-index.json > /dev/null
This step does not output any content; its purpose is to perform a complete parsing of the entire JSON file. If no error messages are output, it means that the file is syntactically valid and can be read normally by WordPress.

5.3 WordPress PHP Code (Reading + Rendering)
Below is the code I currently use in WordPress to implement the recommended list. You can directly insert it into the "Code Snippets" plugin or the "functions.php" file of your current theme:
function add_recommend_list_after_content(content) { // Only applies to single article pages // Avoid executing in environments such as homepage, archive pages, and RSS feeds if (!is_single() || !in_the_loop() || !is_main_query()) { returncontent; } // Current article ID (used for index matching)
post_id = get_the_ID(); // 1. Read the runtime index file (JSON) // The file is pre-generated by a Python scriptarticle_index_file = get_template_directory() . '/cache/article-index.json'; // If the file does not exist, return the original content directly. if (!file_exists(article_index_file)) { returncontent; } // Read JSON content and convert it to a PHP array
article_index = json_decode( file_get_contents(article_index_file), true); // The current article is not in the index if (!isset(article_index[post_id])) { return content; } // 2. Get the related list of the current article // related is an array of recommended article IDsrelated_ids = article_index[[post_id]['related'] ?? []; // Returns directly if no recommended content is found.related_ids)) { returncontent; } // 3. Construct the recommendation list data // Expand article_id → title/url
recommend_list = []; foreach (related_ids as related_id) { // Skip articles that do not exist in the index if (!isset(article_index[related_id])) { continue; }recommend_list[] = [ 'title' => article_index[related_id]['title'], 'url' => article_index[related_id]['url'] ]; } // If no valid recommendation is found, the module will not be rendered if (empty(recommend_list)) { returncontent; } // 4. Generate the recommendation list HTML // Outer container style: card structure
html = ' '; // Title areahtml .= ' 📎 Recommended Reading: '; // Output each recommended article foreach (recommend_list asitem) {
html .= 'item['url']) . '" target="_blank" style=" color:#1d4ed8; text-decoration:none; font-weight:500; "> ' . esc_html(item['title']). ' '; } // End the containerhtml .= ' '; // append to the end of the text return content. .html; } // Mount to the_content filter // priority = 20: ensures add_filter('the_content', 'add_recommend_list_after_content', 20);
5.4 Final Results of the Recommendation System
To date, the entire semantic index-based WordPress recommendation system has been fully implemented. From data construction and runtime index generation to reading and rendering on the WordPress side, the entire chain can run stably in a real-world environment.
On the actual article page, the recommendation list appears after the "Content Structure Tips" and before the comments section, serving as a natural extension module after reading the main text. For example, in the first article of this series:“Adding a Semantic Index to a Blog (Part 1): Structural Design and Construction Process“"For example, the final recommendation list:"

As you can see, the recommendation list is displayed in an independent block structure, containing related reading content semantically connected to the current article, and providing direct jump links. It does not interfere with the reading process of the main text, but rather provides a natural continuation path after reading.
In terms of the final result, this recommendation system is no longer a "functional module", but a fixed component in the reading process.
6. Summary: A Recommendation System Based on Pre-computed Indexes
At this point, this lightweight recommendation system for WordPress based on semantic indexing is considered to be fully operational.
The entire system is actually not complicated: the pre-processing stage passes through semantic-index.json Organize the relationships between articles, and then further build the runtime environment. article-index.jsonFinally, WordPress reads the index and generates the recommendation list when the page loads.
This is precisely why the runtime structure of this solution is very lightweight. It doesn't rely on complex database queries, doesn't require real-time calculation of article similarity, and doesn't involve dynamic semantic analysis. For WordPress, it's more like "consuming" a pre-prepared structured relationship data, rather than participating in real-time recommendation calculations. But looking back, the truly important aspect of this system isn't just "adding a related articles module at the bottom of the page." The real change is that, for the first time, blog articles began to possess a layer of "structural relationships" that could be consumed by the program.
In traditional blogs, articles are mostly independent of each other. The connections between them are often only maintained through categories, tags, search, or manual internal links, creating a relatively loose linking structure. However, when... semantic-index.json After its emergence, the relationships between articles began to be truly preserved for the first time in the form of structured data. From this moment on, articles were no longer just individual pages, but gradually began to become a kind of "content node"; and the blog itself began to have a semantic structure that could be read, organized, and expanded by programs.
Therefore, "related article recommendations" are merely the first manifestation of this structure. Subsequent implementations, whether it's article-level local knowledge graphs, topic aggregation, site-wide semantic navigation, or even more advanced blog RAGs, are all essentially built on the same premise: the relationships between articles are no longer just the author's understanding, but have become a truly existing data structure.
So in the end, the real problem this series wants to solve is not: "How to create a recommendation plugin for WordPress", but rather: "Can the relationships between content in a blog be pre-organized, saved, and reintegrated into the blog's reading experience?"“
At least for me, the answer is yes. And recommendation systems are just the first step in this process.
It's worth noting that although the entire implementation process in this article revolves around WordPress, from an architectural perspective, this system is based on... semantic-index.json and article-index.json The "pre-computation recommendation model" is actually more naturally suited to static websites. This is because its core idea is essentially:All semantic relationships are built offline in advance, and then static data is read directly at runtime.
Static websites inherently lack the pressure of traditional database queries, runtime content calculations, and dynamic rendering. Therefore, this "pre-generated index + front-end direct consumption of JSON" structure is highly consistent with the working method of static blogs. In other words, in WordPress, this system is more like "bypassing the runtime"; while in static websites, it becomes a very natural default architecture.
For example, in static blog systems such as Hugo, Hexo, Jekyll, Astro, and Next.js SSG, it is entirely possible to generate them directly during the build phase. article-index.jsonThen, the front-end JavaScript reads the JSON from the article page and renders the recommendation list. The entire process no longer requires PHP and does not involve any server-side computation.
In a sense, the scenarios that this type of solution is really suitable for are not traditional CMS, but rather blog systems where "content is organized during the construction phase and only responsible for display during runtime".
This is why I later felt more and more that this JSON-based "light semantic structure," although it doesn't seem complicated, is actually starting to resemble a "static knowledge system."
7. Postscript: Why I didn't choose the readily available "Related Articles" plugin
When I first started planning to add a "related articles recommendation" feature to my blog, my initial thought wasn't to implement it myself, but rather to research existing WordPress plugins. After all, "related articles recommendation" is a very mature feature, and plugins like YARPP and Contextual Related Posts have been around for many years. However, after actually researching it, I discovered that while most plugins are called "related articles recommendation," they essentially still perform similarity matching based on tags, categories, keywords, and other information at runtime.
This approach is sufficient for ordinary blogs, but for my increasingly "knowledge-structure-based" content structure, a problem is gradually emerging: many truly important article connections cannot necessarily be accurately expressed through keywords. For example, extensions of a topic, different stages of the same problem, or the recurrence of a certain underlying idea in different fields... these relationships are essentially closer to "content structure" than simple keyword overlap.
This is why I ultimately chose to base my decision directly on... semantic-index.json This is used to build a recommendation system. Because in this model, "related" is no longer a result dynamically guessed at runtime, but rather a content relationship that has been determined in advance during the semantic processing stage.
In other words: traditional plugins infer "what might be relevant" at runtime; semantic-index determines "what is truly relevant" in advance during the build phase. These two approaches differ significantly in terms of result stability and controllability.
At the same time, I'm becoming increasingly cautious about adding heavy plugins to WordPress. Many plugins often come with additional database queries, runtime dynamic calculations, and unpredictable performance overhead. In contrast, my current approach involves almost no complex calculations at runtime; it's essentially just a JSON read and array mapping. For personal blogs, this "pre-calculation + static indexing" approach is lighter and more stable.
Of course, it doesn't come without a cost: when an article is added, the URL is modified, or the semantic relationships are adjusted, the index building process needs to be re-executed. However, for personal blogs that are updated infrequently, this maintenance cost is actually quite acceptable.
In a sense, what truly attracted me to this system was not just that it "implemented the recommendation function," but that the entire recommendation logic finally began to become explainable, controllable, and predictable.
Behind this lies something I've come to value more and more lately—the relationships between content in a blog shouldn't just exist in human understanding, but should also be preserved in a structured way.