گراف دانش به عنوان موتور توصیه: چگونه من «مقالات مرتبط» زمانی را حذف کردم
منتشر شده در 12 May 2026
- صحنه : سهشنبه ۱۲ می، ۱۷:۳۰
- تشخیص: سه نوع روابط که هیچ الگویی نمیبیند
- راهحل: خط لوله Gradle، نه یک تماس LLM داغ
- آنتولوژی نوپدیدار: وقتی مقالات بدون آگاهی از یکدیگر گروه میشوند
- قرارداد DAG: چه کسی برای چه کسی مهم است؟
- چه چیزی بهدست میآوریم (و چه چیزی بهدست نمیآوریم)
- دیدگاهها: وبلاگ بهعنوان گراف دانش عمومی
- نتیجه : سیستم فایلها میداند چه چیزی که قالب نادیده میگیرد
- منابع
شما در حال خواندن یک مقاله در مورد یکپارچهسازی pgvector با LangChain4j هستید. در پایین صفحه، JBake آن «مقالات مرتبط» را پیشنهاد میدهد. شما کلیک میکنید. این یک مقاله در مورد … پیکربندی است از Kitty Terminal. تنها نقطه مشترک بین آنها چیست؟ آنها به کمتر از فاصلهٔ پانزده روز. این یک پیشنهاد نیست. این یک تقویم guise شده به مقالهی تحریری.
صحنه : سهشنبه ۱۲ می، ۱۷:۳۰
من یکی از مقالات خود را دوباره میخوانم — این مقاله درباره Knowledge Graph بهعنوان ابزاری برای درک پایهٔ کد محتوا تراکم دارد : گرهها، لبهها،جامعهها، PlantUML، آموزش اولیه. یک مقاله تکنیک خواندن 15 دقیقهای که فعال میسازد`graphify-gradle`, plantuml-gradle, و مفهومهای تاپولوژی گراف.
در پایین صفحه، «مقالات مرتبط» به من پیشنهاد میدهند :
-
یک مقاله در مورد فرم تماس Firebase (0113)
-
مقالهای در مورد مکانیزم Eager/Lazy (0108)
-
یک مقاله دربارهٔ انتقال اسکریپت Gradle → پلاگین (0102)
-
یک مقاله دربارهٔ تجمیع اشتراکهای Ollama Pro (0120)
سه از این چهار مقاله aucun ارتباط با Knowledge Graph ندارند. آنها اینجا هستند زیرا آخرین منتشرشدهاند — نزدیکی زمانی، نه حوزه معنایی.
من قالب را نگاه میکنم`post.thyme`[No text to translate; output is empty.]
<!-- 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 (compartimentage معرفتشناسانه). این لینکها hard linking ویرایشی — من دارم بهصورت عمدی تصمیم گرفتهاند تا این مفاهیم را متصل کنند.
-
برچسبهای مشترک : هر مقاله دارای است`:jbake-tags:`. مقاله درباره
Graphify + PlantUML (0105) دارد`gradle, graphify, plantuml, knowledge-graph`. مقاله درباره Knowledge Graph (0106) دارد knowledge-graph, graphify, plantuml. سه تگ مشترک از هفت — ۴۳٪ پوشش.
-
existencias nomeadas ham zamān : « 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
Liens durs
Déterministes
Déjà dans graph.json
end note
}
package "طبقه 2 — برچسبهای اشتراکی" #FFFFCC {
[Article A] as A2
[Article C] as C2
A2 ..> C2 : tag_cooccurrence
note right of A2
Poids = tags communs / tags totaux
Déterministes
À extraire via graphify
end note
}
package "لایه 3 — انطولوژی پدیدار" #FFCCCC {
[Corpus A] as A3
[Corpus B] as B3
A3 ..> B3 : entity_overlap
note right of A3
Clusters NLP
Co-occurrences d'entités nommées
TF-IDF / cosine similarity
end note
}
@enduml
امروز، سایت من هیچکدام از این سه لایه را استفاده نمیکند. او استفاده میکند لایهٔ صفر: ترتیب درج در یک لیست جاوا.
راهحل: خط لوله Gradle، نه یک تماس LLM داغ
اولین ترغیب این است که یک LLM را در زمان bake صدا بزنیم: « برای این مقاله، سه مقاله مشابهترین را در بدنه یافت. این کار را نکنید.
|
یک تماس LLM در هر`./gradlew bake`هزینه دارد از زمان، از پول، و یک عدم تعیینیتی را در ساخت شما معرفی میکند. پاسخ LLM میتواند تغییر دادن بین دو ساخت بدون اینکه محتوا تغییر کرده باشد. CI شما میشود غیرقابل بازآفرینی، آزمونهای شما ناپایدار میشوند. |
حل determinationsitic است: یک لایپلاین Gradle که گراف را پیش محاسبه میکند از شباهت و آن را در ذخیره میکند`graph.json`. قالب نتیجه را میخواند — هرگز یک محاسبه را آغاز نمیکند.
معماری: Bakery Graphify را وارد میکند، نه Engine
این مهمترین نکته معماری است. گیج میشود که سیم بندی کردن همکاری در`engine/build.gradle.kts`: موتور اعمالشده graphify برای اسکن، bakery برای bake، و engine بین آنها پل میسازد هر دو.
این یک anti-pattern است. Engine یک ترمینال مصرفکننده است — او اعمال میکند افزونهها، منطق تجاری را پیادهسازی نمیکند. قاعده این است: بار اثبات بر عهدهی پلاگین مالکانه است.
@startuml
skinparam backgroundColor #FEFEFE
skinparam packageBackgroundColor #FFF3CD
title Bakery میآورد Graphify — Engine نمیداند چیزی
package "N3 — موتور" #FFCCCC {
[engine/build.gradle.kts] as ENG
note right of ENG
plugins { bakery }
Zéro connaissance de graphify
4 tâches, ~150 lignes
end note
}
package "N2 — نانوای" #CCFFCC {
[bakery-plugin] as BAK
note right of BAK
plugins { graphify } ← import interne
Lit graph.json
Résout articles connexes
Expose le modèle à JBake
end note
}
package "N0 — گرافیکسازی" #CCE5FF {
[graphify-plugin] as GRF
note right of GRF
Scan workspace → graph.json
Enrichi avec :
- xref: edges
- co-occurrences de tags
- entités nommées (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.
موتور aucune خط کدی که ارجاع میدهد ندارد`graph.json`, relatedPosts, یا هر مفهوم دیگری از تشابه. او bakery را اعمال میکند، نقطه. ال همکاری داخلی به مخبز است.
پایپلاین : اسکن → گراف → الگو
این خط لوله کامل در سه مرحله :
-
Scan (graphify):`graphify-plugin` scanne le workspace. Dans sa
در حالت فعلی، آن را پیش از پیش تشخیص میدهد`xref:`بین`.adoc`و نمایش در`graph.json`مثل لبههای نوع`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`و مقالات مرتبط را برای هر پست حل میکند.
// 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 حالا یک map دریافت میکند
ساختاریده به جای لیست زمانی صاف.
<!-- 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 که هنوز اسکن graphify را ادغام نکرده است), الگو باید بهصورت مهذب تخریب شود:
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)
}
رفتار پیشفرض برابر با رفتار فعلی است. این موتور پیشنهاد یک بهبود تدریجی — نه یک تغییر disrupt
آنتولوژی نوپدیدار: وقتی مقالات بدون آگاهی از یکدیگر گروه میشوند
لایه جذابترین، لایه سوم است: آنتولوژی emergence مقالات که نقل نمیشوند، که تگهای یکسانی ندارند، اما که میگویند دربارهٔ همان چیز بدون بدانستن
یک مثال واقعی بگیریم. سه مقاله از корпуса من:
-
0119 — بنچمارک DGX Spark vs ابر اشتراک LLM
-
0121 — افزونه Gradle برای اجرای دو نمونه Ollama Pro
-
0122 — نسبت کارایی 27x 45x اسطول متخصصان هوش مصنوعی
هیچ xref بین آنها. برچسبهایشان نمیشوند فقط تا 20% همپوشانی دارند (« ollama », « llm » مشترک). اما یک NLP سبک در توصیفها یک خوشه واضح نشان میدهد: « هزینه », « اشتراک»، ابر », « GPU », « کلید API », « Ollama Pro », « کارایی », « نسبت
آنها یک خوشه اونتولوژیک تشکیل میدهند : اقتصاد LLM self-hosted vs cloud . این خوشه در هیچ جای دیگری اعلام نشده است. این خوشه از corpus بیدار میشود.
@startuml
skinparam backgroundColor #FEFEFE
skinparam packageBackgroundColor #E8F8F5
title Cluster Ontologique Émergent — "اقتصاد LLM Self-Hosted"
package "Cluster شناساییشده : اقتصاد‑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
این خوشه ontولوژیک یک لبه ترکیبی در`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 نیست، نیازی به مدل زبانی نیست. Corpus شامل 23 مقاله است، ماتریس مشابهت در یک فایل JSON ۵۰ کیلوبایت.
|
قدرت NLP از پیچیدگی الگوریتم نیست, اما از اندازه corpus و کیفیت توضیحات. یک `:jbake-description:`خوشنوشته 150 کاراکتر حاوی بیشتر از سیگنال برای TF-IDF که یک مقاله کامل ۳۰۰۰ کلمهای است. |
قرارداد DAG: چه کسی برای چه کسی مهم است؟
این همان نقطه است که دسیپلین معماری به ثمر میرسد. DAG N0→N3 تعریف شده در`engine/build.gradle.kts`یک قانون ساده میدهد: هیچ پروژهای مهم نیست یک پروژه سطح بالاتر
| Plugin مصرفکننده | پلاگین وارد شده | سطح methanol | سطح وارد شده | معتبر؟ |
|---|---|---|---|---|
نانوایی-gradle |
graphify-gradle |
N2 |
N0 |
✅ N2 > N0 — OK |
موتور |
بیکری-gradle |
N3 |
N2 |
✅ N3 > N2 — OK |
موتور |
graphify-gradle |
N3 |
N0 |
✅ تکنیک OK، اما بهصورت مفهومی نادرست — همکاری در bakery داخلی است |
موتور بیکری را اعمال میکند. بیکری گرافی میکند. این همه است. اگر یک روز من میخواهم vector store codebase (N1) را برای تحقیق اضافه کنم تقاطع معنایی، bakery نیز آن را وارد میکند. Engine تغییر نمیکند.
|
الگوی است : افزونه N2 مرکز وابستگیهای خود است Engine N3 هاب نیست — این یک ترمینال است که هابها را اعمال میکند. |
چه چیزی بهدست میآوریم (و چه چیزی بهدست نمیآوریم)
سود اصلی ویرایشی است، نه فنی:
| قبل از | پس از |
|---|---|
مقالات مرتبط = 4 آخرین پستها |
مقالات مرتبط = ۴ برتر بر اساس وزن در Knowledge Graph |
خوانندهٔ RAG را میخواند → پیشنهاد Kitty Terminal |
خوانندهٔ RAG را میخواند → توصیه pgvector, chunking, vector store |
عدم شفافیت در مورد چرا |
نشانه`xref 0122`, |
معیار JBake، تلاش صفر، ارزش صفر |
خط لوله Gradle محدودیتی، قابلبازتولید، قابلتست |
بدون منحنی یادگیری برای خواننده |
خواننده topologie محتوای من را مییابد |
چیزی که نه به دست میآوریم :
-
این یک « واقعی » موتور پیشنهاد نیست (بدون تعاملی)
فیلتر، تست A/B نیست، حلقه بازخورد نیست)
-
کیفیت به ثروت متادیتاهای JBake بستگی دارد — اگر شما
توضیحات خالی هستند، TF-IDF چیزی را نمیبیند
-
NLP آفلاین است — المقالات جدید تنها تا
اسکن بعدی (خوب است: ساخت (build) determinitstic باقی میماند)
دیدگاهها: وبلاگ بهعنوان گراف دانش عمومی
این ویژگی یک منظر گستردهتر را باز میکند : و إذا`cheroliv.com` او خود یک knowledge graph navigable میشد؟
مقالات گرهها هستند. برچسبها جامعهها هستند. xref هستند حاشیههای. خواننده دیگر یک مقاله منحصربهفرد را نمیخواند — او در یک گراف دانش که مقاله فعلی نقطه ورود است.
صفحه اصلی را تصور کنید که لیست زمانی را نشان نمیدهد آخرین پستها، اما یک نقشهٔ corpus : خوشههای’ontولوژیک، مقالات pivots (هایی که بیشترین لبه را دارند)، مسیرهای خواندن پیشنهاد شده (« اگر مقالهی دربارهی Graphify را دوست داشتید، سپس این بر پایهٔ Knowledge Graph بهعنوان ابزار درک)
این دیگر یک وبلاگ نیست. این یک اتلاس معنوی است.
و این علم تخیلی نیست`graph.json`قبلاً وجود دارد. او تنها رابط ناوبری را ندارد.
نتیجه : سیستم فایلها میداند چه چیزی که قالب نادیده میگیرد
چه چیزی که در شب سهشنبه طراحی کردم، جایگزینی یک heuristique است کسل (ترتیب زمانی) با یک بازنمایی وفادار از ساختار مجموعه دادههای من (گراف دانش).
الگو`post.thyme`تغییر نمیکند. آنچه تغییر میکند، این است که ما به او غذا میدهد. پیش از: یک لیست Java مرتب بر پایه`date`. بعد : یک گراف وزنی از سه لایهی تحلیل — ارجاعات متقابل، همرخدادی برچسبها، آنتولوژی برخاسته از پردازش زبان طبیعی.
حلقه بسته است. graphify فضای کار را اسکن میکند. Bakery آن را میخواند. گراف و قالب را تغذیه میدهد. خواننده پیشنهادات را میبیند. مربوط. و موتور — هنرمند — حتی نمیداند که همه این وجود دارد.
این همان معماری خوب است. هر پلاگین کاری انجام میدهد. و ترمینال مصرفکننده نیازی به دانستن چگونگی ندارد.