Technology Blog Posts by SAP
cancel
Showing results for 
Search instead for 
Did you mean: 
takayuki_tanaka
Product and Topic Expert
Product and Topic Expert
1,835

はじめに

SAP AI CoreGenerative AI Hubに2024Q4、新たに Grounding(グラウンディング)」 機能が追加されました。これは生成AIの回答を企業固有の知識やデータに紐づけて信頼性を高める仕組みです。一般にグラウンディングとは、モデルの出力を検証可能な情報源に結び付けるプロセスを指し、これにより生成AIの回答を特定のデータに基づいたものにでき、いわゆるハルシネーションを起こりにくくします。本記事では、ハンズオンコンテンツを通しながらSAP BTP 上のSAP AI CoreによるGrounding機能の全体像を技術者向けに解説し、その目的や仕組みについて俯瞰して解説します

Grounding機能の概要

Grounding機能の概要と目

SAP AI CoreGenerative AI Hubに追加されたGrounding機能は、一言でいうと生成AIと企業の専門データをつなぐための機能です。大規模言語モデル(LLM)の汎用的な知識に、企業内の最新かつ正確なデータを組み合わせることで、回答の精度向上と意思決定支援を図る目的があります。例えば、これまでの生成AIはインターネット上に存在し、学習に利用された一般的な知識をもとに回答を生成しますが、Grounding機能を利用すると社内の文書や知識データベースから関連情報を検索・参照して回答に反映できます。この専門データの検索と文脈情報の活用によって、生成AIの出力内容に現実的な裏付け(根拠)を与え、業務で使える信頼性の高い回答を得ることが可能になります​。

Generative AI Hubにおける位置づけと他機能との関

Generative AI Hubは、SAP AI Core / SAP AI Launchpad上で提供される生成AI統合環境であり、様々なLLMへのアクセスやAIワークフローのオーケストレーション機能を備えています。Grounding機能はこのHub内の「オーケストレーションモジュール」に組み込まれた新機能です。オーケストレーションモジュールでは、プロンプトの処理やモデル実行の流れに沿って複数のステップを定義できますが、Groundingはその中で外部データ取り込みや、取り込んだデータからの検索を担うステップとして位置付けられます。つまり、Generative AI Hub上でLLMに質問を投げる際、まずGroundingステップで関連する社内データを検索し、その結果をもとにLLMが回答を生成する、という流れになります

15-Grounding-capabilities-in-SAPs-Generative-AI-Hub-768x432.jpg

引用:SAP® Business AI:2024年第4四半期リリースハイライト

なお、SAP AI Launchpad上からは Grounding Managementと呼ばれる管理画面にもアクセスが可能であり、ここでGrounding用の知識ベース設定を行えるようになっています​。例えば、どのデータソース(後述のSharePointやSAP HANA Cloudなど)をGroundingに利用するかを設定し、インデックス作成の状況をモニタリングするといった操作が可能です。このようにGrounding機能は、Generative AI Hub内のオーケストレーションフローの一部でありながら、他の機能やUIとも連携して統合的に管理・活用できるよう設計されています。

takayuki_tanaka_0-1743666316807.png

 

takayuki_tanaka_1-1743666316815.png

 

 

Grounding機能の技術的な仕組

Screenshot 2025-04-07 at 9.46.09.png

Grounding機能を実現する背後には、SAP HANA Cloud Vector Engineにより提供されるベクトルデータベースを用いた高度な検索技術が使われています​。大まかな仕組みは、「ドキュメント埋め込み(Embedding)+ベクトル検索+生成AI」 というRetrieval Augmented Generation (RAG) のパターンで利用される場合があります。具体的には次のように動作します

  1. 知識ベースの準備: まず企業内でGroundingに使いたいドキュメントやデータ群を「知識ベース」として用意します。WordPDFの社内文書、マニュアル、FAQ集、メール記録、データベースエントリなどが該当します。この知識ベースのデータはそのままではLLMが扱えないため、事前にベクトル化(埋め込み生成)しておく必要があります。この事前のベクトル化処理に際して、Generative AI HubGrounding機能では下記2通りの方法が提供されています​。
    1. データリポジトリに文書をアップロードしてPipeline APIからベクトル化パイプラインを生成し、日次でベクトル化を行う方法
    2. Vector APIでプログラム的に文章を直接的にベクトル化・登録する方法

