<img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=139163818022217&amp;ev=PageView&amp;noscript=1"> <img height="1" width="1" style="display:none" src="https://www.facebook.com/tr?id=271598307802760&amp;ev=PageView&amp;noscript=1">

BoxとPineconeでのコンテキスト検索の使用

 公開日:2025.09.17  更新日:2026.06.10

最近、コンテキスト検索に関する記事を公開しました。この概念はすばらしいのですが、実際にはこれらの知識をどのように応用できるでしょうか。ここでは、(コンテンツソースとして) Box、(ベクトルデータベースとして) Pineconeを使用した例を説明します。Boxは、ファイルを保存したり、AIや電子サインなどの主要なコンテンツ駆動型サービスを提供したりする、企業向けのインテリジェントコンテンツ管理プラットフォームで、Pineconeは、迅速に類似性検索を行うためのマネージドベクトルデータベースです。これらを一緒に使用すると、開発者は、カスタムRAGパイプラインを作成できます。このRAGパイプラインでは、Boxはドキュメントとそのメタデータを提供し、Pineconeはそのドキュメントに対するセマンティック検索を提供し、LLM (OpenAIGPT-4AnthropicClaudeなど) は取得したデータを使用して回答を生成します。

シナリオ: 会社の社内人事の情報 (PDFWordドキュメント、メモ) がBoxに保存されていることを想像してみてください。その個人データを使用して従業員の質問に回答できるAIアシスタントが必要な場合、これはBox AIBox Hubsを使用しても実現できますが、今回の例では、Boxコンテンツのほかに外部データソースも含めたいとします。コンテキスト検索を使用すると、効果を発揮します。実装方法を以下に示します。

PineconeBoxコンテンツのインデックスを作成する: BoxAPIまたはSDKを使用すると、Boxフォルダ内のファイルを反復処理し、そのテキストコンテンツを取得できます。Boxは、一般的なファイル形式 (PDFDOCXなど) のテキストレプリゼンテーションを提供できます。これは、コードによる処理に使用できます。テキストを手頃なサイズのチャンク (それぞれ200500語など) に分割します。

この段階では、任意のLLMを使用して、ドキュメントコンテキストを生成してチャンクに追加したり、ファイルのメタデータを埋め込んだりできます (例: ファイル名やセクションタイトルを先頭に追加する)。その後、OpenAIの埋め込みモデルまたは好きな埋め込みモデルを使用して埋め込みを作成します。最後に、これらのベクトルをPineconeインデックスにアップサートします。Pineconeではメタデータとともにベクトルが格納されます。そのため、各エントリのBoxファイルID、チャンクテキスト、およびタグをメタデータとして保存できます。このメタデータは、後でフィルタをかけたり、すべてのドキュメント参照を取得したりするのに役立ちます。BoxPineconeを関連付けることで、基本的に、LLMに対して検索可能な企業データのメモリを提供することになります。

import os
from time import sleep
from typing import List
from boxsdk.object.file import File
from boxsdk.object.folder import Folder
from pinecone import Pinecone
from box_ai_agents_toolkit import (
    box_folder_list_content,
    box_file_text_extract,
    get_ccg_client,
    BoxClient
)

pinecone: Pinecone = Pinecone(
    api_key=os.getenv("PINECONE_API_KEY"), 
    source_tag="box-contextual-retrieval-demo"
)
pinecone_index_name = os.getenv("PINECONE_INDEX")
pinecone_index = pinecone.Index(pinecone_index_name) # type: ignore
box: BoxClient = get_ccg_client()
items: List[File | Folder] = box_folder_list_content(box, os.getenv("BOX_FOLDER_ID")) # type: ignore
for item in items:
    if item.type == "file":
        text_content: str = box_file_text_extract(box, item.id)
        chunks = []
        start = 0
        while start < len(text_content):
            end = start + int(os.getenv("CHUNK_SIZE", 4000))
            chunk = text_content[start:end]
            contextualized_chunk = f"{item.name}: {chunk}"
            chunks.append(contextualized_chunk)
            start = end - int(os.getenv("CHUNK_OVERLAP", 200))  # Overlap adjustment
        for i, chunk in enumerate(chunks):
            # Get embeddings using the Pinecone inference API
            embeddings = pinecone.inference.embed(
                model="multilingual-e5-large",
                inputs=[chunk],
                parameters={
                    "input_type": "passage",
                    "truncate": "END"
                }
            )
            # Check if embeddings were returned
            if not embeddings or len(embeddings) == 0:
                print(f"No embeddings returned for chunk {i} of file ID: {item.id}")
                continue
            # Extract the actual embedding values (list of floats) from the result
            vectors = embeddings[0]['values']
            # Store the chunk text in metadata
            minimal_metadata = {
                "chunk_id": i, 
                "file_id": item.id, 
                "file_name": item.name, 
                "box_user_id": os.getenv("BOX_SUBJECT_ID"),
                "chunk_text": chunk  # Store the chunk text as part of the metadata
            }
            # Upsert the vector with the chunk ID and metadata into Pinecone
            pinecone_index.upsert([(f"{item.id}_chunk_{i}", vectors, minimal_metadata)], namespace=os.getenv("BOX_SUBJECT_ID"), index_name=pinecone_index_name)
            
            print(f"Upserted chunk {i} for file ID: {item.id} into Pinecone.")
print("All files processed and upserted into Pinecone.")

