ナレッジグラフを使用したGraph RAGの実装

グラフ検索拡張生成(Graph RAG)は、特定の分野に固有の知識と関連情報を使用する生成AIアプリケーションのための強力な手法として台頭しつつあります。Graph RAGはベクトル・データベースを使用するベクトル検索手法の代替手段です。

ナレッジグラフは、Neo4j や Amazon Neptune などのグラフデータベースで構造化データを表現できる知識システムです。ナレッジグラフでは、エッジと呼ばれるデータ・ポイント間の関係が、頂点またはノードと呼ばれるデータ・ポイント間の接続と同様に意味を持ちます。ナレッジグラフを使用すると、ネットワークを横断し、接続されたデータに関する複雑なクエリを処理することが容易になります。ナレッジグラフは、チャットボット、IDソリューション、ネットワーク分析、推奨エンジン、Customer 360、不正アクセス検知などのユースケースに特に適しています。

Graph RAGのアプローチでは、グラフ・データベースの構造的な性質を活用して、ネットワークや複雑な関係に関する取得情報により深いコンテキストを与えます。グラフデータベースを大規模言語モデル(LLM)と組み合わせると、開発者はテキストなどの非構造化データからグラフを作成するプロセスの大部分を自動化できます。LLMはテキストデータを処理してエンティティーを識別し、それらの関係を理解してグラフ構造で表現することができます。

Graph RAGのアプリケーションを作成する方法はいくつもあります。たとえば、Microsoft の GraphRAG や、GPT4 とLlamaIndexを組み合わせる方法などがあります。このチュートリアルでは、オープンソースのグラフ・データベース・ソリューションであるMemGraphを使用し、watsonx上でMetaのLlama-3を使用してRAGシステムを作成します。Memgraphは、宣言型クエリ言語であるCypherを使用しています。SQLといくつかの共有点がありますが、テーブルや行ではなく、ノードと関係に重点を置いています。Llama 3を使用すると、データベース内の非構造化テキストとクエリ情報からグラフ・データベースを作成して入力することができます。

ステップ1

いくつかあるツールの中から選択することもできますが、このチュートリアルでは、Jupyter Notebookを使用してIBMアカウントを設定する方法について説明します。

IBM Cloudアカウントを使用して、watsonx.aiにログインします。

watsonx.aiプロジェクトを作成します。

プロジェクトIDをプロジェクト内から取得します。「管理」タブをクリックし、「全般」ページの「詳細」セクションからプロジェクトIDをコピーします。このチュートリアルには、このプロジェクトIDが必要です。

次に、プロジェクトをwatsonx.ai Runtimeに関連付けます。

a. watsonx.ai Runtime サービス・インスタンスを作成します(無料インスタンスであるLiteプランを選択します)。

b. watsonx.ai RuntimeでAPIキーを生成します。チュートリアルで使用できるように、API キーを保存します。

c. プロジェクトに移動し、「管理」タブを選択します。

d. 左側のタブで、「サービスと統合」を選択します。

e. IBM サービスを選択します。

f. 「アソシエイト・サービス」を選択し、watsonx.ai Runtimeを選択します。

g. watsonx.ai で作成したプロジェクトにwatsonx.ai Runtimeサービスを関連付けます。

ステップ 2

ここで Dockerをインストールします。

Dockerをインストールしたら、Dockerコンテナを使用してMemgraphをインストールします。OSXまたはLinuxでは、ターミナルで次のコマンドを使用できます。

curl https://install.memgraph.com | sh

Windowsコンピューターでは次のコマンドを使用します。

iwr https://windows.memgraph.com | iex

インストール手順に従って、MemGraphエンジンとMemGraphラボを起動して実行します。

コンピューター上で、このプロジェクトのために新たな仮想環境を作成します。

virtualenv kg_rag --python=python3.12

ノートブックのPython環境に、以下のPythonライブラリーをインストールします。

./kg_rag/bin/pip install langchain langchain-openai langchain_experimental langchain-community==0.3.15 neo4j langchain_ibm jupyterlab json-repair getpass4

