सिफ़ारिश इंजन के रूप में Knowledge Graph: मैंने क्रमानुक्रमिक « सम्बन्धित लेख » को कैसे समाप्त कर दिया
प्रकाशित: 12 May 2026
- दृश्य: मंगलवार 12 मई, 17:30
- निदान : तीन प्रकार के संबंध जो कोई टेम्पलेट नहीं देखता
- समाधान: एक Gradle पाइपलाइन, एक गर्म LLM कॉल नहीं
- उभरती ऑन्टोलॉजी : जब लेख एक-दूसरे को जाने बिना इकट्ठा हो जाते हैं
- DAG अनुबंध: कौन किसे महत्व देता है
- जो हम जीतते हैं (और जो हम नहीं जीतते हैं)
- परिप्रेक्ष्य: ब्लॉग को सार्वजनिक ज्ञान ग्राफ़ के रूप में
- निष्कर्ष: फ़ाइल सिस्टम जानता है कि टेम्प्लेट नहीं जानता
- संदर्भ
आप एक लेख पढ़ रहे हैं pgvector के LangChain4j के साथ एकीकरण पर। पृष्ठ के नीचे, JBake आपको 'संबंधित लेख' सुझाया जाता है। आप क्लिक करते हैं। यह एक लेख है… कॉन्फ़िगरेशन के बारे में Kitty Terminal का। दोनों में केवल एक ही सामान्य बिंदु? उन्हें कम से कम प्रकाशित किया गया पंद्रह दिनों का अंतर. यह एक सिफ़ारिश नहीं है। यह एक वेश में कैलेंडर है संपादकीय.
दृश्य: मंगलवार 12 मई, 17:30
मैं अपने लेखों में से एक को फिर से पढ़ रहा हूँ — celui sur le Knowledge Graph comme outil de compréhension de codebase. सामग्री घनिष्ट है : नोड्स, किनारे, समुदाय, PlantUML, ऑनबोर्डिंग. एक लेख 15 मिनट की पढ़ने की तकनीक जो लगाती है`graphify-gradle`, plantuml-gradle, और ग्राफ़ टोपोलॉजी की अवधारणाएँ।
पृष्ठ के नीचे, सम्बंधित लेख मुझे सुझाव देते हैं :
-
फ़ायरबेस कॉन्टैक्ट फ़ॉर्म (0113) पर एक लेख
-
एक लेख ईगर/लेसी मकेनिज़्म पर (0108)
-
एक लेख Gradle स्क्रिप्ट → प्लगिन माइग्रेशन पर (0102)
-
Ollama Pro सब्सक्रिप्शनों के संचय पर एक लेख (0120)
इन चारों लेखों में से तीन का Knowledge Graph से कोई नहीं संबंध है। वे इसलिए वहां हैं क्योंकि वे सबसे हाल में प्रकाशित किए गए हैं — समयिक निकटता, नहीं सैमेंटिक पड़ोस
मैं टेम्पलेट देख रहा हूँ`post.thyme`</think>
<!-- Articles connexes (liens internes SEO) -->
<th:block th:each="post,postStat : ${published_posts}">
<th:block th:if="${!post.uri.equals(content.uri) and postStat.index lt 4}">
...
</th:block>
</th:block>
`published_posts`एक कालक्रमवार सूची है।`postStat.index lt 4`लेता/लेती है पहले चार जो वर्तमान पोस्ट नहीं हैं। यह बस है। कोई तर्क नहीं। समानता का। सामग्री की कोई अवधारणा नहीं। सिर्फ एक लूप।`for`वेश में सिफ़ारिश.
|
यह JBake का बग नहीं है। यह सबका डिफ़ॉल्ट व्यवहार है। स्थिर साइट जनरेटर: पोस्ट की साधारण सूची तारीख के अनुसार क्रमबद्ध है। टेम्प्लेट अपने पास जो भी है, उसी से जो कर सकता है, करता है। |
लेकिन यह इसे स्वीकार करने का कारण नहीं है।
निदान : तीन प्रकार के संबंध जो कोई टेम्पलेट नहीं देखता
मेरे लेखों का संग्रह — 23 पोस्ट, 4 महीनों में — की एक समृद्ध संरचना है जो समयरेखी टेम्प्लेट पूरी तरह से ओवरराइट करता है :
-
स्पष्ट संदर्भ : मैं उपयोग करता हूँ`xref:`मेरे लेखों में बड़े पैमाने पर.
अनुच्छेद 0122 संदर्भ 0106 (knowledge graph) और 0116 (विभाजन ज्ञान संबंधी). ये लिंक संपादकीय hard linking के हैं — मैं इन अवधारणाओं को जोड़ने का जानबूझकर निर्णय लिया गया।
-
Shared Tags : हर लेख के पास कुछ है`:jbake-tags:`. लेख पर
Graphify + PlantUML (0105) a`gradle, graphify, plantuml, knowledge-graph`. नॉलेज ग्राफ़ (0106) पर लेख में है knowledge-graph, graphify, plantuml सात में से तीन साझा टैग — 43% ओवरलैप
-
सह-उभय नामित एंटिटीज़ : « pgvector », « RAG », « embedding
वे चार अलग-अलग लेखों में साथ में दिखाई देते हैं। « LangChain4j », Ollama », « plugin Gradle » छह अन्य में। ये टैग नहीं हैं। घोषित — ये patterns émergents कॉर्पस के हैं जो केवल एक NLP पता लगा सकता है.
@startuml
skinparam backgroundColor #FEFEFE
skinparam defaultTextAlignment center
title तीन परतें अनुच्छेदों के बीच संबंध
package "परत 1 — स्पष्ट संदर्भ" #CCFFCC {
[Article A] as A1
[Article B] as B1
A1 -down-> B1 : xref:
note right of A1
अटल लिंक
निर्धारित
graph.json में पहले से ही
end note
}
package "परत 2 — साझा टैग" #FFFFCC {
[Article A] as A2
[Article C] as C2
A2 ..> C2 : tag_cooccurrence
note right of A2
भार = सामान्य टैग / कुल टैग
नियतिवादी
ग्राफ़ाइ के माध्यम से निकालें
end note
}
package "परत 3 — उभरती ओन्टोलॉजी" #FFCCCC {
[Corpus A] as A3
[Corpus B] as B3
A3 ..> B3 : entity_overlap
note right of A3
समूह NLP
नामित इकाइयों के सह-उपस्थिति
TF-IDF / कोसाइन समानता
end note
}
@enduml
आज, मेरी साइट इन तीनों लेयर्स में से कोई भी नहीं उपयोग करती है. वह उपयोग करता है शून्य परत : जावा सूची में सम्मिलन का क्रम
समाधान: एक Gradle पाइपलाइन, एक गर्म LLM कॉल नहीं
पहली प्रलोभन यह होगा कि bake के समय LLM को बुलाया जाए: « के लिए इस लेख में, corpus में सबसे समान तीन लेख खोजें। इसे मत करो।
|
हर LLM के लिए एक कॉल`./gradlew bake`यह समय और पैसा खर्च करता है, और अपनी बिल्ड में non-determinism लाता है। LLM का उत्तर हो सकता है दो बिल्ड्स के बीच बदलें बिना सामग्री में परिवर्तन किए। आपका CI बन जाता है अपुनरावृत्त, आपके टेस्ट अस्थिर हो जाते हैं। |
समाधान निर्धारित है: एक Gradle पाइपलाइन जो ग्राफ़ का पूर्व-गणना करता है समानता के और इसे में संग्रहीत करता है`graph.json`. टेम्प्लेट परिणाम पढ़ता है — वह एक गणना को कभी नहीं ट्रिगर करता है।
आर्किटेक्चर: बेकरी ग्राफ़ी आयात करती है, इंजन नहीं
यह वास्तुकला का सबसे महत्वपूर्ण बिंदु है। लुभावनी होगी। सहयोग में तार करना`engine/build.gradle.kts`: engine लागू graphify के लिए scan, bakery के लिए bake, और engine के बीच पुल बनाता है दोनों
यह एक एंटी‑पैटर्न है। Engine एक उपभोग करने वाला टर्मिनल — यह लागू करता है प्लगइन, यह व्यावसायिक तर्क लागू नहीं करता है। नियम यह है: स्वामित्व वाले प्लगइन पर प्रमाण का भार है।
@startuml
skinparam backgroundColor #FEFEFE
skinparam packageBackgroundColor #FFF3CD
title Bakery Graphify को आयात करती है — इंजन कुछ नहीं जानता
package "N3 — ENGINE" #FFCCCC {
[engine/build.gradle.kts] as ENG
note right of ENG
plugins { bakery }
graphify के बारे में कोई ज्ञान नहीं
4 कार्य, ~150 पंक्तियाँ
end note
}
package "N2 — BAKERY" #CCFFCC {
[bakery-plugin] as BAK
note right of BAK
plugins { graphify } ← import आंतरिक
पढ़ता है graph.json
सम्बन्धित लेखों को हल करता है
मॉडल को JBake पर दिखाता है
end note
}
package "N0 — GRAPHIFY" #CCE5FF {
[graphify-plugin] as GRF
note right of GRF
स्कैन करें कार्यस्थल → graph.json
इसके साथ संवर्धित :
- xref: किनारों
- टैग्स की सह‑घटना
- नामित इकाइयाँ (NLP)
end note
}
ENG -down-> BAK : "plugins { bakery }"
BAK -down-> GRF : "plugins { graphify } ← N2→N0 OK"
@enduml
DAG अनुबंध का पालन किया गया: bakery (N2) graphify (N0) को आयात करती है, N2 > N0, कोई उल्लंघन नहीं। इंजन (N3) बेकरी (N2) आयात करता है, N3 > N2, OK।
इंजन में कोई कोड लाइन नहीं है जो संदर्भित करता है`graph.json`, relatedPosts, या किसी भी अन्य समानता की अवधारणा। वह bakery लागू करता है, बिंदु। वह सहयोग बेकरी के भीतर है।
पाइपलाइन: स्कैन → ग्राफ़ → टेम्प्लेट
यहाँ पूरा पाइपलाइन तीन चरणों में है:
-
स्कैन (ग्राफ़ाइ) :`graphify-plugin` स्कैन करता है वर्कस्पेस. इसके
वर्तमान रूप में, यह उन्हें पहले से ही पहचानता है`xref:`बीच`.adoc`और में प्रदर्शित`graph.json`जैसे quelques edges de type`reference`.
हम इसे संपादकीय सामग्री के लिए समृद्ध करते हैं: * JBake मेटाडेटा को पार्स (:jbake-tags:, :jbake-description:) प्रत्येक का`.adoc`ब्लॉग का * गणना करता है टैग्स के सह-उपस्थितियों → किनारे`tag_cooccurrence`वजन के साथ * नामित इकाइयों को विवरणों से TF-IDF के माध्यम से निकालें → edges`entity_overlap` * सेक्शन इंजेक्ट करता है`blog_articles`में`graph.json`अस्तित्व में
// Extrait de l'enrichissement graphify pour le blog
fun enrichBlogSection(graphJson: File, blogDir: File): GraphJson {
val articles = blogDir.listFiles { f -> f.extension == "adoc" }
.map { parseJbakeMetadata(it) }
val nodes = articles.map { ArticleNode(it.slug, it.title, it.tags) }
val edges = mutableListOf<GraphEdge>()
// Couche 1 : xref (déjà fait par scanWorkspace)
// Couche 2 : co-occurrences de tags
for (a in articles) {
for (b in articles) {
if (a.slug == b.slug) continue
val common = a.tags.intersect(b.tags)
if (common.isNotEmpty()) {
edges.add(GraphEdge(
source = a.slug,
target = b.slug,
type = "tag_cooccurrence",
weight = common.size.toDouble() / (a.tags.size + b.tags.size)
))
}
}
}
return graphJson.copy(
blogArticles = BlogSection(nodes, edges)
)
}
-
Bake (बेकरी) : के क्षण में`./gradlew bake`, `BakeryPlugin`बिस्तर
`graph.json`और प्रत्येक पोस्ट के लिए संबंधित लेखों को हल करता है। </think> (There is no translatable content; output remains empty.)
// BakeryPlugin — résolution des articles connexes
fun resolveRelatedPosts(
currentSlug: String,
graph: GraphJson,
maxResults: Int = 4
): List<RelatedPost> {
val edges = graph.blogArticles.edges
.filter { it.source == currentSlug || it.target == currentSlug }
return edges
.sortedByDescending { it.weight }
.take(maxResults)
.map { edge ->
val relatedSlug = if (edge.source == currentSlug) edge.target else edge.source
graph.blogArticles.nodes.first { it.slug == relatedSlug }
}
}
-
Template (
post.thyme) : JBake मॉडल अब एक मैप प्राप्त करता है
संरचित, एक सपाट कालानुक्रमिक सूची के बजाय
<!-- Articles connexes basés sur le Knowledge Graph -->
<th:block th:if="${relatedPosts != null and !relatedPosts.empty}">
<section class="mt-5 pt-4 border-top">
<h2 class="h4 mb-3">Articles connexes</h2>
<th:block th:each="related : ${relatedPosts}">
<div class="mb-2">
<a th:href="${content.rootpath} + ${related.uri}"
th:text="${related.title}" class="fw-semibold"></a>
<br/>
<small class="text-muted">
<th:block th:each="reason,iterStat : ${related.reasons}">
<span class="badge bg-light text-dark"
th:text="${reason}"></span>
</th:block>
</small>
</div>
</th:block>
</section>
</th:block>
टेम्पलेट एक ही है — वह नहीं जानता d’où से डेटा आते हैं। केवल bakery और JBake के बीच का अनुबंध बदल गया है :`published_posts`(सूची क्रमानुक्रमिक) बन जाता है`relatedPosts`(ग्राफ द्वारा भारित मानचित्र).
|
बैज « कारण » (उदाहरण:`xref 0122`, |
फ़ॉलबैक कालानुक्रमिक
Si `graph.json`अनुपस्थित है (स्थानीय बिल्ड बिना प्रारंभिक स्कैन, पहला डिप्लॉयमेंट, CI जो अभी तक ग्राफ़ी स्कैन को एकीकृत नहीं किया है), टेम्प्लेट सभ्य ढंग से अपमूल्यन करना चाहिए :
fun resolveRelatedPosts(currentSlug: String, graph: GraphJson?): List<RelatedPost> {
if (graph != null && graph.blogArticles != null) {
return resolveFromGraph(currentSlug, graph)
}
// Fallback chronologique — même comportement qu'aujourd'hui
logger.warn("[bakery] graph.json absent — fallback chronologique")
return resolveFromChronology(currentSlug)
}
डिफ़ॉल्ट व्यवहार वर्तमान व्यवहार के समान है। वह सिफारिश इंजन एक प्रगतिशील सुधार — नहीं एक तोड़ने वाला बदलाव।
उभरती ऑन्टोलॉजी : जब लेख एक-दूसरे को जाने बिना इकट्ठा हो जाते हैं
सबसे दिलचस्प परत तीसरी है: उभरती हुई ऑन्टोलॉजी। जैसे लेख जो उद्धृत नहीं किए जाते, जिनमें समान टैग नहीं होते, लेकिन जो एक ही बात कर रहे हैं बिना जाने.
एक ठोस उदाहरण लेते हैं। मेरे संग्रह के तीन लेख:
-
0119 — बेंचमार्क DGX Spark vs Cloud सदस्यता LLM
-
0121 — Gradle प्लगिन दो Ollama Pro इंस्टेंसेस संचालित करें
-
0122 — प्रभावशीलता अनुपात 27x 45x कृत्रिम बुद्धिमत्ता विशेषंजन दल
ये तीन लेखों के बीच कोई नहीं xref है। उनके टैग नहीं हैं। वे केवल 20% तक ओवरलैप होते हैं (« ollama », « llm » समान)। लेकिन एक हल्का NLP विवरणों पर एक स्पष्ट क्लस्टर खुलासा होता है: « लागत », « सदस्यता», बादल », « GPU », « API कुंजी », « Ollama Pro », « दक्षता », « अनुपात
वे एक ओन्टोलॉजिकल क्लस्टर बनाते हैं: सेल्फ-होस्टेड LLM की अर्थशास्त्र vs cloud. यह क्लस्टर कहीं भी घोषित नहीं है। यह कॉर्पस से उभरता है।
@startuml
skinparam backgroundColor #FEFEFE
skinparam packageBackgroundColor #E8F8F5
title Cluster Ontologique Émergent — "अर्थव्यवस्था LLM Self-Hosted"
package "समूह पाया गया : अर्थव्यवस्था-llm" #D5F5E3 {
[0119 — Benchmark DGX Spark\nvs Cloud Abonnement] as A
[0121 — Plugin Gradle Piloter\nDeux Instances Ollama Pro] as B
[0122 — Ratio Efficacité\n27x 45x Flotte Experts] as C
}
A .. B : entity_overlap (0.73)
B .. C : entity_overlap (0.81)
A .. C : entity_overlap (0.68)
note bottom of A
Aucun xref entre ces articles.
Tags communs : "ollama", "llm".
Cluster découvert par NLP :
co-occurrences "लागत", "GPU",
"सदस्यता", "Ollama Pro".
end note
@enduml
यह अधिभौतिक समूह एक संयुक्त किनारा में`graph.json`:
{
"source": "0119-benchmark-dgx-spark",
"target": "cluster:economie-llm",
"type": "entity_cluster",
"weight": 0.73,
"metadata": {
"clusterLabel": "Économie LLM Self-Hosted vs Cloud",
"commonEntities": ["coût", "GPU", "abonnement", "Ollama Pro", "ratio"],
"articlesInCluster": [
"0119-benchmark-dgx-spark",
"0121-ollama-pro-deux-instances",
"0122-ratio-efficacite-flotte-experts"
]
}
}
NLP इरादतन हल्का है — TF-IDF + cosine similarity पर विवरण। BERT की आवश्यकता नहीं है, भाषा मॉडल की आवश्यकता नहीं है। कोर्पस में 23 लेख हैं, समानता मैट्रिक्स एक में फिट होती है JSON फ़ाइल 50 केबी की।
|
NLP की शक्ति एल्गोरिदम की परिष्कार से नहीं आती, लेकिन corpus का आकार और विवरणों की गुणवत्ता। एक `:jbake-description:`अच्छी तरह लिखी गई 150 अक्षर की सामग्री में अधिक TF-IDF के लिए संकेत कि एक पूरा लेख 3000 शब्दों का। |
DAG अनुबंध: कौन किसे महत्व देता है
यह वह बिंदु है जहाँ आर्किटेक्चरल अनुशासन भुगतान करता है। DAG N0→N3 में परिभाषित`engine/build.gradle.kts`एक सरल नियम दो : कोई परियोजना नहीं किसी भी उच्च स्तर की परियोजना
| प्लगइन उपभोक्ता | आयातित प्लगिन | उपभोक्ता स्तर | आयातित स्तर | वैध? |
|---|---|---|---|---|
bakery-gradle |
graphify-gradle |
N2 |
N0 |
✅ N2 > N0 — OK |
इंजन |
bakery-gradle |
N3 |
N2 |
✅ N3 > N2 — OK |
इंजन |
graphify-gradle |
N3 |
N0 |
✅ Technique OK, लेकिन अवधारणात्मक रूप से गलत — सहयोग बेकरी के भीतर है |
इंजन बेकरी लागू करता है। बेकरी ग्राफ़ाई लागू करती है। यह सब है। यदि एक दिन मैं vector store codebase (N1) को अनुसंधान के लिए जोड़ना चाहता हूँ क्रॉस-कॉर्पस अर्थविज्ञान, बेकरी इसे भी आयात करती है। इंजन में कोई बदलाव नहीं होता।
|
पैटर्न यह है: N2 प्लगइन अपने स्वयं के निर्भरताओं का हब है इंजन N3 एक हब नहीं है — यह एक टर्मिनल है जो हब्स लागू करता है। |
जो हम जीतते हैं (और जो हम नहीं जीतते हैं)
मुख्य लाभ संपादकीय है, तकनीकी नहीं :
| पहले | के बाद |
|---|---|
संबंधित लेख = 4 अंतिम पोस्ट |
संबंधित लेख = top 4 वजन के अनुसार Knowledge Graph में |
पाठक RAG पढ़ता है → किट्टी टर्मिनल की सिफ़ारिश |
पाठक RAG पढ़ता है → pgvector, चंकिंग, वेक्टर स्टोर की सिफ़ारिश |
क्यों पर पारदर्शिता शून्य |
बैज`xref 0122`, |
JBake मानक, शून्य परिश्रम, शून्य मूल्य |
निर्धारित Gradle पाइपलाइन, पुनरावृत्ति योग्य, परीक्षण योग्य |
पाठक के लिए कोई सीखने का वक्र नहीं |
पाठक मेरी सामग्री की टोपोलॉजी की खोज करता है |
जो हम नहीं जीतते :
-
यह एक « सच » सिफ़ारिश इंजन नहीं है (कोलैबोरेटिव नहीं
filtering, A/B testing नहीं, feedback loop नहीं
-
गुणवत्ता JBake मेटाडेटा की समृद्धि पर निर्भर करती है — यदि आपके
विवरण खाली हैं, TF-IDF कुछ भी नहीं देखता
-
NLP ऑफ़लाइन है — नए लेख केवल तक क्लस्टर किए जाते हैं
अगला स्कैन (यह अच्छा है: बिल्ड निर्धारित रहता है)
परिप्रेक्ष्य: ब्लॉग को सार्वजनिक ज्ञान ग्राफ़ के रूप में
इस सुविधा एक अधिक व्यापक परिप्रेक्ष्य: और यदि`cheroliv.com` खुद बन रहा था एक knowledge graph navigable?
लेख नोड्स हैं। टैग समुदाय हैं। xref हैं किनारों। पाठक अब एक अलग लेख नहीं पढ़ता — वह एक में नेविगेट करता है graphe de connaissance जिसका वर्तमान लेख प्रवेश बिंदु है।
होमपेज की कल्पना करें जो क्रोनोलॉजिकल सूची नहीं दिखाता अंतिम पोस्टों के, लेकिन एक कार्पस मानचित्र : ऑन्टोलॉजिकल क्लस्टर पिवोट लेख (जिनके पास सबसे अधिक किनारे हैं), पढ़ने के रास्ते सुझाए गए (« यदि आपको Graphify पर लेख पसंद आया, तो आगे पढ़ें वह Knowledge Graph को समझने के उपकरण के रूप में )
यह अब एक ब्लॉग नहीं है। यह एक अर्थसंबंधी एटलस है।
और यह विज्ञान कथा नहीं है। वह`graph.json`पहले से मौजूद है। उसके पास केवल नेविगेशन इंटरफ़ेस नहीं है।
निष्कर्ष: फ़ाइल सिस्टम जानता है कि टेम्प्लेट नहीं जानता
मंगलवार की शाम मैंने जो डिज़ाइन किया वह एक हीुरिस्टिक का प्रतिस्थापन है। आलसी (क्रमानुक्रमिक क्रम) एक वफादार प्रतिनिधित्व द्वारा मेरे कॉर्पस की संरचना (Knowledge Graph).
टेम्पलेट`post.thyme`बदलता नहीं है। जो बदलता है, वह है जो हम पहले : एक जावा सूची, जो … से क्रमबद्ध`date`. बाद : तीन विश्लेषण परतों से प्राप्त भारित ग्राफ़ — xrefs, co-occurrences tags के, और NLP द्वारा उभरती हुई आन्तोलजी।
लूप बंद हो गया। graphify कार्यक्षेत्र को स्कैन करता है। Bakery पढ़ता है ग्राफ़ टेम्पलेट को पोषण देता है। पाठक को सुझाव दिखाई देते हैं। प्रासंगिक। और engine — संगीत निर्देशक — वह यह भी नहीं जानता कि सभी चीज़ें मौजूद हैं।
यह वही सही वास्तुकला है। हर प्लगइन एक काम करता है। और उपभोक्ता टर्मिनल को जानने की ज़रूरत नहीं है कि कैसे।