前者では、SAP AI Coreが用意するデータパイプラインを実行すると、指定したリポジトリから文書を収集して自動でテキスト分割・ベクトル変換し、ベクトルデータベース(SAP HANA Cloud)に保存が可能です​。後者では、開発者自らが文章を一定長に分割しAPI経由でベクトルデータベースに登録します​。いずれの場合も、一度知識ベースとなるベクトルデータが作成されれば、それ以降はLLM問い合わせ時に高速な類似検索が可能になります

現在、パイプライン機能を利用してGroundingで参照できる文書管理サービス・外部サービスとして、Microsoft Sharepoint / Amazon S3 / SFTPサーバー が公開されています。それぞれpdfとdocxファイルタイプをサポートしており、コンテンツの更新は日次で行われます。詳細は参考文献に記載のSAP Help Portalをご参照ください。

  1. 質問に対する関連情報検索(Grounding処理): エンドユーザーが生成AIに対して質問やプロンプトを投げると、まずGroundingモジュールがその質問内容に関連するベクトルを知識ベースから検索します。これはユーザーの質問をベクトルに変換し、事前に格納されているベクトル群との類似度検索を行うことで実現します。検索にはSAP HANA Cloud Vector Engineにより提供される機能が使われます。この処理により、質問に関連度の高い社内文書の断片やデータ項目がいくつかピックアップされます。今回のSAP AI Core – Generative AI Hub Grounding機能では、Vector APIもしくはRetrieval APIの検索用エンドポイントを利用することで、こちらの検索を外部から実行できます。
  2. 生成AIによる回答生成: 次に、オーケストレーションのフローに従いLLMが呼び出されますが、その際のプロンプトには上記で見つかった関連文書の内容(抜粋)が組み込まれます​。具体的には、「ユーザーの質問 + (該当文書の抜粋) + 必要に応じたシステムプロンプト」を結合したテキストをLLMに与えます。LLMはそれらを文脈として考慮しながら回答を生成するため、社内データに根差した内容が回答に反映されます。たとえば「自社の最新の休暇ポリシーについて教えて」と質問された場合、Groundingが社内ポリシー文書から関連箇所を検索・提供し、モデルはそれを参照して回答を作成します。結果として、「〇〇社の休暇ポリシーでは、有給休暇は年間△日付与され...」といったように社内公式の情報に即した回答が返ってくるわけです​。
  3. 回答の返却と継続: 最終的に生成された回答はユーザーに提示されます。Groundingにより参照された元データの出所(ドキュメント名やURLなど)を回答と一緒に示すことも可能で、回答内容の裏付けをユーザーが確認できる形にすることも推奨されます。また、一度構築したGrounding付きの生成AIは、継続的に知識ベースを更新・拡充することで回答精度を維持向上できます。新しい社内文書を追加したり、不要になった情報を削除したりすれば、次回以降のQAに即座に反映されます。これは通常のLLM自体のファインチューニングよりも容易で、知識の追加・修正が柔軟に行える点も技術的メリットです

 

想定されるユースケース

