Box Llama-Index Readerの導入

Yuko Taniguchi
Box Developer Japan Blog
16 min readAug 26, 2024

Boxドキュメントを最先端の検索拡張生成 (Retrieval Augmented Generation、RAG) や他の大規模言語モデル (LLM) アプリケーションに組み込みたいと思ったことはないでしょうか。

この一連のreaderにより、BoxとLlama-Indexの隔たりがなくなるため、Boxに保存した豊富なコンテンツをLLMワークフロー内で直接活用できるようになります。

利用可能なBoxのreader

Boxには4種類のreaderが用意されています。

他のクラウドストレージプロバイダが提供するような従来のreaderでは、ファイルをダウンロードした後、Llama-Indexパーサーを使用します。

Boxでは、従来のクラウドストレージプロバイダとは異なり、テキスト抽出、汎用的なAIプロンプト、特殊な構造化データのAIによる抽出など、組み込みのサービスを利用できます。

  • Box Reader — Boxからファイルを読み取るためのSimpleReaderインターフェースの実装
  • Box Text Extraction — Boxのテキストレプリゼンテーションを使用して、ドキュメントから直接テキストを抽出
  • Box AI Prompt — Box AIを使用して、ドキュメントからコンテキストを抽出
  • Box AI Extraction — Box AIを使用して、ドキュメントから構造化データを抽出

Box AI機能を利用できるのは、Enterprise Plusをご利用のお客様のみです。

はじめに

Boxのreaderは、こちらのLlamaIndex GitHubにあり、他のreaderと同様にインストールできます。

pip install llama-index-readers-box

認証

BoxのすべてのreaderにはBoxClientが必要です。これにより、readerのコードが簡素化され、開発者の柔軟性が向上します。Boxの認証タイプ (開発者トークン、OAuth 2.0、クライアント資格情報許可、JSONウェブトークン) は、いずれも使用可能で、Boxクライアントで使用できます。

以下の例を参考にしてください。

CCG認証の使用

from box_sdk_gen import CCGConfig, BoxCCGAuth, BoxClient
config = CCGConfig(
client_id="your_client_id",
client_secret="your_client_secret",
enterprise_id="your_enterprise_id",
user_id="your_ccg_user_id", # Optional user id
)
auth = BoxCCGAuth(config)
if config.user_id:
auth.with_user_subject(config.user_id)

client = BoxClient(auth)

reader = BoxReader(box_client=client)

CCGクライアントは、デフォルトで、アプリケーションに関連付けられたサービスアカウントを使用します。ファイルの共有方法によっては、サービスアカウントですべてのファイルにアクセスできるとは限りません。

別のユーザーを選択する場合は、ユーザーIDを指定できます。この場合は、アプリケーションがそのユーザーの代理になれること、またはスコープ内でユーザートークンを生成できることを確認してください。

CCGの設定方法の詳細については、こちらのBox CCGのガイドをご確認ください。

JWT認証の使用

from box_sdk_gen import JWTConfig, BoxJWTAuth, BoxClient

# Using manual configuration
config = JWTConfig(
client_id="YOUR_BOX_CLIENT_ID",
client_secret="YOUR_BOX_CLIENT_SECRET",
jwt_key_id="YOUR_BOX_JWT_KEY_ID",
private_key="YOUR_BOX_PRIVATE_KEY",
private_key_passphrase="YOUR_BOX_PRIVATE_KEY_PASSPHRASE",
enterprise_id="YOUR_BOX_ENTERPRISE_ID",
user_id="YOUR_BOX_USER_ID",
)

# Using configuration file
config = JWTConfig.from_config_file("path/to/your/.config.json")

user_id = "1234" # Optional user id
if user_id:
config.user_id = user_id
config.enterprise_id = None
auth = BoxJWTAuth(config)

client = BoxClient(auth)

reader = BoxReader(box_client=client)