これで、Memgraphに接続する準備が整いました。

ステップ 3

Memgraphでユーザー名とパスワードを使用する構成にした場合は、ここで設定します。それ以外の場合は、ユーザー名もパスワードもないデフォルトを使用できます。実稼働データベースの場合は不適切なやり方ですが、機密データを保管していないローカル開発環境の場合は問題になりません。

import os
from langchain_community.chains.graph_qa.memgraph import MemgraphQAChain
from langchain_community.graphs import MemgraphGraph

url = os.environ.get("MEMGRAPH_URI", "bolt://localhost:7687")
username = os.environ.get("MEMGRAPH_USERNAME", "")
password = os.environ.get("MEMGRAPH_PASSWORD", "")

#initialize memgraph connection
graph = MemgraphGraph(
    url=url, username=username, password=password, refresh_schema=True
)

次に、LLMシステムのグラフ生成機能をテストするために使用できる関係のデータセットを記述するサンプル文字列を作成します。より複雑なデータソースを使用することもできますが、この単純な例はアルゴリズムを示すのに役立ちます。

graph_text = “””
John’s title is Director of the Digital Marketing Group.
John works with Jane whose title is Chief Marketing Officer.
Jane works in the Executive Group.
Jane works with Sharon whose title is the Director of Client Outreach.
Sharon works in the Sales Group.
“””

最初の手順で作成した watsonx APIキーを入力します。

from getpass import getpass

watsonx_api_key = getpass()
os.environ[“WATSONX_APIKEY”] = watsonx_api_key
watsonx_project_id = getpass()
os.environ[“WATSONX_PROJECT_ID”] = watsonx_project_id

次に、テキストを生成するようにWatsonxLLMインスタンスを構成します。ハルシネーションで存在しないエンティティや関係を作り出すことなく、モデルが可能な限り詳細な情報を生成するように設定するには、温度を比較的低くして、トークン数を大きくする必要があります。

from langchain_ibm import WatsonxLLM
from ibm_watsonx_ai.metanames import GenTextParamsMetaNames

graph_gen_parameters = {   
    GenTextParamsMetaNames.DECODING_METHOD: “sample”,
    GenTextParamsMetaNames.MAX_NEW_TOKENS: 1000,
    GenTextParamsMetaNames.MIN_NEW_TOKENS: 1,
    GenTextParamsMetaNames.TEMPERATURE: 0.3,
    GenTextParamsMetaNames.TOP_K: 10,
    GenTextParamsMetaNames.TOP_P: 0.8
}
watsonx_llm = WatsonxLLM(
    model_id=”meta-llama/llama-3-3-70b-instruct”,
    url=”https://us-south.ml.cloud.ibm.com”,
    project_id=os.getenv(“WATSONX_PROJECT_ID”),
    params=graph_gen_parameters,
)

LLMGraphTransformerを使用すると、LLMで生成するノードと関係の種類を設定できます。今回のテキストは会社の従業員と彼らの属するグループ、およびその役職について記述したものです。LLMをこのエンティティーのみに制限すると、知識をグラフで適切に表現できる可能性が高まります。

Convert_to_graph_documents の呼び出しで、LLMGraphTransformerがテキストからナレッジグラフを作成します。このステップでは、情報をグラフ・データベースに挿入し、関連するコンテキストと関連するエンティティーを表すための、正しいNeo4j構文を生成します。

from langchain_experimental.graph_transformers.llm import LLMGraphTransformer
from langchain_core.documents import Document

llm_transformer = LLMGraphTransformer(
    llm=watsonx_llm,
    allowed_nodes=[“Person”, “Title”, “Group”],
    allowed_relationships=[“TITLE”, “COLLABORATES”, “GROUP”]
)
documents = [Document(page_content=graph_text)]
graph_documents = llm_transformer.convert_to_graph_documents(documents)

次に、Memgraphデータベースから古いデータを削除し、新しいノードとエッジを挿入します。