Grounding機能により、これまで難しかった 「社内ナレッジを活用した高度なAIアシスタント」 が実現しやすくなります。以下に代表的なユースケースの例を挙げます

  • 社内文書の対話型検索: 社員が膨大な社内文書から必要な情報を探す際に、Grounding対応のチャットボットに質問すれば的確な回答を得られます。例えば「今年の有給休暇ポリシーは?」と質問すると、SharePointに保存された社内ポリシー文書をもとに最新規程を回答してくれる、といった使い方です​。従来のキーワード検索では辿り着けなかった細かな規定も自然言語で確認でき、社内ヘルプデスク業務の効率化が期待できます。
  • FAQやカスタマーサポートの強化: 顧客向けFAQサイトやチャットサポートにGroundingを組み込むケースです。製品マニュアルや過去の問い合わせ対応集をベクトルDB化しておけば、ユーザーからの質問に対して最新の製品情報やナレッジベース記事に基づく回答を生成できます。例えば「○○製品でエラーコード123が出た場合の対処法は?」という質問に対し、社内サポート資料から該当手順を抜粋して回答するため、回答の正確さと一貫性が向上します。これにより自己解決率アップやオペレーター支援が可能になります
  • 専門資料の要約とレポート生成: Groundingは、単に質問応答だけでなく長文コンテンツの要約生成にも役立ちます。社内の研究レポートや市場分析レポートを知識ベースに登録しておけば、「〇〇プロジェクトの概要を要約して」と指示することで、関連する複数文書から要点を抽出し要約したレポートを自動生成する、といったことも可能です。生成AIが参照すべき情報源を限定できるため、要約内容の信頼性が高まり、レポート作成業務の効率化につながります
  • 社内システムデータの問い合わせ: ナレッジ文書だけでなく、構造化データに基づく回答にもGroundingは応用できます。例えばSAPシステム上の売上データや在庫データをEmbedding化しておけば、「今月の地域別売上トップ3は?」という問いに対し、最新データベースから該当数値を検索して回答させることもできます​。この場合はLLMSQLベースのクエリを組み合わせるイメージで、自然言語の質問を適切なデータ参照にグラウンディングすることで、ビジネスユーザーが対話的にデータを引き出せるようになります​。

以上のように、Grounding機能は社内情報に基づいた高度なQAやコンテンツ生成全般に威力を発揮します。特に社内ポータルや従業員向けアシスタント、顧客対応チャットボット、経営レポート自動化など、「大量のテキストやデータを迅速に活用したい」シナリオで有用です。これにより従業員の情報アクセスが容易になり、日常の意思決定スピードが向上することが期待できます。Groundingは今後、様々な業種・業務のAIユースケースで鍵となる技術になるでしょう

Grounding機能に触れてみる

本ブログでは、Amazon S3をGenerative AI HubのGrounding機能に対してオンボーディングし、Retrieval APIからデータを取得するところまでを扱います。オーケストレーションモジュールを使ってRAGエンドポイントを作成する部分に関しては、別の機会に公開する資料をご参照ください。

P.S. ブログを公開しました。
SAP AI Core Orchestration機能 を解き明かす

 

Githubリポジトリのクローン

今回のハンズオンはSAP Build Code上にて実施します。Devスペースにログインしてターミナルを立ち上げ、まずはサンプルコードをクローンしましょう。

git clone https://github.com/watwatwhat/SAPAICore_Grounding_sample.git

認証情報の設定を行う

今回のハンズオンの前提条件として、Object Store on SAP BTP (Standartプラン)のインスタンスと、SAP AI Core(Extendedプラン)のインスタンスをSAP BTP Cockpitから作成して、SAP AI LaunchpadSAP AI Coreのインスタンスをオンボーディングしておきます。

まずは、今回のハンズオンで用いるサービス群のサービスキーとユーザー定義文字列を設定しましょう。

クローンしたファイル群の中から、下記の3つのファイルを編集します。

credentials/ai_core_sk.json
credentials/object_store_sk.json
credentials/user_defined_variable.json

1,2つ目に関しては、SAP AI Coreのインスタンスに作成したサービスキーのjsonObject Store on SAP BTP のインスタンスに作成したサービスキーのjsonをペーストしてください。3つ目に関しては、任意の値を入力してください。使える文字には制限があるようなので、特にこだわりがなければデフォルトと同じような名前をつけることを推奨します。IDの文字が規定に沿っていないという旨のエラーが後段で出た場合、この辺りを設定し直してください。

Object Store on SAP BTP を経由したAmazon S3Generative AI Hubに対してオンボーディングする

まずは環境のセットアップを行いましょう。SAP AI Coreではリソースグループという単位を作成し、その中でクレデンシャルなどの管理を行います。まずはリソースグループの作成を行い、その後にObject Store on SAP BTPのサービスキーを利用して接続のセットアップを行うため、下記のコマンドを実行します。

. ./01_grounding/01_prerequisites/00_init.sh
node ./01_grounding/01_prerequisites/01_prerequisites.js