JWTクライアントは、デフォルトで、アプリケーションに関連付けられたサービスアカウントを使用します。ファイルの共有方法によっては、サービスアカウントですべてのファイルにアクセスできるとは限りません。

別のユーザーを選択する場合は、ユーザーIDを指定できます。この場合は、アプリケーションがそのユーザーの代理になれること、またはスコープ内でユーザートークンを生成できることを確認してください。

JWTの設定方法の詳細については、こちらのBox JWTのガイドをご確認ください。

JWT認証では、SDKに追加の依存関係が必要です。これらをインストールするには、pip install “box-sdk-gen[jwt]”を実行します。

Box Reader

このreaderは、LlamaIndex SimpleReaderを使用してBoxからファイルを読み取り、Box固有の機能を利用しません。

データの読み込み

このメソッドは、指定されたパラメータに基づいてBoxファイルを取得し、SimpleDirectoryReaderを使用して構造化された形式に処理します。

  • folder_id (Optional[str]): データの読み込み元となるBoxフォルダのID。指定した場合、合わせてis_recursiveをTrueに設定すると、サブフォルダからもデータを取得します。デフォルトではNoneになっています。
  • file_ids (Optional[List[str]]): データの読み込み元となるBoxファイルIDのリスト。指定した場合、folder_idは無視されます。デフォルトではNoneになっています。
  • is_recursive (bool = False): Trueに設定し、folder_idを指定した場合、指定したフォルダ内のサブフォルダからデータを取得します。デフォルトではFalseになっています。

膨大な量のファイルやフォルダが存在する場合、このreaderは現実的ではなくなります。

使用方法:

# Using CCG authentication

from llama_index.readers.box import BoxReader
from box_sdk_gen import CCGConfig, BoxCCGAuth, BoxClient

ccg_conf = CCGConfig(
client_id="your_client_id",
client_secret="your_client_secret",
enterprise_id="your_enterprise_id",
user_id="your_ccg_user_id", # optional
)
auth = BoxCCGAuth(ccg_conf)
client = BoxClient(auth)
reader = BoxReader(box_client=client)

box_client = BoxClient(auth)

reader = BoxReader(box_client=box_client)

# Using a list of file ids
docs = reader.load_data(file_ids=["test_csv_id"])

# Using a folder id
docs = reader.load_data(folder_id="test_folder_id")

リソースの読み込み

特定のリソースからデータを読み込みます。

  • resource (str): リソース識別子 (file_id)

使用方法:

doc = reader.load_resource("test_csv_id")

リソースのリスト取得

指定したフォルダIDまたはファイルIDに基づいて、BoxファイルのIDのリストを取得します。

このメソッドは、指定されたパラメータに基づいてBoxファイル識別子のリストを取得します。ファイルIDのリストを指定するか、オプションのis_recursiveフラグとともにフォルダIDを指定してサブフォルダのファイルを含めることもできます。

  • folder_id (Optional[str]): データの読み込み元となるBoxフォルダのID。指定した場合、合わせてis_recursiveをTrueに設定すると、サブフォルダからもデータを取得します。デフォルトではNoneになっています。
  • file_ids (Optional[List[str]]): データの読み込み元となるBoxファイルIDのリスト。指定した場合、folder_idは無視されます。デフォルトではNoneになっています。
  • is_recursive (bool = False): Trueに設定し、folder_idを指定した場合、指定したフォルダ内のサブフォルダからデータを取得します。デフォルトではFalseになっています。

使用方法:

resources = reader.list_resources(file_ids=["test_csv_id"])

ファイルコンテンツの読み取り

ファイルのバイナリコンテンツを返します。

input_file: Path = Path("test_csv_id")
content = reader.read_file_content(input_file)

リソースの検索

指定された条件に基づいてBoxのリソースを検索し、そのIDのリストを返します。

このメソッドは、Box APIの検索機能を使用して、指定されたパラメータに一致するリソースを探します。その後、見つかったリソースのIDを含むリストを返します。

ヒント: 検索の操作方法の詳細については、検索を参照してください。

