13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

OCI Generative AI の Cohere Command R+とOracle Database 23aiを使ってチャットアプリを試してみた

Last updated at Posted at 2024-06-21

はじめに

Oracle Cloud Infrastructure (OCI) の生成AIサービスから、Cohere社の「Command R+」が登場し遂に日本語にも対応しました。それに伴い、OCIネイティブな生成AIを使った日本語のチャットアプリを簡単に作成できるようになりました。
チャットアプリに使用される生成AIで使われている大規模言語モデル (LLM) は、文章として意味のある回答を生成することに非常に優れていますが、応答に無意味な内容や不正確な情報を含めてしまうこともあります。これが特定のユースケースでは問題となる場合もあります。
そこでこの問題を解決するために使われているのが「検索拡張生成」(Retrieval-Augmented Generation, RAG)という技術です。RAGを使うことで、外部情報をソースとして回答させることができ、生成AIの精度を向上させることができます。この技術を活用して、より正確で信頼性の高い応答を生成できるようになります。

実際にRAGを使ったチャットアプリを試してみると、以下のような具体的な製品情報を踏まえた回答を出力させることができます。

今回はRAGの技術に触れつつ、OCI上でチャットアプリを構築していきたいと思います。

Retrieval Augmented Generation (RAG)とは

RAGは、テキスト生成に外部ソースの情報検索を組み合わせることで回答精度を高める方法です。この技術の背景には、生成AIとのやり取りにおいて、LLMのみを使って回答を作成した場合、LLMが事前に学習した情報からしか回答が作成されないという問題があります。最新の情報を出力に反映できなかったり、根拠がなく信頼性に欠ける回答が出力されるハルシネーションという現象が起こるという課題があり、この課題を解決する方法としてRAGが期待されています。

ベクトル検索とは

RAGにおいて、外部ソースの情報を検索する場合に、質問に関係した内容をデータソースから検索する必要があります。その情報検索の方法の一つにベクトル検索があります。
ベクトル検索の技術を理解する上で、まずはベクトルについて理解する必要があります。ベクトルとは、向きと大きさを持つ量で、数学や物理学でよく使われています。具体的には、ベクトルは矢印で表され、矢印の長さが大きさ、矢印の指す方向が向きを示しています。この矢印はある始点から終点への移動を表していると考えることができ、x, yの成分を持つ矢印として、2次元空間に落とし込むことができます。

文章や単語をベクトル化することをEmbed(埋め込み)と言います。
埋め込みとは、システムがテキストや文章などの現実世界のデータを理解するためにベクトル (数値列) で表現したものです。画像のように意味の近い文章は近いベクトルを持ち、意味が反対の文章は遠いベクトルを持つため、文章をベクトル化することで文章の意味を計算できるようになります。ベクトルを比較することで類似性や相違性を捉えることができるため、その類似性を計算して近しいベクトルを検索することをベクトル検索 (類似度検索) と言います。
埋め込みはデータ内の関係性の発見、センチメント分析、言語翻訳、レコメンドシステムなどに使われます。

そこで今回はRAGの技術を使った簡単なアプリケーションをOCI上で構築してみたいと思います。

OCIで利用するサービス

利用する主なサービスは、以下の通りです。

  • Virtual Machine
  • Oracle Database 23ai
  • OCI Generative AI

Oracle Database 23aiとは

Oracle Database 23aiではAIに重点を置いており、AIを使って生成されたベクトルデータを保存し、効率的にオブジェクトの類似性を検索できるAI Vector Searchという機能が追加されています。ドキュメント、画像、ビデオ、サウンドなど様々なオブジェクトをベクトル化することで、それらの間の類似度を検索できるようになります。Oracle Database 23aiでは、この類似度検索とビジネスデータの検索を簡単に組み合わせることができます。
Oracle Database 23aiでVector Searchを試してみたい方はOCI Tutorialをご確認ください。

OCI Generative AIとは

OCI Generative AIは、Cohere社のモデルを使ったOracle Cloud Infrastructureのフルマネージドな生成AIのサービスです。詳しくはこちらの記事をご覧ください。

OCI Generative AIには4つの機能があります。

