圖譜一定要用圖數(shù)據(jù)庫(kù)嗎:一個(gè)被混淆了十年的問(wèn)題)
開(kāi)篇一個(gè)根深蒂固的聯(lián)想每次有人提知識(shí)圖譜緊接著的一句話幾乎一定是“那我們用 Neo4j 吧?!焙孟裰R(shí)圖譜和圖數(shù)據(jù)庫(kù)是綁定的。上了知識(shí)圖譜就必須上圖數(shù)據(jù)庫(kù)不上圖數(shù)據(jù)庫(kù)就不算知識(shí)圖譜。這是一個(gè)被混淆了十年的概念。知識(shí)圖譜是怎么組織數(shù)據(jù)的決策圖數(shù)據(jù)庫(kù)是存在哪里的選型。它們是兩個(gè)正交的維度可以自由組合。本文做兩件事用一個(gè) SQL 例子證明知識(shí)圖譜可以存在 MySQL 里跑得好好的給出什么時(shí)候才真正需要圖數(shù)據(jù)庫(kù)的判斷標(biāo)準(zhǔn)讀完你能分清數(shù)據(jù)組織方式和存儲(chǔ)介質(zhì)不再被上了知識(shí)圖譜就得用圖數(shù)據(jù)庫(kù)帶偏。一、知識(shí)圖譜 vs 圖數(shù)據(jù)庫(kù)兩個(gè)正交維度所有存記憶/存知識(shí)的方案都可以從兩個(gè)正交維度去看維度 1存什么形態(tài)數(shù)據(jù)組織方式 - 原始消息ChatMessage日志形態(tài) - 結(jié)構(gòu)化條目有 type / subject / status 等元數(shù)據(jù) - 向量嵌入embedding一串?dāng)?shù)字 - 知識(shí)圖譜節(jié)點(diǎn) 邊有向圖 維度 2放哪里存儲(chǔ)介質(zhì) - 內(nèi)存Map進(jìn)程內(nèi) - 文件JSONL / SQLite - 關(guān)系型數(shù)據(jù)庫(kù)MySQL / PostgreSQL - 內(nèi)存型數(shù)據(jù)庫(kù)Redis - 向量數(shù)據(jù)庫(kù)Milvus / pgvector / Pinecone - 圖數(shù)據(jù)庫(kù)Neo4j / Graphiti / Amazon Neptune知識(shí)圖譜落在維度 1圖數(shù)據(jù)庫(kù)落在維度 2。知識(shí)圖譜是一種用圖的方式組織數(shù)據(jù)的形態(tài)選擇–實(shí)體是節(jié)點(diǎn)關(guān)系是邊(用戶(hù)A) --[對(duì)花生過(guò)敏]-- (花生) (用戶(hù)A) --[親屬]-- (用戶(hù)B) (用戶(hù)B) --[對(duì)堅(jiān)果過(guò)敏]-- (堅(jiān)果) (花生) --[屬于]-- (堅(jiān)果類(lèi))這是怎么組織數(shù)據(jù)的問(wèn)題跟存在哪完全無(wú)關(guān)。圖數(shù)據(jù)庫(kù)是一種專(zhuān)門(mén)存圖結(jié)構(gòu)、做圖查詢(xún)的存儲(chǔ)介質(zhì)–它原生存節(jié)點(diǎn)和邊查詢(xún)用 Cypher 這種圖遍歷語(yǔ)言。它們可以自由組合。知識(shí)圖譜可以存在圖數(shù)據(jù)庫(kù)里也可以不存在。二、知識(shí)圖譜可以存在 MySQL 里這是最反直覺(jué)但最重要的一點(diǎn)。用兩張表就能存知識(shí)圖譜-- 節(jié)點(diǎn)表CREATETABLEkg_node(idVARCHARPRIMARYKEY,typeVARCHAR,-- User / Food / Categoryprops JSON-- 其他屬性);-- 邊表CREATETABLEkg_edge(from_idVARCHAR,-- 起點(diǎn)to_idVARCHAR,-- 終點(diǎn)typeVARCHAR,-- RELATIVE_OF / ALLERGIC_TO / BELONGS_TOprops JSON-- 邊的屬性什么時(shí)候說(shuō)的、誰(shuí)說(shuō)的);查用戶(hù)親屬的過(guò)敏史用 SQL JOIN 模擬圖遍歷SELECTf.props-$.contentFROMkg_edge e1-- 用戶(hù)-親屬JOINkg_edge e2ONe1.to_ide2.from_id-- 親屬-事實(shí)JOINkg_node fONe2.to_idf.id-- 事實(shí)內(nèi)容WHEREe1.from_iduserAANDe1.typeRELATIVE_OFANDe2.typeSAIDANDf.props-$.contentLIKE%過(guò)敏%;這是知識(shí)圖譜–節(jié)點(diǎn) 邊組織數(shù)據(jù)完整表達(dá)了用戶(hù)-親屬-事實(shí)的關(guān)聯(lián)結(jié)構(gòu)。但這不是圖數(shù)據(jù)庫(kù)–存在 MySQL 里查詢(xún)用 SQL JOIN沒(méi)有 Neo4j 什么事。而且它能跑。大多數(shù)企業(yè)知識(shí)圖譜就是這么存的沒(méi)毛病。三、知識(shí)圖譜可以存在的五種介質(zhì)不只是 MySQL。知識(shí)圖譜可以落在很多種介質(zhì)里介質(zhì)怎么存知識(shí)圖譜怎么查適合圖數(shù)據(jù)庫(kù)原生節(jié)點(diǎn)邊Cypher 圖遍歷深度遍歷、關(guān)系推理關(guān)系型 DBnodes 表 edges 表SQL JOIN 模擬圖遍歷大多數(shù)企業(yè)知識(shí)圖譜內(nèi)存對(duì)象引用 鄰接表代碼遍歷小規(guī)模圖譜文件JSON / RDF / Turtle加載后遍歷離線知識(shí)圖譜導(dǎo)出向量 DB每個(gè)節(jié)點(diǎn)存一個(gè) embedding向量相似不算真正的圖譜查詢(xún)最后一行要特別注意把知識(shí)圖譜的節(jié)點(diǎn)存在向量數(shù)據(jù)庫(kù)里查詢(xún)只能做找相似節(jié)點(diǎn)做不了順著邊推理用戶(hù)親屬的過(guò)敏史查不出來(lái)。向量數(shù)據(jù)庫(kù)不是知識(shí)圖譜的正確存儲(chǔ)介質(zhì)–這是個(gè)常見(jiàn)的誤用。四、什么時(shí)候才真正需要圖數(shù)據(jù)庫(kù)知識(shí)圖譜存在 MySQL 里能跑但有一個(gè)會(huì)爆炸的問(wèn)題圖遍歷深度變深時(shí)SQL JOIN 的性能急劇下降。1 跳用戶(hù) - 他說(shuō)過(guò)的事實(shí) - 1 次 JOIN毫秒級(jí) 2 跳用戶(hù) - 親屬 - 親屬說(shuō)過(guò)的事實(shí) - 2 次 JOIN毫秒級(jí) 3 跳用戶(hù) - 親屬 - 親屬的同事 - 同事說(shuō)過(guò)的事實(shí) - 3 次 JOIN開(kāi)始慢 5 跳以上多表 JOIN 爆炸 - MySQL 扛不住 - 這時(shí)候需要圖數(shù)據(jù)庫(kù)為什么關(guān)系型 DB 的 JOIN 是笛卡爾積每次 JOIN 都要把兩表的行數(shù)相乘。5 跳意味著 5 次 JOIN復(fù)雜度是 O(表大小^5)–百萬(wàn)行表的 5 次方是 10^30宇宙毀滅都查不完。圖數(shù)據(jù)庫(kù)用鄰接表索引圖遍歷的復(fù)雜度是 O(分支因子^深度)跟表大小無(wú)關(guān)只跟圖的局部結(jié)構(gòu)有關(guān)。加上索引優(yōu)化5 跳查詢(xún)實(shí)測(cè)毫秒級(jí)。Neo4j 查 5 跳O(分支因子^深度)索引優(yōu)化后毫秒級(jí) MySQL 查 5 跳5 次 JOINO(表大小^5)爆炸這就是圖數(shù)據(jù)庫(kù)存在的唯一理由深度遍歷。淺度遍歷的知識(shí)圖譜不需要圖數(shù)據(jù)庫(kù)關(guān)系型 DB 夠深度遍歷的才需要。五、知識(shí)圖譜選型判斷標(biāo)準(zhǔn)你的查詢(xún)要幾跳要不要上圖數(shù)據(jù)庫(kù)看你的核心查詢(xún)的遍歷深度你的知識(shí)圖譜查詢(xún)要幾跳 1 跳用戶(hù) - 他說(shuō)過(guò)的事實(shí) - 關(guān)系型 DB 的 1 次 JOIN毫秒級(jí)完全夠 2 跳用戶(hù) - 親屬 - 親屬的事實(shí) - 關(guān)系型 DB 的 2 次 JOIN毫秒級(jí)夠 3 跳用戶(hù) - 親屬 - 同事 - 同事的事實(shí) - 關(guān)系型 DB 的 3 次 JOIN開(kāi)始慢 - 如果查詢(xún)頻繁考慮圖數(shù)據(jù)庫(kù) 5 跳跨多實(shí)體的復(fù)雜關(guān)聯(lián) - 關(guān)系型 DB 爆炸 - 必須用圖數(shù)據(jù)庫(kù)大多數(shù)企業(yè)知識(shí)圖譜的查詢(xún)是 1-2 跳關(guān)系型 DB 完全夠。不要為了看起來(lái)高級(jí)就上 Neo4j–運(yùn)維成本、學(xué)習(xí)曲線、生態(tài)成熟度都是代價(jià)。六、Agent Memory 場(chǎng)景判斷回到 AI Agent 的記憶系統(tǒng)。要不要知識(shí)圖譜、要不要圖數(shù)據(jù)庫(kù)按場(chǎng)景判斷場(chǎng)景要不要知識(shí)圖譜要不要圖數(shù)據(jù)庫(kù)存用戶(hù)對(duì)花生過(guò)敏不需要一條結(jié)構(gòu)化條目夠不需要存用戶(hù)說(shuō)過(guò) X后來(lái)改口說(shuō) Y不需要標(biāo)記取代鏈夠不需要查用戶(hù)親屬的過(guò)敏史需要跨實(shí)體關(guān)聯(lián)看遍歷深度淺的話關(guān)系 DB 也行查這個(gè) PR 沉寂 3 天了關(guān)聯(lián) CI 失敗 負(fù)責(zé)人請(qǐng)假需要多跳推理大概率需要查3 個(gè)月前這條事實(shí)當(dāng)時(shí)對(duì)不對(duì)需要時(shí)序推理Zep/Graphiti 用圖做雙時(shí)間軸雙時(shí)間軸圖數(shù)據(jù)庫(kù)的另一個(gè)用武之地最后那個(gè)場(chǎng)景3 個(gè)月前這條事實(shí)當(dāng)時(shí)對(duì)不對(duì)值得展開(kāi)。這是圖數(shù)據(jù)庫(kù)特別是 Zep/Graphiti的另一個(gè)殺手級(jí)特性雙時(shí)間軸。validFrom / validTo 事實(shí)在現(xiàn)實(shí)世界中的有效期 對(duì)花生過(guò)敏 validFromT1, validToT3 T1 到 T3 期間為真 不過(guò)敏 validFromT3, validTonull T3 之后為真 recordedAt 系統(tǒng)什么時(shí)候錄入這條記憶 fact1.recordedAt T1 T1 時(shí)錄入 fact2.recordedAt T3 T3 時(shí)錄入關(guān)系型 DB 的記憶只有錄入時(shí)間回答不了上周三時(shí)用戶(hù)對(duì)花生過(guò)敏嗎。圖數(shù)據(jù)庫(kù)通過(guò)邊的 validFrom/validTo 能回答這種時(shí)序問(wèn)題–查 validFrom 周三 validTo 的事實(shí)。如果你的 Agent 需要時(shí)序推理“當(dāng)時(shí)知道什么、什么時(shí)候知道的”圖數(shù)據(jù)庫(kù)是有價(jià)值的選擇。七、總結(jié)知識(shí)圖譜 用圖組織數(shù)據(jù)的形態(tài)選擇維度 1 圖數(shù)據(jù)庫(kù) 專(zhuān)門(mén)存圖、查圖的存儲(chǔ)介質(zhì)維度 2 知識(shí)圖譜 ≠ 圖數(shù)據(jù)庫(kù) 知識(shí)圖譜可以存在圖 DB / 關(guān)系 DB / 內(nèi)存 / 文件 圖數(shù)據(jù)庫(kù)專(zhuān)門(mén)存知識(shí)圖譜節(jié)點(diǎn) 邊 選不選用知識(shí)圖譜看你要不要關(guān)系推理 選不選用圖數(shù)據(jù)庫(kù)看遍歷深度淺的用關(guān)系 DB 夠深的才需要圖 DB三句話收束知識(shí)圖譜是設(shè)計(jì)決策怎么組織數(shù)據(jù)圖數(shù)據(jù)庫(kù)是工程選型存在哪里。它們正交可以自由組合。知識(shí)圖譜不一定要用圖數(shù)據(jù)庫(kù)。兩張表 SQL JOIN 就能存知識(shí)圖譜大多數(shù)企業(yè)就是這么做的。圖數(shù)據(jù)庫(kù)的唯一理由是深度遍歷。淺度遍歷用關(guān)系型 DB 夠深度遍歷5 跳才需要圖 DB。時(shí)序推理是另一個(gè)加分項(xiàng)。下次有人提知識(shí)圖譜先問(wèn)他“你的查詢(xún)要幾跳” 再?zèng)Q定上不上圖數(shù)據(jù)庫(kù)。