> ## Documentation Index
> Fetch the complete documentation index at: https://developer.box.com/llms.txt
> Use this file to discover all available pages before exploring further.

> Box AI抽出 (構造化) の構造および表のフィールドタイプを使用して、サプライヤ契約書から、グループ化されたベンダー詳細データと繰り返しの納品スケジュールを抽出します。

# Box AIを使用したサプライヤ契約書から構造化された調達データへの変換

export const RelatedLinks = ({title, items = []}) => {
  const getBadgeClass = badge => {
    if (!badge) return "badge-default";
    const badgeType = badge.toLowerCase().replace(/\s+/g, "-");
    return `badge-${badge === "ガイド" ? "guide" : badgeType}`;
  };
  if (!items || items.length === 0) {
    return null;
  }
  return <div className="my-8">
      {}
      <h3 className="text-sm font-bold uppercase tracking-wider mb-4">{title}</h3>

      {}
      <div className="flex flex-col gap-3">
        {items.map((item, index) => <a key={index} href={item.href} className="py-2 px-3 rounded related_link hover:bg-[#f2f2f2] dark:hover:bg-[#111827] flex items-center gap-3 group no-underline hover:no-underline border-b-0">
            {}
            <span className={`px-2 py-1 rounded-full text-xs font-semibold uppercase tracking-wide flex-shrink-0 ${getBadgeClass(item.badge)}`}>
              {item.badge}
            </span>

            {}
            <span className="text-base">{item.label}</span>
          </a>)}
      </div>
    </div>;
};

export const Link = ({href, children, className, ...props}) => {
  const localizedHref = localizeLink(href);
  return <a href={localizedHref} className={className} {...props}>
      {children}
    </a>;
};

企業の調達ドキュメントがたった数個のフラットなフィールドで済むことなどほぼありません。1社のサプライヤ契約書には複数の段落や表にわたって、ベンダーの連絡先情報、納期のマイルストーン、出荷先の場所、支払条件が記載されます。

