LoginSignup
5
6

[LangChain編] 新リリース Oracle Database 23ai と Cohere で実装するエンタープライズRAG

Last updated at Posted at 2024-05-13

本記事は日本オラクルが運営する下記Meetupで発表予定の内容になります。発表までに今後、内容は予告なく変更される可能性があることをあらかじめご了承ください。

はじめに

これまで BETAリリースだった Oracle Database のベクトルデータベース機能 AI Vector Search を搭載したバージョンが先日ようやく正式リリースとなりました。その名は Oracle Database 23ai です。これまであらゆるワークロードを取り入れてきた Oracle Database ですが、晴れてベクトルデータベースのカテゴリに仲間入りです。

Oracle Databaseは沢山の先進的な機能を取り込んだデータベースですが、このリリースは名前からわかる通り、著しい早さで拡大しているAI市場を意識したバージョンとなります。とりわけ昨今のAI市場の話題の中心はやはり大規模言語モデル。本記事もそれに倣って、ベクトルデータベースとしてのOracle Database 23aiを使ったシンプルなRAG構成によるテキスト生成の実装サンプルをご紹介したいと思います。

※ RAGとは何ですか?という方はこちらの記事をご参照ください。

RAGの構成とシナリオ

RAGの構成

本記事でご紹介するRAGの構成と処理フローの概要が下図になります。前回はフル・スクラッチでRAGを実装しましたが、今回はLangChainを使った構成となります。LangChainはかなり強力なフレームワークで、LLMを使ったアプリケーションの開発ユーザー御用達のライブラリです。

image.png

構成に利用するサービスは以下の通り。

  • テキスト生成モデル:Cohere(Command R Plus) ※番外編で OCI Genrative AI Service(Command)
  • ドキュメントデータのベクトル化に利用するモデル : Oracle Cloud Generative AI Service(embed-multilingual-v3.0)
  • ベクトルデータベース: Oracle Database 23ai Free(OCI Compute Serviceにインストール)

※LangChainって何?という方は下記の記事をご参照ください。

ドキュメントデータ

ベクトルデータベースにロードするドキュメントデータは下図のような内容のPDFファイルです。テキストの内容としては架空の製品であるロケットエンジンOraBoosterの説明文章です。企業内のデータを想定し、テキストの内容としては、存在しない製品かつ完全な創作文章、ということでLLMが学習しているはずのないデータということになります。後の手順でこちらのPDFファイルをベクトルデータベースにロードします。

image.png

シナリオ

本記事では下記のようなシナリオでテキスト生成を行い、LLMが学習していないデータ(上述のPDFの内容)に関する質問に対してRAG構成でうまく回答できることを確認します。また、非RAG構成とのテキスト生成結果を比較し、RAGの有効性を確認します。

  1. 架空の製品OraBoosterについて質問をします。
  2. プロンプトを入力しOraBoosterの説明テキストが生成されていることを確認します。この際、LangChainのChainの仕組みにより下記の処理が自動化されているため、LangChainを使わないスクラッチのRAGと比べてコードコードが大幅に簡素化されていることが分かります。
    • LLMはこの製品の知識を持ち合わせていないため、ベクトルデータベースに類似検索を行い、ヒントとなるテキストデータ(チャンクテキスト)を検索
    • もとのプロンプトと類似検索で取得したチャンクテキストををテキスト生成モデルに入力しテキストを生成
  3. 前回同様ベクトルデータベースへのChainを使わずに、LLMにクエリしテキスト生成を行い、上記「2」で生成されたテキストと比較しRAGの有効性を確認します。

※ 実装は興味ないので結果だけ知りたいですという方は「テキスト生成を実行する」の章をご参照ください。

実装

1. Oracle Database 23ai Freeをインストールする

下記のインストールマニュアルに従ってOracle Database 23ai Freeをインストールします。

簡単に手順を記載しておきます。基本は下記3ステップでインストールからデフォルトPDBの作成完了。数分の処理です。

Oracle Databaseをインストールする環境にログイン後、プリインストールのRPMをインストールします。