# make sure the database is empty
graph.query(“STORAGE MODE IN_MEMORY_ANALYTICAL”)
graph.query(“DROP GRAPH”)
graph.query(“STORAGE MODE IN_MEMORY_TRANSACTIONAL”)

# create knowledge graph
graph.add_graph_documents(graph_documents)

生成されたCypher 構文は、graph_documentsオブジェクトに保管されます。文字列として表示させれば検証することができます。

print(f”{graph_documents}”)

暗号化によって作成されたスキーマとデータ・タイプは、グラフの「get_schema」プロパティーで確認できます。

graph.refresh_schema()
print(graph.get_schema)

表示内容は以下の通りです。

Node labels and properties (name and type) are:
- labels: (:Title)
properties:
- id: string
- labels: (:Group)
properties:
- id: string
- labels: (:Person)
properties:
- id: string

Nodes are connected with the following relationships:
(:Person)-[:COLLABORATES]->(:Person)
(:Person)-[:GROUP]->(:Group)
(:Person)-[:TITLE]->(:Title)

Memgraphラボ・ビューアーでグラフ構造を確認することもできます。

 

ノードとエッジを示すグラフ・ネットワークの画像 インプットテキストから生成されたMemgraphネットワーク

LLMが、正しいノードと関係を作成するという合理的な作業を実行しました。ここでナレッジグラフをクエリします。

ステップ 4

LLMへの適切なプロンプトには、ある程度のプロンプト・エンジニアリングが必要です。LangChainが提供するFewShotPromptTemplateを使用すれば、正確で簡潔なCypher構文を確実に記述できるよう、プロンプト内でLLMに例を与えることができます。次のコードには、LLMが使用すべき質問とクエリの例がいくつか含まれています。さらに、モデルのアウトプットをクエリのみに制約するよう示しています。饒舌すぎるLLMは、無効なCypherクエリにつながる追加情報を追加する可能性があるため、プロンプトテンプレートはモデルにクエリ自体のみを出力するように指示します。

また、指示的なプレフィックスはモデルの動作を制約することができ、LLMが正しいCypher構文をアウトプットする可能性を高めます。

from langchain_core.prompts import PromptTemplate, FewShotPromptTemplate

examples = [
{
“question”: “<|begin_of_text|>What group is Charles in?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (p:Person {{id: ‘Charles’}})-[:GROUP]->(g:Group) RETURN g.id<|eot_id|>“,
},
{
“question”: “<|begin_of_text|>Who does Paul work with?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (a:Person {{id: ‘Paul’}})-[:COLLABORATES]->(p:Person) RETURN p.id<|eot_id|>“,
},
{
“question”: “What title does Rico have?<|eot_id|>“,
“query”: “<|begin_of_text|>MATCH (p:Person {{id: ‘Rico’}})-[:TITLE]->(t:Title) RETURN t.id<|eot_id|>“,
}
]

example_prompt = PromptTemplate.from_template(
“<|begin_of_text|>{query}<|eot_id|>“
)

prefix = “””
Instructions:
- Respond with ONE and ONLY ONE query.
- Use provided node and relationship labels and property names from the
schema which describes the database’s structure. Upon receiving a user
question, synthesize the schema to craft a precise Cypher query that
directly corresponds to the user’s intent.
- Generate valid executable Cypher queries on top of Memgraph database.
Any explanation, context, or additional information that is not a part
of the Cypher query syntax should be omitted entirely.
- Use Memgraph MAGE procedures instead of Neo4j APOC procedures.
- Do not include any explanations or apologies in your responses. Only answer the question asked.
- Do not include additional questions. Only the original user question.
- Do not include any text except the generated Cypher statement.
- For queries that ask for information or functionalities outside the direct
generation of Cypher queries, use the Cypher query format to communicate
limitations or capabilities. For example: RETURN “I am designed to generate Cypher queries based on the provided schema only.”

Here is the schema information

{schema}

With all the above information and instructions, generate Cypher query for the
user question.

The question is:

{question}

