mirror of
https://github.com/trustgraph-ai/trustgraph.git
synced 2026-04-25 16:36:21 +02:00
Native CLI i18n: The TrustGraph CLI has built-in translation support that dynamically loads language strings. You can test and use different languages by simply passing the --lang flag (e.g., --lang es for Spanish, --lang ru for Russian) or by configuring your environment's LANG variable. Automated Docs Translations: This PR introduces autonomously translated Markdown documentation into several target languages, including Spanish, Swahili, Portuguese, Turkish, Hindi, Hebrew, Arabic, Simplified Chinese, and Russian.
807 lines
42 KiB
Markdown
807 lines
42 KiB
Markdown
---
|
||
layout: default
|
||
title: "מפרט טכני לשיפור ביצועי GraphRAG"
|
||
parent: "Hebrew (Beta)"
|
||
---
|
||
|
||
# מפרט טכני לשיפור ביצועי GraphRAG
|
||
|
||
> **Beta Translation:** This document was translated via Machine Learning and as such may not be 100% accurate. All non-English languages are currently classified as Beta.
|
||
|
||
## סקירה כללית
|
||
|
||
<<<<<<< HEAD
|
||
מפרט זה מתאר שיפורי ביצועים מקיפים עבור אלגוריתם GraphRAG (Graph Retrieval-Augmented Generation) ב-TrustGraph. יישום הנוכחי סובל מבעיות ביצועים משמעותיות המגבילות את יכולת ההרחבה וזמני התגובה. מפרט זה מתייחס לארבעה תחומים עיקריים של אופטימיזציה:
|
||
|
||
1. **אופטימיזציה של מעבר גרפים**: ביטול שאילתות מסד נתונים רקורסיביות לא יעילות ויישום חקר גרפים באצווה.
|
||
2. **אופטימיזציה של פתרון תגיות**: החלפת אחזור תגיות רציף בפעולות מקבילות/באצווה.
|
||
3. **שיפור אסטרטגיית אחסון במטמון**: יישום אחסון במטמון חכם עם פינוי LRU וטעינה מראש.
|
||
4. **אופטימיזציה של שאילתות**: הוספת שמירת תוצאות במטמון ואחסון במטמון של הטמעות לשיפור זמני התגובה.
|
||
|
||
## מטרות
|
||
|
||
**הפחתת נפח שאילתות מסד הנתונים**: השגת הפחתה של 50-80% בסך כל שאילתות מסד הנתונים באמצעות אצווה ואחסון במטמון.
|
||
**שיפור זמני תגובה**: יעד לבניית תת-גרפים מהירה פי 3-5 ופתרון תגיות מהיר פי 2-3.
|
||
**שיפור יכולת הרחבה**: תמיכה בגרפי ידע גדולים יותר עם ניהול זיכרון טוב יותר.
|
||
**שמירה על דיוק**: שמירה על פונקציונליות ואיכות תוצאות GraphRAG הקיימות.
|
||
**אפשרות לעיבוד מקבילי**: שיפור יכולות עיבוד מקבילי עבור בקשות מרובות בו-זמנית.
|
||
**הפחתת טביעת רגל של זיכרון**: יישום מבני נתונים וניהול זיכרון יעילים.
|
||
**הוספת יכולת ניטור**: הכללת מדדי ביצועים ויכולות ניטור.
|
||
**הבטחת אמינות**: הוספת טיפול בשגיאות ומנגנוני תזמון מתאימים.
|
||
=======
|
||
מפרט זה מתאר שיפורים מקיפים בביצועים עבור אלגוריתם GraphRAG (Graph Retrieval-Augmented Generation) ב-TrustGraph. יישום הנוכחי סובל מבעיות ביצועים משמעותיות המגבילות את יכולת ההרחבה וזמני התגובה. מפרט זה מתייחס לארבעה תחומים עיקריים של אופטימיזציה:
|
||
|
||
1. **אופטימיזציה של מעבר גרפים**: ביטול שאילתות מסד נתונים רקורסיביות לא יעילות ויישום חקר גרפים בקבוצות
|
||
2. **אופטימיזציה של פתרון תגיות**: החלפת שליפת תגיות רציפה בפעולות מקבילות/בקבוצות
|
||
3. **שיפור אסטרטגיית אחסון במטמון**: יישום אחסון במטמון חכם עם פינוי LRU וטעינה מראש
|
||
4. **אופטימיזציה של שאילתות**: הוספת שמירת תוצאות במטמון ואחסון במטמון של הטמעות לשיפור זמני התגובה
|
||
|
||
## מטרות
|
||
|
||
**הפחתת נפח שאילתות מסד הנתונים**: השגת הפחתה של 50-80% בסך כל שאילתות מסד הנתונים באמצעות קיבוץ ואחסון במטמון
|
||
**שיפור זמני תגובה**: יעד לבניית תת-גרפים מהירה פי 3-5 ופתרון תגיות מהיר פי 2-3
|
||
**שיפור יכולת הרחבה**: תמיכה בגרפי ידע גדולים יותר עם ניהול זיכרון טוב יותר
|
||
**שמירה על דיוק**: שמירה על פונקציונליות ואיכות תוצאות GraphRAG הקיימות
|
||
**אפשרות לעיבוד מקבילי**: שיפור יכולות עיבוד מקבילי עבור בקשות מרובות בו-זמנית
|
||
**הפחתת טביעת רגל של זיכרון**: יישום מבני נתונים וניהול זיכרון יעילים
|
||
**הוספת יכולת ניטור**: הכללת מדדי ביצועים ויכולות ניטור
|
||
**הבטחת אמינות**: הוספת טיפול מתאים בשגיאות ומנגנוני תזמון
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
## רקע
|
||
|
||
יישום GraphRAG הנוכחי ב-`trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py` מציג מספר בעיות ביצועים קריטיות המשפיעות באופן משמעותי על יכולת ההרחבה של המערכת:
|
||
|
||
### בעיות ביצועים נוכחיות
|
||
|
||
**1. מעבר גרפים לא יעיל (פונקציה `follow_edges`, שורות 79-127)**
|
||
<<<<<<< HEAD
|
||
מבצע 3 שאילתות נפרדות למסד הנתונים עבור כל ישות בכל רמת עומק.
|
||
תבנית שאילתה: שאילתות מבוססות נושא, מבוססות פרידיקט ומבוססות אובייקט עבור כל ישות.
|
||
ללא אצווה: כל שאילתה מעבדת רק ישות אחת בכל פעם.
|
||
ללא זיהוי מעגלים: ניתן לחזור על צמתים זהים מספר פעמים.
|
||
יישום רקורסיבי ללא שמירה במטמון מוביל למורכבות אקספוננציאלית.
|
||
מורכבות זמן: O(entities × max_path_length × triple_limit³)
|
||
|
||
**2. פתרון תגיות רציף (פונקציה `get_labelgraph`, שורות 144-171)**
|
||
מעבד כל רכיב משולש (נושא, פרידיקט, אובייקט) ברצף.
|
||
כל קריאה ל-`maybe_label` עלולה לגרום לשאילתה למסד הנתונים.
|
||
ללא ביצוע מקבילי או אצווה של שאילתות תגיות.
|
||
גורם עד ל-3 × קריאות אישיות למסד הנתונים עבור גודל תת-גרף.
|
||
|
||
**3. אסטרטגיית אחסון במטמון בסיסית (פונקציה `maybe_label`, שורות 62-77)**
|
||
מטמון מילון פשוט ללא מגבלות גודל או TTL.
|
||
מדיניות פינוי מטמון חסרת גבולות מובילה לצמיחה בלתי מוגבלת של זיכרון.
|
||
החסרה במטמון גורמת לשאילתות נפרדות למסד הנתונים.
|
||
ללא טעינה מראש או אחסון במטמון חכם.
|
||
|
||
**4. תבניות שאילתות לא אופטימליות**
|
||
שאילתות דמיון וקטורי לישויות אינן מאוחסנות במטמון בין בקשות דומות.
|
||
ללא שמירת תוצאות במטמון לתבניות שאילתות חוזרות.
|
||
חסרים אופטימיזציות שאילתות לתבניות גישה נפוצות.
|
||
|
||
**5. בעיות קריטיות של חיי אובייקט (`rag.py:96-102`)**
|
||
**אובייקט GraphRag נוצר מחדש עבור כל בקשה**: מופע חדש נוצר עבור כל שאילתה, תוך אובדן כל יתרונות המטמון.
|
||
**אובייקט שאילתה בעל אורך חיים קצר ביותר**: נוצר ונהרס בתוך ביצוע שאילתה יחיד (שורות 201-207).
|
||
**מטמון תגיות מאופס עבור כל בקשה**: חימום מטמון וידע שנצבר אובדים בין בקשות.
|
||
**תקורה של יצירת לקוח מחדש**: לקוחות מסד נתונים פוטנציאליים מוקמים מחדש עבור כל בקשה.
|
||
**ללא אופטימיזציה חוצה בקשות**: לא ניתן להפיק תועלת מתבניות שאילתות או שיתוף תוצאות.
|
||
=======
|
||
מבצע 3 שאילתות נפרדות למסד הנתונים עבור כל ישות בכל רמת עומק
|
||
תבנית שאילתה: שאילתות מבוססות נושא, שאילתות מבוססות פרידיקט ושאילתות מבוססות אובייקט עבור כל ישות
|
||
ללא קיבוץ: כל שאילתה מעבדת רק ישות אחת בכל פעם
|
||
ללא זיהוי מעגלים: ניתן לחזור על צמתים זהים מספר פעמים
|
||
יישום רקורסיבי ללא שמירה במטמון מוביל למורכבות אקספוננציאלית
|
||
מורכבות זמן: O(entities × max_path_length × triple_limit³)
|
||
|
||
**2. פתרון תגיות רציף (פונקציה `get_labelgraph`, שורות 144-171)**
|
||
מעבד כל רכיב משולש (נושא, פרידיקט, אובייקט) ברצף
|
||
כל קריאה ל-`maybe_label` עלולה לגרום לשאילתה למסד הנתונים
|
||
ללא ביצוע מקבילי או קיבוץ של שאילתות תגיות
|
||
גורם עד ל-3 × קריאות אישיות למסד הנתונים עבור גודל תת-גרף
|
||
|
||
**3. אסטרטגיית אחסון במטמון בסיסית (פונקציה `maybe_label`, שורות 62-77)**
|
||
מטמון מילון פשוט ללא מגבלות גודל או TTL
|
||
מדיניות פינוי מטמון חסרת גבולות מובילה לצמיחה בלתי מוגבלת של זיכרון
|
||
החסרה במטמון גורמת לשאילתות נפרדות למסד הנתונים
|
||
ללא טעינה מראש או אחסון במטמון חכם
|
||
|
||
**4. תבניות שאילתות לא אופטימליות**
|
||
שאילתות דמיון וקטורי לישויות אינן מאוחסנות במטמון בין בקשות דומות
|
||
ללא שמירת תוצאות במטמון לתבניות שאילתות חוזרות
|
||
חסרים אופטימיזציות שאילתות לתבניות גישה נפוצות
|
||
|
||
**5. בעיות קריטיות של חיי אובייקט (`rag.py:96-102`)**
|
||
**אובייקט GraphRag נוצר מחדש עבור כל בקשה**: מופע חדש נוצר עבור כל שאילתה, ומאבד את כל יתרונות האחסון במטמון
|
||
**אובייקט שאילתה בעל אורך חיים קצר ביותר**: נוצר ונהרס בתוך ביצוע שאילתה יחידה (שורות 201-207)
|
||
**מטמון תגיות מאופס עבור כל בקשה**: חימום מטמון וידע שנצבר הולכים לאיבוד בין בקשות
|
||
**תקורה של יצירת לקוח**: לקוחות מסד נתונים פוטנציאליים נוצרים מחדש עבור כל בקשה
|
||
**ללא אופטימיזציה חוצה בקשות**: לא ניתן להפיק תועלת מתבניות שאילתות או שיתוף תוצאות
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
### ניתוח השפעת ביצועים
|
||
|
||
תרחיש גרוע ביותר נוכחי עבור שאילתה טיפוסית:
|
||
<<<<<<< HEAD
|
||
**אחזור ישות**: שאילתת דמיון וקטורית אחת.
|
||
**מעבר גרפים**: entities × max_path_length × 3 × שאילתות triple_limit.
|
||
**פתרון תגיות**: 3 × שאילתות תגיות אישיות עבור גודל תת-גרף.
|
||
פלט חוזה (יש לעקוב אחר הפורמט המדויק).
|
||
עבור פרמטרים ברירת מחדל (50 ישויות, אורך נתיב 2, מגבלת 30 משולשים, גודל תת-גרף 150):
|
||
=======
|
||
**שליפת ישות**: שאילתה אחת לדמיון וקטורי
|
||
**מעבר גרפים**: entities × max_path_length × 3 × שאילתות triple_limit
|
||
**פתרון תגיות**: 3 × שאילתות אישיות לגודל תת-גרף
|
||
פלט חוזה (יש לעקוב אחר הפורמט המדויק).
|
||
עבור פרמטרים ברירת מחדל (50 ישויות, אורך נתיב 2, מגבלת טריפל של 30, גודל תת-גרף של 150):
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
**מספר שאילתות מינימלי**: 1 + (50 × 2 × 3 × 30) + (150 × 3) = **9,451 שאילתות למסד נתונים**
|
||
**זמן תגובה**: 15-30 שניות עבור גרפים בגודל בינוני
|
||
**שימוש בזיכרון**: צמיחה בלתי מוגבלת של מטמון לאורך זמן
|
||
**יעילות מטמון**: 0% - המטמון מאופס בכל בקשה
|
||
**תקורה של יצירת אובייקטים**: אובייקטי GraphRag + שאילתה נוצרים/נמחקים עבור כל בקשה
|
||
|
||
מפרט זה מתייחס לפערים אלה על ידי יישום שאילתות באצווה, מטמון חכם ועיבוד מקבילי. על ידי אופטימיזציה של דפוסי שאילתות וגישה לנתונים, TrustGraph יכולה:
|
||
לתמוך בגרפי ידע בקנה מידה ארגוני עם מיליוני ישויות
|
||
לספק זמני תגובה של פחות משנייה עבור שאילתות טיפוסיות
|
||
לטפל במאות בקשות GraphRAG מקבילות
|
||
להתרחב ביעילות עם גודל ומורכבות הגרף
|
||
|
||
## עיצוב טכני
|
||
|
||
### ארכיטקטורה
|
||
|
||
אופטימיזציית הביצועים של GraphRAG דורשת את הרכיבים הטכניים הבאים:
|
||
|
||
#### 1. **שינוי ארכיטקטורה של משך חיי אובייקטים**
|
||
**הפוך את GraphRag לבעל חיים ארוך**: העבר את המופע של GraphRag לרמה של המעבד לצורך שמירה בין בקשות
|
||
**שמור על מטמון**: שמור על מטמון תוויות, מטמון הטבעות ומטמון תוצאות שאילתה בין בקשות
|
||
<<<<<<< HEAD
|
||
**אופטימיזציה של אובייקט שאילתה**: שנה את מבנה אובייקט השאילתה כהקשר ביצוע קל משקל, ולא כמכל נתונים
|
||
=======
|
||
**אופטימיזציה של אובייקט שאילתה**: שנה את מבנה אובייקט השאילתה כך שיהיה הקשר ביצוע קל משקל, ולא מיכל נתונים
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
**שמירה על חיבורים**: שמור על חיבורי לקוח למסד הנתונים בין בקשות
|
||
|
||
מודול: `trustgraph-flow/trustgraph/retrieval/graph_rag/rag.py` (עודכן)
|
||
|
||
#### 2. **מנוע מעבר גרפים מותאם**
|
||
החלף את `follow_edges` רקורסיבי בחיפוש רוחב-ראשוני איטרטיבי
|
||
<<<<<<< HEAD
|
||
הטמעת עיבוד אצווה של ישויות בכל רמת מעבר
|
||
הוסף זיהוי מעגלים באמצעות מעקב אחר צמתים מבקרים
|
||
כלול סיום מוקדם כאשר מגיעים למגבלות
|
||
=======
|
||
הטמע עיבוד אצווה של ישויות בכל רמת מעבר
|
||
הוסף זיהוי מעגלים באמצעות מעקב אחר צמתים שנצפו
|
||
כלול סיום מוקדם כאשר מגיעים לגבולות
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
מודול: `trustgraph-flow/trustgraph/retrieval/graph_rag/optimized_traversal.py`
|
||
|
||
#### 3. **מערכת פתרון תוויות מקבילה**
|
||
שאילתות תוויות באצווה עבור מספר ישויות בו-זמנית
|
||
<<<<<<< HEAD
|
||
הטמעת דפוסי async/await לגישה מקבילה למסד הנתונים
|
||
=======
|
||
הטמע דפוסי async/await לגישה מקבילה למסד הנתונים
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
הוסף אחזור מוקדם לדפוסי תוויות נפוצים
|
||
כלול אסטרטגיות חימום מטמון תוויות
|
||
|
||
מודול: `trustgraph-flow/trustgraph/retrieval/graph_rag/label_resolver.py`
|
||
|
||
#### 4. **שכבת מטמון תוויות שמרנית**
|
||
מטמון LRU עם TTL קצר עבור תוויות בלבד (5 דקות) כדי לאזן בין ביצועים ועקביות
|
||
ניטור מדדי מטמון ויחס פגיעות
|
||
**ללא מטמון הטבעות**: כבר שמורים עבור כל שאילתה, אין יתרון בין שאילתות
|
||
**ללא מטמון תוצאות שאילתה**: עקב חששות לגבי עקביות שינוי גרף
|
||
|
||
מודול: `trustgraph-flow/trustgraph/retrieval/graph_rag/cache_manager.py`
|
||
|
||
#### 5. **מסגרת אופטימיזציה של שאילתות**
|
||
ניתוח אופטימיזציה של דפוסי שאילתות והצעות
|
||
מתאם שאילתות באצווה לגישה למסד הנתונים
|
||
<<<<<<< HEAD
|
||
ניהול בריכת חיבורים וזמן קצוב של שאילתות
|
||
=======
|
||
ניהול בריכת חיבורים ותזמון תפוגה של שאילתות
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
ניטור ביצועים ואיסוף מדדים
|
||
|
||
מודול: `trustgraph-flow/trustgraph/retrieval/graph_rag/query_optimizer.py`
|
||
|
||
### מודלים של נתונים
|
||
|
||
#### מצב מעבר גרפים מותאם
|
||
|
||
מנוע המעבר שומר על מצב כדי להימנע מפעולות מיותרות:
|
||
|
||
```python
|
||
@dataclass
|
||
class TraversalState:
|
||
visited_entities: Set[str]
|
||
current_level_entities: Set[str]
|
||
next_level_entities: Set[str]
|
||
subgraph: Set[Tuple[str, str, str]]
|
||
depth: int
|
||
query_batch: List[TripleQuery]
|
||
```
|
||
|
||
גישה זו מאפשרת:
|
||
<<<<<<< HEAD
|
||
זיהוי יעיל של מעגלים באמצעות מעקב אחר ישויות שביקרו
|
||
הכנת שאילתות באצווה בכל רמת מעבר
|
||
ניהול מצב חסכוני בזיכרון
|
||
סיום מוקדם כאשר מגיעים למגבלות גודל
|
||
=======
|
||
זיהוי יעיל של מעגלים באמצעות מעקב אחר ישויות שכבר נבדקו.
|
||
הכנת שאילתות בקבוצות בכל רמת מעבר.
|
||
ניהול מצב חסכוני בזיכרון.
|
||
סיום מוקדם כאשר מגבלות גודל הושגו.
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
#### מבנה מטמון משופר
|
||
|
||
```python
|
||
@dataclass
|
||
class CacheEntry:
|
||
value: Any
|
||
timestamp: float
|
||
access_count: int
|
||
ttl: Optional[float]
|
||
|
||
class CacheManager:
|
||
label_cache: LRUCache[str, CacheEntry]
|
||
embedding_cache: LRUCache[str, CacheEntry]
|
||
query_result_cache: LRUCache[str, CacheEntry]
|
||
cache_stats: CacheStatistics
|
||
```
|
||
|
||
#### מבני שאילתות אצווה
|
||
|
||
```python
|
||
@dataclass
|
||
class BatchTripleQuery:
|
||
entities: List[str]
|
||
query_type: QueryType # SUBJECT, PREDICATE, OBJECT
|
||
limit_per_entity: int
|
||
|
||
@dataclass
|
||
class BatchLabelQuery:
|
||
entities: List[str]
|
||
predicate: str = LABEL
|
||
```
|
||
|
||
### ממשקי API
|
||
|
||
#### ממשקי API חדשים:
|
||
|
||
**ממשק GraphTraversal API**
|
||
```python
|
||
async def optimized_follow_edges_batch(
|
||
entities: List[str],
|
||
max_depth: int,
|
||
triple_limit: int,
|
||
max_subgraph_size: int
|
||
) -> Set[Tuple[str, str, str]]
|
||
```
|
||
|
||
**ממשק API לפתרון תגיות אצווה**
|
||
```python
|
||
async def resolve_labels_batch(
|
||
entities: List[str],
|
||
cache_manager: CacheManager
|
||
) -> Dict[str, str]
|
||
```
|
||
|
||
**ממשק ניהול מטמון (Cache Management API)**
|
||
```python
|
||
class CacheManager:
|
||
async def get_or_fetch_label(self, entity: str) -> str
|
||
async def get_or_fetch_embeddings(self, query: str) -> List[float]
|
||
async def cache_query_result(self, query_hash: str, result: Any, ttl: int)
|
||
def get_cache_statistics(self) -> CacheStatistics
|
||
```
|
||
|
||
#### ממשקי API שעודכנו:
|
||
|
||
**GraphRag.query()** - שופר עם אופטימיזציות ביצועים:
|
||
הוסף פרמטר cache_manager לשליטה על המטמון
|
||
הוסף ערך החזרה performance_metrics
|
||
הוסף פרמטר query_timeout לאמינות
|
||
|
||
**מחלקה Query** - שופרה לעיבוד באצווה:
|
||
החלף עיבוד ישויות בודדות בפעולות באצווה
|
||
הוסף מנהלי הקשר אסינכרוניים לניקוי משאבים
|
||
הוסף פונקציות החזרה (callbacks) להתקדמות עבור פעולות ארוכות
|
||
|
||
### פרטי יישום
|
||
|
||
#### שלב 0: שינוי ארכיטקטורה קריטי
|
||
|
||
**יישום בעייתי נוכחי:**
|
||
```python
|
||
# INEFFICIENT: GraphRag recreated every request
|
||
class Processor(FlowProcessor):
|
||
async def on_request(self, msg, consumer, flow):
|
||
# PROBLEM: New GraphRag instance per request!
|
||
self.rag = GraphRag(
|
||
embeddings_client = flow("embeddings-request"),
|
||
graph_embeddings_client = flow("graph-embeddings-request"),
|
||
triples_client = flow("triples-request"),
|
||
prompt_client = flow("prompt-request"),
|
||
verbose=True,
|
||
)
|
||
# Cache starts empty every time - no benefit from previous requests
|
||
response = await self.rag.query(...)
|
||
|
||
# VERY SHORT-LIVED: Query object created/destroyed per request
|
||
class GraphRag:
|
||
async def query(self, query, user="trustgraph", collection="default", ...):
|
||
q = Query(rag=self, user=user, collection=collection, ...) # Created
|
||
kg = await q.get_labelgraph(query) # Used briefly
|
||
# q automatically destroyed when function exits
|
||
```
|
||
|
||
**ארכיטקטורה ארוכת טווח ומותאמת:**
|
||
```python
|
||
class Processor(FlowProcessor):
|
||
def __init__(self, **params):
|
||
super().__init__(**params)
|
||
self.rag_instance = None # Will be initialized once
|
||
self.client_connections = {}
|
||
|
||
async def initialize_rag(self, flow):
|
||
"""Initialize GraphRag once, reuse for all requests"""
|
||
if self.rag_instance is None:
|
||
self.rag_instance = LongLivedGraphRag(
|
||
embeddings_client=flow("embeddings-request"),
|
||
graph_embeddings_client=flow("graph-embeddings-request"),
|
||
triples_client=flow("triples-request"),
|
||
prompt_client=flow("prompt-request"),
|
||
verbose=True,
|
||
)
|
||
return self.rag_instance
|
||
|
||
async def on_request(self, msg, consumer, flow):
|
||
# REUSE the same GraphRag instance - caches persist!
|
||
rag = await self.initialize_rag(flow)
|
||
|
||
# Query object becomes lightweight execution context
|
||
response = await rag.query_with_context(
|
||
query=v.query,
|
||
execution_context=QueryContext(
|
||
user=v.user,
|
||
collection=v.collection,
|
||
entity_limit=entity_limit,
|
||
# ... other params
|
||
)
|
||
)
|
||
|
||
class LongLivedGraphRag:
|
||
def __init__(self, ...):
|
||
# CONSERVATIVE caches - balance performance vs consistency
|
||
self.label_cache = LRUCacheWithTTL(max_size=5000, ttl=300) # 5min TTL for freshness
|
||
# Note: No embedding cache - already cached per-query, no cross-query benefit
|
||
# Note: No query result cache due to consistency concerns
|
||
self.performance_metrics = PerformanceTracker()
|
||
|
||
async def query_with_context(self, query: str, context: QueryContext):
|
||
# Use lightweight QueryExecutor instead of heavyweight Query object
|
||
executor = QueryExecutor(self, context) # Minimal object
|
||
return await executor.execute(query)
|
||
|
||
@dataclass
|
||
class QueryContext:
|
||
"""Lightweight execution context - no heavy operations"""
|
||
user: str
|
||
collection: str
|
||
entity_limit: int
|
||
triple_limit: int
|
||
max_subgraph_size: int
|
||
max_path_length: int
|
||
|
||
class QueryExecutor:
|
||
"""Lightweight execution context - replaces old Query class"""
|
||
def __init__(self, rag: LongLivedGraphRag, context: QueryContext):
|
||
self.rag = rag
|
||
self.context = context
|
||
# No heavy initialization - just references
|
||
|
||
async def execute(self, query: str):
|
||
# All heavy lifting uses persistent rag caches
|
||
return await self.rag.execute_optimized_query(query, self.context)
|
||
```
|
||
|
||
שינוי ארכיטקטורה זה מספק:
|
||
**הפחתת שאילתות מסד נתונים ב-10-20%** עבור גרפים עם קשרים נפוצים (בהשוואה ל-0% כיום)
|
||
**ביטול תקורה של יצירת אובייקטים** עבור כל בקשה
|
||
**בריכת חיבורים קבועה** ושימוש חוזר בלקוח
|
||
**אופטימיזציה בין בקשות** בתוך חלונות TTL של מטמון
|
||
|
||
**מגבלה חשובה של עקביות מטמון:**
|
||
אחסון מטמון לטווח ארוך מכניס סיכון של מידע מיושן כאשר ישויות/תוויות נמחקים או משתנים בגרף הבסיסי. מטמון LRU עם TTL מספק איזון בין שיפורי ביצועים ורעננות נתונים, אך אינו יכול לזהות שינויים בזמן אמת בגרף.
|
||
|
||
#### שלב 1: אופטימיזציה של מעבר גרפים
|
||
|
||
**בעיות ביישום הנוכחי:**
|
||
```python
|
||
# INEFFICIENT: 3 queries per entity per level
|
||
async def follow_edges(self, ent, subgraph, path_length):
|
||
# Query 1: s=ent, p=None, o=None
|
||
res = await self.rag.triples_client.query(s=ent, p=None, o=None, limit=self.triple_limit)
|
||
# Query 2: s=None, p=ent, o=None
|
||
res = await self.rag.triples_client.query(s=None, p=ent, o=None, limit=self.triple_limit)
|
||
# Query 3: s=None, p=None, o=ent
|
||
res = await self.rag.triples_client.query(s=None, p=None, o=ent, limit=self.triple_limit)
|
||
```
|
||
|
||
**יישום אופטימלי:**
|
||
```python
|
||
async def optimized_traversal(self, entities: List[str], max_depth: int) -> Set[Triple]:
|
||
visited = set()
|
||
current_level = set(entities)
|
||
subgraph = set()
|
||
|
||
for depth in range(max_depth):
|
||
if not current_level or len(subgraph) >= self.max_subgraph_size:
|
||
break
|
||
|
||
# Batch all queries for current level
|
||
batch_queries = []
|
||
for entity in current_level:
|
||
if entity not in visited:
|
||
batch_queries.extend([
|
||
TripleQuery(s=entity, p=None, o=None),
|
||
TripleQuery(s=None, p=entity, o=None),
|
||
TripleQuery(s=None, p=None, o=entity)
|
||
])
|
||
|
||
# Execute all queries concurrently
|
||
results = await self.execute_batch_queries(batch_queries)
|
||
|
||
# Process results and prepare next level
|
||
next_level = set()
|
||
for result in results:
|
||
subgraph.update(result.triples)
|
||
next_level.update(result.new_entities)
|
||
|
||
visited.update(current_level)
|
||
current_level = next_level - visited
|
||
|
||
return subgraph
|
||
```
|
||
|
||
#### שלב 2: פתרון מקבילי של תגיות
|
||
|
||
**יישום סדרתי נוכחי:**
|
||
```python
|
||
# INEFFICIENT: Sequential processing
|
||
for edge in subgraph:
|
||
s = await self.maybe_label(edge[0]) # Individual query
|
||
p = await self.maybe_label(edge[1]) # Individual query
|
||
o = await self.maybe_label(edge[2]) # Individual query
|
||
```
|
||
|
||
**יישום מקבילי אופטימלי:**
|
||
```python
|
||
async def resolve_labels_parallel(self, subgraph: List[Triple]) -> List[Triple]:
|
||
# Collect all unique entities needing labels
|
||
entities_to_resolve = set()
|
||
for s, p, o in subgraph:
|
||
entities_to_resolve.update([s, p, o])
|
||
|
||
# Remove already cached entities
|
||
uncached_entities = [e for e in entities_to_resolve if e not in self.label_cache]
|
||
|
||
# Batch query for all uncached labels
|
||
if uncached_entities:
|
||
label_results = await self.batch_label_query(uncached_entities)
|
||
self.label_cache.update(label_results)
|
||
|
||
# Apply labels to subgraph
|
||
return [
|
||
(self.label_cache.get(s, s), self.label_cache.get(p, p), self.label_cache.get(o, o))
|
||
for s, p, o in subgraph
|
||
]
|
||
```
|
||
|
||
#### שלב 3: אסטרטגיית אחסון מטמון מתקדמת
|
||
|
||
**מטמון LRU עם TTL:**
|
||
```python
|
||
class LRUCacheWithTTL:
|
||
def __init__(self, max_size: int, default_ttl: int = 3600):
|
||
self.cache = OrderedDict()
|
||
self.max_size = max_size
|
||
self.default_ttl = default_ttl
|
||
self.access_times = {}
|
||
|
||
async def get(self, key: str) -> Optional[Any]:
|
||
if key in self.cache:
|
||
# Check TTL expiration
|
||
if time.time() - self.access_times[key] > self.default_ttl:
|
||
del self.cache[key]
|
||
del self.access_times[key]
|
||
return None
|
||
|
||
# Move to end (most recently used)
|
||
self.cache.move_to_end(key)
|
||
return self.cache[key]
|
||
return None
|
||
|
||
async def put(self, key: str, value: Any):
|
||
if key in self.cache:
|
||
self.cache.move_to_end(key)
|
||
else:
|
||
if len(self.cache) >= self.max_size:
|
||
# Remove least recently used
|
||
oldest_key = next(iter(self.cache))
|
||
del self.cache[oldest_key]
|
||
del self.access_times[oldest_key]
|
||
|
||
self.cache[key] = value
|
||
self.access_times[key] = time.time()
|
||
```
|
||
|
||
#### שלב 4: אופטימיזציה וניטור של שאילתות
|
||
|
||
**איסוף מדדי ביצועים:**
|
||
```python
|
||
@dataclass
|
||
class PerformanceMetrics:
|
||
total_queries: int
|
||
cache_hits: int
|
||
cache_misses: int
|
||
avg_response_time: float
|
||
subgraph_construction_time: float
|
||
label_resolution_time: float
|
||
total_entities_processed: int
|
||
memory_usage_mb: float
|
||
```
|
||
|
||
<<<<<<< HEAD
|
||
**זמן תגובה מקסימלי ומנגנון ניתוב מחדש:**
|
||
=======
|
||
**מגבלת זמן שאילתה ומנגנון ניתוב:**
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
```python
|
||
async def execute_with_timeout(self, query_func, timeout: int = 30):
|
||
try:
|
||
return await asyncio.wait_for(query_func(), timeout=timeout)
|
||
except asyncio.TimeoutError:
|
||
logger.error(f"Query timeout after {timeout}s")
|
||
raise GraphRagTimeoutError(f"Query exceeded timeout of {timeout}s")
|
||
```
|
||
|
||
## שיקולים בנוגע לעקביות מטמון
|
||
|
||
<<<<<<< HEAD
|
||
**פשרות בנוגע לרעננות נתונים:**
|
||
**מטמון תוויות (TTL של 5 דקות)**: סיכון להצגת תוויות של ישויות שנמחקו/ששמותיהן שונו.
|
||
**ללא שמירת מטמון של הטמעות (embeddings)**: לא נדרש - הטמעות כבר שמורות מטמון עבור כל שאילתה.
|
||
**ללא שמירת מטמון של תוצאות**: מונע קבלת תוצאות תת-גרף לא עדכניות מישויות/קשרים שנמחקו.
|
||
=======
|
||
**פשרות בין רעננות נתונים:**
|
||
**מטמון תוויות (TTL של 5 דקות)**: סיכון בהצגת תוויות של ישויות שנמחקו/ששמותיהן שונו.
|
||
**ללא שמירת מטמון של הטמעות (embeddings)**: לא נדרש - הטמעות כבר שמורות מטמון עבור כל שאילתה.
|
||
**ללא שמירת מטמון של תוצאות**: מונע תוצאות תת-גרף שגויות מישויות/קשרים שנמחקו.
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
**אסטרטגיות הפחתה:**
|
||
**ערכי TTL שמרניים**: איזון בין שיפורי ביצועים (10-20%) לבין רעננות נתונים.
|
||
**מנגנוני ביטול מטמון**: שילוב אופציונלי עם אירועי שינוי בגרף.
|
||
<<<<<<< HEAD
|
||
**לוחות מחוונים לניטור**: מעקב אחר אחוזי פגיעה במטמון (cache hit rates) לעומת מקרים של נתונים לא עדכניים.
|
||
**מדיניות מטמון הניתנות לתצורה**: אפשרות לכוונון פר-פריסה בהתאם לתדירות השינויים.
|
||
|
||
**תצורת מטמון מומלצת בהתאם לקצב שינויים בגרף:**
|
||
**קצב שינויים גבוה (>100 שינויים/שעה)**: TTL=60 שניות, גדלי מטמון קטנים יותר.
|
||
**קצב שינויים בינוני (10-100 שינויים/שעה)**: TTL=300 שניות (ברירת מחדל).
|
||
**קצב שינויים נמוך (<10 שינויים/שעה)**: TTL=600 שניות, גדלי מטמון גדולים יותר.
|
||
|
||
## שיקולים בנוגע לאבטחה
|
||
|
||
**מניעת הזרקת שאילתות:**
|
||
אימות כל מזהי ישויות ופרמטרים של שאילתות.
|
||
שימוש בשאילתות מפורמטות עבור כל אינטראקציות עם מסד הנתונים.
|
||
יישום מגבלות על מורכבות השאילתות כדי למנוע התקפות מניעת שירות (DoS).
|
||
|
||
**הגנה על משאבים:**
|
||
אכיפת מגבלות על גודל תת-גרף מקסימלי.
|
||
יישום זמני קצבה לשאילתות כדי למנוע מיצוי משאבים.
|
||
הוספת ניטור מגבלות לשימוש בזיכרון.
|
||
=======
|
||
**לוחות מחוונים לניטור**: מעקב אחר שיעורי פגיעות במטמון לעומת מקרים של נתונים לא מעודכנים.
|
||
**מדיניות מטמון הניתנות לתצורה**: אפשרות לכוונון עדין בהתאם לתדירות השינויים בכל פריסה.
|
||
|
||
**תצורת מטמון מומלצת בהתאם לקצב שינויים בגרף:**
|
||
**שינויים רבים (>100 שינויים/שעה)**: TTL=60 שניות, גדלי מטמון קטנים יותר.
|
||
**שינויים בינוניים (10-100 שינויים/שעה)**: TTL=300 שניות (ברירת מחדל).
|
||
**שינויים מעטים (<10 שינויים/שעה)**: TTL=600 שניות, גדלי מטמון גדולים יותר.
|
||
|
||
## שיקולים בנושא אבטחה
|
||
|
||
**מניעת הזרקת שאילתות:**
|
||
אימות כל מזהי ישויות ופרמטרים של שאילתות.
|
||
שימוש בשאילתות מפורטות עבור כל אינטראקציות עם מסד הנתונים.
|
||
יישום מגבלות מורכבות שאילתות למניעת התקפות מניעת שירות (DoS).
|
||
|
||
**הגנה על משאבים:**
|
||
אכיפת מגבלות גודל תת-גרף מקסימלי.
|
||
יישום זמני קצבי שאילתות למניעת מיצוי משאבים.
|
||
הוספת ניטור מגבלות שימוש בזיכרון.
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
**בקרת גישה:**
|
||
שמירה על בידוד משתמשים ואוספים קיימים.
|
||
הוספת רישום ביקורת עבור פעולות המשפיעות על הביצועים.
|
||
יישום הגבלת קצב עבור פעולות יקרות.
|
||
|
||
<<<<<<< HEAD
|
||
## שיקולים בנוגע לביצועים
|
||
=======
|
||
## שיקולים בנושא ביצועים
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
### שיפורי ביצועים צפויים
|
||
|
||
**הפחתת מספר שאילתות:**
|
||
נוכחי: ~9,000+ שאילתות עבור בקשה טיפוסית.
|
||
אופטימלי: ~50-100 שאילתות מקובצות (הפחתה של 98%).
|
||
|
||
**שיפורי זמן תגובה:**
|
||
מעבר בגרף: 15-20 שניות → 3-5 שניות (מהיר פי 4-5).
|
||
פתרון תוויות: 8-12 שניות → 2-4 שניות (מהיר פי 3).
|
||
שאילתה כוללת: 25-35 שניות → 6-10 שניות (שיפור של פי 3-4).
|
||
|
||
**יעילות זיכרון:**
|
||
גדלי מטמון מוגבלים מונעים דליפות זיכרון.
|
||
מבני נתונים יעילים מפחיתים את טביעת הרגל של הזיכרון בערך ב-40%.
|
||
איסוף אשפה טוב יותר באמצעות ניקוי משאבים נכון.
|
||
|
||
**ציפיות ריאליות בנוגע לביצועים:**
|
||
<<<<<<< HEAD
|
||
**מטמון תוויות**: הפחתה של 10-20% במספר השאילתות עבור גרפים עם קשרים נפוצים.
|
||
**אופטימיזציה של קיבוץ**: הפחתה של 50-80% במספר השאילתות (אופטימיזציה עיקרית).
|
||
=======
|
||
**מטמון תוויות**: הפחתת שאילתות ב-10-20% עבור גרפים עם קשרים נפוצים.
|
||
**אופטימיזציה של קיבוץ**: הפחתת שאילתות ב-50-80% (אופטימיזציה עיקרית).
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
**אופטימיזציה של חיי אובייקט**: ביטול תקורה של יצירה מחדש בכל בקשה.
|
||
**שיפור כולל**: שיפור של פי 3-4 בזמן התגובה בעיקר בזכות קיבוץ.
|
||
|
||
**שיפורי יכולת הרחבה:**
|
||
תמיכה בגרפי ידע גדולים פי 3-5 (מוגבל על ידי צרכי עקביות מטמון).
|
||
קיבולת גבוהה יותר פי 3-5 של בקשות מקבילות.
|
||
ניצול טוב יותר של משאבים באמצעות שימוש חוזר בחיבורים.
|
||
|
||
### ניטור ביצועים
|
||
|
||
**מדדים בזמן אמת:**
|
||
זמני ביצוע שאילתות לפי סוג פעולה.
|
||
<<<<<<< HEAD
|
||
אחוזי פגיעה במטמון ויעילות.
|
||
שימוש בבריכת חיבורים למסד הנתונים.
|
||
=======
|
||
שיעורי פגיעה ויעילות של מטמון.
|
||
שימוש בבריכת חיבורי מסד נתונים.
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
שימוש בזיכרון והשפעת איסוף אשפה.
|
||
|
||
**בדיקות ביצועים:**
|
||
בדיקות רגרסיה אוטומטיות לביצועים
|
||
בדיקות עומסים עם נפחי נתונים ריאליים
|
||
השוואות ביצועים מול המימוש הנוכחי
|
||
|
||
## אסטרטגיית בדיקות
|
||
|
||
### בדיקות יחידה
|
||
<<<<<<< HEAD
|
||
בדיקת רכיבים בודדים עבור מעבר, אחסון במטמון ופתרון תגיות
|
||
הדמיית אינטראקציות עם מסד נתונים לצורך בדיקות ביצועים
|
||
בדיקת פינוי מטמון ותפוגה של זמן תפוגה (TTL)
|
||
=======
|
||
בדיקת רכיבים בודדים עבור מעבר, אחסון במטמון ופתרון תוויות
|
||
הדמיית אינטראקציות עם מסד נתונים לצורך בדיקות ביצועים
|
||
בדיקת פינוי מטמון ותפוגת זמן (TTL)
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
בדיקת טיפול בשגיאות ותסריטי תזמון
|
||
|
||
### בדיקות אינטגרציה
|
||
בדיקות מקצה לקצה של שאילתות GraphRAG עם אופטימיזציות
|
||
בדיקת אינטראקציות עם מסד נתונים עם נתונים אמיתיים
|
||
טיפול בבקשות מקבילות וניהול משאבים
|
||
<<<<<<< HEAD
|
||
זיהוי דליפות זיכרון ואימות ניקוי משאבים
|
||
=======
|
||
זיהוי דליפות זיכרון ובדיקת ניקוי משאבים
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
### בדיקות ביצועים
|
||
בדיקות ביצועים מול המימוש הנוכחי
|
||
בדיקות עומסים עם גדלים ומורכבויות גרף משתנים
|
||
בדיקות לחץ עבור מגבלות זיכרון וחיבורים
|
||
בדיקות רגרסיה לשיפורי ביצועים
|
||
|
||
### בדיקות תאימות
|
||
אימות תאימות של ממשקי API קיימים של GraphRAG
|
||
בדיקה עם מנועי גרפים שונים
|
||
אימות דיוק התוצאות בהשוואה למימוש הנוכחי
|
||
|
||
## תוכנית יישום
|
||
|
||
### גישת יישום ישירה
|
||
<<<<<<< HEAD
|
||
מכיוון שמותר לשנות ממשקי API, ליישם אופטימיזציות ישירות ללא מורכבות של העברה:
|
||
|
||
1. **החלפת `follow_edges`:** כתיבה מחדש עם מעבר אצווה איטרטיבי
|
||
2. **אופטימיזציה של `get_labelgraph`:** יישום פתרון תגיות מקבילי
|
||
3. **הוספת GraphRag ארוך טווח:** שינוי של מעבד כדי לשמור על מופע קבוע
|
||
4. **יישום אחסון במטמון של תגיות:** הוספת מטמון LRU עם TTL למחלקת GraphRag
|
||
|
||
### היקף השינויים
|
||
**מחלקה של שאילתות:** החלפת כ-50 שורות ב-`follow_edges`, הוספת כ-30 שורות לטיפול באצווה
|
||
**מחלקה של GraphRag:** הוספת שכבת אחסון במטמון (כ-40 שורות)
|
||
**מחלקה של מעבד:** שינוי לשימוש במופע קבוע של GraphRag (כ-20 שורות)
|
||
=======
|
||
מכיוון שמותר לשנות ממשקי API, ליישם אופטימיזציות ישירות ללא מורכבות של מעבר:
|
||
|
||
1. **החלפת `follow_edges`:** כתיבה מחדש עם מעבר אצווה איטרטיבי
|
||
2. **אופטימיזציה של `get_labelgraph`:** יישום פתרון תוויות מקבילי
|
||
3. **הוספת GraphRag ארוך טווח:** שינוי של Processor לשמירה על מופע קבוע
|
||
4. **יישום אחסון תוויות במטמון:** הוספת מטמון LRU עם TTL למחלקת GraphRag
|
||
|
||
### היקף השינויים
|
||
**מחלקת שאילתה:** החלפת כ-50 שורות ב-`follow_edges`, הוספת כ-30 שורות לטיפול באצווה
|
||
**מחלקת GraphRag:** הוספת שכבת אחסון במטמון (כ-40 שורות)
|
||
**מחלקת Processor:** שינוי לשימוש במופע קבוע של GraphRag (כ-20 שורות)
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
**סה"כ:** כ-140 שורות של שינויים ממוקדים, בעיקר בתוך מחלקות קיימות
|
||
|
||
## ציר זמן
|
||
|
||
**שבוע 1: יישום ליבה**
|
||
החלפת `follow_edges` עם מעבר אצווה איטרטיבי
|
||
<<<<<<< HEAD
|
||
יישום פתרון תגיות מקבילי ב-`get_labelgraph`
|
||
הוספת מופע GraphRag ארוך טווח למעבד
|
||
יישום שכבת אחסון במטמון של תגיות
|
||
=======
|
||
יישום פתרון תוויות מקבילי ב-`get_labelgraph`
|
||
הוספת מופע GraphRag ארוך טווח ל-Processor
|
||
יישום שכבת אחסון במטמון
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
**שבוע 2: בדיקות ושילוב**
|
||
בדיקות יחידה ללוגיקה חדשה של מעבר ואחסון במטמון
|
||
בדיקות ביצועים מול המימוש הנוכחי
|
||
בדיקות אינטגרציה עם נתוני גרף אמיתיים
|
||
סקירת קוד ואופטימיזציה
|
||
|
||
**שבוע 3: פריסה**
|
||
פריסת המימוש המותאם
|
||
ניטור שיפורי ביצועים
|
||
כוונון עדין של זמן תפוגה של מטמון וגדלי אצווה בהתבסס על שימוש אמיתי
|
||
|
||
## שאלות פתוחות
|
||
|
||
**בריכת חיבורים למסד נתונים:** האם עלינו ליישם בריכת חיבורים מותאמת אישית או להסתמך על בריכת חיבורים קיימת של לקוח מסד נתונים?
|
||
<<<<<<< HEAD
|
||
**שימור מטמון:** האם מטמון התגיות וההטבעות צריך להתמיד בין הפעלות מחדש של השירות?
|
||
**אחסון במטמון מבוזר:** עבור פריסות מרובות מופעים, האם עלינו ליישם אחסון במטמון מבוזר עם Redis/Memcached?
|
||
**פורמט תוצאות שאילתה:** האם עלינו לייעל את הייצוג הפנימי של משולש לטובת יעילות זיכרון טובה יותר?
|
||
**שילוב ניטור:** אילו מדדים יש לחשוף למערכות ניטור קיימות (Prometheus, וכו')?
|
||
=======
|
||
**שימור מטמון:** האם מטמון תוויות ו-embedding צריך להתמיד בין הפעלות מחדש של השירות?
|
||
**אחסון מבוזר:** עבור פריסות מרובות מופעים, האם עלינו ליישם אחסון מבוזר עם Redis/Memcached?
|
||
**פורמט תוצאות שאילתה:** האם עלינו לייעל את הייצוג הפנימי של משולש לטובת יעילות זיכרון טובה יותר?
|
||
**שילוב ניטור:** אילו מדדים צריכים להיות חשופים למערכות ניטור קיימות (Prometheus, וכו')?
|
||
>>>>>>> 82edf2d (New md files from RunPod)
|
||
|
||
## הפניות
|
||
|
||
[מימוש מקורי של GraphRAG](trustgraph-flow/trustgraph/retrieval/graph_rag/graph_rag.py)
|
||
[עקרונות ארכיטקטורה של TrustGraph](architecture-principles.md)
|
||
[מפרט ניהול אוספים](collection-management.md)
|