2-qism · Agentlar va bilim izlash·07-bob
Bilim grafikasi va GraphRAG
Ma'lumotni bog'langan tarmoq sifatida saqlash. LLM bilan graf qurish, Neo4j va Cypher, GraphRAG va graf ustida fikrlash — ishlaydigan kod bilan.
Ikki bob davomida RAG bilan ishladik. Uning butun mantiqi bitta g'oyaga tayanadi: matnni bo'laklarga bo'lib, vektorga aylantirish.
Lekin bir savol turi bor, unga vektor RAG hech qachon yaxshi javob bera olmaydi:
"A dori B kasallikni C oqsil orqali qanday davolaydi?"
Bu savolga javob berish uchun bir necha faktni zanjirga ulash kerak. Vektor qidiruvi bo'lak topadi, lekin bo'laklar orasidagi bog'lanishni ko'rmaydi.
Talaba tashbehi#
Kitobni o'qib, hamma joyini bo'yab chiqqan odamni ko'rgansiz. Natija ma'lum:
Hamma narsani chizib chiqish — hech narsani chizmaslik bilan barobar.
Yaxshi talaba boshqacha qiladi — sxema chizadi. Asosiy tushunchalar va ular orasidagi bog'lanishlar. Ixcham, aniq, ortiqchasiz.
Mana shu sxema — bu graf. Va agar unda haqiqiy dunyo ob'ektlari va ular orasidagi ma'noli munosabatlar bo'lsa — bu bilim grafikasi.
Uchlik — grafning atomi#
Bilim grafikasidagi har bir fakt uchlik (triplet) shaklida saqlanadi:
(bosh, munosabat, quyruq)
(Napoleon, tug'ilgan, Ayachcho)
(aspirin, davolaydi, bosh_og'rig'i)
(Anvar, ishlaydi, Mediline)
Grafning uch afzalligi:
- 1
- Bo'sh katak muammosi yo'qJadvalda ma'lumot bo'lmasa NULL qoladi. Grafda esa shunchaki qirra chizilmaydi. Turli xil ob'ektlar turli xususiyatlarga ega bo'lishi mumkin.
- 2
- Birlashtirish osonIkki jadvalni birlashtirish azob — qaysi ustunni qaysi ustunga, dublikatlar nima bo'ladi. Ikki grafni birlashtirish — uchliklarni bir joyga qo'yish, xolos.
- 3
- Har qanday ma'lumot mos keladiJSON, XML, CSV, daraxt, ijtimoiy tarmoq — hammasi grafga tushadi. Rekursiv va tsiklik tuzilmalarni ham tabiiy ifodalaydi.
Ontologiya: qoidalar qatlami#
Oddiy graf va bilim grafikasining farqi shundaki, ikkinchisida ma'no oshkor yozilgan — shuning uchun undan xulosa chiqarish mumkin.
Ontologiya — bu grafga qoidalar qo'shadi: munosabat tranzitivmi, simmetrikmi, qanday xususiyatlar bor.
Grafni LLM bilan qurish#
Graf qurishning eng qiyin qismi — matndan ob'ektlar va munosabatlarni ajratib olish. Ilgari bu qo'lda yorliqlangan ma'lumot va oylab ish talab qilar edi. LLM buni tubdan o'zgartirdi.
Ikki vazifa#
LLM bilan uchlik chiqarish#
import anthropic
import json
client = anthropic.Anthropic()
def uchliklarni_chiqar(matn: str) -> list[dict]:
"""Matndan (bosh, munosabat, quyruq) uchliklarini ajratadi."""
prompt = f"""Quyidagi matndan bilim grafikasi uchun uchliklar ajrating.
Har bir uchlik: bosh ob'ekt, munosabat, quyruq ob'ekt.
Odamning kasbini, joyni, tashkilotni alohida ob'ekt sifatida ajrating.
Faqat matnda AYTILGAN faktlarni oling — to'qib chiqarmang.
Natijani QAT'IY quyidagi JSON formatida bering:
[
{{"bosh": "...", "munosabat": "...", "quyruq": "...",
"bosh_turi": "ODAM|SHAHAR|TASHKILOT|KASB|...",
"quyruq_turi": "..."}}
]
Matn:
{matn}"""
javob = client.messages.create(
model="claude-opus-5",
max_tokens=2048,
messages=[{"role": "user", "content": prompt}],
)
matn_javob = "".join(b.text for b in javob.content if b.type == "text")
# JSON qismini ajratib olamiz
boshi = matn_javob.find("[")
oxiri = matn_javob.rfind("]") + 1
return json.loads(matn_javob[boshi:oxiri])
matn = """Salvatore Raieli — italyan olimi. U sun'iy intellekt bo'yicha
tadqiqotchi va Milanda ishlaydi. U mashina o'rganish sohasida mutaxassis."""
for u in uchliklarni_chiqar(matn):
print(f"({u['bosh']}) --[{u['munosabat']}]--> ({u['quyruq']})")(Salvatore Raieli) --[millati]--> (italyan)
(Salvatore Raieli) --[kasbi]--> (tadqiqotchi)
(Salvatore Raieli) --[ishlaydi]--> (Milan)
(Salvatore Raieli) --[mutaxassislik]--> (mashina o'rganish)
Ma'no noaniqligini hal qilish#
LLM'ning kuchli tomoni — kontekstni tushunishi:
# "Apple" — meva, kompaniya yoki xonanda (Fiona Apple)?
matnlar = [
"Apple yangi iPhone chiqardi", # kompaniya
"Har kuni bitta apple yeyish foydali", # meva
]
for m in matnlar:
uchliklar = uchliklarni_chiqar(m)
print(m, "→", uchliklar)
# Model kontekstdan to'g'ri turni aniqlaydiNeo4j va Cypher#
Grafni saqlash uchun graf bazasi kerak. Neo4j — eng mashhuri.
Neo4j'ni ishga tushirish
Eng oson yo'l — Docker:
terminaldocker run -d --name neo4j -p 7474:7474 -p 7687:7687 -e NEO4J_AUTH=neo4j/parol123 neo4j:5Brauzerda
http://localhost:7474ochilib, grafni ko'rish mumkin.Python'dan ulanish
terminalpip install neo4jfrom neo4j import GraphDatabase driver = GraphDatabase.driver( "bolt://localhost:7687", auth=("neo4j", "parol123"), ) def uchlikni_yoz(tx, bosh, munosabat, quyruq): """MERGE — agar tugun bor bo'lsa qayta yaratmaydi.""" tx.run( """ MERGE (b:Obyekt {nom: $bosh}) MERGE (q:Obyekt {nom: $quyruq}) MERGE (b)-[:BOGLANGAN {turi: $munosabat}]->(q) """, bosh=bosh, munosabat=munosabat, quyruq=quyruq, ) with driver.session() as session: for u in uchliklarni_chiqar(matn): session.execute_write( uchlikni_yoz, u["bosh"], u["munosabat"], u["quyruq"] )Cypher bilan so'rov
Cypher — Neo4j'ning so'rov tili. U grafdagi naqshlarni izlaydi:
def qidir(tx, nom): """Berilgan ob'ekt bilan bog'liq hamma narsani topadi.""" natija = tx.run( """ MATCH (b:Obyekt {nom: $nom})-[r:BOGLANGAN]->(q:Obyekt) RETURN b.nom AS bosh, r.turi AS munosabat, q.nom AS quyruq """, nom=nom, ) return [dict(qator) for qator in natija] with driver.session() as session: for f in session.execute_read(qidir, "Salvatore Raieli"): print(f"{f['bosh']} --[{f['munosabat']}]--> {f['quyruq']}")Cypher sintaksisi vizual —
()tugun,-[]->yo'naltirilgan qirra. So'rov aslida grafning rasmi kabi o'qiladi.
LLM'ni Cypher generatoriga aylantirish#
Eng chiroyli qismi: foydalanuvchi o'zbekcha so'raydi, LLM uni Cypherga aylantiradi.
def tabiiy_sorov(savol: str, sxema: str) -> str:
"""Tabiiy tilni Cypher so'roviga aylantiradi."""
prompt = f"""Sen Neo4j Cypher mutaxassisisan.
Graf sxemasi:
{sxema}
Quyidagi savol uchun Cypher so'rovini yoz.
Faqat so'rovni qaytar, izohsiz.
Savol: {savol}"""
javob = client.messages.create(
model="claude-opus-5",
max_tokens=512,
messages=[{"role": "user", "content": prompt}],
)
return "".join(b.text for b in javob.content if b.type == "text").strip()
sxema = "(:Obyekt {nom})-[:BOGLANGAN {turi}]->(:Obyekt {nom})"
cypher = tabiiy_sorov("Milanda kim ishlaydi?", sxema)
print(cypher)
# MATCH (p:Obyekt)-[:BOGLANGAN {turi: 'ishlaydi'}]->(:Obyekt {nom: 'Milan'})
# RETURN p.nomLangChain buni tayyor beradi:
from langchain_neo4j import Neo4jGraph, GraphCypherQAChain
from langchain_anthropic import ChatAnthropic
graph = Neo4jGraph(url="bolt://localhost:7687", username="neo4j", password="parol123")
llm = ChatAnthropic(model="claude-opus-5")
chain = GraphCypherQAChain.from_llm(llm, graph=graph, verbose=True, allow_dangerous_requests=False)
natija = chain.invoke({"query": "Milanda kim ishlaydi?"})
print(natija["result"])allow_dangerous_requests=False — yuqoridagi xavfsizlik muammosini hal qiladi.
GraphRAG#
Endi grafni LLM bilan bog'laymiz. Tuzilma vektor RAG bilan bir xil, faqat baza o'rnida graf.
Granularlik — nimani olib kelamiz#
Vektor RAG'da granularlikni bo'lak o'lchami boshqaradi. GraphRAG'da bu yerda nimani olishni tanlaysiz:
| Daraja | Nima olinadi | Qachon |
|---|---|---|
| Tugun | Faqat ob'ekt va xususiyatlari | "Aspirin nima?" |
| Uchlik | Ob'ekt + munosabat | "Aspirin nimani davolaydi?" |
| Yo'l | X dan Y gacha butun zanjir | "Bu ikki kasallik qanday bog'liq?" |
| Qism graf | Tugunlar va munosabatlar to'plami | Murakkab tahlil |
def yol_topish(tx, boshlanish, tugash, maks_uzunlik=4):
"""Ikki ob'ekt orasidagi eng qisqa yo'lni topadi."""
natija = tx.run(
"""
MATCH yol = shortestPath(
(a:Obyekt {nom: $boshlanish})-[*..%d]-(b:Obyekt {nom: $tugash})
)
RETURN [tugun IN nodes(yol) | tugun.nom] AS zanjir
""" % maks_uzunlik,
boshlanish=boshlanish, tugash=tugash,
)
qator = natija.single()
return qator["zanjir"] if qator else None
# "Aspirin bilan yurak xuruji qanday bog'liq?"
zanjir = yol_topish(session, "aspirin", "yurak_xuruji")
# ['aspirin', 'qon_suyultirish', 'tromboz', 'yurak_xuruji']Vektor RAG bu zanjirni hech qachon topa olmasdi — chunki hech bir bo'lakda to'rttala tushuncha birga yozilmagan bo'lishi mumkin. Graf esa ularni bog'lab beradi.
Vektor RAG yoki GraphRAG?#
Bu yo yoki yo savoli emas.
- Bog'lanishlar va ko'p qadamli mantiq: «A → B → C qanday bog'liq?»
- Xulosa chiqarish: ontologiya qoidalari bilan yangi fakt
- Aniq ob'ektlar orasidagi munosabatlar
- Ma'lumot manbasini aniq kuzatish
- Erkin matn ichidan parcha topish: «bu haqda nima yozilgan?»
- Qurish arzon — hujjatni tashlaysiz va tayyor
- Yangilash oson — yangi hujjat qo'shasiz
- Ontologiya, NER, RE bilan shug'ullanish shart emas
Ko'p real tizimlarda IKKALASI birga ishlaydi — router har bir so'rovni qayerga yuborishni hal qiladi. Oddiy fakt savollari vektor RAG'ga, bog'lanish savollari GraphRAG'ga.
Graf ustida fikrlash#
Grafning eng katta qiymati — undan yangi bilim chiqarish mumkinligi.
(Anvar, eri, Dilnoza) ∧ (Anvar, otasi, Sardor)
↓
(Dilnoza, onasi, Sardor) ← hech kim yozmagan, mantiq chiqardi
TransE — grafni vektorga#
Graf embeddingi grafni past o'lchamli vektor fazosiga joylashtiradi. TransE — eng sodda va eng chiroyli g'oya:
bosh + munosabat ≈ quyruq
Birinchi bobdagi podshoh − erkak + ayol ≈ malika ni eslaysizmi? Aynan o'sha g'oya, faqat
so'zlar emas, faktlar ustida.
import numpy as np
def transe_bashorat(bosh_vek, munosabat_vek, hamma_obyektlar):
"""bosh + munosabat ga eng yaqin ob'ektni topadi."""
maqsad = bosh_vek + munosabat_vek
masofalar = {
nom: np.linalg.norm(maqsad - vek)
for nom, vek in hamma_obyektlar.items()
}
return sorted(masofalar.items(), key=lambda x: x[1])[:3]
# (Napoleon + tug'ilgan) ≈ ? → Ayachcho
natija = transe_bashorat(
obyektlar["Napoleon"], munosabatlar["tug'ilgan"], obyektlar
)Bu yetishmayotgan bog'lanishlarni bashorat qilish (link prediction) imkonini beradi — grafda yozilmagan, lekin mantiqan mavjud bo'lgan faktlarni.
GNN — graf neyron tarmoqlari#
Klassik neyron tarmoq to'rtburchak ma'lumot kutadi. Graf esa to'rtburchak emas.
GNNning asosiy mexanizmi — xabar uzatish (message passing):
Bir necha qadamdan keyin har bir tugun nafaqat o'zi, balki atrofidagi butun mahalla haqidagi ma'lumotni o'zida saqlaydi. Bu tugun tasnifi, bog'lanish bashorati kabi vazifalar uchun ishlatiladi.
Amaliyot: hujjatdan grafgacha#
To'liq quvur — matndan so'rab bo'ladigan grafgacha.
class GrafRAG:
def __init__(self, neo4j_driver, client):
self.driver = neo4j_driver
self.client = client
def hujjatni_qosh(self, matn: str):
"""Matndan uchliklar chiqarib grafga yozadi."""
uchliklar = uchliklarni_chiqar(matn) # yuqoridagi funksiya
with self.driver.session() as s:
for u in uchliklar:
s.execute_write(
uchlikni_yoz, u["bosh"], u["munosabat"], u["quyruq"]
)
return len(uchliklar)
def sora(self, savol: str) -> str:
"""Tabiiy tilda so'raydi."""
# 1. Savolni Cypher'ga aylantiramiz
cypher = tabiiy_sorov(savol, self.sxema())
# 2. Xavfsizlik tekshiruvi
if any(t in cypher.upper() for t in ["DELETE", "CREATE", "SET", "REMOVE"]):
return "Faqat o'qish so'rovlariga ruxsat"
# 3. Grafda bajaramiz
with self.driver.session() as s:
natijalar = [dict(q) for q in s.run(cypher)]
if not natijalar:
return "Grafda javob topilmadi"
# 4. Natijani tabiiy tilga o'giramiz
javob = self.client.messages.create(
model="claude-opus-5",
max_tokens=1024,
messages=[{
"role": "user",
"content": f"Savol: {savol}\n\nGraf natijasi: {natijalar}\n\n"
f"Shu ma'lumotga tayanib tabiiy tilda javob bering.",
}],
)
return "".join(b.text for b in javob.content if b.type == "text")
def sxema(self):
return "(:Obyekt {nom})-[:BOGLANGAN {turi}]->(:Obyekt {nom})"Kichik bilim grafikasi quring
Neo4j'ni ishga tushiring va kichik bilim grafikasi quring.
- 5-10 ta paragraf matn oling (Vikipediya, kompaniya hujjati — nima bo'lsa ham)
- Har biridan LLM bilan uchliklar chiqaring
- Neo4j'ga yozing va brauzerda grafni ko'ring — ob'ektlar bir-biriga qanday bog'langanini vizual ko'rish juda foydali
- Tabiiy tilda 5 ta savol bering
Keyin solishtiring:
- Bir xil matndan vektor RAG ham quring (5-bobdagi kod bilan)
- Ikkala tizimga "X va Y qanday bog'liq?" turidagi savol bering
- Qaysi biri yaxshiroq javob berdi? Nima uchun?
Maslahatni ko'rsatish
Ma'lumot manbalari: - O'zingizga tanish sohaning Vikipediya maqolalari - Kompaniya xodimlari va bo'limlari haqidagi hujjat - Kitob personajlari va ular orasidagi munosabatlar Neo4j Docker bilan ishga tushiring, keyin: 1. 5-10 ta paragraf oling 2. Har biridan uchliklar chiqaring (haiku bilan, arzon) 3. Neo4j'ga yozing 4. Brauzerda (localhost:7474) grafni KO'RING — bu eng qiziqarli qism 5. Tabiiy tilda 5 ta savol bering Vektor RAG bilan solishtirish uchun: - Bir xil matndan vektor RAG ham quring - 'X va Y qanday bog'liq?' turidagi savol bering - Qaysi biri yaxshiroq javob berdi? Graf vizualizatsiyasi Cypher: MATCH (n)-[r]->(m) RETURN n,r,m LIMIT 50
Ochiq muammolar#
Bilim grafikalari kuchli, lekin muammosiz emas:
- Qurish qimmat — vektor RAG'da hujjatni tashlaysiz va tayyor. Graf uchun ontologiya, NER, RE, tozalash kerak
- To'liq emas — har doim yetishmayotgan bog'lanishlar bo'ladi
- Yangilash murakkab — yangi fakt kelganda uni butun grafga moslash kerak
- Baholash standarti yo'q — grafning "yaxshi"ligini o'lchash hali ochiq masala
Xulosa#
- 01Graf — bog'lanishlar haqida. «X va Y qanday bog'liq?» turidagi savollar uchun grafsiz iloji yo'q. Oddiy faktlar uchun vektor RAG arzonroq va yetarli.
- 02LLM graf qurishni arzonlashtirdi. Ilgari yorliqlangan ma'lumot va oylab ish kerak edi. Endi yaxshi yozilgan prompt yetadi.
- 03LLM'ni Cypher generatoriga aylantiring — foydalanuvchi o'zbekcha so'raydi, model so'rov yozadi. Lekin yozuv operatsiyalarini albatta bloklang.
- 04TransE — bosh + munosabat ≈ quyruq. Birinchi bobdagi so'z arifmetikasining o'zi, faqat faktlar ustida. Yetishmayotgan bog'lanishlarni bashorat qiladi.
- 05Ikkalasi raqib emas. Ko'p real tizimlarda router bilan vektor RAG va GraphRAG birga ishlaydi.
Endi agent bilim topishni ham, uni tarmoq sifatida saqlashni ham biladi. Lekin keyingi bobda butunlay boshqa savol turadi: agent o'z tajribasidan o'rganishi mumkinmi?
Javob — mustahkamlovchi o'qitishda.