機能名 モデル
チャット・モデル cohere.command-r-plus, cohere.command-r-16k, meta.llama-3-70b-instruct
テキスト生成 cohere.command, cohere.command-light, meta.llama-2-70b-chat
テキスト要約 cohere.command
テキスト埋め込み cohere.embed-english-v3.0, cohere.embed-multilingual-v3.0,
cohere.embed-english-light-v3.0, cohere.embed-multilingual-light-v3.0

今回は、2024/6/18から新しく使えるようになったCommand R+のチャット・モデルcohere.command-r-plusと、テキスト埋め込みのモデルcohere.embed-multilingual-v3.0を使います。

Command R+とは

Command R+は、Cohere社のインタラクティブな会話と長文のタスクに最適化されたLLMです。非常に高いパフォーマンスを実現しており、エンタープライズ企業の本番利用向けに適したモデルです。RAGとmulti-step toolを使った複雑なワークフローに適しています。日本語を含む13カ国語に対応しています。
詳細はこちらをご確認ください。

アーキテクチャ

今回は以下のようなアーキテクチャを考えます。Base Databaseにpdfのデータをベクトル化して保存します。Virtual Machine上でベクトル検索を行うAPIの提供とチャット画面となるアプリを起動させます。

今回APIでフロントエンドとバックエンドを連携している理由は、疎結合なアーキテクチャにも対応したかったからです。実際の本番環境では、フロントとバックの環境をVMを分割して作成したり、APIをサーバレスなOCI Functionなどの環境で動かす可能性があるため、このような疎結合なアーキテクチャにも対応した構成にしました。
rag_architecture_detail.png

前提

  • Oracle Cloud Infrastructureの環境
    • ネットワークリソースの構築
    • Virtual Machineの構築
    • Oracle Base Database 23aiの構築
    • Chicago or Frankfurt Regionのサブスクライブ
  • Virtual Machineの環境構築
    • Python環境
    • oci-sdkが利用できる環境

手順

  1. 事前準備
  2. Tableの作成
  3. pdfデータのInsert
  4. Textデータのベクトル化
  5. ベクトル検索
  6. FAST APIを使ったAPI作成
  7. Streamlitを使ったフロントエンドの作成
  8. Table削除

0. 事前準備

OCIの環境構築

上記のアーキテクチャを構築します。

Oracle Database 23aiの構築

今回はデータソースと保存先としてOracle Database 23aiを使います。
Oracle Database 23aiの構築方法は、OCI Tutorialの「Oracle Database編 - Base Database Service (BaseDB) を使ってみよう」か、「Oracle Database - Oracle AI Vector Searchを使ってみよう」を参考にしてください。
今回のコードは、oracleユーザーで実行してください。

Virtual Machineの構築

今回はPythonの実行環境としてOCI上のVirtual Machineを使用します。
こちらのOCI Tutorial「その3 - インスタンスを作成する」を参考にVirtual Machineを作成してください。また、追加でPythonをインストールして実行環境を作成してください。
Utuntu22.04, Python 3.11.9の環境で動作確認済みです。

$ lsb_release -d
Description:    Ubuntu 22.04.4 LTS

$ python --version
Python 3.11.9

Oracle Linux 8.9でも動作確認済みです。Oracle Linux 8.9を利用する場合は、ol8_appstreamリポジトリからlibglvnd-develのパッケージを追加でインストールしてください。

$ sudo yum --enablerepo=ol8_appstream install libglvnd-devel
...
  mesa-libglapi-23.1.4-2.el8.x86_64
  xorg-x11-proto-devel-2020.1-3.el8.noarch

Complete!

GitリポジトリのClone

今回使うソースコードはこのGitHubに公開しています。

ご自身の環境にGitリポジトリをCloneします。

$ git clone https://github.com/sh-sho/oci_rag_sample_kit.git

この記事ではsrcディレクトリで作業を行います。srcに移動します。

$ cd oci_rag_sample_kit/src/
$ tree -a
.
├── .env.example
├── 01.create_table.py
├── 02.insert_data.py
├── 03.embed_save_texts.py
├── 04.execute_vector_search.py
├── 05.vector_search_api.py
├── 06.front_app_streamlit.py
├── 07.drop_table.py
├── chatclass.py
├── data
│   ├── autonomous-database-self-securing-wp-ja.pdf
│   ├── oracle-cloud-infrastructure-waf-data-sheet-ja.pdf
│   └── oracle-gov-cloud-oci-ja.pdf
├── requirements.txt
├── table_detail.py
└── utils.py