sudo -s
dnf -y install oracle-database-preinstall-23ai

Oracle Database 23ai FreeのRPMをダウンロードしインストールします。

wget https://download.oracle.com/otn-pub/otn_software/db-free/oracle-database-free-23ai-1.0-1.el8.x86_64.rpm
dnf -y install oracle-database-free-23ai-1.0-1.el8.x86_64.rpm

configureを実行しデータベースを構成します。

/etc/init.d/oracle-free-23ai configure

cofigureの途中でパスワードの設定があり、これがSYS, SYSTEM、PDBADMINユーザー共通のパスワードとなります。

上記3ステップでOracle Database 23ai Freeのインストール完了です。

configureが完了するとデフォルトPDB(FREEPDB1)が出来上がりますので今回もこのPDBをそのまま利用します。

次に Oracleユーザーの環境変数の定義をしておきます。

sudo su - oracle
vi ~/.bashrc

bashrcにお馴染みの下記環境変数を定義します。

export ORACLE_SID=FREE
export ORACLE_BASE=/opt/oracle
export ORACLE_HOME=/opt/oracle/product/23ai/dbhomeFree
export PATH=$PATH:$ORACLE_HOME/bin

環境変数の有効化

source ~/.bashrc

次にアプリケーションからの接続用にデータベースユーザーを作成するため、SYSユーザーでPDBに接続します。

sqlplus sys@localhost:1521/freepdb1 as sysdba

DBユーザーを作成(ユーザー名docuser, パスワードdocuser)、念のためどこからでも接続できるようにしておきます。

grant connect, ctxapp, unlimited tablespace, create credential, create procedure, create table to docuser identified by docuser; grant execute on sys.dmutil_lib to docuser; 
grant create mining model to docuser;


BEGIN
    DBMS_NETWORK_ACL_ADMIN.APPEND_HOST_ACE(
        host => '*',
        ace => xs$ace_type(
            privilege_list => xs$name_list('connect'),
            principal_name => 'docuser',
            principal_type => xs_acl.ptype_db
        )
    );
END;
/

2. ドキュメントをベクトルデータベースにロードする

ここからは、PDFファイルをテキストチャンクに分割し、埋め込みモデルでベクトルデータベースにロードする処理です。下図のようなフローでPDFファイルを最終的にベクトルデータベースにロードしてゆきます。前回のフルスクラッチでのRAGの実装とは異なり、今回はLangChainを利用するためこの過程は非常にシンプルなコードとなります。

image.png

ここからはPythonで処理を行います。本記事ではPythonの実行環境はOracle Databaseと同じOS環境にAnacondaをインストールしていますが、どこにPython環境を作成していても大丈夫です。

まずは、LangChainのドキュメントローダーで/tmpに配置したPDFファイルをロードし、テキストに変換します。

from langchain.document_loaders import PyPDFLoader

loader = PyPDFLoader("/tmp/rocket.pdf")
documents = loader.load_and_split()
print(documents)

下記のようにPDFファイルドキュメントローダーにより構造解析されて、テキストに変換されます。

[Document(page_content='当社が開発したロケットエンジンである OraBooster は、次世代の宇宙探査を支える先進的な推進技術の\n象徴です。その独自の設計は、高性能と革新性を融合させ、人類の宇宙進出を加速させるための革命的\nな一歩となります。\nこのエンジンの核となるのは、量子ダイナミックス・プラズマ・ブースターです。このブースターは、\n量子力学の原理に基づいてプラズマを生成し、超高速で加速させます。その結果、従来の化学反応より\nもはるかに高い推力を発生し、遠く離れた惑星や星系への探査を可能にします。\nさらに、エンジンの外殻にはナノファイバー製の超軽量かつ超強度の素材が使用されています。この素\n材は、宇宙空間の過酷な環境に耐え、高速での飛行中に生じる熱や衝撃からロケットを守ります。\nまた、ハイパーフォトン・ジャイロスコープが搭載されており、極めて高い精度でロケットの姿勢を維\n持し、目標を追跡します。これにより、長時間にわたる宇宙飛行中でも安定した飛行軌道を維持し、\nミッションの成功を確保します。\nさらに、バイオニック・リアクション・レスポンダーが統合されています。このシステムは、人工知能\nと生体認識技術を組み合わせ、ロケットの異常な振動や動きを検知し、自己修復機能を活性化します。\n総じて、この新開発のロケットエンジンは、革新的な技術と未来志向の設計によって、宇宙探査の新た\nな時代を切り開くことでしょう。その高い性能と信頼性は、人類の夢を実現するための力強い支援とな\nることでしょう。', metadata={'source': '/tmp/rocket.pdf', 'page': 0})]

