آنچه در این مقاله میخوانید
گرافهای دانش و RAG با Gradient: تولید پاسخهای دقیقتر و هوشمندتر
۳۰ آبان ۱۴۰۴
در پردازش زبان طبیعی، پیدا کردن اطلاعات دقیق و مرتبط برای مدلهای زبان بزرگ همیشه یک چالش بوده است. در این آموزش، ما یک راهکار نوآورانه ارائه میکنیم: استفاده از گرافهای دانش برای تقویت سیستمهای RAG. شما یادمیگیرید چگونه با ترکیب تشخیص موجودیتها و مدلسازی گراف، دادههای خود را به شکل ساختاریافته و قابل پرسوجو درآورید و به مدل زبان اجازه دهید پاسخهای دقیقتر و معنادارتری ارائه کند. این روش هم برای دادههای ساختاریافته و هم غیرساختاریافته کاربرد دارد و پایهای قوی برای ایجاد سیستمهای هوشمند مبتنی بر گراف فراهم میکند.
در ادامه خواهید خواند:
- مهارتها و مفاهیمی که با این آموزش میآموزید
- چیزهایی که برای شروع نیاز دارید
- چرا برای RAG از گرافهای دانش استفاده کنیم؟
- جمع بندی

مهارتها و مفاهیمی که با این آموزش میآموزید
در این آموزش، یاد میگیرید چگونه با استفاده از یک گراف پایگاه داده، یک سیستم RAG بسازید که بتواند اطلاعات مرتبط را پیدا کرده و پاسخهای دقیق تولید کند. ابتدا دادهها را با تشخیص موجودیتهای نامگذاری شده (NER) پردازش میکنیم تا موجودیتها و روابط بین آنها در گراف ثبت شود و شبکهای از اطلاعات مرتبط ایجاد گردد. سپس، این روابط را برای استخراج قطعات متنی مرتبط و با زمینه دقیق پرسوجو میکنیم تا مدل زبان بزرگ بتواند پاسخهای دقیقتر، هدفمندتر و معتبرتر ارائه دهد.
این آموزش یک راهنمای گامبهگام ارائه میدهد که نشان میدهد چگونه دادههای ساختاریافته و گراف را با تواناییهای تولید زبان ترکیب کنید تا یک سیستم RAG هوشمند و قابل اعتماد بسازید. با دنبال کردن این آموزش، شما پایه و مهارت لازم برای مدیریت دادههای متنی و روابط پیچیده بین آنها را خواهید داشت.
چیزهایی که برای شروع نیاز دارید
برای اینکه بهترین استفاده را از این آموزش ببرید، بهتر است موارد زیر را آماده داشته باشید:
- یک لپتاپ توسعهدهنده با سیستمعامل لینوکس یا مک
- اگر از ویندوز استفاده میکنید، بهتر است از یک ماشین مجازی (VM) یا یک سرور مجازی ابری کمک بگیرید
- پایتون نسخه 3.10 یا بالاتر روی سیستم نصب باشد
- پیشنهاد میشود از یک محیط مجازی مانند miniconda یا venv استفاده کنید
- Docker نصب شده (روی لینوکس یا مک) برای اجرای نسخه محلی Neo4j
- کمی آشنایی با دستورات Shell
چرا برای RAG از گرافهای دانش استفاده کنیم؟
سیستمهای RAG کاملا به توانایی آنها در بازیابی درست اطلاعات وابسته هستند. ذخیرهسازیهای برداری (Vector Stores) بسیار سریع هستند و در یافتن بخشهای متنی که از نظر معنایی شبیه هم هستند، عالی عمل میکنند. اما این ذخیرهسازها معمولا روابط بین دادهها را نادیده میگیرند، درحالی که در دادههای واقعی این روابط اهمیت زیادی دارند.
بهعنوان مثال، شما ممکن است دادههایی دربارهی مشتریان، تامینکنندگان، سفارشها و محصولات داشته باشید. ارتباط بین این عناصر فراتر از شباهت متنی است. پایگاه دادههای گرافی میتوانند این ارتباطات را ردیابی کنند و امکان اجرای پرسوجوهای چندمرحلهای را فراهم کنند که به سوالات پیچیدهتر پاسخ میدهند.
با سرویس هوش مصنوعی لیارا، مدلهای AI خودت رو بدون دغدغه اجرا و مدیریت کن.
✅ پشتیبانی از GPU و CPU✅ مناسب مدلهای متنباز✅ اجرای پایدار و سریع
خرید و راهاندازی سرویس هوش مصنوعی
گراف پایگاه داده برای عاملهای RAG
یکی دیگر از مزایای بزرگ پایگاه دادههای گرافی، شفافیت است. ساختارهای گرافی بهراحتی قابل تصور (Visualize) و اشکالزدایی (Debug) هستند. اگر یک مدل زبان یه اشتباه به بخشی از دادهها استناد کند، شما میتوانید مسیر گرهها (Nodes) و یالها (Edged) را دنبال کنید تا ببینید این اشتباه از کجا آمده است.
این رویکر باعث میشود:
- میزان توهم (Hallucination) مدل کاهش یابد
- اعتماد به نتایج بیشتر شود
- توسعهدهندگان بتوانند سریعتر خطاها را شناسایی و اصلاح کنند.
مرحله 1: راهندازی پیشنیازهای پروژه
ابتدا باید وابستگیهای پایتون را با pip نصب کنید:
pip install neo4j \
requests \
ctransformers \
spacy \
flask \
openai
سپس یک پایگاه داده Neo4j را با استفاده از Docker اجرا کنید:
docker run \
-d \
--publish=7474:7474 --publish=7687:7687 \
-v $HOME/neo4j/data:/data \
-v $HOME/neo4j/logs:/logs \
-v $HOME/neo4j/import:/var/lib/neo4j/import \
-v $HOME/neo4j/plugins:/plugins \
neo4j:5
مرحله 2: بارگذاری دادهها در پایگاه داده گرافی
قبل از اینکه بتوانیم پرسوجو انجام دهیم، باید دادهها را وارد پایگاه داده کنیم. کد زیر یک اسکریپت پایتون است که از spaCy برای شناسایی موجودیتهای نامگذاری شده (NER) استفاده میکند و دادهها را در Neo4j ذخیره میکند. این کد مجموعه داده BBC را پردازش کرده، موجودیتها را برچسبگذاری میکند و آنها را به اسناد متصل مینماید.
import os
import uuid
import spacy
from neo4j import GraphDatabase
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "<YOUR PASSWORD>"
NEO4J_PASSWORD = "<YOUR USERNAME>"
DATASET_PATH = "./bbc" # Path to the unzipped BBC dataset folder
def ingest_bbc_documents_with_ner():
# Load spaCy for NER
nlp = spacy.load("en_core_web_sm")
driver = GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
with driver.session() as session:
# Optional: clear old data
session.run("MATCH (n) DETACH DELETE n")
for category in os.listdir(DATASET_PATH):
category_path = os.path.join(DATASET_PATH, category)
if not os.path.isdir(category_path):
continue # skip non-directories
for filename in os.listdir(category_path):
if filename.endswith(".txt"):
filepath = os.path.join(category_path, filename)
# FIX #1: handle potential £ symbol or other characters
# Option 1: Use a different codec
# with open(filepath, "r", encoding="latin-1") as f:
# text_content = f.read()
#
# Option 2: Replace invalid bytes (keep utf-8):
with open(filepath, "r", encoding="utf-8", errors="replace") as f:
text_content = f.read()
# Generate a UUID in Python
doc_uuid = str(uuid.uuid4())
# Create (or MERGE) the Document node
create_doc_query = """
MERGE (d:Document {doc_uuid: $doc_uuid})
ON CREATE SET
d.title = $title,
d.content = $content,
d.category = $category
RETURN d
"""
session.run(
create_doc_query,
doc_uuid=doc_uuid,
title=filename,
content=text_content,
category=category
)
# Named Entity Recognition
doc_spacy = nlp(text_content)
# For each entity recognized, MERGE on name+label
for ent in doc_spacy.ents:
# Skip small or numeric or purely punctuation
if len(ent.text.strip()) < 3:
continue
# Generate a unique ID for new entities
entity_uuid = str(uuid.uuid4())
merge_entity_query = """
MERGE (e:Entity { name: $name, label: $label })
ON CREATE SET e.ent_uuid = $ent_uuid
RETURN e.ent_uuid as eUUID
"""
record = session.run(
merge_entity_query,
name=ent.text.strip(),
label=ent.label_,
ent_uuid=entity_uuid
).single()
ent_id = record["eUUID"]
# Now create relationship by matching on doc_uuid & ent_uuid
rel_query = """
MATCH (d:Document { doc_uuid: $docId })
MATCH (e:Entity { ent_uuid: $entId })
MERGE (d)-[:MENTIONS]->(e)
"""
session.run(
rel_query,
docId=doc_uuid,
entId=ent_id
)
print("Ingestion with NER complete!")
if __name__ == "__main__":
ingest_bbc_documents_with_ner()
این کد نشان میدهد چطور:
- یک گره Document ایجاد میشود
- موجودیتهای شناسایی شده به آن لینک میشوند
- ساختار کلی در پایگاه داده ذخیره میشود
شما میتوانید دادههای خودتان را جایگزین کنید. ایدهی اصلی این است که وقتی این روابط ساخته شدند، میتوانید با پرسوجو از آنها بینشهای معنادارتر بهدست آورید؛ چیزی فراتر از صرفا بازیابی یک متن.
چطور RAG و گرافهای دانش جلوی توهمات هوش مصنوعی را میگیرند؟
توهمات هوش مصنوعی
مرحله 3: پرسوجوی عامل RAG با استفاده از گراف دانش
بعد از وارد کردن دادهها، میتوان از آنها پرسوجو کرده و سوالات موردنظر را مطرح نمود.
- موجودیتها را از پرسش کاربر با spaCy استخراج میکند.
- این موجودیتها را در پایگاه داده Neo4j استخراج میکند.
- اسناد مرتبط را جمعآوری میکند.
- در نهایت با ترکیب این زمینهها، پرسش را به مدل زبانی میفرستد تا پاسخ تولید کند.
import spacy
from neo4j import GraphDatabase
import openai
import os
NEO4J_URI = "bolt://localhost:7687"
NEO4J_USER = "<YOUR PASSWORD>"
NEO4J_PASSWORD = "<YOUR USERNAME>"
def connect_neo4j():
return GraphDatabase.driver(NEO4J_URI, auth=(NEO4J_USER, NEO4J_PASSWORD))
def extract_entities_spacy(text, nlp):
doc = nlp(text)
return [(ent.text.strip(), ent.label_) for ent in doc.ents if len(ent.text.strip()) >= 3]
def fetch_documents_by_entities(session, entity_texts, top_k=5):
if not entity_texts:
return []
query = """
MATCH (d:Document)-[:MENTIONS]->(e:Entity)
WHERE toLower(e.name) IN $entity_list
WITH d, count(e) as matchingEntities
ORDER BY matchingEntities DESC
LIMIT $topK
RETURN d.title AS title, d.content AS content, d.category AS category, matchingEntities
"""
entity_list_lower = [txt.lower() for txt in entity_texts]
results = session.run(query, entity_list=entity_list_lower, topK=top_k)
docs = []
for record in results:
docs.append({
"title": record["title"],
"content": record["content"],
"category": record["category"],
"match_count": record["matchingEntities"]
})
return docs
def generate_answer(question, context):
"""
Replaces the local LLM server call with a DigitalOcean Gradient Agent call,
which is OpenAI API-compatible.
"""
# Build a RAG-style prompt
prompt = f"""You are given the following context from multiple documents:
{context}
Question: {question}
Please provide a concise answer.
Answer:
"""
# Example of using the ChatCompletion endpoint (Chat API)
# If you prefer the older Completion endpoint, you can adapt similarly.
try:
openai_client = openai.OpenAI(
# Comment the next 2 lines out to point to a DigitalOcean Gradient Agent
# base_url = "https://<YOUR AGENT URL>/api/v1/",
# api_key=os.environ.get("DIGITALOCEAN_GENAI_ACCESS_TOKEN_GENERIC"),
)
completion = openai_client.chat.completions.create(
model="n/a",
messages=[
{"role": "user", "content": prompt}
],
)
return completion.choices[0].message.content
except Exception as e:
print("Error calling the DigitalOcean Gradient Agent:", e)
return "Error generating answer"
if __name__ == "__main__":
user_query = "What do these articles say about Ernie Wise?"
nlp = spacy.load("en_core_web_sm")
recognized_entities = extract_entities_spacy(user_query, nlp)
entity_texts = [ent[0] for ent in recognized_entities]
driver = connect_neo4j()
with driver.session() as session:
docs = fetch_documents_by_entities(session, entity_texts, top_k=5)
combined_context = ""
for doc in docs:
snippet = doc["content"][:300].replace("\n", " ")
combined_context += f"\n---\nTitle: {doc['title']} | Category: {doc['category']}\nSnippet: {snippet}...\n"
final_answer = generate_answer(user_query, combined_context)
print("RAG-based Answer:", final_answer)
جریان کار به این صورت است.
- موجودیتهای پرسش کاربر با spaCy شناسایی میشوند.
- این موجودیتها در گراف Neo4j تطبیق داده میشوند تا اسناد مرتبط پیدا شوند.
- بخشهایی از این اسناد بهعنوان زمینه انتخاب میشود.
- پرسش به همراه زمینه به مدل زبانی داده میشود.
این رویکرد باعث میشود مدل روی اطلاعات دقیق و ساختاریافته تمرکز کند. بهجای جستجو در یک نمایهی متنی بزرگ، شما دادههای انتخابشده و دارای روابط مشخص را بازیابی میکنید. نتیجه، پاسخهایی با کیفیت بالاتر و توانایی بهتر در مدیریت سوالات پیچیده است؛ چیزی فراتر از تطبیق سادهی کلمات کلیدی.
مقایسه Agentic RAG و RAG؛ کدام برای پروژههای AI بهتر است؟
مقایسه Agentic RAG و RAG
جمع بندی
پایگاه دادههای گرافی یک بعد تازه به جریانهای کاری RAG اضافه میکنند. این پایگاهها قادرند روابط جزئی پیچیده را مدیریت کنند، پاسخهای غیر مفید را کاهش دهند و این امکان را بدهند که مسیر رسیدن سیستم به یک نتیجه را ردیابی کنید. وقتی این رویکرد با تشخیص موجودیتها و یک مدل زبانی قدرتمند ترکیب شوند، یک خط لوله ایجاد میشود که میتواند جزئیات و بافت واقعی دادهها را بهخوبی ثبت کند.
کدهایی که در این آموزش دیدید، نقطه شروعی برای ساخت یک عامل RAG قدرتمند هستند. شما میتوانید این طراحی را با افزودن کدهای خود، تغییر منطق پرسوجو یا آزمایش قابلیتهای بیشتر گراف گسترش دهید. چه هدف شما ساخت یک چتبات کاربر محور باشد یا یک ابزار تحلیلی داخلی، گرافهای دانشی میتوانند شفافیت و عمق بیشتری به تجربههای هوش مصنوعی شما اضافه کنند.