Галоўная / Артыкулы

Графічныя сеткі End-to-End GraphRAG з FalkorDB, эмбедынгамі і маршрутаванням праз LangGraph

Гібрыдны працэйны цыкл GraphRAG на наборэ дадзэнай пра адзін фільм: запыткі графа FalkorDB, эмбеддынгі MiniLM для вектарнага пошуку і механізм роутавання LangGraph, які выбірае шлях для выкарыстоўвання.

Выдавец: REACTAPP.TOP12 хв чытання2737 слоў

Крыніцы: A Complete End-to-End Guide to Implementing a GraphRAG Workflow Using FalkorDB, Hugging Face, Groq, LangChain, and LangGraph

Большы мовныя мадэлі вмеюць адпаведаць плавна на базе дадзеных, отрэнаваных у час навчання, але сталквуюцца з труднасцямі, калі пытанне выклікае неабычную структуру: хто рэжысаваў той чы іншы фільм, якія актары бралі удзел у зйомках, якія зв’язкі спаяваюць два об’екты. Сістэмы GraphRAG усунуць гэтыя проблемы, спаўнюючы вектарны пошук у неструктураваным тэксце з явнымі запітамі да графаў, якія аналізуюць зв’язкі межу об’ектамі, а пасля дазваляючы большай мовной модэлі робіць выводы на адной з гэтых базаў.

У данай інструкцыі показана рабочая схема працы з дадзенымі па фільмах: FalkorDB як хранальнік графаў, эмбеддынгі Hugging Face MiniLM для семантычнага адшуку, мадэлі, размешчаныя на Groq, для направлення запітоў і выдачы адпаведзей, інструменты LangChain для апрацоўкі дадзеных, а таксама LangGraph для коордынаціі всіх процэсаў. Мета — не проста дэманстрацыя, а шаблон, які можна прыладзіць да будзь-якай сферы, дзе зв’язкі межу элементамі маюць такое ж значэнне, як і самы тэкст.

Чаму графы спаўнююць вектары

Вектарный пошук знаходзіць падобныя фрагменты. Запіты графа знаходзіць паведамленні па апрацоўваным шляху. Калі запытацца пра фільмы, рэжысераваныя Крыстоферам Ноланам, то графічныя зв’язкі будуць точнейшымі, чым простая адгадка на адной з найбліжэйшых варыянтаў. Калі запытацца пра «фільмы-грабежы з нечаканым развіццем, падобныя да „Інсепшн“», тады вектары пакажуць сваю эфектывнасць. Рэальныя асістэнты патрабуюць і таго, і другога, а таксама рутэра, які будзе выбіраць, які шлях адкрыць — чы ўсё жа гібрыдны.

MATCH (d:Director {name: 'Christopher Nolan'})-[:DIRECTED]->(m:Movie) RETURN m.title
MATCH (m:Movie)-[r]->(d:Director) RETURN m, r, d

Сераўіс і FalkorDB

Створыце вяртуальную срэду, установіце Jupyter, калі хочаце працаваць з нотабукамі для дакладнага аналізу, і закрэпіце версіі. Функцыях LangChain можа змінювацца; закрэпленне версій ёсць часткай можлівасці повторнае стварэння тых сябе рэзультатаў.

$ pip install jupyterlab
$ jupyter lab
# 1. SETUP WSL (Run in Windows PowerShell as Administrator)
wsl --install
# Restart your computer if prompted, then create your Linux username/password.
# Inside the WSL/Ubuntu terminal, update packages:
sudo apt update && sudo apt upgrade -y

# 2. SET ENVIRONMENT VARIABLES (Paste your keys from Groq & Hugging Face)
export GROQ_API_KEY="your_groq_api_key_here"
export HUGGINGFACEHUB_API_TOKEN="your_hf_token_here"

# 3. ENVIRONMENT & DEPENDENCY SETUP
# Install Python pip and virtual environment tools
sudo apt install python3-pip python3-venv -y

# Create and activate a project directory
mkdir movie-graphrag && cd movie-graphrag
python3 -m venv venv
source venv/bin/activate


# Verify everything is correctly configured
echo "Groq Key: $GROQ_API_KEY"
echo "HF Token: $HUGGINGFACEHUB_API_TOKEN"