上記のテキストをLangChainのテキストスプリッターでチャンクテキストに分割します。今回もとりあえずCharacterTextSplitterで固定長で区切ってみます。(LangChainにはほかにもいくつかのテキストスプリッターがありますので興味ある方はいろいろためしてみてください。)

from langchain.text_splitter import CharacterTextSplitter

text_splitter = CharacterTextSplitter(separator="", chunk_size=100, chunk_overlap=10)
docs = text_splitter.split_documents(documents)
print(docs)

下記の9つのチャンクテキストに分割されたことがわかります。

[Document(page_content='当社が開発したロケットエンジンである OraBooster は、次世代の宇宙探査を支える先進的な推進技術の\n象徴です', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='その独自の設計は、高性能と革新性を融合させ、人類の宇宙進出を加速させるための革命的\nな一歩となります。\nこのエンジンの核となるのは、量子ダイナミックス・プラズマ・ブースターです', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='このブースターは、\n量子力学の原理に基づいてプラズマを生成し、超高速で加速させます。その結果、従来の化学反応より\nもはるかに高い推力を発生し、遠く離れた惑星や星系への探査を可能にします', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='さらに、エンジンの外殻にはナノファイバー製の超軽量かつ超強度の素材が使用されています。この素\n材は、宇宙空間の過酷な環境に耐え、高速での飛行中に生じる熱や衝撃からロケットを守ります', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='また、ハイパーフォトン・ジャイロスコープが搭載されており、極めて高い精度でロケットの姿勢を維\n持し、目標を追跡します', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='これにより、長時間にわたる宇宙飛行中でも安定した飛行軌道を維持し、\nミッションの成功を確保します。\nさらに、バイオニック・リアクション・レスポンダーが統合されています', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='このシステムは、人工知能\nと生体認識技術を組み合わせ、ロケットの異常な振動や動きを検知し、自己修復機能を活性化します', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='総じて、この新開発のロケットエンジンは、革新的な技術と未来志向の設計によって、宇宙探査の新た\nな時代を切り開くことでしょう', metadata={'source': '/tmp/rocket.pdf', 'page': 0}), 
Document(page_content='その高い性能と信頼性は、人類の夢を実現するための力強い支援とな\nることでしょう', metadata={'source': '/tmp/rocket.pdf', 'page': 0})]

見にくいので少し整形します。

import pandas as pd
contents = []
for doc in docs:
    contents.append(doc.page_content)
pd.DataFrame(contents)

下記9つのチャンクテキストです。

image.png

以降が、このチャンクテキストをベクトルデータベースにロードしつつ、埋め込みモデルを使って、ベクトル化を行う処理です。

まずは、作成済のdocuserでfreepdb1に接続します。

import oracledb

username = "docuser"
password = "docuser"
dsn = "localhost/freepdb1"

try:
    connection = oracledb.connect(user=username, password=password, dsn=dsn)
    print("Connection successful!")
except Exception as e:
    print("Connection failed!")

必要なライブラリをimportします。

from langchain_community.vectorstores import oraclevs
from langchain_community.vectorstores.oraclevs import OracleVS
from langchain_community.vectorstores.utils import DistanceStrategy
from langchain_core.documents import Document
from langchain_community.embeddings import OCIGenAIEmbeddings