この処理により、下記の処理が行われます。

  • SAP AI Coreのインスタンスが提供するAPIへのアクセストークンを取得
  • SAP AI CoreGrounding検証用のリソースグループを作成
    • ext.ai.sap.com/document-grounding タグにより、当該リソースグループにおけるGrounding機能の有効・無効が制御されます。既存のリソースグループを更新・タグを付与してハンズオンを進めることも可能です。その場合はPATCHリクエストでリソースグループを更新してください。
  • Amazon S3へのアクセスに必要なシークレットをリソースグループ内に登録

主要なスクリプト部分は下記の通りです。

// リソースグループ作成
async function createResourceGroup(token) {
    const url = `${AI_API_HOST}/v2/admin/resourceGroups`;

    const payload = {
        resourceGroupId,
        labels: [
            {
                key: 'ext.ai.sap.com/document-grounding',
                value: 'true',
            },
        ],
    };

    try {
        await axios.post(url, payload, {
            headers: {
                Authorization: `Bearer ${token}`,
                'Content-Type': 'application/json',
            },
        });
        console.log(' Resource Group created!');
    } catch (err) {
        if (err.response && err.response.status === 409) {
            console.log(' Resource Group already exists');
        } else {
            throw err;
        }
    }
}

// S3 Secret 作成
async function createS3Secret(token) {
    const url = `${AI_API_HOST}/v2/admin/secrets`;
    const payload = {
        name: secretName,
        data: {
            url: Buffer.from(`https://${s3Info.bucketName}.s3.${s3Info.region}.amazonaws.com`).toString('base64'),
            authentication: "Tm9BdXRoZW50aWNhdGlvbg==",
            description: Buffer.from('Generic secret for accessing S3 bucket for grounding').toString('base64'),
            access_key_id: Buffer.from(s3Info.accessKeyId).toString('base64'),
            secret_access_key: Buffer.from(s3Info.secretAccessKey).toString('base64'),
            bucket: Buffer.from(s3Info.bucketName).toString('base64'),
            host: Buffer.from(s3Info.host).toString('base64'),
            region: Buffer.from(s3Info.region).toString('base64'),
            username: Buffer.from(s3Info.username).toString('base64'),
        },
        labels: [
            {
                key: 'ext.ai.sap.com/document-grounding',
                value: 'true',
            },
            {
                key: 'ext.ai.sap.com/documentRepositoryType',
                value: 'S3',
            },
        ],
    };

    try {
        await axios.post(url, payload, {
            headers: {
                Authorization: `Bearer ${token}`,
                'Content-Type': 'application/json',
                'AI-Resource-Group': resourceGroupId,
            },
        });
        console.log(' S3 Secret created');
    } catch (err) {
        if (err.response && err.response.status === 409) {
            console.log(' S3 Secret already exists');
        } else {
            throw err;
        }
    }
}

これが完了すると、SAP AI LaunchpadSAP AI Core Administration -> Generic SecretsAmazon S3へのアクセス用の認証情報が登録されます。SAP AI Coreのパイプラインは、この認証情報を用いて、Amazon S3のバケットへアクセスします。

takayuki_tanaka_3-1743666316822.png

 

Pipeline APIを利用してベクトル化処理を行う

次に、SAP AI Coreのパイプライン機能を使って、Amazon S3の中に格納された文書を、ベクトルデータベース上に取り込んで行きます。が、その前にAmazon S3の中に文書を格納しておきましょう。今回は、下記のパスに格納されたpdfAmazon S3に格納しておきます。

./01_grounding/docs/SAP_BTP_Overview.pdf

早速、下記のNodeスクリプトを実行しましょう。

node ./01_grounding/02_pipelineAPI/02_uploadDocs.js

うまく実行できると、署名付きURLが表示されるように構成しています。ここにアクセスすると、誰でも1時間はpdfを読み取ることができます。Amazon S3へのpdf文書の格納が完了したことへの確認としてご利用ください。

このスクリプトでは、下記の通りAWS SDKを利用して、Amazon S3にpdfファイルをアップロードする処理が記述されています。

