GraphRAG Oracle Database 23aiでのLangchainおよびOracle Graphの使用(パート1) (2024/11/21)
GraphRAG Oracle Database 23aiでのLangchainおよびOracle Graphの使用(パート1) (2024/11/21)
投稿者:Rahul Tasker
生成AIの新しい世界では、企業は自社の能力とその欠点を認識し始めています。AIは、豊富な情報を持つ非常に強力な世代エンジンですが、トレーニングされた情報に限定され、プライベート・ビジネス・データに関する洞察はありません。つまり、Large Language Model(LLM)には、最近のイベントやビジネス・データに関する情報がなく、次の質問が求められます。
データを使用してLLMから関連する回答を得て、情報に基づいたビジネス上の意思決定を行うには、どうすればよいでしょうか。
可能なソリューションには、カスタム生成事前トレーニング済トランスフォーマ(GPT)、ベクトル・ベース取得拡張生成(RAG)およびグラフ・ベース取得拡張生成(GraphRAG)があります。カスタムGPTの作成は煩雑で、広範なトレーニングを使用できるようにする必要があり、新しいデータが常にビジネスに来るときにエラーが発生しやすく、カスタムGPTは新しいデータで再トレーニングされないため、多くの人にとってメンテナンスが懸念されます。RAGは、一部のユース・ケースでは優れたオプションですが、コンテキストや、私たちが真であると知っているものとのつながりが欠けています。これは、グラフが特に役立つ場合です。
グラフは、LLMからより正確な結果を得るために情報取得のコンテキストを提供できます。より複雑なユース・ケースでは、ベクター・ベースとグラフ・ベースの取得を組み合せてLLMから高品質な結果を得ることができます(パート2で説明します)。ただし、多くの場合、グラフのみを使用して情報を取得すれば十分です。
この記事では、Oracle Database 23aiでSQLプロパティ・グラフをChatGPTなどのAIサービスとともに使用して、LLMが質問に答えるためのより多くのコンテキストを提供する方法の簡単な例をいくつか説明します。PythonとLangchainを使用して、すべてをきちんとしたパッケージにまとめます。
前提条件
- Python 3.11以上のJupyterノートブック環境。
- python-oracledb、langchain、langchain_openaiおよびlangchain_coreライブラリがインストールされています。
- グラフが有効なユーザーを使用して、23ai Oracle Databaseにアクセスできます。そうでない場合は、常に無料のAutonomous Databaseインスタンスにサインアップすることを検討してください。
- OpenAI APIキーがあります。
データベースへの接続
最初に、python-oracledbライブラリを使用してOracle Database 23aiインスタンスに接続します。
- Jupyterノートブック環境を起動し、新しいノートブックを作成します。
- 新しいセルで、python-oracledbライブラリをインポートし、新しい接続を作成し、その接続のカーソルを作成します。カーソルは、SQL文の実行およびSQL問合せの結果の表示に使用されます。
import oracledb
connection = oracledb.connect(
user="<your_user>",
password='<your_password>',
dsn="<your_connection_string>"
)
cursor = connection.cursor()
表の作成およびデータのロード
次に、いくつかの表を作成し、データをロードします。これらの表を使用してグラフを作成するため、GraphRAGソリューションのグラフを問い合せることができます。この例では、顧客、映画、および各顧客の視聴習慣を表す3つの小さなサンプル表を作成します。
1. 新しいセルで、次のコードを実行して、MOVIES、MOVIES_CUSTOMERおよびWATCHED_MOVIEの各表を作成します。
create_movie_table = """
CREATE TABLE MOVIES (
MOVIE_ID NUMBER,
TITLE VARCHAR2(400),
GENRES JSON,
SUMMARY VARCHAR2(16000)
)
"""
create_customer_table = """
CREATE TABLE MOVIES_CUSTOMER (
CUST_ID NUMBER,
FIRSTNAME VARCHAR(200),
LASTNAME VARCHAR(200)
)
"""
create_watched_table = """
CREATE TABLE WATCHED_MOVIE (
DAY_ID TIMESTAMP(6),
MOVIE_ID NUMBER,
PROMO_CUST_ID NUMBER
)
"""
cursor.execute(create_movie_table)
cursor.execute(create_customer_table)
cursor.execute(create_watched_table)
2. 次のコードを実行して、作成した表にいくつかの行を挿入します。
cursor.execute("""
INSERT INTO MOVIES (MOVIE_ID, TITLE, GENRES, SUMMARY) VALUES
(1, 'Inception', '{"Action": "Sci-Fi"}', 'A thief who steals corporate secrets through the use of dream-sharing technology is given the inverse task of planting an idea into the mind of a C.E.O.'),
(2, 'The Matrix', '{"Action": "Sci-Fi"}', 'A computer hacker learns from mysterious rebels about the true nature of his reality and his role in the war against its controllers.'),
(3, 'The Godfather', '{"Drama": "Crime"}', 'The aging patriarch of an organized crime dynasty transfers control of his clandestine empire to his reluctant son.'),
(4, 'Titanic', '{"Romance": "Drama"}', 'A seventeen-year-old aristocrat falls in love with a kind but poor artist aboard the luxurious, ill-fated R.M.S. Titanic.'),
(5, 'Toy Story', '{"Animation": "Adventure"}', 'A cowboy doll is profoundly threatened and jealous when a new spaceman figure supplants him as top toy in a boy''s room.')
""")
cursor.execute("""
INSERT INTO MOVIES_CUSTOMER (CUST_ID, FIRSTNAME, LASTNAME) VALUES
(101, 'John', 'Doe'),
(102, 'Jane', 'Smith'),
(103, 'Sam', 'Wilson'),
(104, 'Emily', 'Clark'),
(105, 'Michael', 'Johnson')
""")
cursor.execute("""
INSERT INTO WATCHED_MOVIE (DAY_ID, MOVIE_ID, PROMO_CUST_ID) VALUES
(TO_TIMESTAMP('2024-10-30 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF'), 1, 101),
(TO_TIMESTAMP('2024-10-31 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF'), 2, 101),
(TO_TIMESTAMP('2024-09-30 12:34:56.123456', 'YYYY-MM-DD HH24:MI:SS.FF'), 3, 101),
(TO_TIMESTAMP('2024-10-31 09:15:23.654321', 'YYYY-MM-DD HH24:MI:SS.FF'), 2, 102),
(TO_TIMESTAMP('2024-11-01 16:45:12.987654', 'YYYY-MM-DD HH24:MI:SS.FF'), 3, 103),
(TO_TIMESTAMP('2024-11-02 18:22:43.123456', 'YYYY-MM-DD HH24:MI:SS.FF'), 4, 104),
(TO_TIMESTAMP('2024-11-03 20:01:00.000000', 'YYYY-MM-DD HH24:MI:SS.FF'), 5, 105)
""")
SQLプロパティ・グラフの作成
次に、頂点表およびエッジ表として作成した表を使用して、SQLプロパティ・グラフを作成します。
新しいセルで、次のコードを実行して、CUSTOMER_WATCHED_MOVIESという名前のプロパティ・グラフを作成します。
cpg = """
CREATE PROPERTY GRAPH CUSTOMER_WATCHED_MOVIES
VERTEX TABLES (
MOVIES_CUSTOMER AS CUSTOMER
KEY(CUST_ID),
MOVIES AS MOVIE
KEY(MOVIE_ID)
)
EDGE TABLES(
WATCHED_MOVIE AS WATCHED
KEY(DAY_ID, MOVIE_ID, PROMO_CUST_ID)
SOURCE KEY (PROMO_CUST_ID) REFERENCES CUSTOMER(CUST_ID)
DESTINATION KEY (MOVIE_ID) REFERENCES MOVIE(MOVIE_ID)
)
"""
cursor.execute(cpg)
この例では、MOVIES_CUSTOMER表およびMOVIES表は頂点(またはノード)になります。AS句を使用して、グラフのラベルをMOVIES_CUSTOMERからCUSTOMERに、MOVIESをMOVIEに変更できます。同様に、WATCHED_MOVIE表がエッジになり、AS句を使用してラベルをWATCHED_MOVIEからWATCHEDに変更できます。
今では、このグラフでモデル化されたパターンを、顧客が視聴した動画で簡単にクエリできるようになりました。
サンプル・グラフ問合せの実行
現在は、単純なSQLクエリを実行して、顧客がグラフを使用してどのムービーを見たかを調べ、python-oracledbライブラリを使用してクエリを実行できます。
次のコードを実行してグラフ問合せを実行し、結果セットの各行を出力します。
sample_graph_query = """
SELECT * FROM GRAPH_TABLE(CUSTOMER_WATCHED_MOVIES
MATCH (c IS CUSTOMER) -[w IS WATCHED]-> (m IS MOVIE)
COLUMNS(c.FIRSTNAME AS FIRSTNAME, c.LASTNAME AS LASTNAME, m.TITLE AS MOVIE_TITLE, w.DAY_ID as DAY_WATCHED)
)
"""
for row in cursor.execute(sample_graph_query):
print(row)
SQLプロパティ・グラフおよびLangchainを使用した生成AI問合せのコンテキストの向上
Langchainを組み合せてプロンプト・テンプレートを作成し、SQLプロパティ・グラフ問合せからより多くのコンテキストを提供してLLMから特定の回答を取得できるようにします。
例1— 結果セットのすべての行に関するインサイトを取得
新しいセルで、必要なLangchainライブラリをインポートし、レスポンスの取得に使用するLLMを設定します。これにより、Langchainライブラリを介してLLMにアクセスできるようになります。
from langchain_openai import ChatOpenAI
# Set up OpenAI LLM
print ("WARNING: The step will fail if the API key is not present or is incorrect.")
print ("Please update the OpenAI_API_key before calling the llm the next step.")
# set the LLM to get response
llm = ChatOpenAI(
model_name='gpt-3.5-turbo-16k',
temperature = 0.1,
openai_api_key="<your OpenAI API key>",
max_tokens=2000
)
print("The LLM model you will use is OpenAI ChatGPT 3.5")
次に、プロンプト・テンプレートとチェーンを作成します。
プロンプト・テンプレートは、プロンプトを生成するための再現可能な方法です。これにより、問合せまたはフィールド名のプレースホルダを持つ既存の文に基づいてプロンプトを作成できます。チェーンは、AIコンポーネントを結び付けてコンテキスト対応の応答を提供する一連の自動アクションです。チェーンは、LangChainのコア概念であり、リンクで構成され、各アクションが結合されます。
この例では、プロンプト・テンプレートを使用して、サマリーに基づいて特定のムービーのジャンルを検索します。
from langchain_core.prompts import PromptTemplate
# Create a prompt template
template = "What is the genre of {movie} based on this summary: {summary}?"
prompt = PromptTemplate.from_template(template)
# Create a chain with the prompt and LLM
chain = prompt | llm
次に、SQLプロパティ・グラフ問合せを実行し、レスポンスに基づいてチェーンを起動できます。この例では、質問に答えます
「お客様が視聴したすべての映画について、映画の概要に基づくジャンルは何ですか?」
そのためには、最初にSQLプロパティ・グラフ問合せを記述して、顧客が視聴したすべてのムービーを検索し、結果セットとしてムービーのタイトルとサマリーを取得します。その後、プロンプト・テンプレート内のプレースホルダを参照して、結果セットを繰り返し処理し、データをチェーンに渡すことができます。
# Define your SQL query
sql = """
SELECT DISTINCT MOVIE_TITLE, MOVIE_SUMMARY
FROM GRAPH_TABLE( CUSTOMER_WATCHED_MOVIES
MATCH (c1 IS CUSTOMER)-[e1 IS WATCHED]->(m IS MOVIE)
COLUMNS (m.title as MOVIE_TITLE, m.summary as MOVIE_SUMMARY)
)
"""
# Execute the SQL query
cursor.execute(sql)
# Fetch all rows from the executed query
rows = cursor.fetchall()
# print(rows)
# Print the results
for row in rows:
# Run the chain and print the output
result = chain.invoke({'movie': row[0], 'summary': row[1]})
print(result.content)
結果セット内の各ムービーのジャンルを示す、LLMからのレスポンスが表示されます。
例2— 結果セットに基づいて傾向を分析
データセットに映画のジャンルがあるので、これは最も影響力のあるクエリではないかもしれません。そのため、別の例を実行して、LLMにユーザーの視聴習慣に基づいてユーザーの視聴プリファレンスを分析するように依頼します。
そのためには、新しいプロンプト・テンプレートを使用して問合せを実行します。ここでは、LLMのコンテキストとして、特定の顧客の視聴習慣の結果セットを提供することにより、次の質問に答えます。
「顧客が視聴したこの映画のデータセットに基づいて、映画のタイトル、ジャンル、要約が含まれています。「このユーザの映画鑑賞をどのように表現しますか?」
# Create a prompt template
template = "Based on this dataset of movies a customer has watched, containing movie titles, genres and summaries. How would you describe the movie watching preferences of this user? {data}"
prompt = PromptTemplate.from_template(template)
chain = prompt | llm
# Define your SQL query
sql = """
SELECT DISTINCT MOVIE_TITLE, MOVIE_SUMMARY
FROM GRAPH_TABLE( CUSTOMER_WATCHED_MOVIES
MATCH (c1 IS CUSTOMER)-[e1 IS WATCHED]->(m IS MOVIE)
WHERE c1.CUST_ID = 101
COLUMNS (m.title as MOVIE_TITLE, m.summary as MOVIE_SUMMARY)
)
"""
# Execute the SQL query
cursor.execute(sql)
# Fetch all rows from the executed query
rows = cursor.fetchall()
# print(rows)
result = chain.invoke({'data': rows})
print(result.content)
LLMからの応答が表示され、顧客101の視聴習慣の分析が示されます。これは、お客様がどのタイプの映画を好む傾向があるかについて情報に基づいた意思決定を行うために役立つ可能性があるため、カスタマイズされた推奨事項を提供できます。
例3— 少ない労力でより大きな結果セットに関するインサイトを得る
最後に、結果セットについてLLMの質問を行うことで、問合せの作成にかかる時間を短縮できます。たとえば、次の質問に答えたい場合は集計を行う必要があり、新しいSQLユーザーであればわかりにくい場合があります。
「どのユーザーが一番映画を見たか?」
この質問に答えることができる完全なSQLまたはSQLプロパティ・グラフ問合せを記述するかわりに、結果セットをLLMに提供し、LLMにその問合せを特定するように依頼できます。
# Create a prompt template
template = "Based on this dataset of movies that customers have watched, containing customer IDs, movie IDs and date watched, Which customer has watched the most movies? {data}"
prompt = PromptTemplate.from_template(template)
chain = prompt | llm
# Define your SQL query
sql = """
SELECT *
FROM GRAPH_TABLE( CUSTOMER_WATCHED_MOVIES
MATCH (c1 IS CUSTOMER)-[e1 IS WATCHED]->(m IS MOVIE)
COLUMNS (c1.CUST_ID as customer_id, m.MOVIE_ID as movie_id, e1.DAY_ID as date_watched)
)
"""
# Execute the SQL query
cursor.execute(sql)
# Fetch all rows from the executed query
rows = cursor.fetchall()
# print(rows)
result = chain.invoke({'data': rows})
print(result.content)
LLMからの応答が表示され、どのユーザーが最も多くの映画を視聴したかが示されます。これは、詳細な問合せを記述せずにデータに関する質問にすばやく回答できるため、コードの記述にかかる時間を短縮できるため、ビジネスは必要な情報をより迅速に取得できます。
次のステップ
おめでとう! 単純なGraphRAGソリューションを実装しました。SQLプロパティ・グラフの詳細は、Oracle Graphのドキュメントを参照してください。この記事のパート2については、複雑なユースケースのためにこのソリューションをさらに強化するためにベクトル検索を組み込みます。
コメント
コメントを投稿