利用する埋め込みモデルを定義します。今回はOCI Generative AI Serviceのembed-multilingual-v3.0というモデルを利用します。

embeddings = OCIGenAIEmbeddings(
    model_id="cohere.embed-multilingual-v3.0",
    service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
    compartment_id="ocid1.compartment.oc1..aaaaaaaxxxxxxx",
)

LangChainのお決まりの関数であるfrom_documentsでベクトルデータベースにチャンクテキストをロードします。以下のように、ここまでの手順で定義済のオブジェクトを使って下記一つのコードでチャンクテキストをベクトル化しますが、ここでチャンクテキストとベクトルデータをロードする表を指定することになります。

vector_store_dot = OracleVS.from_documents(
    # 作成済のチャンクテキスト
    docs,
    # 定義済の埋め込みモデル
    embeddings,
    # 定義済のデータベースのconnectionオブジェクト
    client=connection,
    # 新規作成する表の名前を任意で指定
    table_name="doc_table",
    # ベクトル検索時に使う距離計算の方法
    distance_strategy=DistanceStrategy.DOT_PRODUCT,
)

今回はベクトル検索時に使う距離計算の方法を「ドット積」にしています。Generative AIの埋め込みモデル(Cohereのembed-multilingual-v3.0)はモデルを学習させる際にドット積を使っていることからそれに合わせてみました。(英語版のモデルであるembed-english-v3.0はコサイン類似度を使っているそうです。)こちらはチューニングポイントになりますから様々な距離計算を試してみてください。上記のDOT_PRODUCTの部分を他の計算方法(例えば、EUCLIDEAN_DISTANCEやCOSINEに変更するだけです。詳細はこちら

また、今回はどちらでも構いませんが、以下のようにコードを実行すると索引も作成できます。下記の例ではIVF索引を作成しています。詳細はこちら

oraclevs.create_index(connection, vector_store_dot, params={"idx_name": "rocket", "idx_type": "IVF"})

ここまでで、PDFファイルをベクトルデータベースにロードする処理が完了です。

3. ベクトルデータベースに作成されたデータを確認してみる(オプション)

ロードされたベクトルデータを確認してみます。まずはdocuserでデータベースに接続します。

sqlplus docuser/docuser@freepdb1

SQLPLUSの表示設定を変更します。

SET ECHO ON 
SET FEEDBACK 1 
SET NUMWIDTH 10 
SET LINESIZE 80 
SET TRIMSPOOL ON 
SET TAB OFF 
SET PAGESIZE 10000 
SET LONG 10000

まず、作成した表の確認行います。下記のように、作成した表doc_tableには4つのカラムが作成されていることがわかります。

SQL> desc doc_table;
 名前                                    NULL?    
 ----------------------------------------- -------- ----------------------------
 ID                                        NOT NULL RAW(16)
 TEXT                                               CLOB
 METADATA                                           CLOB
 EMBEDDING                                          VECTOR(1024, FLOAT32)

行数を確認してみます。作成された9つのチャンクがあることがわかります。

SQL> select count(*) from doc_table;

  COUNT(*)
----------
         9

1行が選択されました。

続いて、id列を確認します。これがチャンクIDとなります。

SQL> select id from doc_table;

ID
--------------------------------
0F7492ED808A1434
119D5547095529E1
51093CB177D05731
7B55580B08284136
7D4C74E363F4D84B
9A7388795F6F32F9
A982E7674CE83517
E310339899A10640
E32DA2E49B802D4A

9行が選択されました。

続いて、text列を確認します。これがチャンクテキストになります。

SQL> select text from doc_table;

TEXT
--------------------------------------------------------------------------------
このブースターは、
量子力学の原理に基づいてプラズマを生成し、超高速で加速させます。その結果、従来の
化学反応より もはるかに高い推力を発生し、遠く離れた惑星や星系への探査を可能にし
ます

また、ハイパーフォトン・ジャイロスコープが搭載されており、極
めて高い精度でロケットの姿勢を維
持し、目標を追跡します

さらに、エンジンの外殻にはナノファイバー製の超軽量かつ超強度
の素材が使用されています。この素
材は、宇宙空間の過酷な環境に耐え、高速での飛行中に生じる熱や衝撃からロケットを守
ります

当社が開発したロケットエンジンである OraBooster は、次世代の宇
宙探査を支える先進的な推進技術の
象徴です

その独自の設計は、高性能と革新性を融合させ、人類の宇宙進出を
加速させるための革命的
な一歩となります。
このエンジンの核となるのは、量子ダイナミックス・プラズマ・ブースターです


その高い性能と信頼性は、人類の夢を実現するための力強い支援と

ることでしょう

これにより、長時間にわたる宇宙飛行中でも安定した飛行軌道を維
持し、
ミッションの成功を確保します。
さらに、バイオニック・リアクション・レスポンダーが統合されています


このシステムは、人工知能
と生体認識技術を組み合わせ、ロケットの異常な振動や動きを検知し、自己修復機能を活
性化します

総じて、この新開発のロケットエンジンは、革新的な技術と未来志
向の設計によって、宇宙探査の新た
な時代を切り開くことでしょう


9行が選択されました。

続いて、METADATA列を確認します。これがメタデータ(各チャンクの元のファイル名、ディレクトリ、ページ)になります。

SQL> select metadata from doc_table;

METADATA
--------------------------------------------------------------------------------
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}
{"source": "/tmp/rocket.pdf", "page": 0}