// AWS S3 クライアントの設定
const s3 = new AWS.S3({
    accessKeyId: s3Info.accessKeyId,
    secretAccessKey: s3Info.secretAccessKey,
    endpoint: s3Info.host, // SAP BTPのObject Storeの場合も有効
    region: s3Info.region,
    signatureVersion: 'v4',
    s3ForcePathStyle: true,
});

// アップロード対象のファイル
const filePath = path.join(__dirname, 'docs/SAP_BTP_Overview.pdf');
const fileContent = fs.readFileSync(filePath);
const fileName = 'SAP_BTP_Overview.pdf';

// アップロードパラメータ
const uploadParams = {
    Bucket: s3Info.bucketName,
    Key: fileName,
    Body: fileContent,
    ContentType: 'application/pdf'
};

// アップロードと署名付きURLの生成
s3.upload(uploadParams, (err, data) => {
    if (err) {
        console.error(' アップロード失敗:', err);
        return;
    }

    console.log(' アップロード成功:', data.Location);

    // Presigned URL 発行(1時間有効)
    const signedUrl = s3.getSignedUrl('getObject', {
        Bucket: s3Info.bucketName,
        Key: fileName,
        Expires: 3600, // 秒数(例: 3600秒 = 1時間)
    });

    console.log(' 署名付きURL (1時間有効):');
    console.log(signedUrl);
});

次に、パイプラインを生成します。次のnodeスクリプトを実行してください。

node ./01_grounding/02_pipelineAPI/03_createPipeline.js

このスクリプトでは、下記の主要な処理が実行されています。

// Pipeline 作成
async function createS3Pipeline(token) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/pipelines`;

    const payload = {
        type: "S3",
        configuration: {
            destination: secretName
        }
    };

    console.log(' payload 内容:', JSON.stringify(payload, null, 2));

    try {
        const response = await axios.post(url, payload, {
            headers: {
                Authorization: `Bearer ${token}`,
                'Content-Type': 'application/json',
                'AI-Resource-Group': resourceGroupId,
            },
        });

        console.log(' Pipeline 作成成功!');
        console.log(' Pipeline ID:', response.data.id);
        return response.data;
    } catch (err) {
        if (err.response) {
            console.error(' Pipeline 作成エラー:', {
                status: err.response.status,
                statusText: err.response.statusText,
                data: err.response.data,
            });
        } else if (err.request) {
            console.error(' Pipeline 作成エラー: No response received', err.request);
        } else {
            console.error(' Pipeline 作成エラー:', err.message);
        }
    }
}

パイプラインの生成がうまくいくと、下記のnodeスクリプトからパイプラインの一覧を確認できます。

node /01_grounding/02_pipelineAPI/04_managePipeline.js list

このスクリプトでは、下記処理が実行されます。

// Pipeline 一覧取得
async function listPipelines(token) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/pipelines`;
    const response = await axios.get(url, getRequestOptions(token));
    console.log(' Pipeline 一覧:');
    console.log(JSON.stringify(response.data, null, 2));
}

// 実行処理
(async () => {
    try {
        const action = process.argv[2];           // list / get / status / delete
        const pipelineId = process.argv[3];       // オプション:get/status/delete用

        if (!action || !['list', 'get', 'status', 'delete'].includes(action)) {
            console.log(' 使用方法:');
            console.log('node 04_managePipeline.js list');
            console.log('node 04_managePipeline.js get <pipelineId>');
            console.log('node 04_managePipeline.js status <pipelineId>');
            console.log('node 04_managePipeline.js delete <pipelineId>');
            return;
        }

        const token = await getXsuaaToken();

        switch (action) {
            case 'list':
                await listPipelines(token);
                break;
            case 'get':
                if (!pipelineId) throw new Error('Pipeline ID が必要です');
                await getPipeline(token, pipelineId);
                break;
            case 'status':
                if (!pipelineId) throw new Error('Pipeline ID が必要です');
                await getPipelineStatus(token, pipelineId);
                break;
            case 'delete':
                if (!pipelineId) throw new Error('Pipeline ID が必要です');
                await deletePipeline(token, pipelineId);
                break;
        }

    } catch (err) {
        if (err.response) {
            console.error(' エラー:', {
                status: err.response.status,
                statusText: err.response.statusText,
                data: err.response.data,
            });
        } else {
            console.error(' エラー:', err.message);
        }
    }
})();