1 directory, 15 files

次にPythonのライブラリをインストールします。requirements.txtに必要なライブラリが記載されているので以下のコマンドでインストールをします。

$ pip install -r requirements.txt

Vector Searchを行うためpython-oracledbのバージョンは2.2.1以上のものを使用してください。

環境変数の設定

次に環境変数を設定します。
.env.exampleファイルをコピーして.envファイルを作成します。

$ cp .env.example .env

.envの内容をご自身の環境に合わせて書き換えます。

UN=username
PW=password
DSN=dsn
OCI_CONFIG_FILE=~/.oci/config
OCI_COMPARTMENT_ID=ocid1.compartment.oc1..aaaaaaaaxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
CSV_DIRECTORY_PATH=/tmp_data
CONFIG_PROFILE=CHICAGO
PDF_DIRECTORY_PATH='./data/'

UN, PW, DSNはOracleDatabaseに接続するための環境変数です。本コードはoracleユーザか同等の権限を持ったユーザで実行可能です。対応するユーザ名、パスワード、DSNを設定してください。
CONFIG_PROFILEはOCI Generative AIをCallする際に使用するのでChicago (or Frankfurt) RegionのProfile名を指定してください。

以上が事前準備です。

1. Tableの作成

01.create_table.pyを実行し、Base DatabaseにTableを作成します。
SOURCE_TABLE, DOCUMENT_TABLE, CHUNK_TABLEの3つのTableが作成されます。このコードでは、SOURCE_TABLEにpdfの名前、DOCUMENT_TABLEにその内容が保存されています。そのTableのデータからChunkを作成し、CHUNK_TABLEに保尊します。これは既存のDatabaseを想定した場合に、pdfの名前とその中身からChunkとベクトルを作成し、IDで連携するケースが多いためこのようなTable構造にしています。(今回のコードでLangChainのIntegrationのOracleVSを使用していない理由は、このTable構造が作成できないためです。)

SOURCE_TABLE

このTableにはpdfの名前が保存されます。

Column名 Data Type Size
ID NUMBER 9
NAME VARCHAR2 100 BYTE

DOCUMENT_TABLE

このTableにはpdfのTextが保存されます。

Column名 Data Type Size
ID NUMBER 9
DOCUMENT CLOB

CHUNK_TABLE

このTableにはTextがChunkに分割された値とそのChunk ID、Chunkのベクトルが保存されます。Chunk IDとは1つのドキュメントの中の何番目のChunkかを特定するために使用するIDです。

Column名 Data Type Size
ID NUMBER 9
CHUNK_ID NUMBER
CHUNK VARCHAR2 2000 BYTE
VECTOR VECTOR

コードを実行すると以下のような内容が出力されます。

$ python 01.create_table.py 
Start Creating Table.
End Creating Table.

無事にTableを作成できました。

2. pdfデータのInsert

Tableが作成できたので、次にデータを用意します。
今回サンプルとして扱うデータはOCIの技術に関するpdfです。
Autonomous Databaseに関する資料、OCI WAFに関する資料、OCIの概要資料の3つのpdfを事前に用意しています。データはsrc/data/の下に保存されており、そこにpdfを追加すると追加したpdfもデータとしてベクトル化してくれます。

$ tree ./data/
./data/
├── autonomous-database-self-securing-wp-ja.pdf # Autonomous Databaseに関する資料
├── oracle-cloud-infrastructure-waf-data-sheet-ja.pdf # OCI WAFに関する資料
└── oracle-gov-cloud-oci-ja.pdf # OCIの概要資料

0 directories, 3 files

ここからpdfデータをベクトルデータにしていきます。
具体的な処理のステップを以下の画像に記載しています。
splits.png

まずpdfのドキュメントデータからTextデータに変換し、Databaseに保存します。
実行するコードは02.insert_data.pyです。

doc_loaderという関数内で、langchain_community.document_loadersライブラリのUnstructuredFileLoader関数を使いpdfデータをドキュメントデータに変換しています。

def doc_loader(pdf_path: str) -> np.ndarray:
    loader = UnstructuredFileLoader(PDF_DIRECTORY + pdf_path)
    doc = loader.load()
    return doc