9行が選択されました。

続いて、embedding列を確認します。これがチャンクテキストのベクトル値です。

SQL> select embedding from doc_table;

EMBEDDING
--------------------------------------------------------------------------------
[7.70187378E-003,2.79846191E-002,-1.53961182E-002,2.55432129E-002,-2.21405029E-0
02,-1.93977356E-003,2.78282166E-003,-2.00805664E-002,8.43048096E-003,-3.52478027
E-003,8.34960938E-002,3.3203125E-002,6.65283203E-003,3.6239624E-004,5.10406494E-
003,4.47387695E-002,4.81262207E-002,-2.25524902E-002,3.35083008E-002,-1.48544312
E-002,-3.94287109E-002,1.68914795E-002,7.90405273E-002,-8.69140625E-002,-3.38439
941E-002,-1.61895752E-002,-8.81195068E-003,2.21252441E-002,-3.78112793E-002,-3.4
8815918E-002,-3.40270996E-002,2.78778076E-002,5.53131104E-003,3.23791504E-002,-1
.26037598E-002,-1.50985718E-002,2.1484375E-002,-1.53274536E-002,1.38244629E-002,
2.2064209E-002,-7.36236572E-003,-1.89361572E-002,-1.33514404E-003,3.9100647E-003
,-4.34112549E-003,-3.92913818E-003,9.04846191E-003,2.92778015E-003,3.75061035E-0
02,-3.05938721E-003,4.9621582E-002,-1.42974854E-002,-8.46862793E-003,-6.99234009
E-003,7.70187378E-003,1.63421631E-002,-1.32675171E-002,6.78253174E-003,3.9703369
1E-002,1.26266479E-003,1.93481445E-002,3.27682495E-003,-3.44238281E-002,1.457214

4. RAGを実装する(LangChainのChainの実装)

ここからがRAGの実装です。前回のフルスクラッチと異なり、この部分のコードはLangChainにより大幅にシンプルになります。下図のような処理を経て、テキスト生成が行われますが、実際のコードはLangChainのChainを定義する作業になります。

image.png

Chainとは「繋ぐ」という意味になりますが、文字通り、プロンプトテキスト、Embeddingモデル、ベクトルデータベース、テキスト生成モデルを繋ぐChainを定義するコードが以下になります。

まずは下記コードでプロンプトテンプレートを作成します。

from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain_core.prompts import PromptTemplate

template = """
次のコンテキストに基づいて日本語で回答してください。: {context}
 
質問: {question}
"""
prompt = PromptTemplate.from_template(template)

次に、テキスト生成のモデルを定義するため、今回利用するCohereのAPIキーを定義します。

import os
import getpass

os.environ["COHERE_API_KEY"] = getpass.getpass("Cochere API Key:")

上記でCohereのモデルが使えるようになりましたので、実際にテキスト生成モデルを指定します。今回使うのはcohereの最新モデル command-r-plusです。

from langchain_cohere import ChatCohere

llm_cohere = ChatCohere(model="command-r-plus")

ベクトル検索を実行するベクトルデータベースを定義します。もちろんここでは事前に定義したvector_store_dotを指定します。

retriever = vector_store_dot.as_retriever()

これでChainを作る必要なパーツが全て定義できました。これまで定義したオブジェクトを全て使い、下記のようにChainを定義します。

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm_cohere
    | StrOutputParser()
)