この状態で、1回目のパイプラインが実行されます。これにより、Amazon S3内に格納された文書は分割・ベクトル化処理され、ベクトルデータベースに格納されます。この結果として、SAP AI Launchpadからはチャンク化されたpdfの内容が確認できるようになります。

takayuki_tanaka_4-1743666316830.png

 

Retrieval APIを利用して関連度の高い文書チャンクを抽出する

これで事前のベクトル化処理は完了です。最後に、Retrieval APIから関連する文書を抽出できることを確認しましょう。下記のコマンドで、関連文書を検索することができます。

node ./01_grounding/03_retrievalAPI/06_searchRetrieval.js "What is BTP?"

もしくは、Groundingを行う対象の文書を格納したリポジトリを指定して、下記のように検索を行うこともできます。

node ./01_grounding/03_retrievalAPI/06_searchRetrieval.js "What is BTP?" <リポジトリID>

takayuki_tanaka_5-1743666316836.png

 

takayuki_tanaka_6-1743666316845.png

 

このnodeスクリプトでは内部的にSAP AI Coreに用意された下記のエンドポイントを呼び出していますが、これをRAGアプリケーションのRetrievalツールの中から呼び出すことで、SAP AI Core – Generative AI HubGrounding機能を利用したRAGを構成することができます。

const url = `${AI_API_HOST}/v2/lm/document-grounding/retrieval/search`;

このスクリプトの全体的な主要な処理は下記の通りです。

// Retrieval Search 呼び出し
async function searchRetrieval(token, query, repositoryId = '*', maxChunkCount = 3) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/retrieval/search`;

    const payload = {
        query: query,
        filters: [
            {
                id: "filter-1",
                searchConfiguration: {
                    maxChunkCount: maxChunkCount
                },
                dataRepositories: [repositoryId],
                dataRepositoryType: "vector"
            }
        ]
    };

    try {
        const response = await axios.post(url, payload, {
            headers: {
                Authorization: `Bearer ${token}`,
                'AI-Resource-Group': resourceGroupId,
                'Content-Type': 'application/json',
            },
        });

        console.log(' 検索結果:');
        console.log(JSON.stringify(response.data, null, 2));
    } catch (err) {
        if (err.response) {
            console.error(' Retrieval Search エラー:', {
                status: err.response.status,
                statusText: err.response.statusText,
                data: err.response.data,
            });
        } else {
            console.error(' Retrieval Search エラー:', err.message);
        }
    }
}

// 実行処理
(async () => {
    const userQuery = process.argv[2];          // 例: "What is SAP BTP?"
    const repositoryId = process.argv[3] || '*'; // 任意:特定のrepositoryId or "*"(デフォルト)

    if (!userQuery) {
        console.log(' 使用方法: node 07_searchRetrieval.js "<query>" [repositoryId]');
        return;
    }

    try {
        console.log(' トークン取得中...');
        const token = await getXsuaaToken();

        console.log(` 検索実行中: "${userQuery}" 対象: ${repositoryId}`);
        await searchRetrieval(token, userQuery, repositoryId);

        console.log(' 検索完了!');
    } catch (err) {
        console.error(' 実行エラー:', err.message);
    }
})();

 

Vector APIを利用してベクトル化処理を行う

これまでで一連のGroundingの流れはさらいましたが、もう一つのベクトル化処理の方法として用意されているVector APIを実行してみます。下記の作業を順次行っていくことで、環境を整えることができます。

  • Collectionを作成する
  • Collectionの中に、実際の文書であるDocumentをチャンク化して登録する

構成要素の包含関係は下記のイメージです。

Screenshot 2025-04-03 at 17.05.23.png

下記のnodeスクリプトを実行することで、Collectionを作成できます。SAP AI Launchpadを確認する限り、先ほどのパイプラインでベクトル化処理を行う場合で言うところの「Repository」に該当する概念のようです。

node ./01_grounding/04_vectorAPI/07_manageCollection.js create <title: 例えばfolkTale> <embedding name: 例えばtext-embedding-ada-002>

無事実行が完了すると、下記のようにCollectionが生成されたことが確認できます。

takayuki_tanaka_7-1743666316849.png

今回は、この中にDocumentを作成していきます。まずは下記のコマンドを利用して、今作成したRepository(Collection)のIDを取得します。

node ./01_grounding/04_vectorAPI/07_manageCollection.js list

このスクリプトでは下記の通りの処理が行われます。

// 一覧取得
async function listCollections(token) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/vector/collections`;
    const res = await axios.get(url, getRequestOptions(token));
    console.log(' コレクション一覧:\n', JSON.stringify(res.data, null, 2));
}

