Реалізація побудови графа знань з текстів
Граф знань — це структурована репрезентація знань у вигляді троїк (субʼєкт, предикат, обʼєкт), збережених як граф. Автоматична побудова з текстів дозволяє перетворити неструктуровані корпуси на навігований, запитуваний граф знань.
Що таке граф знань та коли він потрібен
На відміну від реляційної БД, граф знань гнучко представляє багатозв'язні відносини: "Іван Петров → працює_в → Газпром → розташований_в → Москва → є_столицею → Росія". Запити на графі ("Знайди всіх співробітників компаній, розташованих у Москві") неможливі або незручні в SQL.
Граф знань потрібен коли:
- Дані сильно пов'язані з багатьма типами відносин
- Потрібні багатарівневі запити (обхід графа)
- Планується об'єднання даних з різних джерел
- Потрібна пояснюваність: "чому система так вирішила" — тому що A пов'язаний з B через C
Архітектура автоматичної побудови
Три ключові компоненти працюють послідовно:
Entity Extraction — NER з розширеним набором типів. Для корпоративних графів: PERSON, ORGANIZATION, LOCATION, PRODUCT, EVENT, DATE, MONEY, ROLE.
Relation Extraction — визначення типу зв'язку між парами сутностей у реченні або абзаці. REBEL (Babelscape) — найкращий відкритий інструмент для end-to-end видобування троїк.
Coreference Resolution — розв'язання кореференцій: "Газпром... Компанія... Вона..." — всі вказують на одну сутність. Використовує NeuralCoref або spaCy-experimental.
Entity Linking — прив'язка згаданих сутностей до канонічних записів у базі (Wikidata, DBpedia): "ВТБ", "Банк ВТБ", "VTB Bank" → один вузол графа.
Технічний стек
# REBEL для видобування троїк
from transformers import AutoModelForSeq2SeqLM, AutoTokenizer
tokenizer = AutoTokenizer.from_pretrained("Babelscape/rebel-large")
model = AutoModelForSeq2SeqLM.from_pretrained("Babelscape/rebel-large")
def extract_triplets(text: str) -> list[tuple]:
inputs = tokenizer(text, return_tensors="pt", max_length=512, truncation=True)
outputs = model.generate(**inputs, max_length=256)
decoded = tokenizer.batch_decode(outputs, skip_special_tokens=False)[0]
# Парсинг спеціального формату REBEL: <triplet> <subj> <rel> <obj>
return parse_rebel_output(decoded)
Зберігання графа
Neo4j — де факто стандарт для графових БД:
from neo4j import GraphDatabase
driver = GraphDatabase.driver("bolt://localhost:7687", auth=("neo4j", "password"))
def add_triplet(tx, subject, predicate, obj, source_doc):
tx.run("""
MERGE (s:Entity {name: $subject})
MERGE (o:Entity {name: $obj})
MERGE (s)-[r:RELATION {type: $predicate, source: $source_doc}]->(o)
""", subject=subject, predicate=predicate, obj=obj, source_doc=source_doc)
Запити на Cypher:
// Знайти всіх колег людини (працюють у тій же компанії)
MATCH (p:Entity {name: "Іван Петров"})-[:RELATION {type: "працює_в"}]->
(org:Entity)<-[:RELATION {type: "працює_в"}]-(colleague:Entity)
WHERE colleague <> p
RETURN colleague.name
Інтеграція з LLM (GraphRAG)
Граф знань + LLM = GraphRAG: замість семантичного пошуку по чанках, LLM отримує контекст зі зв'язного підграфа. Microsoft GraphRAG (реалізація LangChain) показує значно кращі результати для запитань про відносини між сутностями порівняно з класичним RAG.
Workflow:
- Запит користувача → видобування сутностей
- Обхід графа від цих сутностей (2–3 рівня)
- Підграф → текстова репрезентація → контекст LLM
- LLM генерує відповідь