Below are a number of examples of questions and their corresponding Cypher queries.”””

cypher_prompt = FewShotPromptTemplate(
    examples=examples,
    example_prompt=example_prompt,
    prefix=prefix,
    suffix=”User input: {question}\nCypher query: “,
    input_variables=[“question”, “schema”],
)

次に、Memgraphから返された情報を使用してLLMが質問に答える方法を制御するためのプロンプトを作成します。LLMがグラフ・データベースからコンテキスト情報を受け取った後の応答方法について、いくつかの例と手順を示します。

 

qa_examples = [
    {
        “question”: “<|begin_of_text|>What group is Charles in?<|eot_id|>“,
        “context”: “[{{‘g.id’: ‘Executive Group’}}]”,
        “response”: “Charles is in the Executive Group<|eot_id|>“
    },
    {
        “question”: “<|begin_of_text|>Who does Paul work with?<|eot_id|>“,
        “context”: “[{{‘p.id’: ‘Greg’}}, {{‘p2.id’: ‘Norma’}}]”,
        “response”: “Paul works with Greg and Norma<|eot_id|>“
    },
    {
        “question”: “What title does Rico have?<|eot_id|>“,
        “context”: “[{{‘t.id’: ‘Vice President of Sales’}}]”,
        “response”: “Vice President of Sales<|eot_id|>“
    }
]

qa_template = “””
Use the provided question and context to create an answer.Question: {question}

Context: {context}
Use only names departments or titles contained within {question} and {context}.
“””
qa_example_prompt = PromptTemplate.from_template(“”)

qa_prompt = FewShotPromptTemplate(
    examples=qa_examples,
    prefix=qa_template,
    input_variables=[“question”, “context”],
    example_prompt=qa_example_prompt,
    suffix=” “
)

次に、質問応答チェーンを作成します。MemgraphQAChainを使用すると、使用するLLM、使用するグラフスキーマ、およびデバッグに関する情報を設定できます。温度を0にし、長さに関するペナルティを設定することで、LLMはCypherプロンプトを短く簡単に保つよう促します。

query_gen_parameters = {
    GenTextParamsMetaNames.DECODING_METHOD: “sample”,
    GenTextParamsMetaNames.MAX_NEW_TOKENS: 100,
    GenTextParamsMetaNames.MIN_NEW_TOKENS: 1,
    GenTextParamsMetaNames.TEMPERATURE: 0.0,
    GenTextParamsMetaNames.TOP_K: 1,
    GenTextParamsMetaNames.TOP_P: 0.9,
    GenTextParamsMetaNames.LENGTH_PENALTY: {‘decay_factor’: 1.2, ‘start_index’: 20}
}

chain = MemgraphQAChain.from_llm(
        llm = WatsonxLLM(
        model_id=”meta-llama/llama-3-3-70b-instruct”,
        url=”https://us-south.ml.cloud.ibm.com”,
        project_id=”dfe8787b-1f6f-4e18-b36a-e22c00f141d1”,
        params=query_gen_parameters
    ),
    graph = graph,
    allow_dangerous_requests = True,
    verbose = True,
    return_intermediate_steps = True, # for debugging
    cypher_prompt=cypher_prompt,
    qa_prompt=qa_prompt
)

これで、自然言語による質問でチェーンを呼び出すことができます(LLMは完全に決定論的ではないため、応答が多少異なる可能性があることに注意してください)。

chain.invoke(“What is Johns title?”)

アウトプットは以下のようになります。

> Entering new MemgraphQAChain chain...
Generated Cypher:
 MATCH (p:Person {id: 'John'})-[:TITLE]->(t:Title) RETURN t.id
Full Context:
[{'t.id': 'Director of the Digital Marketing Group'}]

> Finished chain.
{'query': 'What is Johns title?',
 'result': ' \nAnswer: Director of the Digital Marketing Group.',
 'intermediate_steps': [{'query': " MATCH (p:Person {id: 'John'})-[:TITLE]->(t:Title) RETURN t.id"},
  {'context': [{'t.id': 'Director of the Digital Marketing Group'}]}]}