// 実行部
(async () => {
    const action = process.argv[2];
    const arg1 = process.argv[3];
    const arg2 = process.argv[4];

    if (!action || !['list', 'get', 'create', 'delete', 'creationStatus', 'deletionStatus'].includes(action)) {
        console.log(' 使用方法:');
        console.log('  node 07_manageCollection.js list');
        console.log('  node 07_manageCollection.js get <collectionId>');
        console.log('  node 07_manageCollection.js create <title> <embeddingModel>');
        console.log('  node 07_manageCollection.js delete <collectionId>');
        console.log('  node 07_manageCollection.js creationStatus <collectionId>');
        console.log('  node 07_manageCollection.js deletionStatus <collectionId>');
        return;
    }

    try {
        const token = await getXsuaaToken();

        switch (action) {
            case 'list':
                await listCollections(token);
                break;
            case 'get':
                await getCollection(token, arg1);
                break;
            case 'create':
                await createCollection(token, arg1, arg2 || 'text-embedding-model');
                break;
            case 'delete':
                await deleteCollection(token, arg1);
                break;
            case 'creationStatus':
                await getCreationStatus(token, arg1);
                break;
            case 'deletionStatus':
                await getDeletionStatus(token, arg1);
                break;
        }

        console.log(' 完了!');
    } catch (err) {
        if (err.response) {
            console.error(' エラー:', {
                status: err.response.status,
                statusText: err.response.statusText,
                data: err.response.data,
            });
        } else {
            console.error(' 実行エラー:', err.message);
        }
    }
})();

次に、そのCollection IDを利用してCollection内にテキストを格納していきます。今回はdocsディレクトリに格納した、下記のMomotaro.txtを格納しましょう。

takayuki_tanaka_8-1743666316858.png

node ./01_grounding/04_vectorAPI/08_manageDocument.js create <collectionId: 直前で取得したCollection ID> <filePath: ./01_grounding/docs/Momotaro.txt>

なお、SAP AI Core – Generative AI HubのGrounding機能に用意されたVector APIでは、文書を分割する機能は提供されていません。従って、開発者が外部で文書を分割し、整理された状態でAPIにリクエストを送る必要があります。詳細な処理としては、下記にスクリプトによる処理内容を記載の通り、今回は100文字ずつに分割しています。