ここまでの処理でRAGのフロー実装完了です。LangChainにより、RAGの実装は事実上Chainを定義するだけというシンプルなものになることがわかります。

5. テキスト生成を実行する

RAGが実装できましたので、テキスト生成を行います。

print(chain.invoke("OraBoosterとは何ですか?"))

上記プロンプトで生成されたテキストが以下になります。ベクトルデータベースのデータを使ってテキスト生成していることがわかります。

OraBooster 次世代の宇宙探査を支える先進的な推進技術を象徴するロケットエンジンです量子力学の原理に基づいてプラズマを生成し超高速で加速させることで従来の化学反応よりもはるかに高い推力を発生させますこれにより遠く離れた惑星や星系への探査が可能となりますまたハイパーフォトンジャイロスコープを搭載しており極めて高い精度でロケットの姿勢を維持し目標を追跡することができますその高い性能と信頼性は人類の夢を実現するための力強い支援となるでしょう

前回同様、非RAG構成と比較してみます。Chainオブジェクトではなく、LLMを定義しただけのオブジェクトを使ってテキスト生成を実行してみます。

response = llm_cohere.invoke("OraBoosterとは何ですか?")
print(response.content)

ベクトルデータベースを参照しないため、ハルシネーション満載のテキストが生成されます。しかも前回同様、口腔衛生用品としてテキスト生成がされています。これはOraBoosterという単語が Oral Careの単語に類似しているからだと思われます。

OraBooster 歯の健康をサポートし口腔内の環境を改善することを目的とした口腔衛生用品のブランドですOraBooster の製品には歯磨き粉マウスウォッシュ歯ブラシなどが含まれ口腔内の細菌のバランスを整え歯と歯茎の健康を促進するとされています

OraBooster の製品は天然成分を使用しており口腔内の pH バランスを最適化することでプラークや歯垢の形成を抑制し歯肉炎や歯周病を予防する効果が期待できますまた一部の製品にはフッ素が含まれており虫歯の予防にも役立ちます

OraBooster 口腔内の健康が全身の健康と密接に関わっているという考えに基づいて開発されており口腔内の環境を改善することでより健康的な生活を送ることを目指していますこれらの製品は歯科医や医療専門家と協力して開発されており安全性と有効性がテストされています

OraBooster の製品はオンラインや一部の小売店で購入することができ定期的な歯磨きやフロスとともに使用することでより効果的な口腔ケアが実現できます

以上、RAGと非RAG構成の違いでした。

テキスト生成時にORA-03043エラーが出力される場合は要修正

2024/5/13 現在 LangChainにはBugがあります。テキスト生成を実行し、内部的にベクトルデータベースへの類似検索が実行された際に下記のエラーがでます。SQL内でのバインド変数の使い方に不具合がありORA-03043エラーとなります。

# テキスト生成実行
print(chain.invoke("OraBoosterとは何ですか?"))

# 出力されるエラー
2024-05-13 12:55:14,818 - ERROR - An unexpected error occurred: ORA-03043: Invalid value specified for the number of rows or partitions to fetch (':'). Must be a positive integer literal.
Help: https://docs.oracle.com/error-help/db/ora-03043/
Traceback (most recent call last):
  File "/home/opc/anaconda3/lib/python3.11/site-packages/langchain_community/vectorstores/oraclevs.py", line 54, in wrapper
    return func(*args, **kwargs)