以下のコードで指定のディレクトリ内(./src/data/)のpdfを取得し、ドキュメントデータへ変換しTableにInsertしています。

pdf_files = [f for f in os.listdir(PDF_DIRECTORY) if f.lower().endswith('.pdf')]
for idx, pdf_file in enumerate(pdf_files):
  pdf_data = doc_loader(pdf_path=pdf_file)
  
  insert_table1_sql = f"""
      INSERT INTO {td.table1_name} ({td.table1_index["index_id"]}, {td.table1_index["index_name"]})
      VALUES (:sample_id, :sample_name)
  """

  cursor.execute(insert_table1_sql, sample_id=idx+1, sample_name=pdf_data[0].metadata['source'].replace(PDF_DIRECTORY, ""))
  print(f"Insert data {idx+1} to table1")

  insert_table2_sql = f"""
      INSERT INTO {td.table2_name} ({td.table2_index["index_id"]}, {td.table2_index["index_docs"]})
      VALUES (:sample_id, :sample_doc)
  """

  cursor.execute(insert_table2_sql, sample_id=idx+1, sample_doc=pdf_data[0].page_content)
  print(f"Insert data {idx+1} to table2")

02.insert_data.pyを実行すると、以下の内容が出力されます。

$ python 02.insert_data.py 
Start Insert data.
Insert data 1 to table1
Insert data 1 to table2
Insert data 2 to table1
Insert data 2 to table2
Insert data 3 to table1
Insert data 3 to table2
End Insert data

出力結果を見ると、3つのpdfがTableに保存されていることがわかります。

3. Textデータのベクトル化

次に、TextデータをChunkに分割します。コードは03.embed_save_texts.pyです。
Chunkへ分割する際に、Chunkの文字数を指定するChunk SizeやChunkが前後の文とどれだけ重複するかを指定するChunk Overlapの値を決めることができます。これらの値はベクトル検索の精度に影響してきます。
今回は、Chunk Sizeが400文字、Chunk Overlapが40文字で指定しています。

CHUNK_SIZE = 400
CHUNK_OVERLAP = 40

Chunk分割のコードを説明します。
まずfetch_data_from_db関数でドキュメントデータをDatabaseから取得します。

def fetch_data_from_db(connection, table_name, table_index) -> np.ndarray:
    """ Fetch data from Database """
    engine = create_engine("oracle+oracledb://", creator=lambda: connection)
    select_sql = f"""
        SELECT {table_index["index_id"]}, {table_index["index_docs"]}
        FROM {table_name}
        ORDER BY {table_index["index_id"]} DESC
    """
    fetch_data = pd.read_sql(select_sql, engine)
    return fetch_data

次に、split_text_to_chunks関数でドキュメントデータをChunkに分割しています。

def split_text_to_chunks(df, table_index, chunk_columns) -> np.ndarray:
    """ Text to chunks """
    all_chunks = []
    for _, row in df.iterrows():
        chunks = text_splitter.split_text(row[table_index["index_docs"].lower()])
        for i, chunk in enumerate(chunks):
            all_chunks.append([row[table_index["index_id"].lower()], i + 1, chunk])
    df_chunks = pd.DataFrame(data=all_chunks, columns=chunk_columns)
    return df_chunks

最後に、96データずつのBatchにしています。OCI Generative AIのEmbedのモデルでは、最大96セットのデータをまとめてEmbed可能です。Modelの種類や制限はこちらのドキュメントをご確認ください。