Запускайце FalkorDB локальна за допамогай Docker, або выкарыстоўвайце кераваны экземпляр, які падходзіць вашай командзе. Локальны Docker дапамагае залічваць вартасці експерыментаў:

docker run -p 6379:6379 -p 3000:3000 -it --rm -v ./data:/data falkordb/falkordb:edge

Пасля адчысцення старых установак LangChain установіце самэ тыя бібліятэкі, проты якіх тэставалася гэта рабочая схема:

pip uninstall langchain langchain-core langchain-community langchain-groq \
              langchain-huggingface langgraph -y

pip install falkordb pandas pydantic sentence-transformers
pip install "langchain==0.3.26"
pip install "langchain-core==0.3.68"
pip install "langchain-community==0.3.23"
pip install "langchain-groq==0.2.5"
pip install "langchain-huggingface==0.1.2"
pip install "langgraph==0.3.34"

Завантажыце набор дадзеных у стылі IMDB

З’явіце CSV-файл з фільмамі у стыле Top-1000, а пасля завантажыце яго за дапамою імпортаў і налаштаванняя з’язоў, якіх чакае ваш нотбук:

import os, re, sys, logging
import pandas as pd
from typing import List, TypedDict, Literal
from falkordb import FalkorDB
from langchain_community.document_loaders.csv_loader import CSVLoader
from langchain_community.vectorstores.falkordb_vector import FalkorDBVector
from langchain_community.graphs import FalkorDBGraph
from langchain_community.chains.graph_qa.falkordb import FalkorDBQAChain
from langchain_core.documents import Document
from langchain_core.prompts import ChatPromptTemplate
from langchain.chains import create_retrieval_chain
from langchain.chains.combine_documents import create_stuff_documents_chain
from langchain.output_parsers import PydanticToolsParser
from pydantic import BaseModel, Field          # pydantic v2 — not langchain_core.pydantic_v1
from langchain_huggingface import HuggingFaceEmbeddings  # not langchain.embeddings (deprecated)
from langchain_groq import ChatGroq
from langgraph.graph import END, StateGraph

GROQ_API_KEY = "your_groq_api_key"
CSV_PATH     = "imdb_top_1000.csv"
os.environ["HF_TOKEN"] = "your_hf_token"

logging.basicConfig(stream=sys.stdout, level=logging.WARNING)

# Connect to FalkorDB and select the graph
db    = FalkorDB(host='localhost', port=6379)
graph = db.select_graph('imdb')

data = pd.read_csv(CSV_PATH)

Стварэнне графа знаёмасцей

Стварыце вузлы фільмаў з такімі атрыбутамі, як назва, годзіна выходу і сертыфікат. Стварыце вузлы рэжысёраў і актароў. З’яеднайце іх за дапамою адносаў з паказаным тыпам, каб запіты у стыле Cypher маглі адпавядаць на запытанні пра актарскі склад і каманду, не спакушаючыся тым, што эмбедынг запамятае аднос.

def clean_column(column_data):
    """Remove special characters that would break Cypher queries."""
    return column_data.apply(
        lambda x: re.sub(r"[^a-zA-Z0-9\s]", "", x) if pd.notnull(x) else 'NA'
    )

for col in ['Series_Title', 'Director', 'Certificate', 'Runtime', 'Genre',
            'Overview', 'Star1', 'Star2', 'Star3', 'Star4']:
    data[col] = clean_column(data[col])

data['IMDB_Rating']   = data['IMDB_Rating'].fillna('NA')
data['Released_Year'] = pd.to_numeric(data['Released_Year'], errors='coerce').fillna(-1)

star_columns = ['Star1', 'Star2', 'Star3', 'Star4']

