زمان خواندن : 14 minutes

شما در حال خواندن یک مقاله در مورد یکپارچه‌سازی pgvector با LangChain4j هستید. در پایین صفحه، JBake آن «مقالات مرتبط» را پیشنهاد می‌دهد. شما کلیک می‌کنید. این یک مقاله در مورد …​ پیکربندی است از Kitty Terminal. تنها نقطه مشترک بین آن‌ها چیست؟ آن‌ها به کمتر از فاصلهٔ پانزده روز. این یک پیشنهاد نیست. این یک تقویم guise شده به مقاله‌ی تحریری.

صحنه : سه‌شنبه ۱۲ می، ۱۷:۳۰

من یکی از مقالات خود را دوباره می‌خوانم — این مقاله درباره Knowledge Graph به‌عنوان ابزاری برای درک پایهٔ کد محتوا تراکم دارد : گره‌ها، لبه‌ها،جامعه‌ها، PlantUML، آموزش اولیه. یک مقاله تکنیک خواندن 15 دقیقه‌ای که فعال می‌سازد`graphify-gradle`, plantuml-gradle, و مفهوم‌های تاپولوژی گراف.

در پایین صفحه، «مقالات مرتبط» به من پیشنهاد می‌دهند :

  1. یک مقاله در مورد فرم تماس Firebase (0113)

  2. مقاله‌ای در مورد مکانیزم Eager/Lazy (0108)

  3. یک مقاله دربارهٔ انتقال اسکریپت Gradle → پلاگین (0102)

  4. یک مقاله دربارهٔ تجمیع اشتراک‌های 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 ماه — یک ساختار غنی دارد که قالب زمانی به‌طور کامل برتری می‌گیرد :

  1. منابع صریح : من استفاده می‌کنم`xref:`به‌طور گسترده در مقالاتم.

ماده 0122 به 0106 (knowledge graph) و 0116 (compartimentage معرفت‌شناسانه). این لینک‌ها hard linking ویرایشی — من دارم به‌صورت عمدی تصمیم گرفته‌اند تا این مفاهیم را متصل کنند.

  1. برچسب‌های مشترک : هر مقاله دارای است`:jbake-tags:`. مقاله درباره

Graphify + PlantUML (0105) دارد`gradle, graphify, plantuml, knowledge-graph`. مقاله درباره Knowledge Graph (0106) دارد knowledge-graph, graphify, plantuml. سه تگ مشترک از هفت — ۴۳٪ پوشش.

  1. 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 یک ترمینال مصرف‌کننده است — او اعمال می‌کند افزونه‌ها، منطق تجاری را پیاده‌سازی نمی‌کند. قاعده این است: بار اثبات بر عهده‌ی پلاگین مالکانه است.

معماری صحیح — Bakery Graphify را وارد می‌کند
@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 را اعمال می‌کند، نقطه. ال همکاری داخلی به مخبز است.

پایپلاین : اسکن → گراف → الگو

این خط لوله کامل در سه مرحله :

  1. 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)
    )
}
  1. 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 }
        }
}
  1. 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`, tag:gradle, cluster:pgvector-rag) به خواننده چرا این مقاله مرتبط است. این نیست فقط شفافیت — این آموزش دربارهٔ توپولوژی است محتواهای شخصی شما ====

گزینهٔ یدکی کرانولوژیکی

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 مقالات که نقل نمی‌شوند، که تگ‌های یکسانی ندارند، اما که می‌گویند دربارهٔ همان چیز بدون بدانستن

یک مثال واقعی بگیریم. سه مقاله از корпуса من:

  1. 0119 — بنچمارک DGX Spark vs ابر اشتراک LLM

  2. 0121 — افزونه Gradle برای اجرای دو نمونه Ollama Pro

  3. 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`, tag:gradle, `cluster:rag`قابل مشاهده

معیار 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 آن را می‌خواند. گراف و قالب را تغذیه می‌دهد. خواننده پیشنهادات را می‌بیند. مربوط. و موتور — هنرمند — حتی نمی‌داند که همه این وجود دارد.

این همان معماری خوب است. هر پلاگین کاری انجام می‌دهد. و ترمینال مصرف‌کننده نیازی به دانستن چگونگی ندارد.

مقالات مرتبط