def fetch_batches() -> np.ndarray:
    """ Data fetch and transform to chunks """
    with pool.acquire() as connection:
        df_select = fetch_data_from_db(connection, td.table2_name, td.table2_index)
        
        if df_select.empty:
            print(f"{td.table2_name} doesn't contain any data.")
            exit(1)

        chunk_columns = [td.table3_index["index_id"], td.table3_index["index_chunkid"], td.table3_index["index_chunk"]]
        
        try:
            df_chunks = split_text_to_chunks(df_select, td.table2_index, chunk_columns)
        except Exception as e:
            print("Error chunk loop:", e)
            exit(1)

        df_chunks.reset_index(inplace=True, drop=True)
        batches = np.array_split(df_chunks, len(df_chunks) // BATCH_SIZE + 1)
        print(f"Fetched all data: {len(df_chunks)}")
        return batches

次に、96データずつBatchにしたTextデータをベクトル化します。

def embed_text(texts) -> np.ndarray:
    """ Text to Vector """
    text_vector = utils.embed_documents(texts)
    return text_vector

実際にベクトル化を行っているのはutils.pyファイルのembed_documents関数です。OCI Generative AIを使いベクトル化を行っています。

config = oci.config.from_file(file_location=OCI_CONFIG_FILE, profile_name=CONFIG_PROFILE)
generative_ai_inference_client = oci.generative_ai_inference.GenerativeAiInferenceClient(config)

def embed_documents(texts: List[str]) -> List[List[float]]:
    embed_text_response = generative_ai_inference_client.embed_text(
        embed_text_details=oci.generative_ai_inference.models.EmbedTextDetails(
            inputs=texts,
            serving_mode=oci.generative_ai_inference.models.OnDemandServingMode(
                model_id="cohere.embed-multilingual-v3.0"),
            compartment_id=OCI_COMPARTMENT_ID,
            input_type="SEARCH_DOCUMENT"))

    embeddings = embed_text_response.data.embeddings
    return embeddings

最後にbulk_insertCHUNK_TABLEにChunk ID, Chunk, Vectorの値をInsertしています。

03.embed_save_texts.pyのコードを実行すると、以下のような出力が得られます。

$ python 03.embed_save_texts.py 
...
0.0202178955078125, 0.052215576171875, 0.03814697265625, -0.035491943359375]))]
Total Run Time  1.683833360671997
success delete csv directory

出力内容を見ると、Chunkに分割されてベクトル化された値が出力されてます。

    ID  CHUNK_ID                                              CHUNK                                       CHUNK_VECTOR
