どのようにNeo4jのデータを作成するといいのか、どのように検索するといいのかを検証しました
プログラムなどはこちらに公開しています
検証で使用するデータ
-
LLM: ChatGPTのGPT-4o
-
Neo4jに入れる対象データ
- WikiPediaのキアヌリーブスのページの文章を抜粋
- https://ja.wikipedia.org/wiki/キアヌ・リーブス
-
質問
- キアヌ・リーブスが国際的スターとなるきっかけとなった映画は何ですか?
- ハートブルーという映画について教えてください
抜粋した内容
俳優
レバノンで生まれ、カナダのトロントで育った。9歳のとき、演劇『くたばれ!ヤンキース』で舞台出演。本人は少年時代を振り返り「僕はサニー・チバ(千葉真一)の映画とともに育ったようなものなんだ」と語っている[3]。この頃の影響がアクション映画への思い入れとなっていく[3]。
1989年に主演を務めたコメディ映画『ビルとテッドの大冒険』がヒット、続編も制作され、キアヌは「テッド役の 20代の俳優」として世間に認知されるようになる。1991年にはパトリック・スウェイジとの共演で『ハートブルー』、そして親友のリヴァー・フェニックスと共に『マイ・プライベート・アイダホ』に出演した。
1994年の映画『スピード』の大ヒットにより国際的スターとなった。続く『スピード2』にも出演オファーがあったが 『ディアボロス/悪魔の扉』と『死にたいほどの夜』に専念するために断っている。
1999年の映画『マトリックス』が世界的に大ヒットし再ブレイク、3部作に主演し、人気を不動のものとした。
2014年に、米中合作映画『ファイティング・タイガー』でメインキャストとして出演するとともに監督デビューした[4]。また、同年には伝説の殺し屋役に扮したアクション作『ジョン・ウィック』が公開。大ヒットを記録してシリーズ化された(2023年現在第4作までが公開。スピンオフ作品製作決定)。
2020年には、出世作『ビルとテッド』シリーズの約30年ぶりとなる最新作、『ビルとテッドの時空旅行 音楽で世界を救え!』が公開された。
2021年12月には、代表作の一つである『マトリックス』シリーズの続編『マトリックス レザレクションズ』が公開、約18年ぶりに主役のトーマス・A・アンダーソン / ネオを演じた。同年、コミック『BRZRKR』出版を予定しており、これに基づき主演した映画をNetflixで配信予定である[5]。さらにこれに基づくアニメシリーズでは声優を務める予定である。
音楽
1991年に結成されたグランジ・ロック・バンドの「ドッグスター」ではベースを担当し、好んでフェンダー製のプレシジョン・ベースを弾いている。
商業的には決して大きなものはなく、同バンドはアルバム2枚を発表したが、2002年10月の来日公演を最後に活動休止した。その公演は岡山県倉敷市で行われ、1万人以上の観客を集めた。その後、キアヌは「ベッキー」という別のバンドでもベースを弾いていたが、2006年には音楽活動を取りやめたことが報じられた[6]。
2022年にドッグスターは活動を再開し、2023年5月のアメリカでのライブを封切りに、9月には大阪・横浜で来日公演をした[2]。
ミュージック・ビデオ
1991年にはポーラ・アブドゥルの「あふれる想い」のミュージックビデオに相手役として出演。
人物
2019年、サンパウロにてサインするキアヌ・リーブス。彼は左利きである。
好尚
上述のとおり千葉真一の熱狂的なファンで[3][7]、「(千葉主演の)『激突! 殺人拳』からアクションと芝居を学んだ。僕は映画用のカンフーならできるけど、サニー千葉は実際に人をボコボコにできる。情熱を感じる」と受けた影響を語っており、主演映画『ジョン・ウィック』のプロモーションで来日した2015年10月に千葉と待望の対面を果たした際には、"Oh my god!" を連発[8]。千葉へ低頭しながら「ハジメマシテ、マエストロ(巨匠)!」と固い握手を交わし、挨拶した[8]。キアヌは「あなたはキャラクターを演じるだけでなく、そこにアクションを盛り込んだ。屈強なキャラクターにもあなたが演じると心が感じられる」と身振り手振りを交えながら、終始嬉しそうに大はしゃぎの様子だった[8]。
日本文化好きでも知られ[9]、帝国ホテルが大のお気に入りである。映画『JM』では「洗濯を頼みたい。帝国ホテルで頼むような洗濯だ。東京の……」というアドリブを入れた。ラーメンが大好きで、中でも味噌ラーメンがお気に入りである。来日した時には必ずラーメン屋に立ち寄り、ハシゴすることもしばしばで贔屓の店まである。
キアヌは残りの人生で最も聴きたい曲はどれか?の質問に対し、何度か呻きながら首を振った後、イアン・カーティスが亡くなった翌月にリリースされたジョイ・ディヴィジョンの“Love Will Tear Us Apart”を挙げている。
オートバイ好きでレースに出場することも多く[3]、ノートン製のバイク(コマンド750、コマンド850)2台の他、ハーレー・ダヴィッドソン、オフロード用のバイクなど複数台のバイクを所有しており、バイクに乗る姿が目撃されている。一方で転倒や事故も数回起こしており、特に『マトリックス』のクランクイン前に首や脊椎を痛める大事故を起こし、手術が必要なほどの重傷を負った。術後、首にギプスをしたまま映画のアクションシーンの訓練をしている姿が『マトリックス』の特典映像にも見られた。車は黒いポルシェを所有しているが、友人と出かける時に乗る程度で埃をかぶっていることが多いらしい。『マトリックス』三部作が完結したあと、「映画の成功は特殊効果チームのおかげ」と言い、映画に関わったクリエーターたち全員にハーレー・ダヴィッドソンをプレゼントしたという逸話もある。2015年7月26日に開催された鈴鹿8時間耐久ロードレースのために来日し、決勝日前に自身がプロデュースしている『Arch Motorcycle(英語版)』を繰ってサーキットを疾走した[10]。レース当日にも前日同様に自らのマシンでサーキットを走った上、8耐レースのスターターとしてスターティングフラッグを振り、鈴鹿サーキット内でトークショーも開催した[11]。
映画『リトル・ブッダ』では、ゴータマ・シッダールタ役を演じたが、キアヌ本人は仏教に関心はあるとしつつ無宗教であると述べている[12][13]。
文章からNeo4jのデータを作成する検証
検証概要
文章からエンティティを抽出する方法LLMを使用する方法はいくつかあるようですが、LLMが一番良さそうなため今回はLLMを使用する方法でためしてみました。この辺りは要検証した方がいいと思う
検証は以下の方針で進めます。
検証手順
- 文章から情報抽出
- 文章を一括でLLMに渡して情報をJSONで出力してもらう。ラベルなどの項目は適宜適切に抜き出しもらう
- 文章情報からCypher作成
- 抜き出したJSONをもとにLLMにノード作成用のCypherを作ってもらう
- Cypherは単純なクエリのためLLMで問題ないはず。
- Cypherを実行して確認を行う
文章から情報抽出
文章を一括でLLMに渡して情報をJSONで出力してもらいます。ラベルなどの項目は適宜適切に抜き出しもらうようにします
課題
- データをノードとして定義するのか。プロパティとして定義するのか悩むところ。今回はグラフDBで使用するため、プロパティは最低限として、ノードを増やすのがよさそう
最終プロンプト
# 命令
あなたは、GraphRAGに最適なナレッジグラフの構造を設計する専門家です。
以下の**「モデリングの基本方針」**と**「出力JSONの仕様」**に厳密に従い、入力された文章からグラフ構造を定義したJSONを生成してください。
# モデリングの基本方針
グラフの目的は、単なる情報のリストではなく、**LLMが「繋がり」を効率的に辿って答えを発見できる「思考の地図」を構築することです。以下の判断基準を最優先**してください。
1. **ハブ・ノード化の鉄則 (The Iron Rule of Hub-Nodification):**
ある情報(例:「エンジニア」という職業、「東京」という場所)について考える。もし、その情報を基点として**「他に同じものを持つ(または関連する)エンティティは何か?」という問いが将来的に成立しうる**ならば、それは**必ず独立した「ハブ・ノード」**としなければならない。これは、グラフの価値を決定づける最も重要なルールです。
2. **プロパティとして残す条件 (Conditions for Remaining a Property):**
上記に当てはまらず、そのエンティティを個別に説明するための**具体的な属性値**であり、他のエンティティとの共通の繋がりを探す起点になりにくい情報のみ、`properties`として保持します。(例:身長や体重などの**数値**、ウェブサイトの**URL**、**true/false** のフラグ、**固有名詞ではない一般的な説明文**など)
3. **主要エンティティの扱い (Handling of Primary Entities):**
人物、組織、製品、イベントなどの文章の主題となるエンティティは、当然ノードとします。
# 出力JSONの仕様
- ルート要素は`nodes`と`relationships`の2つのキーを持つJSONオブジェクトとします。
- **nodes**: オブジェクトの配列。各オブジェクトは`id`(一意の名前)、`label`(カテゴリ)、`properties`(属性オブジェクト)のキーを持ちます。
- **relationships**: オブジェクトの配列。各オブジェクトは`source`(始点ノードのid)、`target`(終点ノードのid)、`type`(関係性の種類)のキーを持ちます。
# 具体例
- **入力文章:**`A-社のエンジニアである田中太郎は、2023年に東京で開催されたカンファレンスでPythonについて発表した。`
- **望ましい出力JSON:**
```
{
"nodes": [
{ "id": "田中太郎", "label": "Person", "properties": {} },
{ "id": "A-社", "label": "Company", "properties": {} },
{ "id": "エンジニア", "label": "Occupation", "properties": {} },
{ "id": "カンファレンス", "label": "Event", "properties": {} },
{ "id": "2023", "label": "Year", "properties": {} },
{ "id": "東京", "label": "City", "properties": {} },
{ "id": "Python", "label": "Topic", "properties": {} }
],
"relationships": [
{ "source": "田中太郎", "target": "A-社", "type": "WORKS_FOR" },
{ "source": "田中太郎", "target": "エンジニア", "type": "HAS_OCCUPATION" },
{ "source": "田中太郎", "target": "カンファレンス", "type": "PRESENTED_AT" },
{ "source": "カンファレンス", "target": "2023", "type": "HELD_IN_YEAR" },
{ "source": "カンファレンス", "target": "東京", "type": "HELD_IN_CITY" },
{ "source": "カンファレンス", "target": "Python", "type": "FEATURED_TOPIC" }
]
}
```
*(この例では、「エンジニア」という職業も「他にエンジニアはいるか?」という問いが成立するため、ハブ・ノードとして分離しています)*
# 入力文章
<入力文章>
実行結果
{
"nodes": [
{ "id": "キアヌ・リーブス", "label": "Person", "properties": { "利き手": "左利き" } },
{ "id": "俳優", "label": "Occupation", "properties": {} },
{ "id": "レバノン", "label": "Country", "properties": {} },
{ "id": "トロント", "label": "City", "properties": {} },
{ "id": "サニー・チバ", "label": "Person", "properties": {} },
{ "id": "アクション映画", "label": "Genre", "properties": {} },
{ "id": "ビルとテッドの大冒険", "label": "Movie", "properties": { "公開年": "1989" } },
{ "id": "テッド", "label": "Character", "properties": {} },
{ "id": "ハートブルー", "label": "Movie", "properties": { "公開年": "1991" } },
{ "id": "パトリック・スウェイジ", "label": "Person", "properties": {} },
{ "id": "マイ・プライベート・アイダホ", "label": "Movie", "properties": { "公開年": "1991" } },
{ "id": "リヴァー・フェニックス", "label": "Person", "properties": {} },
{ "id": "スピード", "label": "Movie", "properties": { "公開年": "1994" } },
{ "id": "ディアボロス/悪魔の扉", "label": "Movie", "properties": {} },
{ "id": "死にたいほどの夜", "label": "Movie", "properties": {} },
{ "id": "マトリックス", "label": "MovieSeries", "properties": {} },
{ "id": "ジョン・ウィック", "label": "MovieSeries", "properties": {} },
{ "id": "ファイティング・タイガー", "label": "Movie", "properties": { "公開年": "2014" } },
{ "id": "BRZRKR", "label": "Comic", "properties": {} },
{ "id": "Netflix", "label": "Company", "properties": {} },
{ "id": "ドッグスター", "label": "Band", "properties": { "ジャンル": "グランジ・ロック" } },
{ "id": "フェンダー", "label": "Brand", "properties": {} },
{ "id": "岡山県倉敷市", "label": "City", "properties": {} },
{ "id": "大阪", "label": "City", "properties": {} },
{ "id": "横浜", "label": "City", "properties": {} },
{ "id": "ポーラ・アブドゥル", "label": "Person", "properties": {} },
{ "id": "あふれる想い", "label": "MusicVideo", "properties": { "公開年": "1991" } },
{ "id": "帝国ホテル", "label": "Hotel", "properties": {} },
{ "id": "味噌ラーメン", "label": "Food", "properties": {} },
{ "id": "ジョイ・ディヴィジョン", "label": "Band", "properties": {} },
{ "id": "Love Will Tear Us Apart", "label": "Song", "properties": {} },
{ "id": "ノートン", "label": "Brand", "properties": {} },
{ "id": "ハーレー・ダヴィッドソン", "label": "Brand", "properties": {} },
{ "id": "ポルシェ", "label": "Brand", "properties": {} },
{ "id": "Arch Motorcycle", "label": "Company", "properties": {} },
{ "id": "鈴鹿サーキット", "label": "Place", "properties": {} },
{ "id": "鈴鹿8時間耐久ロードレース", "label": "Event", "properties": { "開催年": "2015" } },
{ "id": "リトル・ブッダ", "label": "Movie", "properties": {} },
{ "id": "仏教", "label": "Religion", "properties": {} }
],
"relationships": [
{ "source": "キアヌ・リーブス", "target": "俳優", "type": "HAS_OCCUPATION" },
{ "source": "キアヌ・リーブス", "target": "レバノン", "type": "BORN_IN" },
{ "source": "キアヌ・リーブス", "target": "トロント", "type": "GREW_UP_IN" },
{ "source": "キアヌ・リーブス", "target": "サニー・チバ", "type": "INSPIRED_BY" },
{ "source": "キアヌ・リーブス", "target": "アクション映画", "type": "INTERESTED_IN" },
{ "source": "キアヌ・リーブス", "target": "ビルとテッドの大冒険", "type": "ACTED_IN" },
{ "source": "ビルとテッドの大冒険", "target": "テッド", "type": "FEATURED_CHARACTER" },
{ "source": "キアヌ・リーブス", "target": "ハートブルー", "type": "ACTED_IN" },
{ "source": "ハートブルー", "target": "パトリック・スウェイジ", "type": "CO_STARRED_WITH" },
{ "source": "キアヌ・リーブス", "target": "マイ・プライベート・アイダホ", "type": "ACTED_IN" },
{ "source": "マイ・プライベート・アイダホ", "target": "リヴァー・フェニックス", "type": "CO_STARRED_WITH" },
{ "source": "キアヌ・リーブス", "target": "スピード", "type": "ACTED_IN" },
{ "source": "キアヌ・リーブス", "target": "ディアボロス/悪魔の扉", "type": "ACTED_IN" },
{ "source": "キアヌ・リーブス", "target": "死にたいほどの夜", "type": "ACTED_IN" },
{ "source": "キアヌ・リーブス", "target": "マトリックス", "type": "STARRED_IN" },
{ "source": "キアヌ・リーブス", "target": "ジョン・ウィック", "type": "STARRED_IN" },
{ "source": "キアヌ・リーブス", "target": "ファイティング・タイガー", "type": "ACTED_IN" },
{ "source": "キアヌ・リーブス", "target": "ファイティング・タイガー", "type": "DIRECTED" },
{ "source": "キアヌ・リーブス", "target": "BRZRKR", "type": "CREATED" },
{ "source": "BRZRKR", "target": "Netflix", "type": "STREAMED_ON" },
{ "source": "キアヌ・リーブス", "target": "ドッグスター", "type": "MEMBER_OF" },
{ "source": "ドッグスター", "target": "フェンダー", "type": "USES_BRAND" },
{ "source": "ドッグスター", "target": "岡山県倉敷市", "type": "PERFORMED_IN" },
{ "source": "ドッグスター", "target": "大阪", "type": "PERFORMED_IN" },
{ "source": "ドッグスター", "target": "横浜", "type": "PERFORMED_IN" },
{ "source": "キアヌ・リーブス", "target": "あふれる想い", "type": "APPEARED_IN" },
{ "source": "あふれる想い", "target": "ポーラ・アブドゥル", "type": "FEATURED_ARTIST" },
{ "source": "キアヌ・リーブス", "target": "帝国ホテル", "type": "FAVORITE_PLACE" },
{ "source": "キアヌ・リーブス", "target": "味噌ラーメン", "type": "FAVORITE_FOOD" },
{ "source": "キアヌ・リーブス", "target": "Love Will Tear Us Apart", "type": "FAVORITE_SONG" },
{ "source": "Love Will Tear Us Apart", "target": "ジョイ・ディヴィジョン", "type": "PERFORMED_BY" },
{ "source": "キアヌ・リーブス", "target": "ノートン", "type": "OWNS" },
{ "source": "キアヌ・リーブス", "target": "ハーレー・ダヴィッドソン", "type": "OWNS" },
{ "source": "キアヌ・リーブス", "target": "ポルシェ", "type": "OWNS" },
{ "source": "キアヌ・リーブス", "target": "Arch Motorcycle", "type": "FOUNDED" },
{ "source": "Arch Motorcycle", "target": "鈴鹿サーキット", "type": "RACED_AT" },
{ "source": "キアヌ・リーブス", "target": "鈴鹿8時間耐久ロードレース", "type": "PARTICIPATED_IN" },
{ "source": "鈴鹿8時間耐久ロードレース", "target": "鈴鹿サーキット", "type": "HELD_AT" },
{ "source": "キアヌ・リーブス", "target": "リトル・ブッダ", "type": "ACTED_IN" },
{ "source": "キアヌ・リーブス", "target": "仏教", "type": "INTERESTED_IN" }
]
}
文章情報からCypher作成
抜き出したJSONをもとにLLMにノード作成用のCypherを作ってもらいます
最終プロンプト
# 命令
あなたは、JSONデータを基に安全で効率的なCypherクエリを生成するNeo4jのエキスパートです。
入力されたJSONデータ(グラフの構造定義)を、Neo4jにデータを登録するための**Cypherクエリ**に変換してください。
# Cypher生成ルール
1. **冪等性の確保:**
ノードとリレーションシップの作成には、重複を防ぐため必ず`MERGE`を使用してください。
2. **ノードの作成と更新:**
- ノードは `:Label {id: '...'}` の形式で`MERGE`します。
- `properties`に属性がある場合、`ON CREATE SET`(新規作成時)と`ON MATCH SET`(既存時)の両方を使い、常にプロパティが最新の状態に保たれるようにしてください。
3. **リレーションシップの作成(最重要):**
- **ノード作成とリレーションシップ作成の分離:** 全てのノードを`MERGE`し終えた後、最初のリレーションシップ作成ブロックの前に、必ず `WITH true as ignored` という句を挿入してください。これにより、書き込み処理と次の読み込み処理が構文的に正しく分離されます。
- **リレーションシップブロック間の分離:** 同様に、2つ目以降のリレーションシップを作成するブロックの前にも、必ず `WITH true as ignored` を挿入してください。
- **ブロックの構成:** 各リレーションシップ作成ブロックは、`MATCH`文で始点と終点を特定し、その後`MERGE`文でリレーションシップを作成する構成となります。
4. **出力形式:**
最終的な出力は、実行可能な単一のCypherクエリ文のみとしてください。説明や他のテキストは不要です。
# 具体例
- **入力JSON:**
```
{
"nodes": [
{ "id": "田中太郎", "label": "Person", "properties": { "title": "エンジニア" } },
{ "id": "A-社", "label": "Company", "properties": {} },
{ "id": "東京", "label": "City", "properties": {} }
],
"relationships": [
{ "source": "田中太郎", "target": "A-社", "type": "WORKS_FOR" },
{ "source": "田中太郎", "target": "東京", "type": "LIVES_IN" }
]
}
```
- **望ましい出力Cypher:**
```
// ノードのMERGE
MERGE (n1:Person {id: '田中太郎'})
ON CREATE SET n1.title = 'エンジニア'
ON MATCH SET n1.title = 'エンジニア'
MERGE (n2:Company {id: 'A-社'})
MERGE (n3:City {id: '東京'})
// ノード作成とリレーションシップ作成をWITHで区切る
WITH true AS ignored
// リレーションシップの作成 (1つ目)
MATCH (source1:Person {id: '田中太郎'})
MATCH (target1:Company {id: 'A-社'})
MERGE (source1)-[:WORKS_FOR]->(target1)
// 2つ目以降のリレーションシップを作成する前にWITHで区切る
WITH true AS ignored
// リレーションシップの作成 (2つ目)
MATCH (source2:Person {id: '田中太郎'})
MATCH (target2:City {id: '東京'})
MERGE (source2)-[:LIVES_IN]->(target2)
```
# 入力JSON
<入力JSON>
実行結果
// ノードのMERGE
MERGE (n1:Person {id: 'キアヌ・リーブス'})
ON CREATE SET n1.利き手 = '左利き'
ON MATCH SET n1.利き手 = '左利き'
MERGE (n2:Occupation {id: '俳優'})
MERGE (n3:Country {id: 'レバノン'})
MERGE (n4:City {id: 'トロント'})
MERGE (n5:Person {id: 'サニー・チバ'})
MERGE (n6:Genre {id: 'アクション映画'})
MERGE (n7:Movie {id: 'ビルとテッドの大冒険'})
ON CREATE SET n7.公開年 = '1989'
ON MATCH SET n7.公開年 = '1989'
MERGE (n8:Character {id: 'テッド'})
MERGE (n9:Movie {id: 'ハートブルー'})
ON CREATE SET n9.公開年 = '1991'
ON MATCH SET n9.公開年 = '1991'
MERGE (n10:Person {id: 'パトリック・スウェイジ'})
MERGE (n11:Movie {id: 'マイ・プライベート・アイダホ'})
ON CREATE SET n11.公開年 = '1991'
ON MATCH SET n11.公開年 = '1991'
MERGE (n12:Person {id: 'リヴァー・フェニックス'})
MERGE (n13:Movie {id: 'スピード'})
ON CREATE SET n13.公開年 = '1994'
ON MATCH SET n13.公開年 = '1994'
MERGE (n14:Movie {id: 'ディアボロス/悪魔の扉'})
MERGE (n15:Movie {id: '死にたいほどの夜'})
MERGE (n16:MovieSeries {id: 'マトリックス'})
MERGE (n17:MovieSeries {id: 'ジョン・ウィック'})
MERGE (n18:Movie {id: 'ファイティング・タイガー'})
ON CREATE SET n18.公開年 = '2014'
ON MATCH SET n18.公開年 = '2014'
MERGE (n19:Comic {id: 'BRZRKR'})
MERGE (n20:Company {id: 'Netflix'})
MERGE (n21:Band {id: 'ドッグスター'})
ON CREATE SET n21.ジャンル = 'グランジ・ロック'
ON MATCH SET n21.ジャンル = 'グランジ・ロック'
MERGE (n22:Brand {id: 'フェンダー'})
MERGE (n23:City {id: '岡山県倉敷市'})
MERGE (n24:City {id: '大阪'})
MERGE (n25:City {id: '横浜'})
MERGE (n26:Person {id: 'ポーラ・アブドゥル'})
MERGE (n27:MusicVideo {id: 'あふれる想い'})
ON CREATE SET n27.公開年 = '1991'
ON MATCH SET n27.公開年 = '1991'
MERGE (n28:Hotel {id: '帝国ホテル'})
MERGE (n29:Food {id: '味噌ラーメン'})
MERGE (n30:Band {id: 'ジョイ・ディヴィジョン'})
MERGE (n31:Song {id: 'Love Will Tear Us Apart'})
MERGE (n32:Brand {id: 'ノートン'})
MERGE (n33:Brand {id: 'ハーレー・ダヴィッドソン'})
MERGE (n34:Brand {id: 'ポルシェ'})
MERGE (n35:Company {id: 'Arch Motorcycle'})
MERGE (n36:Place {id: '鈴鹿サーキット'})
MERGE (n37:Event {id: '鈴鹿8時間耐久ロードレース'})
ON CREATE SET n37.開催年 = '2015'
ON MATCH SET n37.開催年 = '2015'
MERGE (n38:Movie {id: 'リトル・ブッダ'})
MERGE (n39:Religion {id: '仏教'})
// ノード作成とリレーションシップ作成をWITHで区切る
WITH true AS ignored
// リレーションシップの作成 (1つ目)
MATCH (source1:Person {id: 'キアヌ・リーブス'})
MATCH (target1:Occupation {id: '俳優'})
MERGE (source1)-[:HAS_OCCUPATION]->(target1)
WITH true AS ignored
MATCH (source2:Person {id: 'キアヌ・リーブス'})
MATCH (target2:Country {id: 'レバノン'})
MERGE (source2)-[:BORN_IN]->(target2)
WITH true AS ignored
MATCH (source3:Person {id: 'キアヌ・リーブス'})
MATCH (target3:City {id: 'トロント'})
MERGE (source3)-[:GREW_UP_IN]->(target3)
WITH true AS ignored
MATCH (source4:Person {id: 'キアヌ・リーブス'})
MATCH (target4:Person {id: 'サニー・チバ'})
MERGE (source4)-[:INSPIRED_BY]->(target4)
WITH true AS ignored
MATCH (source5:Person {id: 'キアヌ・リーブス'})
MATCH (target5:Genre {id: 'アクション映画'})
MERGE (source5)-[:INTERESTED_IN]->(target5)
WITH true AS ignored
MATCH (source6:Person {id: 'キアヌ・リーブス'})
MATCH (target6:Movie {id: 'ビルとテッドの大冒険'})
MERGE (source6)-[:ACTED_IN]->(target6)
WITH true AS ignored
MATCH (source7:Movie {id: 'ビルとテッドの大冒険'})
MATCH (target7:Character {id: 'テッド'})
MERGE (source7)-[:FEATURED_CHARACTER]->(target7)
WITH true AS ignored
MATCH (source8:Person {id: 'キアヌ・リーブス'})
MATCH (target8:Movie {id: 'ハートブルー'})
MERGE (source8)-[:ACTED_IN]->(target8)
WITH true AS ignored
MATCH (source9:Movie {id: 'ハートブルー'})
MATCH (target9:Person {id: 'パトリック・スウェイジ'})
MERGE (source9)-[:CO_STARRED_WITH]->(target9)
WITH true AS ignored
MATCH (source10:Person {id: 'キアヌ・リーブス'})
MATCH (target10:Movie {id: 'マイ・プライベート・アイダホ'})
MERGE (source10)-[:ACTED_IN]->(target10)
WITH true AS ignored
MATCH (source11:Movie {id: 'マイ・プライベート・アイダホ'})
MATCH (target11:Person {id: 'リヴァー・フェニックス'})
MERGE (source11)-[:CO_STARRED_WITH]->(target11)
WITH true AS ignored
MATCH (source12:Person {id: 'キアヌ・リーブス'})
MATCH (target12:Movie {id: 'スピード'})
MERGE (source12)-[:ACTED_IN]->(target12)
WITH true AS ignored
MATCH (source13:Person {id: 'キアヌ・リーブス'})
MATCH (target13:Movie {id: 'ディアボロス/悪魔の扉'})
MERGE (source13)-[:ACTED_IN]->(target13)
WITH true AS ignored
MATCH (source14:Person {id: 'キアヌ・リーブス'})
MATCH (target14:Movie {id: '死にたいほどの夜'})
MERGE (source14)-[:ACTED_IN]->(target14)
WITH true AS ignored
MATCH (source15:Person {id: 'キアヌ・リーブス'})
MATCH (target15:MovieSeries {id: 'マトリックス'})
MERGE (source15)-[:STARRED_IN]->(target15)
WITH true AS ignored
MATCH (source16:Person {id: 'キアヌ・リーブス'})
MATCH (target16:MovieSeries {id: 'ジョン・ウィック'})
MERGE (source16)-[:STARRED_IN]->(target16)
WITH true AS ignored
MATCH (source17:Person {id: 'キアヌ・リーブス'})
MATCH (target17:Movie {id: 'ファイティング・タイガー'})
MERGE (source17)-[:ACTED_IN]->(target17)
WITH true AS ignored
MATCH (source18:Person {id: 'キアヌ・リーブス'})
MATCH (target18:Movie {id: 'ファイティング・タイガー'})
MERGE (source18)-[:DIRECTED]->(target18)
WITH true AS ignored
MATCH (source19:Person {id: 'キアヌ・リーブス'})
MATCH (target19:Comic {id: 'BRZRKR'})
MERGE (source19)-[:CREATED]->(target19)
WITH true AS ignored
MATCH (source20:Comic {id: 'BRZRKR'})
MATCH (target20:Company {id: 'Netflix'})
MERGE (source20)-[:STREAMED_ON]->(target20)
WITH true AS ignored
MATCH (source21:Person {id: 'キアヌ・リーブス'})
MATCH (target21:Band {id: 'ドッグスター'})
MERGE (source21)-[:MEMBER_OF]->(target21)
WITH true AS ignored
MATCH (source22:Band {id: 'ドッグスター'})
MATCH (target22:Brand {id: 'フェンダー'})
MERGE (source22)-[:USES_BRAND]->(target22)
WITH true AS ignored
MATCH (source23:Band {id: 'ドッグスター'})
MATCH (target23:City {id: '岡山県倉敷市'})
MERGE (source23)-[:PERFORMED_IN]->(target23)
WITH true AS ignored
MATCH (source24:Band {id: 'ドッグスター'})
MATCH (target24:City {id: '大阪'})
MERGE (source24)-[:PERFORMED_IN]->(target24)
WITH true AS ignored
MATCH (source25:Band {id: 'ドッグスター'})
MATCH (target25:City {id: '横浜'})
MERGE (source25)-[:PERFORMED_IN]->(target25)
WITH true AS ignored
MATCH (source26:Person {id: 'キアヌ・リーブス'})
MATCH (target26:MusicVideo {id: 'あふれる想い'})
MERGE (source26)-[:APPEARED_IN]->(target26)
WITH true AS ignored
MATCH (source27:MusicVideo {id: 'あふれる想い'})
MATCH (target27:Person {id: 'ポーラ・アブドゥル'})
MERGE (source27)-[:FEATURED_ARTIST]->(target27)
WITH true AS ignored
MATCH (source28:Person {id: 'キアヌ・リーブス'})
MATCH (target28:Hotel {id: '帝国ホテル'})
MERGE (source28)-[:FAVORITE_PLACE]->(target28)
WITH true AS ignored
MATCH (source29:Person {id: 'キアヌ・リーブス'})
MATCH (target29:Food {id: '味噌ラーメン'})
MERGE (source29)-[:FAVORITE_FOOD]->(target29)
WITH true AS ignored
MATCH (source30:Person {id: 'キアヌ・リーブス'})
MATCH (target30:Song {id: 'Love Will Tear Us Apart'})
MERGE (source30)-[:FAVORITE_SONG]->(target30)
WITH true AS ignored
MATCH (source31:Song {id: 'Love Will Tear Us Apart'})
MATCH (target31:Band {id: 'ジョイ・ディヴィジョン'})
MERGE (source31)-[:PERFORMED_BY]->(target31)
WITH true AS ignored
MATCH (source32:Person {id: 'キアヌ・リーブス'})
MATCH (target32:Brand {id: 'ノートン'})
MERGE (source32)-[:OWNS]->(target32)
WITH true AS ignored
MATCH (source33:Person {id: 'キアヌ・リーブス'})
MATCH (target33:Brand {id: 'ハーレー・ダヴィッドソン'})
MERGE (source33)-[:OWNS]->(target33)
WITH true AS ignored
MATCH (source34:Person {id: 'キアヌ・リーブス'})
MATCH (target34:Brand {id: 'ポルシェ'})
MERGE (source34)-[:OWNS]->(target34)
WITH true AS ignored
MATCH (source35:Person {id: 'キアヌ・リーブス'})
MATCH (target35:Company {id: 'Arch Motorcycle'})
MERGE (source35)-[:FOUNDED]->(target35)
WITH true AS ignored
MATCH (source36:Company {id: 'Arch Motorcycle'})
MATCH (target36:Place {id: '鈴鹿サーキット'})
MERGE (source36)-[:RACED_AT]->(target36)
WITH true AS ignored
MATCH (source37:Person {id: 'キアヌ・リーブス'})
MATCH (target37:Event {id: '鈴鹿8時間耐久ロードレース'})
MERGE (source37)-[:PARTICIPATED_IN]->(target37)
WITH true AS ignored
MATCH (source38:Event {id: '鈴鹿8時間耐久ロードレース'})
MATCH (target38:Place {id: '鈴鹿サーキット'})
MERGE (source38)-[:HELD_AT]->(target38)
WITH true AS ignored
MATCH (source39:Person {id: 'キアヌ・リーブス'})
MATCH (target39:Movie {id: 'リトル・ブッダ'})
MERGE (source39)-[:ACTED_IN]->(target39)
WITH true AS ignored
MATCH (source40:Person {id: 'キアヌ・リーブス'})
MATCH (target40:Religion {id: '仏教'})
MERGE (source40)-[:INTERESTED_IN]->(target40)
Cypher実行
CypherをNeoj4の管理画面で実行して、正しく実行できていることがわかりました。
まとめ
- LLMを使用してneo4jのデータを作成できることが確認できました
検索の方法を検証
検証概要
ユーザー入力からグラフ検索をする際には、ユーザー入力をCypherに変換する必要があります。
文章からNeo4jのデータを作成する検証で行った方法と同じ方針で進めます
検証手順
- ユーザー入力から情報抽出
- ユーザー入力をLLMに渡して情報をJSONで出力してもらう。
- Neo4jにあるスキーマ情報を渡す
- ユーザー入力をLLMに渡して情報をJSONで出力してもらう。
- ユーザー入力からCypher作成
- 抜き出したJSONをもとにLLMにノード作成用のCypherを作ってもらう
- Cypherを実行してCypherの確認を行う
ユーザー入力から情報抽出
文章を一括でLLMに渡して情報をJSONで出力してもらいます。
Neo4jにすでにあるスキーマを使用して欲しいためNeo4jにあるスキーマを渡します
以下のCypherを実行します
CALL db.schema.nodeTypeProperties()
YIELD nodeLabels, propertyName
UNWIND nodeLabels AS label
WITH label, COLLECT(propertyName) AS properties
RETURN label, properties
ORDER BY label
実行結果
[
{
"label": "Band",
"properties": [
"id",
"ジャンル"
]
},
{
"label": "Brand",
"properties": [
"id"
]
},
{
"label": "Character",
"properties": [
"id"
]
},
{
"label": "City",
"properties": [
"id"
]
},
{
"label": "Comic",
"properties": [
"id"
]
},
{
"label": "Company",
"properties": [
"id"
]
},
{
"label": "Country",
"properties": [
"id"
]
},
{
"label": "Event",
"properties": [
"id",
"開催年"
]
},
{
"label": "Food",
"properties": [
"id"
]
},
{
"label": "Genre",
"properties": [
"id"
]
},
{
"label": "Hotel",
"properties": [
"id"
]
},
{
"label": "Movie",
"properties": [
"id",
"公開年"
]
},
{
"label": "MovieSeries",
"properties": [
"id"
]
},
{
"label": "MusicVideo",
"properties": [
"id",
"公開年"
]
},
{
"label": "Occupation",
"properties": [
"id"
]
},
{
"label": "Person",
"properties": [
"id",
"利き手"
]
},
{
"label": "Place",
"properties": [
"id"
]
},
{
"label": "Religion",
"properties": [
"id"
]
},
{
"label": "Song",
"properties": [
"id"
]
}
]
最終プロンプト
# 命令
あなたは、ユーザーの質問文を解釈し、**Neo4jで実行するクエリの設計図となるJSON**を生成する、高度なAIアナリストです。
質問文に含まれる**「エンティティ(何を探しているか)」**と**「インテント(何をしたいか)」**を抽出し、提供された**「データスキーマ」に厳密に従って**、純粋なJSONオブジェクトで出力してください。
# JSON出力構造
生成するJSONは、以下のキーを持つオブジェクトです。
- `intent` (必須): ユーザーの主な意図。`<意図リスト>`から最も適切なものを選択。
- `entities` (必須): 質問文から抽出した具体的なエンティティのリスト(配列)。
- `id`: 抽出した固有名詞。
- `label`: `<データスキーマ>`に存在するラベルから選択。
- `filters` (任意): 検索結果を絞り込むための条件。
- キーは`<データスキーマ>`に存在するプロパティ名でなければなりません。
- 値は `{ "operator": "...", "value": "..." }` の形式とします。
- `return_properties` (任意): 返してほしいプロパティ名のリスト。
# ルール
1. **スキーマへの厳格な準拠 (最重要):**`entities`の`label`や`filters`のキー(プロパティ名)は、必ず`<データスキーマ>`で定義されているものの中から選択してください。**スキーマに存在しないラベルやプロパティは、決して創作・使用しないでください。**
2. **マッピング不能な条件の処理:**
ユーザーの要求に、スキーマにマッピングできない条件(例: 「人気商品」など、対応するプロパティがない)が含まれている場合、その条件の`filters`は**生成しないでください**。代わりに、より広範な情報を返す`explore_node`のような意図を選択し、ユーザーが自身で判断できる材料を提供してください。
3. **意図(Intent)の特定:**
質問文全体の文脈を読み取り、ユーザーの最も主要な目的を`<意図リスト>`から選択してください。
4. **エンティティ(Entities)の抽出:**
グラフ内に実在する可能性が高い**固有名詞**(製品名、人名、会社名など)を正確に抜き出します。
5. **クリーンな出力:**
出力は純粋なJSONオブジェクトのみとし、余計な説明やコメント、マークダウン(` ```)は一切含めないでください。
# 具体例
- **入力文:** `製品Aについて教えて`
- **スキーマ:** `{"Person": ["name"], "Product": ["name", "price"]}`
- **望ましい出力JSON:**
```
{
"intent": "explore_node",
"entities": [
{ "id": "製品A", "label": "Product" }
]
}
```
- **入力文:** `Aさんが担当した2020年以降のプロジェクト名を教えて`
- **スキーマ:** `{"Employee": ["name"], "Project": ["projectName", "year"]}`
- **望ましい出力JSON:**
```
{
"intent": "filter_and_return",
"entities": [
{ "id": "Aさん", "label": "Employee" }
],
"filters": {
"year": { "operator": ">=", "value": 2020 }
},
"return_properties": ["projectName"]
}
```
- **入力文:** `製品Aの主力機能は?`
- **スキーマ:** `{"Product": ["name", "price"], "Feature": ["name"]}`
- **望ましい出力JSON (「主力」がスキーマにないため、広範な探索にフォールバック):**
```
{
"intent": "explore_node",
"entities": [
{ "id": "製品A", "label": "Product" }
]
}
```
# ── 意図リスト (Intent List) ──
- **explore_node**: 特定のノードとその直接の周辺情報を知りたい (例: 「〜について教えて」)
- **find_path**: 2つ以上のノード間の関係性や経路を知りたい (例: 「〜と〜の関係は?」)
- **count_results**: 条件に合うノードや関係の数を数えたい (例: 「〜はいくつある?」)
- **filter_and_return**: 特定の条件で絞り込み、指定された情報を得たい (例: 「〜で、〜なのは?」)
# ── データスキーマ (Data Schema) ──
[ここに `CALL db.schema.nodeTypeProperties()` で取得したスキーマ情報を貼り付けてください]
# ── 入力文 (User Input) ──
[ここにユーザーの質問文を貼り付けてください]
実行結果
A1
{
"intent": "explore_node",
"entities": [
{ "id": "キアヌ・リーブス", "label": "Person" }
]
}
A2
{
"intent": "explore_node",
"entities": [
{
"id": "ハートブルー",
"label": "Movie"
}
]
}
ユーザー入力からCypher作成
抜き出したJSONをもとにLLMにノード作成用のCypherを作ってもらいます。
最終プロンプト
# 命令
あなたは、**クエリの設計図として構造化されたJSON**を解釈し、**実行可能で効率的なNeo4j Cypherクエリ**に変換する、データベースの専門家です。
入力されたJSONオブジェクトの`intent`キーを基に、以下のルールに従って最適なクエリを1つ生成してください。
# クエリ生成ルール
1. **`intent`に応じたクエリパターンの選択:**
入力JSONの`intent`の値を読み取り、以下のロジックに従ってCypherクエリの基本構造を決定します。
- **`intent: "explore_node"`**:
単一のノードを起点に、それに直接接続されている全てのノードとリレーションシップを返します。
**パターン:** `MATCH (n:{label} {id: {id}})-[r]-(m) RETURN n, r, m`
- **`intent: "find_path"`**:
`entities`リスト内の2つのノード間の最短経路を検索します。
**パターン:** `MATCH (n1:{label1} {id: {id1}}), (n2:{label2} {id: {id2}}) MATCH p = allShortestPaths((n1)-[*]-(n2)) RETURN p`
- **`intent: "count_results"`**:
指定されたパターンに一致するノードやリレーションシップの総数を返します。
**パターン:** `MATCH (n:{label} {id: {id}})-->(m) RETURN count(m)`
- **`intent: "filter_and_return"`**:
`entities`を起点とし、`filters`で条件を絞り込み、`return_properties`で指定されたプロパティのみを返します。`filters`や`return_properties`が存在しない場合は、それぞれ省略してください。
**パターン:** `MATCH (n:{label} {id: {id}})-->(m) WHERE m.{prop} {op} {val} RETURN m.{return_prop}`
2. **プレースホルダーの置換:**
選択したパターン内の `{...}` プレースホルダーを、JSON内の対応する値(`id`, `label`, `filters`など)で正確に置換してください。
3. **クリーンな出力:**
出力は純粋なCypherクエリの文字列のみとします。余計な説明、コメント、マークダウン(` ```)は一切含めないでください。
# 具体例
- **入力JSON (`intent: "explore_node"`):**
```
{
"intent": "explore_node",
"entities": [
{ "id": "製品A", "label": "Product" }
]
}
```
- **望ましい出力Cypher:**
```
MATCH (n:Product {id: '製品A'})-[r]-(m) RETURN n, r, m
```
- **入力JSON (`intent: "find_path"`):**
```
{
"intent": "find_path",
"entities": [
{ "id": "Aさん", "label": "Employee" },
{ "id": "Bさん", "label": "Employee" }
]
}
```
- **望ましい出力Cypher:**
```
MATCH (n1:Employee {id: 'Aさん'}), (n2:Employee {id: 'Bさん'}) MATCH p = allShortestPaths((n1)-[*]-(n2)) RETURN p
```
- **入力JSON (`intent: "filter_and_return"`):**
```
{
"intent": "filter_and_return",
"entities": [
{ "id": "Aさん", "label": "Employee" }
],
"filters": {
"year": { "operator": ">=", "value": 2020 }
},
"return_properties": ["projectName"]
}
```
- **望ましい出力Cypher:**
```
MATCH (n:Employee {id: 'Aさん'})-->(m:Project) WHERE m.year >= 2020 RETURN m.projectName
```
# ── 入力JSON ──
[ここに前のステップで生成されたJSONを貼り付けてください]
実行結果
A1
MATCH (n:Person {id: 'キアヌ・リーブス'})-[r]-(m) RETURN n, r, m
A2
MATCH (n:Movie {id: 'ハートブルー'})-[r]-(m) RETURN n, r, m
Cypherの実行(検索結果)
長いので抜粋
[
{
"n": {
"identity": 68,
"labels": [
"Person"
],
"properties": {
"id": "キアヌ・リーブス",
"利き手": "左利き"
},
"elementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68"
},
"r": {
"identity": 47,
"start": 68,
"end": 30,
"type": "STARRED_IN",
"properties": {},
"elementId": "5:1285ddc4-687d-4135-9481-ec11a50b3435:47",
"startNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68",
"endNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:30"
},
"m": {
"identity": 30,
"labels": [
"MovieSeries"
],
"properties": {
"id": "ジョン・ウィック"
},
"elementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:30"
}
},
{
"n": {
"identity": 68,
"labels": [
"Person"
],
"properties": {
"id": "キアヌ・リーブス",
"利き手": "左利き"
},
"elementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68"
},
"r": {
"identity": 39,
"start": 68,
"end": 22,
"type": "ACTED_IN",
"properties": {},
"elementId": "5:1285ddc4-687d-4135-9481-ec11a50b3435:39",
"startNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68",
"endNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:22"
}
]
2
長いので抜粋
[
{
"n": {
"identity": 22,
"labels": [
"Movie"
],
"properties": {
"公開年": "1991",
"id": "ハートブルー"
},
"elementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:22"
},
"r": {
"identity": 39,
"start": 68,
"end": 22,
"type": "ACTED_IN",
"properties": {},
"elementId": "5:1285ddc4-687d-4135-9481-ec11a50b3435:39",
"startNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68",
"endNodeElementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:22"
},
"m": {
"identity": 68,
"labels": [
"Person"
],
"properties": {
"id": "キアヌ・リーブス",
"利き手": "左利き"
},
"elementId": "4:1285ddc4-687d-4135-9481-ec11a50b3435:68"
}
}
]
まとめ
- LLMを使用してNeo4jのから検索を行えることを確認できました
最終的な回答を出す検証
検証概要
Neo4jの検索結果は文章ではなくノードなどの断片的な情報がかえってくるため、LLMを使用して文章に変換する必要があります
その検証を行います
検証手順
- Neo4jの検索結果を最終回答に変換
- LLMでいい感じにNeo4jの検索結果をまとめる
Neo4jの検索結果を最終回答に変換
最終プロンプト
# 命令
あなたは、データベースから返された構造化データ(JSON形式)を、ユーザーの**元の質問**に合わせて、**自然で分かりやすい文章に要約する**AIアシスタントです。
# 守るべきルール
1. **元の質問に直接答える:**
必ず「元の質問」で何を聞かれていたかを意識し、それに答える形で文章を作成してください。
2. **専門用語を使わない:**
「ノード」や「リレーションシップ」、「プロパティ」といったデータベースの専門用語は絶対に使わず、日常的な言葉で説明してください。
3. **事実に忠実であること:**
提供された「検索結果」に含まれる情報**のみ**を使い、事実に基づいて回答してください。検索結果にない情報を推測したり、創作したりしてはいけません。
4. **結果がない場合の対応:**
検索結果が空(`[]`)の場合は、「申し訳ありませんが、その情報は見つかりませんでした。」と正直に伝えてください。
5. **見やすいフォーマット:**
複数の項目を列挙する場合は、箇条書き(リスト形式)を使って、ユーザーが見やすいように整理してください。
# 具体例
- **元の質問:** `製品Aについて教えてください。`
- **検索結果:** `[{"product": {"name": "製品A", "price": 5000}, "company": {"name": "X社"}}]`
- **望ましい出力:**`製品Aは、X社が開発した製品で、価格は5000円です。`
- **元の質問:** `AさんとBさんの関係は?`
- **検索結果:** `[{"path": [{"name": "Aさん"}, {"name": "Cさん"}, {"name": "Bさん"}]}]`
- **望ましい出力:**`AさんとBさんに直接の関係性はありませんが、AさんはCさんを介してBさんと繋がっているようです。`
- **元の質問:** `Aさんが担当したプロジェクトの数は?`
- **検索結果:** `[{"count": 5}]`
- **望ましい出力:**`Aさんが担当したプロジェクトは5件です。`
- **元の質問:** `製品Zについて教えてください。`
- **検索結果:** `[]`
- **望ましい出力:**`申し訳ありませんが、製品Zに関する情報は見つかりませんでした。`
# ── 元の質問 ──
[ここにユーザーの最初の質問文を貼り付けてください]
# ── データベースからの検索結果 ──
[ここにCypherクエリの実行結果(JSON形式)を貼り付けてください]
最終回答
最終回答は以下のようになりました。
質問に対して正しく回答できていることが確認でできました
1
Q: キアヌ・リーブスが国際的スターとなるきっかけとなった映画は何ですか?
A: キアヌ・リーブスが国際的なスターとなるきっかけとなった映画は、1994年に公開された『スピード』です。この作品での活躍によって、彼は世界的に広く知られる存在となりました。
2
Q: ハートブルーという映画について教えてください
A: 映画『ハートブルー』は1991年に公開された作品です。主な出演者には、キアヌ・リーブス(左利き)とパトリック・スウェイジがいます。
まとめ
- Neo4jで正しく回答できていることが確認できました
その他検証ポイント
-
LLMをふんだんに使用したがもっといいプロンプトはないかどうか。
-
情報はノードにするのか、エンティティにするのかよく考えた方がいい。
-
この辺は明確な答えはなくどんな種類の検索システムを作りたいかによると思う。つまりドメイン知識をふんだんに活用する形になると思う。
-
エンティティに情報を増やしてしまうとグラフの意味があまりなくなってしまうし、ノードを増やしすぎると検索が難しくなってしまう
例えば、
キアヌリーブス 国籍: カナダ 利き手: 左利きととるのか
カナダ <- キアヌリーブス -> 左利きとデータを作るのか。
-
じゃあエンティティにも持つがリレーションもするのはどうだろか?
⇒ Geminiは推奨しないとのこと。データの2重管理が起き。不整合が起こる可能性があるとのこと ← 確かにその通りだと思う。
カナダ <- キアヌリーブス(カナダ、左利き) -> 左利き
-
-
エンティティの項目はこちらで決めた方がいいのか?それともきめの方がいいのか?
- こちらで定義した方が精度上がりそうだが、色々なジャンルの文章に対応できなくなる可能性あるのでその辺も検証
-
文章から情報抽出する際の文章は1文ずつの方がいいのか?まとめた方がいいのか?どのくらいまとめれるか?
- 個人的にはラベルの付け方を一意にした方がいいため1分ずつではなくなるべくまとめた方がいいと思う。とはいえ全文は送れないので適切に分けるのが大事
- 情報を抽出際に現在Neo4jに入っているスキーマデータを渡すのがいいと思う。
-
今回は「情報をJSONで抽出」と「Cypherの作成」の工程を分けたが1回のプロンプトでできないかどうか
- できそうな気がする
- Gminiは分けない方がいいと言っていた。
- 役割を明確にした方がデバッグがしやすいとのこと。
-
どのモデルを使用するのが適切か
- 大量にLLMを実行するためなるべく安いモデルがいい
-
文章から情報抽出する方法はいくつかありそうなので他の方法も検討してみる
- 機械学習ベースの NERライブラリ活用
- Hugging Face Transformers で高性能モデル
- LLM を使う方法(今回はこの方法)
-
エンティティの抽出、サイファーの出力の際になにか前処理した方がいいかどうか
- 重複を省いたほうがいい?
- 問題なさそう
- 全て英語にしたほうがいい?おそらく「りんご」と「apple」は区別されてしまうと思う。
- 重複を省いたほうがいい?
参考
1
ノード全削除
MATCH (n) DETACH DELETE n;