for index, row in data.iterrows():
    movie_title   = row['Series_Title']
    director_name = row['Director']
    released_year = int(row['Released_Year']) if row['Released_Year'] != -1 else None

    if released_year is None:
        print(f"Skipping '{movie_title}' — invalid year.")
        continue

    movie_node = (
        f"MERGE (m:Movie {{"
        f"title: '{movie_title}', year: {released_year}, "
        f"certificate: '{row['Certificate']}', runtime: '{row['Runtime']}', "
        f"genre: '{row['Genre']}', imdb_rating: {row['IMDB_Rating']}, "
        f"description: '{row['Overview']}'"
        f"}})"
    )
    director_node     = f"MERGE (d:Director {{name: '{director_name}'}})"
    directed_relation = "MERGE (d)-[:DIRECTED]->(m)"

    star_nodes, starred_relations = "", ""
    for s, star_col in enumerate(star_columns, start=1):
        star_name = row[star_col]
        if star_name != 'NA':
            star_nodes        += f"MERGE (s{s}:Star {{name: '{star_name}'}})\n"
            starred_relations += f"MERGE (s{s})-[:STARRED_IN]->(m)\n"

    cypher_query = (
        f"{movie_node} {director_node} {directed_relation} "
        f"{star_nodes.strip()}\n{starred_relations.strip()}"
    )
    graph.query(cypher_query)

print("Knowledge Graph created.")

Эмбедынгі для сэмантычнага воспамінання

Ўжывайце all-MiniLM-L6-v2 праз сучасны пакет інтэграцыі Hugging Face замест абанованых шляхоў імпорта:

embedding_model = HuggingFaceEmbeddings(
    model_name="sentence-transformers/all-MiniLM-L6-v2"
)

result = graph.query(
    "MATCH (m:Movie) RETURN m.title AS title, m.description AS description"
)

documents = [
    Document(
        page_content=row[1] if row[1] else "",
        metadata={"title": row[0], "description": row[1]}
    )
    for row in result.result_set if row[1]
]

vector_index = FalkorDBVector(host='localhost', port=6379, embedding=embedding_model)
vector_index.add_documents(documents)
print("Vector index created.")

Зберагаюце вектары разам з графам або са ўзаемным супакоўненням, каб гібрыдныя вузлы маглі выкарыстоўваць назвы фільмаў з сэмантычнага пошуку, а пасля розгледваць іх за дапамою адносаў.

GraphState для LangGraph

З’явіце TypedDict (או эквівалент) GraphState, які мае ў сабе запіт корыстніка, рашэнне па маршруту, падзапыты, рэзультаты вектарнага пошуку, адпаведзі графа і заканчоўны адказ. LangGraph зберагае гэты стан між вузламі, тады наступныя запыты можаўць вікорыстоўваць кантэкст замест таго, каб пачынаць з порожніго запіту.

class GraphState(TypedDict):
    """
    Tracks state across the entire LangGraph workflow.
    Every node reads from and writes to this dictionary.
    """
    question:            str
    documents:           object
    movie_titles:        List[str]
    subqueries:          object
    prompt_with_context: object

Маршрутуванне запытаваў

Не кожны запіт павінен праходзіць праз кожную падсістэму. Маршрутызатор класіфікуе, чы розпачынаецца запіт прыватна з аспекта взаімаўзаемасці, прыватна з аспекта сэмантыкі, ёсць гібрыдны чы не належыць да сферы дзеяння. Гэта зменшае затрымкі і кантрадыкцыйны кантэкст.

llm = ChatGroq(groq_api_key=GROQ_API_KEY, model_name="llama-3.3-70b-versatile")

class RouteQuery(BaseModel):
    """Route a user query to the most relevant datasource."""
    datasource: Literal["vector search", "graph query"] = Field(
        ...,
        description="Route to 'vector search' for similarity queries, "
                    "'graph query' for relationship/fact queries.",
    )

structured_llm_router = llm.with_structured_output(RouteQuery)

route_system_prompt = """You are an expert at routing user questions.

Route to VECTOR SEARCH for questions involving:
- Similarity (similar, related, like, closest, relevant)
- Themes or topics (movies about space, about love, etc.)
- "Find movies similar to X"

Route to GRAPH QUERY for questions involving:
- Specific facts (who directed X, what year was X released)
- Specific people (movies by Christopher Nolan)
- Filtering by property (movies from 1990, Action movies)
"""

route_prompt    = ChatPromptTemplate.from_messages([
    ("system", route_system_prompt),
    ("human",  "{question}"),
])
question_router = route_prompt | structured_llm_router

def route_question(state: GraphState):
    source = question_router.invoke({"question": state["question"]})
    if source.datasource == "vector search":
        return "decomposer"
    else:
        return "graph_qa"

Разбівач