このサンプルでは、Boxフォルダ内のファイルを順番に処理し、そのテキストを取得してチャンクに分割し、それをコンテキスト化して、埋め込みを作成します。コンテキスト化のシンプルな形式として、チャンクテキストの先頭にファイル名を追加します。メタデータを含む各ベクトルは、Pineconeにアップサートされます (実際のアプリでは、効率化のために一括アップサートを使用し、より高度なチャンク戦略を使用する場合があります)。この後、PineconeBoxコンテンツのベクトルインデックスを保持します。BM25またはキーワード検索は、Pineconeのハイブリッド検索 (有効になっている場合) で処理できます。また、Boxメタデータまたは外部検索エンジンを使用して並列インデックスを維持することもできます。

LLMを使用してコンテキストステートメントを生成する: 前の例では、ごく基本的なコンテキストステートメントを紹介しました。これにより、コンテキストが追加されますが、さらに追加したくなることがあるかもしれません。次の例では、コードを少し編集し、選択したLLMにチャンクとドキュメント全体を評価させ、広範なドキュメントにおいてそのチャンクが持つコンテキストと重要性を説明するステートメントを生成するという手法を実装した関数を示します。

まず、OpenAIクライアントをインポートして初期化する必要があります。その後、チャンクにファイル名を追加するコード行を、contextualize_chunkという新しい関数の呼び出しに置き換える必要があります。この関数は、OpenAIクライアント、チャンク、およびファイルのテキスト全体を引数として受け取ります。

from openai import OpenAI

openai = OpenAI(api_key=os.getenv("OPENAI_API_KEY", ""))
while start < len(text_content):
            end = start + int(os.getenv("CHUNK_SIZE", 4000))
            chunk = text_content[start:end]
            contextualized_chunk = contextualize_chunk(openai, chunk, text_content)
            chunks.append(contextualized_chunk)
            start = end - int(os.getenv("CHUNK_OVERLAP", 200))  # Overlap adjustment

次の手順では、新しい関数を追加します。この関数は、テキスト全体とチャンクを受け取り、コンテキストステートメントを生成するプロンプトとともにgpt-4o (ただし、任意のモデルも使用可能) に送信します。返却された応答をチャンクの先頭に追加してから、返却します。

def contextualize_chunk(openai, chunk, content):
    prompt = f"""
        Given the document below, explain what the chunk captures in the context of the whole document.

        <document>
        {content}
        </document>
        Here is the chunk we want to explain:
        <chunk>
        {chunk}
        </chunk>
        Answer ONLY with a succinct explanation of the meaning of the chunk in the context of the whole document above.
    """.format(content=content, chunk=chunk)
   
    response = openai.chat.completions.create(
        model="gpt-4o", # Or another suitable model
        messages=[
            {"role": "system", "content": "You are a helpful assistant."},
            {"role": "user", "content": prompt}
        ],
        max_tokens=100,
        temperature=0.7,
    )
    contextual_explanation = response.choices[0].message.content.strip()
    return f"{contextual_explanation}: {chunk}"

このような微調整により、各チャンクで十分にコンテキストを認識できるようになります。プロンプトを自由に微調整し、他のモデルやプロバイダをテストして、自分や自分のコンテンツ、ユースケースに最も効果的なものを見つけてください。

このBoxPineconeの取り込み設定は、コンテキスト検索の動作例です。Boxの豊富なコンテンツとPineconeのベクトル検索を利用することで、AIアシスタントは常に適切なコンテキストを把握しています。AIアシスタントは、(汎用的なトレーニングデータだけでなく) 自分のデータから回答を取得し、(ドキュメント情報、メタデータ、クエリフィルタといった) コンテキストを使用して、ユーザーが何を必要としているかを正確に特定します。その結果、組織のナレッジに基づいた、より関連性が高く信頼できる回答が得られます。

まとめと次のステップ

コンテキスト検索は、どのRAGベースのAIアプリケーションにとっても強力な追加機能です。これにより、孤立した情報と、考えられる最良の結果につながる豊富なコンテキストの知識の間の隔たりがなくなります。ドキュメントチャンクのソースコンテキストであれ、ユーザーのリクエストの状況的なコンテキストであれ、「全体像」を把握することにより、AIで生成される応答の品質を大幅に向上させることができます。テキストチャンクに説明のためのコンテキストを追加する手法や、クエリのフィルタにセッションデータを使用するなどの手法によって、検索の精度とユーザーの満足度がどのように向上するかを確認してきました。Anthropicなどの研究者が先駆けて開発した手法やそのコンテキスト埋め込みは、今では、開発者が自身のプロジェクトに実装できるようになりました。

次のAIソリューションを構築する際は、コンテキスト検索によってシステムをもっと賢くしユーザーのニーズと合致させることができる方法を検討してください。オープンソーススタックを使用する場合でも、BoxPineconeなどのプラットフォームで迅速に作業を開始する場合でも、AIが適切なタイミングで適切なナレッジを検索できるようAIにコンテキストを提供するという原則は変わりません。

インテリジェントなコンテンツ駆動型アプリケーションの作成の詳細については、Boxをフォローして最新情報やチュートリアルを入手してください。YouTubeで動画を確認したり、Mediumで他のガイドを読んだり、GitHubでサンプルプロジェクトを探したりできます。つながりを維持することで、次世代のコンテキストに対応したAIソリューションをいつでも一緒に作成できるようになります。


RECENT POST「開発者」の最新記事


開発者

Box、MCPアプリのサポート対象をChatGPT、Microsoft 365 Copilot、Gleanに拡大

開発者

AIエージェントにコンテンツの活用方法を教える: OpenAI Codex向けBox Skillの構築

開発者

Box AIとOpenAI Agents SDKで自律的なドキュメントワークフローを実行

開発者

Box CLI: 開発者とAIエージェントのためのコンテンツCLI