const CHUNK_SIZE = 100; // 任意のチャンクサイズ
async function createDocument(token, collectionId, filePath) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/vector/collections/${collectionId}/documents`;
    const rawText = fs.readFileSync(filePath, 'utf8');
    const chunks = [];
    for (let i = 0; i < rawText.length; i += CHUNK_SIZE) {
        chunks.push({
            content: rawText.substring(i, i + CHUNK_SIZE),
            metadata: [{ key: "index", value: [(i / CHUNK_SIZE + 1).toString()] }]
        });
    }
    const payload = {
        documents: [
            {
                metadata: [
                    { key: "source", value: [path.basename(filePath)] }
                ],
                chunks: chunks
            }
        ]
    };
    const res = await axios.post(url, payload, getRequestOptions(token));
    console.log(' ドキュメント作成成功:\n', JSON.stringify(res.data, null, 2));
}


(async () => {
    const action = process.argv[2];
    const arg1 = process.argv[3];
    const arg2 = process.argv[4];
    const filePath = process.argv[4];

    const token = await getXsuaaToken();

    switch (action) {
        case 'list':
            await listDocuments(token, arg1);
            break;
        case 'get':
            await getDocument(token, arg1, arg2);
            break;
        case 'create':
            if (!filePath || !fs.existsSync(filePath)) {
                console.error(' ファイルが存在しません:', filePath);
                return;
            }
            await createDocument(token, arg1, filePath);
            break;
        case 'update':
            if (!filePath || !fs.existsSync(filePath)) {
                console.error(' ファイルが存在しません:', filePath);
                return;
            }
            await updateDocument(token, arg1, arg2, filePath);
            break;
        case 'delete':
            await deleteDocument(token, arg1, arg2);
            break;
        case 'search':
            await vectorSearch(token, arg1, arg2);
            break;
        default:
            console.log(' 使用方法:');
            console.log('node 08_manageDocument.js list <collectionId>');
            console.log('node 08_manageDocument.js get <collectionId> <documentId>');
            console.log('node 08_manageDocument.js create <collectionId> <filePath>');
            console.log('node 08_manageDocument.js update <collectionId> <documentId> <filePath>');
            console.log('node 08_manageDocument.js delete <collectionId> <documentId>');
            console.log('node 08_manageDocument.js search "<query>" <collectionId>');
    }
})();

うまくいくと、下記のようにデータが格納されます。

takayuki_tanaka_9-1743666316867.png

最後に、ここに対して検索を行います。下記のnodeスクリプトを実行してください。

node ./01_grounding/04_vectorAPI/08_manageDocument.js search "<query: 質問文>" <collectionId: 直前で取得したCollection ID>

takayuki_tanaka_10-1743666316874.png

無事、関連すると思われるチャンクが返ってきています。

処理内容は下記です。

async function vectorSearch(token, query, collectionId) {
    const url = `${AI_API_HOST}/v2/lm/document-grounding/vector/search`;
    const payload = {
        query: query,
        filters: [
            {
                id: "search-1",
                collectionIds: [collectionId],
                configuration: {},
                documentMetadata: [],
                chunkMetadata: [],
                collectionMetadata: []
            }
        ]
    };
    const res = await axios.post(url, payload, getRequestOptions(token));
    console.log(' 検索結果:\n', JSON.stringify(res.data, null, 2));
}

また、SAP AI Launchpadから検索することもできます。開発時には、やりやすい方を利用してください。

takayuki_tanaka_11-1743666316880.png

おわりに

本記事では、2024年第4四半期にSAP AI Core – Generative AI Hubに追加された新機能「Grounding」について、技術的な背景から具体的なセットアップ手順、さらにはユースケースまでをハンズオン形式で詳しく紹介しました。

Groundingは、生成AIを業務で活用する上での大きな課題であった「信頼性」や「正確性」を補完するための重要な技術です。企業固有のデータや文書を活用し、生成AIの出力を現実の情報にしっかりと根付かせることで、業務に耐えうるアウトプットが可能になります。

特にSAP AI Coreのオーケストレーション機能と組み合わせることで、文書のベクトル化から検索、回答生成までを一貫して処理できるため、開発者にとっても扱いやすく、拡張性の高いソリューションとなっています。Pipeline APIによるバッチ処理や、Vector APIによる細かなデータ操作など、ユースケースに応じた柔軟なアプローチが用意されている点も魅力です。

今後は、社内ポータルやナレッジマネジメント、顧客対応チャットボットなど、さまざまな業務領域においてこのGrounding技術が重要な役割を果たしていくことが予想されます。ぜひ本記事を参考に、みなさまの業務や開発における生成AI活用の第一歩として、Generative AI HubのGrounding機能を試してみてください。

What's Next?

今回取り扱ったGrounding機能を、SAP AI CoreのOrchestration機能内で活用することができます。そちらについては、次のブログをご参照ください。

SAP AI Core Orchestration機能 を解き明かす
https://community.sap.com/t5/technology-blogs-by-sap/sap-ai-core-orchestration%E6%A9%9F%E8%83%BD-%E3...

 

免責事項
本ブログに記載されているすべての見解および意見は、私個人のものであり、個人的な立場で述べられたものです。SAPは、本ブログに掲載された内容について一切の責任を負いませんので、あらかじめご了承ください。

参考資料