Калі працюе гібрыдны маршрут, разбівач разбівае запіт корыстніка на падзапыты, якія можаюць быць выкарыстаны вектарнымі і графамі вузламі:

class SubQuery(BaseModel):
    """A single sub-query decomposed from the original question."""
    sub_query: str = Field(
        ..., description="A unique paraphrasing of the original question.",
    )

decompose_system = """You are an expert at breaking down complex questions.
Given a user question, split it into exactly two sub-queries:
  1. A query for vector similarity search (find related/similar items)
  2. A query for a graph database (find specific relationships or facts)

Example:
Question: Find movies directed by Christopher Nolan related to dreams.
sub_query 1: Find movies related to dreams or the subconscious.
sub_query 2: Find movies directed by Christopher Nolan.
"""

decompose_prompt = ChatPromptTemplate.from_messages([
    ("system", decompose_system),
    ("human",  "{question}"),
])

llm_with_tools = llm.bind_tools([SubQuery])
parser         = PydanticToolsParser(tools=[SubQuery])
query_analyzer = decompose_prompt | llm_with_tools | parser

def decomposer(state: GraphState):
    question   = state["question"]
    subqueries = query_analyzer.invoke({"question": question})
    return {"subqueries": subqueries, "question": question}

Вузел вектарнага пошуку

Рэалізуйце пошук на адпоўненасць за дапамогою сучасных інструментаў адзыскві LangChain:

def vector_search(state: GraphState):
    question  = state["question"]
    retriever = vector_index.as_retriever(search_kwargs={"k": 3})

    qa_prompt = ChatPromptTemplate.from_messages([
        ("system",
         "You are a movie expert. Use the context below to answer the question.\n\n"
         "Context:\n{context}"),
        ("human", "{input}"),
    ])

    combine_docs_chain = create_stuff_documents_chain(llm, qa_prompt)
    retrieval_chain    = create_retrieval_chain(retriever, combine_docs_chain)

    result      = retrieval_chain.invoke({"input": question})
    source_docs = result.get("context", [])

    extracted_data, movie_titles = [], []
    for doc in source_docs:
        title       = doc.metadata.get("title", "Unknown")
        description = doc.metadata.get("description", "")
        extracted_data.append({"title": title, "description": description})
        movie_titles.append(title)

    return {
        "movie_titles": movie_titles,
        "documents":    extracted_data,
        "question":     question,
        "subqueries":   state["subqueries"],
    }

Вузел адпаведзенняў на запыты графа

Перакладзіце рэляцыянныя падпытанні на запіты да графа. Для запіту у стыле фільмаграфіі рэжысёра граф павінен вярнуць назвы фільмаў, з’ёднаныя рэшткамі «рэжысер:», а не пераказанаю, спраўненую наоснове прыпускаў адказ:

def graph_qa(state: GraphState):
    question      = state["question"]
    graph_wrapper = FalkorDBGraph(database="imdb", host="localhost", port=6379)

    chain  = FalkorDBQAChain.from_llm(
        llm,
        graph=graph_wrapper,
        verbose=True,
        allow_dangerous_requests=True,
    )
    result = chain.invoke({"query": question})
    answer = result.get("result", str(result))

    return {"documents": answer, "question": question}

Запіты да графа з вектарным кантэкстам

Гібрыдны вузол берае назвы, выявлены сэмантычна, і расшырвае іх за дапамогою структуры графа — актары, каст, супадзяючыя фільмы — пры тым, калі LLM піша заканчоўны адказ:

def graph_qa_with_context(state: GraphState):
    titles      = state["movie_titles"]
    question    = state["question"]
    input_query = (
        f"Return the title, genre, director, and IMDB rating of movies "
        f"with these titles: {str(titles)}"
    )

    graph_wrapper = FalkorDBGraph(database="imdb", host="localhost", port=6379)
    chain  = FalkorDBQAChain.from_llm(
        llm,
        graph=graph_wrapper,
        verbose=True,
        allow_dangerous_requests=True,
        return_intermediate_steps=True,
    )
    result  = chain.invoke({"query": input_query})
    context = ""
    steps   = result.get("intermediate_steps", [])
    if len(steps) > 1:
        context = str(steps[1].get("context", ""))

    return {
        "documents": context,
        "subqueries": state["subqueries"],
        "question":   question,
    }