query = "invoice"
resources = reader.search_resources(query=query)

メタデータによるリソースの検索

メタデータに基づいてBoxのリソースを検索し、そのIDのリストを返します。

このメソッドは、Box APIの検索機能を使用して、指定されたメタデータクエリに一致するリソースを探します。その後、見つかったリソースのIDを含むリストを返します。

ヒント: クエリの作成方法の詳細については、クエリ構文を参照してください。

from_ = (
test_data["enterprise_1234"] # your enterprise id
+ "rbInvoicePO" # your metadata template key
)
ancestor_folder_id = "test_folder_invoice_po_id"
query = "documentType = :docType "
query_params = {"docType": "Invoice"}

resources = reader.search_resources_by_metadata(
from_=from_,
ancestor_folder_id=ancestor_folder_id,
query=query,
query_params=query_params,
)

リソース情報の取得

特定のリソースに関する情報を取得します。

resource = reader.get_resource_info(file_id=test_data["test_csv_id"])

実践的な例

次のコードを考えてみましょう。

import os
from typing import List
import dotenv

from box_sdk_gen import CCGConfig, BoxCCGAuth, BoxClient, File
from llama_index.readers.box import (
BoxReader,
BoxReaderTextExtraction,
BoxReaderAIPrompt,
BoxReaderAIExtract,
)
from llama_index.core.schema import Document

def get_box_client() -> BoxClient:
dotenv.load_dotenv()

# Common configurations
client_id = os.getenv("BOX_CLIENT_ID", "YOUR_BOX_CLIENT_ID")
client_secret = os.getenv("BOX_CLIENT_SECRET", "YOUR_BOX_CLIENT_SECRET")

# CCG configurations
enterprise_id = os.getenv("BOX_ENTERPRISE_ID", "YOUR_BOX_ENTERPRISE_ID")
ccg_user_id = os.getenv("BOX_USER_ID")

config = CCGConfig(
client_id=client_id,
client_secret=client_secret,
enterprise_id=enterprise_id,
user_id=ccg_user_id,
)

auth = BoxCCGAuth(config)
if config.user_id:
auth.with_user_subject(config.user_id)

return BoxClient(auth)

def get_testing_data() -> dict:
return {
"disable_folder_tests": True,
"test_folder_id": "273980493541",
"test_doc_id": "1584054722303",
"test_ppt_id": "1584056661506",
"test_xls_id": "1584048916472",
"test_pdf_id": "1584049890463",
"test_json_id": "1584058432468",
"test_csv_id": "1584054196674",
"test_txt_waiver_id": "1514587167701",
"test_folder_invoice_po_id": "261452450320",
"test_folder_purchase_order_id": "261457585224",
"test_txt_invoice_id": "1517629086517",
"test_txt_po_id": "1517628697289",
}

def print_docs(label: str, docs: List[Document]):
print("------------------------------")
print(f"{label}: {len(docs)} document(s)")
for doc in docs:
print("------------------------------")
file = File.from_dict(doc.extra_info)
print(f"File ID: {file.id}\nName: {file.name}\nSize: {file.size} bytes")
# print("------------------------------")
print(f"Text: {doc.text[:100]} ...")
print("------------------------------\n\n\n")

def main():
box_client = get_box_client()
test_data = get_testing_data()
reader = BoxReader(box_client=box_client)
docs = reader.load_data(file_ids=[test_data["test_txt_invoice_id"]])
print_docs("Box Simple Reader", docs)
if __name__ == "__main__":
main()

結果は次のようになります。

------------------------------
Box Simple Reader: 1 document(s)
------------------------------
File ID: 1517629086517
Name: Invoice-Q2468.txt
Size: 176 bytes
Text: Vendor: Quasar Innovations
Invoice Number: Q2468
Purchase Order Number: 003
Line Items:
- Infini ...
------------------------------

アイデア、コメント、フィードバックがある場合は、コミュニティフォーラム (英語のみ) にコメントをお送りください。

--

--