Ian Chou's Blog

CtxFST 的 entities schema,為什麼不會自己長出 similarity?

CtxFST 的 entities schema,為什麼不會自己長出 similarity?

很多人第一次看到 CtxFST 的這種結構時,腦中都會跳出一個很自然的問題:

entities:
  - id: entity:python
    name: Python
    type: skill
    aliases: [python3]

chunks:
  - id: chunk:python-api
    entities: [entity:python, entity:fastapi]

既然都已經有 entities 了,那是不是代表系統已經知道:

答案是:不是。

CtxFSTentities schema 本身不會自動產生相似度
它做的事情是先把 entity 整理成一組乾淨、穩定、可計算的節點
真正的 similarity,通常是你後面把這些節點拿去做 embeddinggraph embedding 之後,才算出來的。

如果只用一句話講:

entities 定義的是「有哪些標準節點」;
chunks[].entities 定義的是「哪些 chunk 連到哪些節點」;
embedding graph 才負責算出「哪些節點彼此相近」。

所以更準確地說:

schema 是圖的骨架,similarity 是後處理算出來的邊權重。


先把角色分清楚

要理解這件事,最重要的是不要把三層東西混在一起:

第一層:entities

這一層在做的是節點標準化

例如:

entities:
  - id: entity:kubernetes
    name: Kubernetes
    type: platform
    aliases: [K8s]

這代表的是:

這層解決的是「這個概念到底是誰」。

第二層:chunks[].entities

這一層在做的是chunk 和 entity 的連結

例如:

chunks:
  - id: chunk:k8s-deploy
    context: "Kubernetes deployment basics for backend services"
    entities: [entity:kubernetes, entity:docker]

這代表:

這層解決的是「哪段內容提到哪些節點」。

第三層:similarity graph

這一層才是在回答:

這層解決的是「哪些節點彼此相近」。

而這個答案,不是 frontmatter 自己寫死的,而是你後面算出來的。


為什麼 schema 不會自己產生 similarity?

因為 schema 只是在定義資料格式,不是在做推論。

你可以把它想成這樣:

也就是說,schema 給你的是可分析的原料,不是最後的分析結果。

如果 CtxFST 一開始就假設:

那反而會把很多不該綁在一起的東西硬綁起來。

例如:

所以比較合理的做法是:

  1. 先把節點標準化
  2. 先把 chunk 跟節點連好
  3. 再用你想要的方法去算 similarity

這樣 graph 才乾淨,也比較能解釋。


Entity similarity 最常見的 3 種來源

真正的相似度通常有三種主要來源。

1. 用 entity 自己的文字做 embedding

這是最直覺的方法。

你把每個 entity 轉成一段可以拿去 embed 的文字,再交給 embedding model。

例如 entity:fastapi 最陽春的版本可以寫成:

FastAPI
type: framework
aliases: []

但更實用的版本通常會補一些描述:

FastAPI
type: framework
used in: Python backend APIs, async services, REST API development
aliases: []

接著你把每個 entity 都變成向量,再去算 cosine similarity:

這些分數就是 entity graph 裡面「相似邊」的來源之一。

這種方法的優點

這種方法的缺點

所以這種方法能用,但通常不是最強版本。


2. 用 chunk 反推 entity 的語意

這通常比只 embed entity 名字更強。

因為在 CtxFST 裡,每個 chunk 本來就已經有:

這表示你其實可以把某個 entity 被連到的所有 chunk 收集起來,反過來整理成它的語意描述。

例如 entity:python 的 representation 可以長這樣:

Python
type: skill
appears in chunks about REST APIs, ETL pipelines, FastAPI services, data processing
related entities: FastAPI, Pandas, PostgreSQL

或者更白話一點:

Python is mentioned in chunks about backend API development, ETL pipelines,
data processing, and service implementation. Related entities include FastAPI,
Pandas, and PostgreSQL.

然後再去 embed 這一段。

這樣做的好處是:

這也是為什麼 chunks[].entities 很重要。

它不只是拿來做連結而已,還可以拿來替 entity 反推出語意輪廓。


3. 用圖結構再做 graph embedding

如果你已經有下面這些關係:

那你就可以再往前一步,直接跑 graph embedding。

例如:

這時候相似度不只是看文字,而是看圖上的鄰居關係。

換句話說:

這兩個都可以成立,而且很常混在一起用。

例如你可以:

  1. 先用文字 embedding 找語意近鄰
  2. 再用 graph embedding 修正結構近鄰
  3. 最後把兩種分數做加權融合