Вузел адказу

Нормалізаваце заканчоўны адказ на нейтральнай мове, пры можлівасці дадзіце інфармацыю пра магчымыя крыніцы і вычыстыце проміжныя поля, якія не хочаце адкрыць кліенту:

def respond(state: GraphState):
    question = state["question"]
    context  = state["documents"]

    respond_prompt = ChatPromptTemplate.from_messages([
        ("system",
         "You are a helpful movie expert assistant.\n"
         "Use the context below to answer the user's question clearly and concisely.\n\n"
         "Context:\n{context}"),
        ("human", "{question}"),
    ])

    answer = (respond_prompt | llm).invoke({"context": context, "question": question})
    return {"documents": answer.content}

Складанне рабочага прайсепту LangGraph

З’яўьце звязкі межау рутэром, дэкампазерам, вектарным, графічным, гібрыдным і вузлам адказу за дапамогою умовных рэштак, якія адпавядаюць пазначэнням маршрута:

workflow = StateGraph(GraphState)

# Register all nodes
workflow.add_node("graph_qa",              graph_qa)
workflow.add_node("decomposer",            decomposer)
workflow.add_node("vector_search",         vector_search)
workflow.add_node("graph_qa_with_context", graph_qa_with_context)
workflow.add_node("respond",               respond)

# Conditional entry point — route_question decides the first node
workflow.set_conditional_entry_point(
    route_question,
    {
        "decomposer": "decomposer",
        "graph_qa":   "graph_qa",
    },
)

# Hybrid path
workflow.add_edge("decomposer",            "vector_search")
workflow.add_edge("vector_search",         "graph_qa_with_context")
workflow.add_edge("graph_qa_with_context", "respond")
workflow.add_edge("respond",               END)

# Direct graph path
workflow.add_edge("graph_qa", END)

app = workflow.compile()

Візуалізаваце граф для документацыі і нараджэння спецяўстаў:

from IPython.display import Image, display

try:
    display(Image(app.get_graph().draw_mermaid_png()))
except Exception:
    pass

Тэст і інтэрактыўная консоль

Адкрыйце запытанні, напісаныя у вачорку, якія абрабатваюць рэляцыйныя, сэмантычныя і гібрыдныя кейсы:

# Graph query — specific fact question
result1 = app.invoke({"question": "Which movies did Christopher Nolan direct?"})
print(result1["documents"])

Потым выкорыстаце простую петлю консолі для ручнага аналізу:

print("\n" + "=" * 60)
print("INTERACTIVE MODE — type a question (or 'quit' to exit)")
print("=" * 60)

while True:
    user_question = input("\nYour question: ").strip()
    if user_question.lower() in ("quit", "exit", "q"):
        print("Goodbye!")
        break
    if not user_question:
        continue
    try:
        result = app.invoke({"question": user_question})
        print("\nANSWER:", result["documents"])
    except Exception as e:
        print(f"Error: {e}")

Рэзультаты, якія трэба пераканаліць

Пераканаліцеся, што запытанні пра рэжысероў у стыле Нолана вяртаюць тытулы, аднесеныя да графа, што запытанні пра атмасферу вяртаюць фільмы, сэмантычна значамыя, і што гібрыдныя запытанні адналежаць кожнаму з двух тыпоў доказаў. Калі адпаведзі змінююцца, перагляньце даныя стану между вузламі, а не толькі фінальны тэкст.

Падсумак і прадукты адаптацыі

GraphRAG — гэта як проблема маршрутавання, так і проблема зберагчэння дадзейна. FalkorDB (або будзь-якая база дадзейна графа з нізкім часам адпаведзьбы) дае структуру, сформульваную за дапамою тыпаў. Эмбеддынгі спакойваюць працэс адзыскання дадзейна, зменшываючы колькасць непатрэбных рэзультатаў. LangGraph робіць прабег кантролю достатна ясным для тэставання. Заменіце фільмы на элементы вашай сферы — часткі і прадавцы, паціенты і протаколы, службы і залежнасці — заўсёды падтрымляючы тыя ж ролі вузлаў: маршрутаванне, адзысканне, пашырэнне, адпаведзь.