0    3         1  Oracle Cloud Infrastructure Web Application Fi...  [0.045928955, 0.07409668, -0.026168823, 0.0170...
1    3         2  はもちろん、侵害の緩和、回避、侵害後の修正、クリーンアップのためのコスト\n\n拡大に追従し...  [0.053100586, 0.044433594, -0.056152344, 0.041...
2    3         3  cloud.oracle.com/iaas\n\n@oracleiaas\n\n1ペー\n\...  [0.029891968, 0.048217773, -0.031433105, 0.020...

4. ベクトル検索

Chunkに分割したpdfデータに対してベクトル検索を行います。
コードは04.execute_vector_search.pyです。
入力時に検索する文字列を入力します。その文字列をベクトル化して、そのベクトル値に近いChunkをベクトル検索により取得します。
Oracle Database 23ai内のVECTOR_DISTANCE関数を使って、文字列のベクトルとChunkのベクトル間の距離を計算しています。

具体的なコードを以下に示しています。

def query_text(text: str):
    embed_text = array.array('f', utils.embed_documents([text])[0])

    print(f"Start Vector Search")
    with oracledb.connect(user=UN, password=PW, dsn=DSN) as connection:
        with connection.cursor() as cursor:
            cursor.setinputsizes(oracledb.DB_TYPE_VECTOR)
            select_sql = f"""
                SELECT
                    ct.{td.table3_index["index_id"]},
                    pt.{td.table1_index["index_name"]},
                    ct.{td.table3_index["index_chunk"]},
                    VECTOR_DISTANCE({td.table3_index["index_vector"]}, :1, COSINE) as distance
                FROM
                    {td.table3_name} ct
                JOIN
                    {td.table1_name} pt ON ct.{td.table3_index["index_id"]} = pt.{td.table1_index["index_id"]}
                ORDER BY distance
                FETCH FIRST 3 ROWS ONLY
            """
            cursor.execute(select_sql, [embed_text])

            print(f"============検索結果============")
            index = 1
            for row in cursor:
                print(f"{index}: {row}")
                index += 1
            print(f"================================")
        connection.commit()
    print(f"End Vector Search")

今回はAutonomous Databaseについて質問してみます。
04.execute_vector_search.pyコードと質問を入力して実行すると以下内容が出力されます。ベクトル検索が行われAutonomous Databaseの可用性に関する情報が取得できています。

$ python 04.execute_vector_search.py 'Autonomous Databaseの高可用性について教えて'
Search Query:Autonomous Databaseの高可用性について教えて
Start Vector Search
============Result============
1: (2, 'autonomous-database-self-securing-wp-ja.pdf', '自己修復:Autonomous Databaseにより、あらゆる計画外および計画停止時間に対する予防的な保護が提供され、停止時間なしに障害から迅速に自動リカバリできます。\n\nAutonomous Databaseの可用性とパフォーマンスの管理は、AIベースの自律性を使用して次のレベルへと進みます。これによって複数の領域の診断が統合され、実行時に分析して措\n\n置を講じることができるようになり、操作の中断を最小限に抑えるか排除できます。\n\n図1:Oracle CloudのAutonomous Databaseコンポーネント\n\n自己修復の必要性について\n\nあらゆる規模の組織が、停止時間やデータ損失、データ・アクセスに影響を与えるパフォーマンスのボトルネックに関連するリスクについて詳しく知るようになってきています。90 %以上の企業が、', 0.32765958104735404)
2: (2, 'autonomous-database-self-securing-wp-ja.pdf', '9\n\n技術概要 | Oracle Autonomous Database\n\nCopyright © 2020, Oracle and/or its affiliates | 公開\n\nこの実績あるアーキテクチャは、世界中の何千ものインストールをサポートしており、これにはFortune 100企業が運用する世界的にも最もミッション・クリティカルなデータベースも 含まれます。\n\n他の自律機能と連携する自己修復', 0.3282624711042831)
3: (2, 'autonomous-database-self-securing-wp-ja.pdf', 'さまざまな問題を防止して修復します。Oracle Autonomous Databaseが提供するこの自己修復機能のコレクションは、業界の他のどのクラウド(またはオンプレミス)データベースの 追随も許しません。', 0.33745563121903666)
================================
End Vector Search

5. Fast APIを使ったAPI作成

入力された質問に対して、pdfをもとに回答を生成するチャットアプリを作ります。
今回は、バックエンドはFast API、フロントエンドはStreamlitで作成します。
質問してから回答を出力するまでのチャットのフローを以下に記載しています。

chatflow.png

FAST APIを使い上記のベクトル検索の処理とOCI Generative AIのCommand R+を使ったチャットの処理を提供するAPI作成します。コードは05.vector_search_api.pyです。

まずベクトル検索のAPIを提供するPythonコードは以下の通りです。/vector_search/をエンドポイントとして、入力に対するベクトル検索の結果を返します。

app = FastAPI()

@app.get("/vector_search/")
async def vector_search(
    input_text: str = 'Hello'
):
    embed_text = array.array('f', utils.embed_documents([input_text])[0])
    with oracledb.connect(user=UN, password=PW, dsn=DSN) as connection:
        with connection.cursor() as cursor:
            cursor.setinputsizes(oracledb.DB_TYPE_VECTOR)
            select_sql = f"""
                SELECT
                    ct.{td.table3_index["index_id"]},
                    pt.{td.table1_index["index_name"]},
                    ct.{td.table3_index["index_chunk"]},
                    VECTOR_DISTANCE({td.table3_index["index_vector"]}, :1, COSINE) as distance
                FROM
                    {td.table3_name} ct
                JOIN
                    {td.table1_name} pt ON ct.{td.table3_index["index_id"]} = pt.{td.table1_index["index_id"]}
                ORDER BY distance
                FETCH FIRST 1 ROWS ONLY
            """
            cursor.execute(select_sql, [embed_text])
            results = cursor.fetchall()
    return {"result": results}

次にCommand Rを使ったチャットのAPIを作成します。
/chat_doc/をエンドポイントとして、入力した質問に対する回答を返します。
documentsのパラメータにはpdfのTextを格納しています。Cohereのモデルでは、このdocumentsモードを用いることで、pdfに関連する質問に対して、与えたpdfの内容を根拠として回答を作成してくれます。
documentsに関しての詳細はこちらをご確認ください。

@app.get('/chat_doc/')
async def chat_command_r_documents(
    input_text: str = 'Hello', 
    pdf_files: str = None
    ) -> str:
    documents = create_documents(pdf_files=[pdf_files])
    chat_detail = ChatDetails(
        chat_request=CohereChatRequest(
            documents=documents,
            message=input_text,
            max_tokens=500
            ),
        compartment_id=OCI_COMPARTMENT_ID,
        serving_mode=OnDemandServingMode(
            model_id="cohere.command-r-16k"
        ))
    chat_response = generative_ai_inference_client.chat(chat_detail)
    return chat_response.data.chat_response.text

05.vector_search_api.pyのコードを実行してAPIを提供させます。

$ python 05.vector_search_api.py 
INFO:     Started server process [81175]
INFO:     Waiting for application startup.
INFO:     Application startup complete.
INFO:     Uvicorn running on http://0.0.0.0:8000 (Press CTRL+C to quit)

6. Streamlitを使ったフロントエンドの作成

Streamlitを使って、簡易的なチャットアプリのフロントエンドを作成します。
StreamlitのドキュメントにLLM chat アプリの作成方法が記載されているので、こちらを参考にしています。

以下のコードがStreamlitでフロント部分を作成している箇所です。
ユーザーから質問が入力された際に、chat_docのAPIをCallし回答を作成しています。
質問と回答をsession_state.messagesに一時的に保存し、その内容を表示しています。

st.title("OCI Chat Bot")

if "messages" not in st.session_state:
    st.session_state.messages = []

for message in st.session_state.messages:
    with st.chat_message(message["role"]):
        st.markdown(message["content"])

if prompt := st.chat_input("What is up?"):
    st.session_state.messages.append({"role": "user", "content": prompt})

    with st.chat_message("user"):
        st.markdown(prompt)

    with st.chat_message("assistant"):
        response = f"Response: {chat_doc_response(prompt)}"
        st.markdown(response)
    st.session_state.messages.append({"role": "assistant", "content": response})

別のbashを開き、06.front_app_streamlit.pyのコードを実行すると以下のような出力とアプリを作成することができます。

$ streamlit run 06.front_app_streamlit.py

Collecting usage statistics. To deactivate, set browser.gatherUsageStats to false.


  You can now view your Streamlit app in your browser.

  Local URL: http://localhost:8501

http://localhost:8501に接続しすると、以下のようなアプリの画面が作成されます。
”What is up”と書かれている枠が入力のテキストボックスです。左側のパラメータをもとに質問の回答が生成されます。

実際に質問を投げてやり取りを行ってみました。
まずは”こんにちは”とpdfとは関係のない内容を質問しました。問題なく回答が作成されいています。
次に、pdfをデータソースとしたRAGの処理を確認します。”Autonomous Databaseについて教えて”と質問しました。pdfに書かれている内容をもとに作成された回答が返ってきました。裏側でAutonomous Databaseの資料がベクトル検索されて取得されていることが確認できます。

次に、Command R plusのパラメータを変更して回答の生成を行ってみます。
以下の画像のようにMax_tokensのパラメータを1000に変更して、質問を投げてみます。

max_tokens.png

以下のような回答が得られます。1000Tokensとまではいきませんが、さきほどより文字数の多い回答が作成されています。質問やユースケースに応じてパラメータを変更して回答を生成させることが可能です。

7. Tableの削除

05.drop_table.pyを実行し作成したTableを削除します。
実行すると以下の内容が出力されます。

$ python 05.drop_table.py 
Start Drop Table
Drop Table CHUNK_TABLE
Drop Table DOCUMENT_TABLE
Drop Table SOURCE_TABLE
End Drop Table

作成したテーブルが削除できました。
またStreamlit, FastAPIはCtrl+cで停止させることができます。

おわりに

今回はOCIから日本語対応した生成AIであるCohere社のCommand R+が出たので、OCI上でRAGを使った日本語対応のチャットアプリケーションを作成しました。

本コードでは、LangChainのOracle DatabaseのIntegrationであるOracleVSを使っていません。SQLでTableを作成し、ベクトル検索をしています。理由は、このコードを書いた時点では、OCI Generative AIのCommand R+のIntegrationが出ていなかったことと、Tableの形を自由に決めたかったからです。LangChainのIntegrationを利用するとTable構造は変わりますが、もう少し簡単に作成できます。

また、本コードではPythonを使ってOracle Databaseの操作やベクトル検索をしますが、Oracle Database 23aiの中で行う方法もあります。こちらの記事をご参考ください。

Oracle Database 23aiを情報ソースとしたRAGを考えている方は是非試してみてください。

参考

13
9
2

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
13
9

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?