このチュートリアルでは、複雑な情報をわかりやすく構造化された出力に変換する方法を説明します。Box AI抽出 (構造化) の`struct`および`table`フィールドタイプを使用してドキュメントの形式に合わせたスキーマを定義した後、[抽出エージェント (強化)](/guides/box-ai/ai-tutorials/extract-metadata-structured#enhanced-extract-agent) で抽出エンドポイントを呼び出して、下流システム、自動化、Boxメタデータに使用できる状態のデータを返します。

## 構築する内容

このチュートリアルの最後には、次のような機能を備えた、動作するPythonプロジェクトが完成します。

* Boxに対する認証を実行し、Boxフォルダに保存されているサプライヤ契約書を読み取る。
* グループ化されたベンダー詳細データに`struct`フィールドを使用し、繰り返しの納品スケジュールに`table`フィールドを使用する抽出スキーマを定義する。
* [抽出エージェント (強化)](/guides/box-ai/ai-tutorials/extract-metadata-structured#enhanced-extract-agent) を使用して[POST /2.0/ai/extract\_structured](/reference/post-ai-extract-structured)エンドポイントを呼び出す。
* レスポンスを、ERP、調達プラットフォーム、プロジェクトトラッカーにプッシュできるように調達レポートにマッピングする。

## 構造および表を選ぶ理由

[POST /2.0/ai/extract\_structured](/reference/post-ai-extract-structured)エンドポイントは、`string`、`float`、`date`、`enum`、`multiSelect`などのスカラー型に加え、2つの複雑なフィールドタイプをサポートしています。

| フィールドタイプ | 戻り値                    | 用途                                    |
| -------- | ---------------------- | ------------------------------------- |
| `struct` | 単一のネストされたJSONオブジェクト    | ベンダーの正式名称、住所、連絡先情報など、属性が同じ関連のある値。     |
| `table`  | 同じスキーマを持つJSONオブジェクトの配列 | 納期のマイルストーン、明細行、支払いスケジュールなど、繰り返し出現する行。 |

これらのタイプを使用すると、データが実際にどのように使用されているかを反映させることができます。抽出後に1つの長大なベンダーブロックを解析したり、納品スケジュールを単一の文字列にまとめたりするのではなく、結果をサプライヤのマスターレコードまたは調達ワークフローに直接マッピングします。

<Tip>
  両方のフィールドタイプと、それらがサポートするサブフィールドのタイプの詳細なリファレンスについては、<Link href="/guides/box-ai/ai-tutorials/extract-metadata-structured#struct-and-table-field-types">structおよびtableフィールドタイプ</Link>を参照してください。
</Tip>

## 前提条件

始める前に、以下が揃っていることを確認してください。

* Box AI APIへのアクセスを提供する無料の<Link href="https://account.box.com/signup/developer#ty9l3">Box Developerアカウント</Link>。
* **クライアント資格情報許可**認証で構成されたBoxアプリケーション。
* Python 3.11以上。
* Boxにアップロードされたサプライヤ契約書。<Link href="/static/quickstarts/extract/files/supplier-agreement-sample.pdf">サンプルのサプライヤ契約書</Link>をダウンロードして、それをBoxフォルダにアップロードできます。その**ファイルID**はURLで決まることに注意してください。たとえば、URLが`https://app.box.com/file/123456789`の場合、ファイルIDは`123456789`となります。
* アプリで以下のスコープが有効になっていること。
  * Boxに格納されているすべてのファイルとフォルダの読み取りと書き込み
  * AIを管理する

## 手順

このチュートリアルでは、Box Platformの機能とBox AIエージェントをそれぞれ1つずつ使用します。

| コンポーネント            | 目的                                       | API                                    |
| ------------------ | ---------------------------------------- | -------------------------------------- |
| **Box AI Extract** | グループ化された構造化データや繰り返し構造を持つ構造化データを契約書から取得する | `POST /2.0/ai/extract_structured`      |
| **抽出エージェント (強化)**  | ネストされたフィールドや繰り返し出現するフィールドの精度を向上させる       | `ai_agent.id = enhanced_extract_agent` |

抽出エージェント (強化) は、あらかじめ定義されたBox AIエージェントであるため、自分で作成したり構成したりする必要はありません。これは、以下で説明する**抽出関数の構築**ステップで構築する抽出リクエストの`ai_agent`パラメータのID (`enhanced_extract_agent`) で参照されます。詳細については、<Link href="/guides/box-ai/ai-tutorials/extract-metadata-structured#enhanced-extract-agent">抽出エージェント (強化)</Link> に関するリファレンスを参照してください。

<Steps>
  <Step title="開発環境のセットアップ">
    1. ターミナルを開き、新しいプロジェクトディレクトリを作成します。

    ```bash theme={null}
    mkdir supplier-extraction && cd supplier-extraction
    ```

    2. Pythonの仮想環境を作成してアクティブ化します。

    ```bash theme={null}
    python3 -m venv .venv
    source .venv/bin/activate
    ```

    アクティブ化すると、ターミナルのプロンプトの先頭に`(.venv)`と表示されます。これにより、仮想環境内で作業していることがわかります。

    <Note>
      新しいターミナルウィンドウやタブを開くたびに、プロジェクトディレクトリから`source .venv/bin/activate`を実行して、仮想環境を再アクティブ化します。コマンドの実行中に`ModuleNotFoundError`が表示される場合、通常、venvがアクティブ化されていないことを意味します。
    </Note>

    3. 必要なパッケージをインストールします。

    ```bash theme={null}
    pip install box-sdk-gen python-dotenv
    ```

    4. 資格情報を保存するための`.env`ファイルを作成し、以下の内容を追加します。プレースホルダの値を、Box開発者コンソールで確認した実際の資格情報で置き換えます。

    ```bash theme={null}
    BOX_CLIENT_ID=your_client_id
    BOX_CLIENT_SECRET=your_client_secret
    BOX_ENTERPRISE_ID=your_enterprise_id
    AGREEMENT_FILE_ID=your_file_id
    ```

    <Warning>
      `.env`ファイルはバージョン管理システムにコミットさせないでください。`.env`を`.gitignore`に追加します。
    </Warning>
  </Step>

  <Step title="Boxクライアントの認証">
    `box_client.py`という名前のファイルを作成して次のコードを追加します。これにより、SDKを通じてBox AIを呼び出す際に使用する、認証済みのクライアントが作成されます。

    ```python theme={null}
    import os
    from dotenv import load_dotenv
    from box_sdk_gen import BoxClient, BoxCCGAuth, CCGConfig

    load_dotenv()

    def get_box_client() -> BoxClient:
        config = CCGConfig(
            client_id=os.getenv("BOX_CLIENT_ID"),
            client_secret=os.getenv("BOX_CLIENT_SECRET"),
            enterprise_id=os.getenv("BOX_ENTERPRISE_ID"),
        )
        auth = BoxCCGAuth(config=config)
        return BoxClient(auth=auth)
    ```

    <Tip>
      エンドユーザーが関与しないサーバー間の自動化には、クライアント資格情報許可をお勧めします。その他の認証オプションについては、<Link href="/guides/authentication/select">認証方法の選択</Link> を参照してください。
    </Tip>

    <Warning>
      CCGアプリケーションは、コンテンツへのアクセス権限が自動的には付与されない別個のサービスアカウントユーザーとして動作します。サービスアカウントのメールアドレス ([開発者コンソール](https://app.box.com/developers/console)の \[**一般設定**] で確認可能) を、契約書が保管されているフォルダのコラボレータとして招待します。アクセス権限がない場合は、APIコールで`404 Not found`が返されます。
    </Warning>
  </Step>

  <Step title="抽出スキーマの定義">
    `schema.py`という名前のファイルを作成して以下のコードを追加します。このスキーマでは、2つの複雑なフィールドを使用して契約書を記述しています。

    * `vendor`は、関連するベンダーの詳細を1つのネストされたオブジェクトにグループ化する`struct`フィールドです。
    * `delivery_schedule`は、マイルストーンごとに1行を返す`table`フィールドです。

    上記の複雑なフィールドはそれぞれ、そのサブフィールドを定義する`fields`配列を必要とします。サブフィールドではスカラー型のみがサポートされます。ネストされた`struct`または`table`型は使用できません。

    ```python theme={null}
    from box_sdk_gen import CreateAiExtractStructuredFields

    EXTRACTION_FIELDS = [
        CreateAiExtractStructuredFields(
            key="agreement_effective_date",
            display_name="Effective date",
            type="date",
            prompt="The date the agreement takes effect.",
        ),
        CreateAiExtractStructuredFields(
            key="payment_terms",
            display_name="Payment terms",
            type="string",
            prompt="The payment terms, for example Net 30.",
        ),
        CreateAiExtractStructuredFields(
            key="vendor",
            display_name="Vendor",
            type="struct",
            prompt="The vendor or supplier issuing the agreement.",
            fields=[
                CreateAiExtractStructuredFields(key="legal_name", type="string", display_name="Legal name"),
                CreateAiExtractStructuredFields(key="address", type="string", display_name="Address"),
                CreateAiExtractStructuredFields(key="country", type="string", display_name="Country"),
                CreateAiExtractStructuredFields(key="contact_person", type="string", display_name="Contact person"),
                CreateAiExtractStructuredFields(key="email", type="string", display_name="Email"),
                CreateAiExtractStructuredFields(key="phone", type="string", display_name="Phone"),
            ],
        ),
        CreateAiExtractStructuredFields(
            key="delivery_schedule",
            display_name="Delivery schedule",
            type="table",
            prompt="Each delivery milestone defined in the agreement.",
            fields=[
                CreateAiExtractStructuredFields(key="phase", type="string", display_name="Phase"),
                CreateAiExtractStructuredFields(key="items", type="string", display_name="Items"),
                CreateAiExtractStructuredFields(key="delivery_date", type="date", display_name="Delivery date"),
                CreateAiExtractStructuredFields(key="destination", type="string", display_name="Destination"),
                CreateAiExtractStructuredFields(key="status", type="string", display_name="Status"),
            ],
        ),
    ]
    ```

    <Tip>
      表の抽出は、視覚的にフォーマットされた表に限定されません。`table`タイプは、グリッド、キー/値ペア、フォームレイアウト、単なる文のいずれとして表示されている場合でも、繰り返しデータを抽出します。
    </Tip>
  </Step>

  <Step title="抽出関数の構築">
    `extract.py`という名前のファイルを作成して以下のコードを追加します。これは、SDKの`create_ai_extract_structured`メソッドを使用してスキーマをBox AIに送信し、抽出エージェント (強化) を指定します。これにより、ネストされたフィールドや繰り返し出現するフィールドの精度が向上します。

    ```python theme={null}
    from box_sdk_gen import AiItemBase, AiAgentReference, AiAgentReferenceTypeField
    from box_client import get_box_client
    from schema import EXTRACTION_FIELDS

    def extract_agreement(file_id: str) -> dict:
        client = get_box_client()

        response = client.ai.create_ai_extract_structured(
            items=[AiItemBase(id=file_id)],
            fields=EXTRACTION_FIELDS,
            ai_agent=AiAgentReference(
                id="enhanced_extract_agent",
                type=AiAgentReferenceTypeField.AI_AGENT_ID,
            ),
        )

        return response.to_dict()["answer"]
    ```

    <Note>
      抽出エージェント (強化) は厳密には必須ではありませんが、特にネストされたフィールドや繰り返し出現するフィールドが含まれている場合、より詳細なスキーマや複雑なドキュメントレイアウトにおいて結果の精度が向上します。
    </Note>
  </Step>

  <Step title="構造化された出力のマッピング">
    `app.py`という名前のファイルを作成して以下のコードを追加します。Box AIは、`vendor`フィールドをネストされたオブジェクトとして返し、`delivery_schedule`フィールドを行のリストとして返します。このスクリプトは、抽出を実行し、その結果を下流システムで使用できるフラットなレコードにマッピングします。

    ```python theme={null}
    import json
    import os
    from dotenv import load_dotenv
    from extract import extract_agreement

    load_dotenv()

    def to_procurement_record(answer: dict) -> dict:
        vendor = answer.get("vendor", {})
        schedule = answer.get("delivery_schedule", [])

        return {
            "vendor_name": vendor.get("legal_name"),
            "vendor_country": vendor.get("country"),
            "vendor_contact": vendor.get("contact_person"),
            "vendor_email": vendor.get("email"),
            "payment_terms": answer.get("payment_terms"),
            "effective_date": answer.get("agreement_effective_date"),
            "milestones": [
                {
                    "phase": row.get("phase"),
                    "due": row.get("delivery_date"),
                    "destination": row.get("destination"),
                    "status": row.get("status"),
                }
                for row in schedule
            ],
        }

    if __name__ == "__main__":
        file_id = os.getenv("AGREEMENT_FILE_ID")

        answer = extract_agreement(file_id)
        print("Raw extraction:")
        print(json.dumps(answer, indent=2))

        record = to_procurement_record(answer)
        print("\nProcurement record:")
        print(json.dumps(record, indent=2))
    ```

    この時点でプロジェクトディレクトリには、以下のファイルが含まれていることになります。

    ```
    supplier-extraction/
    ├── .env
    ├── .venv/
    ├── app.py
    ├── box_client.py
    ├── extract.py
    └── schema.py
    ```
  </Step>

  <Step title="抽出の実行">
    仮想環境がアクティブ化されている`supplier-extraction`ディレクトリを開いていることを確認したら、スクリプトを実行します。

    ```bash theme={null}
    python3 app.py
    ```

    Box AIは、`struct`フィールドを単一のネストされたオブジェクトとして返し、`table`フィールドをオブジェクトのリストとして返します。出力は次のようになります。

    ```json theme={null}
    {
      "agreement_effective_date": "2026-06-01",
      "payment_terms": "Net 45",
      "vendor": {
        "legal_name": "Nexus Industrial Solutions Ltd.",
        "address": "47 Canary Wharf, Level 12, London, E14 5AB",
        "country": "United Kingdom",
        "contact_person": "Oliver Hartmann",
        "email": "o.hartmann@nexusindustrial.co.uk",
        "phone": "+44 20 7946 0832"
      },
      "delivery_schedule": [
        {
          "phase": "Phase 1 - Infrastructure",
          "items": "Server Racks (qty 6), UPS (qty 3)",
          "delivery_date": "2026-07-15",
          "destination": "Chicago, IL",
          "status": "Scheduled"
        },
        {
          "phase": "Phase 2 - Networking",
          "items": "48-Port Switches (all), Fibre Cables (all)",
          "delivery_date": "2026-08-01",
          "destination": "Chicago, IL",
          "status": "Scheduled"
        },
        {
          "phase": "Phase 3 - Security",
          "items": "NGFW Cluster (all), Remaining Racks",
          "delivery_date": "2026-08-20",
          "destination": "San Francisco, CA",
          "status": "Pending"
        },
        {
          "phase": "Phase 4 - Services",
          "items": "Installation, Training",
          "delivery_date": "2026-09-01",
          "destination": "San Francisco, CA",
          "status": "Pending"
        }
      ]
    }
    ```

    その後、このスクリプトはマッピングされた調達レコードを出力します。これは、お使いのERP、調達プラットフォーム、プロジェクトトラッカーに送信できます。
  </Step>
</Steps>

## トラブルシューティング

<AccordionGroup>
  <Accordion title="ModuleNotFoundError: 「...」という名前のモジュールが見つからない">
    仮想環境がアクティブ化されていません。`python3`コマンドを実行する前に、プロジェクトディレクトリから`source .venv/bin/activate`を実行してください。新しく開いたターミナルタブは、すべて個別にアクティブ化する必要があります。
  </Accordion>

  <Accordion title="invalid_client: クライアント資格情報が無効である">
    以下のように、`.env`ファイルを確認します。

    * `BOX_CLIENT_ID`および`BOX_CLIENT_SECRET`が、開発者コンソール > \[構成] の値と一致していることを確認します。
    * `BOX_ENTERPRISE_ID`が自分のEnterprise IDであることを確認します。
    * アプリが開発者コンソールで承認されていて、クライアント資格情報許可を使用していることを確認します。
  </Accordion>

  <Accordion title="404 Not Found">
    サービスアカウントに、そのファイルへのアクセス権限がありません。開発者コンソール > \[一般設定] に記載されているサービスアカウントのメールアドレスを、契約書が保管されているフォルダのコラボレータとして招待します。
  </Accordion>

  <Accordion title="サブフィールドの値が空またはマージ済みで返される">
    ネストされた`struct`および`table`タイプは、サブフィールドとしてサポートされていません。また、`struct`または`table`フィールドには`fields`配列を含める必要があります。サブフィールドにはスカラー型のみが使用されていることを確認し、抽出対象を明確にするために複雑なフィールドに`prompt`を追加してください。
  </Accordion>
</AccordionGroup>

## 本番環境へのスケーリング

<AccordionGroup>
  <Accordion title="結果のBoxメタデータへの書き戻し">
    契約書をBox内で検索可能およびルーティング可能にするには、フラット化されたトップレベルの値をメタデータインスタンスとしてファイルに書き戻したうえで、<Link href="/guides/metadata/queries">メタデータクエリ</Link>を使用して、ベンダー、国、または発効日でフィルタをかけます。エンドツーエンドのメタデータ書き戻しパターンについては、<Link href="/guides/tutorials/invoice-intake">Box AI Extractを使用した請求書取り込みの自動化</Link>を参照してください。
  </Accordion>

  <Accordion title="自動的な抽出の開始">
    スクリプトを手動で実行せず、契約書のフォルダにWebhookを登録して、アップロードが行われるたびに抽出が開始されるようにします。受信したリクエストを実稼働環境で検証する方法については、<Link href="/guides/webhooks/v2/signatures-v2">Webhook署名の検証</Link>を参照してください。
  </Accordion>

  <Accordion title="レイアウトが複雑な場合の精度の調整">
    フィールドレベルの`prompt`を抽出のガイドとして追加し、表が密集している複数ページの契約書に対して抽出エージェント (強化) を引き続き使用します。複数のドキュメント間でスキーマの一貫性を維持には、フィールドをインラインではなく<Link href="/guides/box-ai/ai-tutorials/extract-metadata-structured#use-metadata-template">メタデータテンプレート</Link>で定義してください。
  </Accordion>
</AccordionGroup>

## 次の手順

<CardGroup cols={2}>
  <Card title="請求書の取り込み自動化" href="/guides/tutorials/invoice-intake" icon="file-invoice" arrow="true">
    フォルダに新しい請求書がないか監視し、フィールドを抽出して検索可能なメタデータとして書き戻します。
  </Card>

  <Card title="APIリファレンスの抽出" href="/reference/post-ai-extract-structured" icon="code" arrow="true">
    抽出 (構造化) のための詳細なAPI仕様を確認します。
  </Card>
</CardGroup>

<RelatedLinks
  title="関連するガイド"
  items={[
{ label: translate("Extract metadata from file (structured)"), href: "/guides/box-ai/ai-tutorials/extract-metadata-structured", badge: "GUIDE" },
{ label: translate("Extract structured data quick start"), href: "/guides/box-ai/quick-start/box-ai-extract", badge: "QUICKSTART" },
{ label: translate("Extract APIs overview and use cases"), href: "/guides/box-ai/ai-tutorials/extract-use-cases", badge: "GUIDE" },
{ label: translate("Override AI model configuration"), href: "/guides/box-ai/ai-tutorials/default-agent-overrides", badge: "GUIDE" }
]}
/>