Зафіксавайце версіі, зберагчыце набор «золатых» запытанняў для кожнага тыпу маршруту, і ліставайце пераходы стану. Гэтая аперацыйная дысціпліна мае большое значэнне, чым тое, з якаго набора дадзейна вы запускалі працу.

Прыметкі да дызайну, якія вытрымаюць контакт з рэальнай сістэмай

Распакаванне аб’екта будзе глыбокаю часткай рэальных развёрткаў. Назвы фільмаў ў стацыярным стане; назвы компаній і SKU-коды продуктав не ў такім. Планаваце крок нормалізацыі пры стварэнні вузлаў і трывожце карточку супарабоцтва між неапранутымі стрэнгамі і канонічнымі ID-амі. Без гэтага запыты да графа стануць фрагментаванымі, а рэзультаты вектарнага пошуку будуць аднасоўвацца да дуплікатаў.

Эвалюяцыя схемы выклікае патрэбу ў міграціях, так сама як і SQL. Дадаванне новага тыпу адносаў лёгкае ў нотбукі, але складнае, калі дашборды залежаць ад старых рэлясіяў. Версіюйце схему графа і храніце яе разам з кодам рабочага прайсупутніку.

Бюджэты на затрымкі павінны быць чытка адзначаныя. Выклікі LLM-роутэра дадаюць час прычыну, яшчэ перш чым пачынаецца сам пошук. Для сістэм з высокай кантэнцыяю запытаў (QPS) рассмотрзіце можлівасць вікорыстоўвання правілаў аб малых класыфікатораў для явных патэранаў адносаў (“хто рэжысаваў X”) і застосоўвання роутэраў на базе LLM толькі для неодназначных тэкстаў.

Ацэнка павінна выканаць раздзельную оцэнку точнасці маршрута і якосці адказу. Ідэальны адказ на неправільны маршрут усё равно спалюе бюджэт і не будзе працаваць, калі аднай з падсістэм будзе проблема. Абавяць сітуацыю можна, выключыўшы граф або вектарны індэкс і пераканаўшыся, што робочы працэс справядліва паслабляецца.

Безпека мае важнае значэнне: генераванне запытоў да графа павінна быць обмежана. Лепш выкарыстоўваць параметрызаваныя шаблоны, чым вільнай формы Cypher з модэлю. Тут праўіла, якія застосоўваюцца да інструментаў SQL, таксама дапамагаюць.

Контроль віток: кэшаваць эмбеддынгі стабільных дакументаў, кэшаваць рашэння маршрутазатора для ідэнтычных запытоў пры короткім таймах жыцця кэша, і утрымвацца ад гібрыднага расшырэння, калі чысты граф ужо вяртае ентыты з высокай доверлівасцю.

Працюючы над адказамі на запітанні пад час інтэрв’ю, спачатку накрэсліце об’ект стану, а пасля — ўзьязцы меж. Інтэрв’юеры шырэй розумеюць логіку кантролю, чым назвы пакетаў. Упамяніце FalkorDB за шырочыню працы з графамі атрыбутаў, MiniLM для локальных эмбеддынгаў, а Groq — толькі як аднаго з можлівых хостаў LLM; ваша архітэктура не павінна быць прычаленая да аднаго-едзінага прадавца сервісаў інферэнсу.

У канцы, неабяжна задокументаваць паведамленні пра абэранцыях, якія пакажуцься корыстнікам, калі граф акульна або рутэр не функцыонуе. Самэтакія „безгучныя галюцынацыі“ ўскладнююць адкрыцце дэманав GraphRAG і прыводзяць да падзею довер'я пасля першай недзелі. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце фіксаты на рэвэльні зеленымі прытаманна расшырэнню схемы графа новымі тыпамі энтыцэй. Зберагаюце рут…

Зберагачы на рэвеўні e-уровня павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту павінны заставацца зеленымі пры расшырэнні схемы графа да новых тыпаў энтытаў. Зберагачы на рэвеўні маршруту fiХраніце колеру «зелены» для фіксатываў на рэве-рэвені, прычаму расшыраеце схему графа да новых тыпаў энтытаў.Перакладзіце гэты англійскій тэкст на беларускую мову прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў. Зберагачыце фіксатары на рэве зеленымі прыяўшчэнне шыматы графа ў новыя тыпы аб’ектаў.