這樣做出來的 entity graph,通常會比只靠單一方法更有用。


這個 schema 到底幫了什麼忙?

其實幫很大。

因為它先解決了最麻煩的一件事:entity identity 不穩定

以前如果你只有 tags 或 chunk 文字,你會一直遇到這些問題:

一旦沒有 entity layer,你後面做 embedding 時,常常是在拿一堆髒字串做比對。

這樣很容易出現:

現在有了標準化的 entity 定義:

entities:
  - id: entity:kubernetes
    name: Kubernetes
    type: platform
    aliases: [K8s]

你後面算 embedding 的時候,算的是一個穩定的 canonical node,不是一堆東一塊西一塊的字串。

這就是整個 entity graph 能不能長乾淨的關鍵。


一個最實際的做法:先建 entity,再建 similarity 邊

如果你現在想真的把這件事做起來,最實際的流程通常長這樣:

  1. 先定義 entities
  2. 把每個 chunk 連到對應的 entities
  3. 為每個 entity 組一段 representation text
  4. 對 representation text 做 embedding
  5. 算 entity 和 entity 之間的 cosine similarity
  6. 超過 threshold 就建邊

例如:

你甚至可以把它想成兩段式圖:

Chunk Graph:
chunk -> entity

Similarity Graph:
entity -> entity

兩張圖可以存在同一個 graph DB,也可以拆開存。


Entity representation 怎麼組比較實用?

如果你目前的 entity 最少只有這些欄位:

那其實已經夠開始了。

但如果你想讓 similarity 更準,我會建議把 chunk 資訊補進去。

例如 entity:fastapi 的 representation 可以組成:

name: FastAPI
type: framework
aliases: []
mentioned in chunks:
- Python backend skills focused on REST APIs and service implementation
- Python skills for API development, service work, and data processing
related usage:
- async services
- REST API development
- Python backend

然後再 embed 這一段。

這樣做的結果通常會是:

這比只丟一個 FastAPI 名字進 embedding model,辨識度高很多。


Similarity 不是存在哪裡?

這點非常重要。

在目前的 CtxFST schema 裡,沒有內建一個 entity_similarity 欄位

也就是說,相似度不是直接寫死在 frontmatter 裡,而是通常由下游系統動態產生。

常見做法像是:

所以更準確的說法是:

相似度不是 schema 提供的。
schema 提供的是讓你能穩定算相似度的節點基礎。


為什麼這件事反而是好事?

因為 similarity 本來就不是一個永遠固定的真理,而是跟你的任務有關。

例如同樣是 Python

如果 schema 一開始就把 similarity 寫死,你反而會失去很多彈性。

現在的做法比較合理:

這樣同一份 CtxFST 原始資料,就可以長出不同版本的 graph。


最後一句最關鍵

以前如果你只有 chunk,你算的是:

這段和那段像不像?

現在有了 entity layer,你可以開始算的是:

這個概念和那個概念像不像?

這件事很重要,因為它把你的系統從「段落檢索」往前推了一步,變成更接近真正的 Skill GraphKnowledge Graph

所以結論可以收成一句很白話的話:

CtxFSTentities schema 不負責幫你生出 similarity,
它負責先把節點整理乾淨,讓你後面能把 similarity 算得準。


一個簡單的 pipeline 圖

如果你想把整件事記成一張流程圖,可以直接記這個版本:

CtxFST frontmatter
  ├─ entities            -> 定義標準節點
  ├─ chunks[].entities   -> 定義 chunk 連到哪些節點
  └─ chunks[].context    -> 提供語意描述素材

Entity builder
  └─ 為每個 entity 組 representation text

Embedding stage
  └─ 對 entity representation 做 embedding

Similarity stage
  └─ 計算 entity-entity cosine similarity

Graph stage
  └─ 把高分 pair 建成 related_to / similar_to 邊

這樣看就很清楚:


結語

很多人看到 entities 之後,會以為相似度已經「藏在 schema 裡」了。其實不是。

CtxFST 真正厲害的地方,不是它直接給你 similarity,而是它先把 entity identity、chunk 關聯、context 資訊這些基礎打穩。只要這一層乾淨,你後面不管是做向量相似、graph embedding、GraphRAG,還是 skill recommendation,都會容易很多。

所以別把 entities schema 當成 similarity engine。
它比較像是一個把概念節點整理乾淨的地基層

地基穩了,後面的 similarity graph 才會長得漂亮。


延伸閱讀