こちらのエラーは既にgithubのLangChain公式リポジトリにファイルされており、今後、こちらのように修正され、修正版がリリースされることになりますが、githubで記されている下記赤枠の内容と同じ修正をするとエラーなく動作します。

image.png

具体的には下記2点です。(oraclevs.pyファイルのパスはエラーに出力されています。)

  • ファイルoraclevs.pyの626行目の「:k」を「{k}」に変更
  • 同ファイルの630行目から「, k=」を削除

6. 番外編

RAG構成をあまり沢山のクラウドサービスに分散させたくないケースは多いと思います。ということで全てのサービスをOracle Cloudにそろえたパターンで構成すると下図のようになります。

image.png

テキスト生成モデルをCohere Command R Plusから OCI Generative AI Serviceの Command に置き換えた構成です。同モデルはまだ multilingual対応していない英語版のモデルですが、どれくらいのテキストが生成できるか試してみたいと思います。

英語版のモデルなのでプロンプトテンプレートは英語にしてみます。

from langchain.schema.output_parser import StrOutputParser
from langchain.schema.runnable import RunnablePassthrough
from langchain_core.prompts import PromptTemplate

template = """Answer the question based only on the following context:
{context}
 
Question: {question}
"""
prompt = PromptTemplate.from_template(template)

そして、Generative AI Serviceのテキスト生成モデルを定義します。

from langchain_community.llms import OCIGenAI

llm = OCIGenAI(
    model_id="cohere.command",
    service_endpoint="https://inference.generativeai.us-chicago-1.oci.oraclecloud.com",
    compartment_id="ocid1.compartment.oc1..aaaaaaaapxxxxxxxxxxxxxxxxxxxxxx",
)

次にベクトルデータベースの定義します。こちらはこれまでと同じです。

retriever = vector_store_dot.as_retriever()

Chainの定義もこれまでと同じです。

chain = (
    {"context": retriever, "question": RunnablePassthrough()}
    | prompt
    | llm
    | StrOutputParser()
)

まずは英語のプロンプトでテキスト生成を実行してみます。

print(chain.invoke("what is the OraBooster?"))

ベクトルデータベース内のテキストを英訳して、テキスト生成を実行しています。内容も悪くはないです。

OraBooster is a symbol of advanced progressive technology that will help support future space exploration endeavors.

お次は日本語のプロンプトでテキスト生成を実行してみます。

print(chain.invoke("OraBoosterとは何ですか?"))

英語プロンプトのときとほぼ同様の内容でした。

The OraBooster is a symbol of advanced progressive technology that will help support future space exploration missions.

以上、番外編でした。
Generative AI Serviceの multilingual のテキスト生成モデルはもう間もなくリリースされるそうなので待ち遠しい限りです。

さいごに

ベクトルデータベースに今回と同じOracle Database 23aiを使った以前の記事ではフルスクラッチでRAGの実装をしてみました。そして今回はLangChainを使っての実装です。

両者とも最もシンプルなRAG構成の実装ですが、比較してみると、やはりLangChainのほうがコーディングはかなり楽です。ドキュメントローダー、テキストスプリッターからベクトルデータベースへのロード、Chainの定義など全てのフローにおいて、LangChainユーザーにはお決まりの関数、お決まりのコード順序で、コード量もかなり少ないと思います。

その反面、やはりフルスクラッチでないと詳細な調整ができないのだなとも感じました。例えば、一番重要なベクトルデータベースへの類似検索のSQLをユーザー自身が定義できるのはフルスクラッチの大きなメリットだと思います。

その他、RAGの実装では様々な論文が毎月のように提出され、RAGのフロー設計においては新しいタイプのものが提案されています。この中にはLangChainがまだ実装できていないフローも多々あり、先端の手法を取り入れたい場合はやはりフルスクラッチでの実装ができなければいけないのだなという印象です。

5
6
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
5
6