次の質問では、チェーンにもう少し複雑な質問をします。

chain.invoke(“Who does John collaborate with?”)

以下の内容が返されます。

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘John’})-[:COLLABORATES]->(c:Person) RETURN c
Full Context:
[{‘c’: {‘id’: ‘Jane’}}]

> Finished chain.
{‘query’: ‘Who does John collaborate with?’,
‘result’: ‘ \nAnswer: John collaborates with Jane.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘John’})-[:COLLABORATES]->(c:Person) RETURN c”},
{‘context’: [{‘c’: {‘id’: ‘Jane’}}]}]}

正しい答えが応答に含まれています。場合によっては、エンドユーザーに回答を返す前に削除が必要な、余分なテキストが含まれる可能性があります。

Memgraphチェーンにグループ関係について質問することができます。

chain.invoke(“What group is Jane in?”)

これにより、次のものが返されます。

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘Jane’})-[:GROUP]->(g:Group) RETURN g.id
Full Context:
[{‘g.id’: ‘Executive Group’}]

> Finished chain.
{‘query’: ‘What group is Jane in?’,
‘result’: ‘Jane is in Executive Group.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘Jane’})-[:GROUP]->(g:Group) RETURN g.id”},
{‘context’: [{‘g.id’: ‘Executive Group’}]}]}

これが正しい答えです。

最後に、チェーンにアウトプットが2つある質問をします。

chain.invoke(“Who does Jane collaborate with?”)

これにより、次の情報が出力されます。

> Entering new MemgraphQAChain chain...
Generated Cypher:
MATCH (p:Person {id: ‘Jane’})-[:COLLABORATES]->(c:Person) RETURN c
Full Context:
[{‘c’: {‘id’: ‘Sharon’}}]

> Finished chain.
{‘query’: ‘Who does Jane collaborate with?’,
‘result’: ‘ Jane collaborates with Sharon.’,
‘intermediate_steps’: [{‘query’: “ MATCH (p:Person {id: ‘Jane’})-[:COLLABORATES]->(c:Person) RETURN c”},
{‘context’: [{‘c’: {‘id’: ‘Sharon’}}]}]}

チェーンは両方の協力者を正しく識別します。

まとめ

このチュートリアルでは、Memgraphとwatsonxを使用してGraph RAGアプリケーションを構築し、グラフデータ構造を生成し、クエリを実行しました。watsonxを介したLLMを使用して、自然言語のソース・テキストと生成された暗号クエリー構文からノードとエッジ情報を抽出し、グラフ・データベースに入力しました。次に、watsonxを使用して、そのソース・テキストに関する自然言語の質問をCypherクエリーに変換し、グラフ・データベースから情報を抽出しました。LLMは、プロンプト・エンジニアリングを使用して、Memgraphデータベースの成果を自然言語による応答に変換しました。

関連ソリューション
IBM watsonx.ai

AI開発者向けの次世代エンタープライズ・スタジオであるIBM watsonx.aiを使用して、生成AI、基盤モデル、機械学習機能をトレーニング、検証、チューニング、導入しましょう。わずかなデータとわずかな時間でAIアプリケーションを構築できます。

watsonx.aiの詳細はこちら
人工知能ソリューション

IBMの業界をリードするAIの専門知識とソリューションのポートフォリオを活用して、AIをビジネスの業務に利用しましょう。

AIソリューションの詳細はこちら
人工知能(AI)コンサルティングおよびサービス

IBMコンサルティングAIサービスは、企業がAIをトランスフォーメーションに活用する方法を再考するのに役立ちます。

AIサービスの詳細はこちら
次のステップ

AIを使用することで、IBM Concertはお客様のオペレーションに関する重要な洞察を明らかにし、改善のためのアプリケーション固有の推奨事項を提供します。Concertがどのようにビジネスを前進させることができるかをご覧ください。

Concertの詳細はこちら ビジネス・プロセス自動化ソリューションの詳細はこちら