Sodech Developer Blog

株式会社ソデック技術ブログ

【Salesforce】Agentforceを理解するなら最初に知っておきたい!RAGとは何かを初心者向けに解説

この記事で学べること

この記事で学べることは、とてもシンプルです。

RAGとは何かが分かる。

Agentforceでは、ユーザーからの質問に対して、LLM(大規模言語モデル)を活用して回答を生成します。

その際、LLMが持っていない情報を検索して取得し、回答に必要な情報を補う仕組みがRAGです。

最近はAgentforceやLLMという言葉はよく聞くようになりましたが、

「じゃあ、RAGって結局具体的に何をしているの?」

と思ったことはありませんか?

私もSalesforceの導入支援をしていますが、実はRAGだけは曖昧な理解のままでした。

そこでSalesforceのトレーニングを受講したところ、

「なるほど!RAGってそういうことだったのか!」とやっと理解できました。

この記事では、同じようにRAGがよく分からない方向けに、できるだけ分かりやすく解説したいと思います。

 


RAGとは何?

RAGは、Retrieval Augmented Generation の略です。

日本語では検索拡張生成と訳されます。

でも…

正直この日本語だけ見ても何をするものなのか分かりません(笑)

まずは英語の意味を見てみましょう。

単語 意味
Retrieval 必要な情報を検索・取得する
Augmented 取得した情報をプロンプトへ追加(補強)する
Generation その情報を基に回答を生成する

 

ここで大切なのは、RAGは「検索する技術」ではないということです。

検索から回答生成までを一連で行う仕組み全体をRAGと呼びます。

RAGの3つの処理

RAGがないとどうなる?

では、RAGが無いとどうなるのでしょうか。

例えば社員がAgentforceに

「社内規定を教えて」

と質問したとします。

しかし社内規定はSharePointやPDFで管理されています。

RAGが無いAgentforceは、それらの情報を取得できません。

つまり、「見に行けない」ので回答できないのです。

商品マニュアルも同じです。

製品仕様書やFAQサイトがSalesforce外にあった場合、

Agentforceはそれらを根拠として回答できません。

社内規定や商品マニュアルを見たいのに見られない…

RAGは「検索」だけではない

ここが私も勘違いしていたところでした。

RAGは「検索する機能なんだ」と思っていました。

実は違います。

RAGは

  1. 必要な情報を検索し
  2. その情報をLLMへ渡すプロンプトへ追加し
  3. その情報を基にLLMが回答を生成する

ところまで担当しています。

つまり、

検索して終わりではありません。

例:返品ルールの流れ

なぜ回答精度が上がるの?

LLMは学習した知識だけでも回答できます。

しかし、

会社独自のルールや最新の商品情報までは知りません。

そこでRAGが

「この情報を参考に回答してください」

とLLMへ情報を渡します。

これをSalesforceではグラウンディング(Grounding)と呼んでいます。

GroundingとはLLMに実際のデータ(社内規定や商品マニュアル)を結びつけることを指します。

その結果、LLMは推測ではなく、根拠を基に回答できるようになるのです。

これがハルシネーションを抑える理由でもあります。

RAGによって根拠ある回答ができる

まとめ

RAGは単なる

「外部情報へアクセスする機能」

ではありません。

RAGは

  • 必要な情報を検索する(Retrieval)
  • 情報をプロンプトへ追加する(Augmented)
  • その情報を基にLLMが回答を生成する(Generation)

という一連の仕組みです。

この仕組みによって、Agentforceは社内規定や商品マニュアル、FAQなどを根拠として回答できるようになり、より業務に役立つAIエージェントになります。

 

【Salesforce】Apexクラス上でzipファイルを作成してみた

この記事で学べること

本記事では、Apexクラスを用いてSalesforce上でzipファイルを生成する方法を

紹介します。

投稿の動機

LWC(Lightning Web Components)(html、js)を利用してzipファイルを生成する実装方法は知っていたが、新しくApexクラス上でzipファイルを生成する方法について学んだので、その実装方法を共有したいと思い本投稿を作成しました。

対象

・Apexクラスの実装経験がある程度ある方

Salesforce上でzipファイルを生成する必要があり、実装方法に困っている方

サンプルコード

例として、取引先レコードに添付されているpdfファイルを一つのzipファイルに圧縮し、それを取引先に添付する実装を記述します。

public void createZipBlob(Id accountId){
// 取引先IDに紐づくContentDocumentLinkをもとにContentVersionを取得 List cvList = [ SELECT PathOnClient
, VersionData FROM ContentVersion WHERE ContentDocumentId IN ( SELECT ContentDocumentId FROM ContentDocumentLink WHERE LinkedEntityId = :accountId ) ]; // ZipWriterインスタンス生成 Compression.ZipWriter writer = new Compression.ZipWriter(); // zipファイルに格納するファイル情報をZipWriterインスタンスに追加する for(ContentVersion cv : cvList){ writer.addEntry(cv.PathOnClient, cv.VersionData); } // zipファイルをBlob型として取得する Blob zipAttachment = writer.getArchive(); // 取引先にZipファイルを添付する ContentVersion newCV = new ContentVersion(); newCV.Title = 'サンプルZIP'; newCV.PathOnClient = 'サンプルZIP.zip'; newCV.VersionData = zipAttachment; insert newCV; ContentVersion getCV = [ SELECT ContentDocumentId FROM ContentVersion WHERE Id = :newCV.Id ]; ContentDocumentLink newCDL = new ContentDocumentLink(); newCDL.LinkedEntityId = accountId; newCDL.ContentDocumentId = getCV.ContentDocumentId; insert newCDL; }

例をもとにzipファイルを生成する過程についてのみ解説をします。

  1. ZipWriterインスタンスを生成する
  2. ファイル情報をZipWriterに追加する
  3. Blob型としてzipファイルを取得する

手順①:ZipWriterインスタンスの生成

 『Compression.ZipWriter writer = new Compression.ZipWriter();』

 と記述し、ZipWriterインスタンスを生成する。

 ここで定義したZipWriter型変数にzipファイルに格納するファイル情報を

 追加していきます。

手順②:ファイル情報の追加

 『writer.addEntry(cv.PathOnClient, cv.VersionData);』

 と記述することでZipWriter変数にファイル情報を追加することができます。

 ここでポイントとしては、addEntryメソッドの引数です。

 第一引数には完全パス、第二引数にはBlob情報を記述しています。

 今回の例だとContentVersionを使用していますが、

 引数に渡す情報が完全パスとBlob情報であればContentVersionを使用しなくても

 zipファイルを生成することが可能です。

手順③:zipファイルのBlob情報を取得

 『Blob zipAttachment = writer.getArchive();』

 と記述することでZipWriter変数からzipファイルの情報をBlob型として取得する

 ことが可能です。

※※※ 注意 ※※※

・addEntryメソッドでファイル情報を追加する際、1つのZipWriter変数に重複した

 完全パスを追加するとエラーになる

補足

LWC上で対象のファイルをzip化することが可能です。

サンプルとして、本記事ではローカルから選択した複数のファイルをzipに格納し、Apexクラスにzipファイル情報を渡してContentDocumentとして登録する方法を紹介します。

fileUploader.js

import { LightningElement, track } from 'lwc';
import { loadScript } from 'lightning/platformResourceLoader';
import JSZIP from '@salesforce/resourceUrl/jszip';
import uploadZip from '@salesforce/apex/FileZipController.uploadZip';

export default class FileUploader extends LightningElement {
    jsZipInitialized = false;
    jszip;
    @track filesData = [];

    connectedCallback() {
        if (!this.jsZipInitialized) {
            loadScript(this, JSZIP).then(() => {
                this.jszip = new window.JSZip();
                this.jsZipInitialized = true;
            });
        }
    }

    handleFileChange(event) {
        if (event.target.files.length > 0) {
            [...event.target.files].forEach(file => {
                let reader = new FileReader();
                reader.onload = () => {
                    let base64 = reader.result.split(',')[1];
                    this.filesData.push({
                        fileName: file.name,
                        base64: base64
                    });
                };
                reader.readAsDataURL(file);
            });
        }
    }

    async handleUpload() {
        if (this.filesData.length === 0) {
            alert('ファイルを選択してください');
            return;
        }

        let zip = new window.JSZip();
        this.filesData.forEach(f => {
            zip.file(f.fileName, f.base64, { base64: true });
        });

        const zipBlob = await zip.generateAsync({ type: 'blob' });

        // Blob → Base64変換
        const reader = new FileReader();
        reader.onload = async () => {
            const zipBase64 = reader.result.split(',')[1];
            try {
                await uploadZip({
                    fileName: 'merged.zip',
                    base64Data: zipBase64
                });
                alert('ZIPファイルをSalesforceに保存しました');
            } catch (err) {
                console.error(err);
            }
        };
        reader.readAsDataURL(zipBlob);
    }
}

fileUploader.html

<template>
    <lightning-card title="ZIPファイル作成 (LWC)">
        <lightning-input type="file" multiple onchange={handleFileChange}></lightning-input>
        <lightning-button label="ZIP化してSalesforceに保存" onclick={handleUpload}></lightning-button>
    </lightning-card>
</template>

FileZipController.cls

public with sharing class FileZipController {
    @AuraEnabled
    public static Id uploadZip(String fileName, String base64Data) {
        Blob fileBlob = EncodingUtil.base64Decode(base64Data);

        ContentVersion cv = new ContentVersion();
        cv.Title = fileName;
        cv.PathOnClient = fileName;
        cv.VersionData = fileBlob;
        insert cv;

        return cv.Id;
    }
}

最後に

ここまで見ていただきありがとうございます。

今回の投稿をあげるきっかけになったのは、業務上の案件で顧客からSalesforce上で

zipファイルを生成したいというご要望をいただき、実際に実装してみて面白いなと

思ったので投稿しました。

これからも実務で役立つ実装方法を紹介していきたいと思いますので、

別の投稿も見ていただけたら嬉しいです!

 

 

【Salesforce】Agentforce Virtual Hackathonに参加してみた!

こんにちは!

先月末まで募集されていた「Agentforce Virtual Hackathonにエントリーしました!

developer.salesforce.com

結果は後述しますが、本当に素晴らしい経験だったので、ぜひ皆さんにも共有させてください!

ReadyHead
  • そもそもAgentforce Virtual Hackathonって何?
  • 参加のきっかけ
  • ReadyHeadの誕生
  • ReadyHead ができること
  • 使用した機能
  • 紹介動画
  • やってみて学んだ事
  • まとめ

そもそもAgentforce Virtual Hackathonって何?

続きを読む

【Salesforce】S3とCRM Analyticsを接続する方法

0.投稿の動機

データの手動インポートやエクスポートに時間を取られている方に向けて、Amazon S3Salesforce CRM Analyticsを連携させることで、これらの作業を自動化し、業務を効率化する方法をお伝えしたいと思い、本記事を執筆しました。

本記事では、その手順を実際の経験を踏まえて解説します。外部システムとのデータ連携をスムーズに行いたい方にぜひ参考にしていただければと思います。

1. 読者の対象

  • SalesforceAmazon S3をすでに利用しているが、複数のシステム間の連携に悩んでいる。

  • 手動でデータのインポートやエクスポートを行っており、非効率さを感じている。

  • CRM Analyticsを使ってデータ分析を行いたいが、外部データを取り込む手間を減らしたい。

2. IAMでユーザーを作成する

  1. AWS管理コンソールでIAMを開き、新しいユーザーを作成します。

  2. S3のフルアクセス権限を付与します。

    • グループを作成し、適切なポリシー(例: AmazonS3FullAccess)を割り当てます。

  3. ユーザー作成後、アクセスキーを作成します。

  4. アクセスキーとシークレットアクセスキーを保存します(後で使用します)。

3. S3にバケット・フォルダ・ファイルを作成する

  1. バケットを作成します。

    • わかりやすい名前を付けると便利です。

  2. バケットの下にフォルダを作成します。

    • フォルダ名は「オブジェクト名」にすると管理しやすいです。

  3. 入力接続の場合は必要なファイルをアップロードします。

    • schema_sample.csv: テーブルの項目名を1行目に記載したCSVファイル。

    • データファイル: SalesforceにインポートするデータのCSVファイル。

Amazon S3 接続方法

https://help.salesforce.com/s/articleView?id=sf.bi_integrate_connectors_S3.htm&type=5

4. CRM Analyticsで接続を設定する

入力設定

  1. データマネージャーで以下を操作します。
    • 「接続」→「新しい接続」→「入力」タブで「Amazon S3 コネクタ」を選択。

  2. 必要事項を入力します。

    • 接続名: 任意の名前。

    • API参照名: 任意。

    • アクセスキーとシークレットアクセスキー: IAMで作成したキーを設定。

    • フォルダーパス: バケット名を入力。

    • リージョンネーム: Asia Pacific (Tokyo) を指定。

  3. オブジェクトを編集し、対象フォルダ(schema_sample.csvをアップロード済みのフォルダ)を選択します。

  4. データマネージャーで以下を操作します。

    • 「接続」→「新しい接続」→「出力」タブで「Salesforce 出力コネクタ」を選択。

  5. 必要事項を入力します。

Salesforce出力接続

https://help.salesforce.com/s/articleView?id=sf.bi_integrate_connectors_output_salesforce.htm&type=5

出力設定

  1. データマネージャーの「出力」タブで「Amazon S3 出力コネクタ」を選択。

  2. 入力設定と同じ情報を入力します。

Amazon S3 出力方法

https://help.salesforce.com/s/articleView?id=sf.bi_integrate_connectors_output_amz_s3.htm&type=5

5. レシピを作成する

入力の設定

  1. 入力設定で選択したオブジェクトをレシピの「入力データ」に設定します。

  2. 必要に応じてデータ型をSalesforceの形式への変換や参照項目の結合などをします。

  3. Salesforceへの出力:

    • 書き込み先: 「出力接続」。

    • 接続名:設定した「Salesforce 出力コネクタ」

    • 項目の対応付け

出力の設定

  1. SFDC-LOCALの対象のオブジェクトを入力データで設定をします。

    • 出力先: 「Salesforce 出力コネクタ」。

    • オブジェクト名: 書き込み対象のオブジェクトを指定。

    • 外部ID: 項目の対応付けを行います。

  2. S3への出力:

    • 書き込み先: 「出力接続」。

    • 接続名:設定した「Amazon S3 出力コネクタ」

    • オブジェクト名:設定したフォルダ名(オブジェクト名)

6. まとめ

S3とCRM Analyticsを接続する方法を紹介しました。

この流れを参考に、ぜひ効率的なデータ連携を実現してください。

 

 

Salesforce 未検証ユーザーへの検証メール自動送信処理

1.記事を書いた理由

突然ですが皆さんはユーザーの検証はおこなっていますか?

Summer'24 のメジャーリリースの際に未検証ユーザーからメールを送信することができなくなりました。

今後、ユーザーからメールを送るためにはユーザーを検証済みにする必要があります。

▼メール検証要件を満たすためのメールアドレスの検証 https://help.salesforce.com/s/articleView?id=release-notes.rn_security_email_verification.htm&release=250&type=5

私の担当しているプロジェクトで未検証のユーザーを確認したところ、一部のユーザーが未検証のままでした。プロジェクトではMFA(多要素認証)対応としてSSO(シングルサインオン)を導入しており、未検証ユーザーが増えていたのかなと予想しています。

このままではApexやフローなどで未検証のユーザーからメールアラートが送れなくなってしまう点と、セキュリティ的にも脆弱だと考え、ユーザーへ検証メールを送ることにしました。

未検証ユーザーの数がかなり多いため、Apexバッチで一括送信を行いました。

この記事では複数のユーザーへ一括してメールを送るための手順を紹介します。

2.この記事の対象者

Salesforceの管理者をしている方

・未検証ユーザーが多く、検証メールを一括で送信したい方

・定期的に未検証ユーザーへ検証メールを送信したい方

3.ユーザーのメールアドレス検証

ユーザーのメールアドレスを検証するにはいくつかの方法がありますが、ここでは2つ紹介します。

1.システム管理者がユーザーへ検証メールを送信する

[設定] > [ユーザー] を押下する。 該当のユーザーを選択してメール横の[検証]リンクを押下する。 スクリーンショット 2024-10-12 15.02.20.png

メール文中のリンクを押下する。 スクリーンショット 2024-10-12 15.19.18.png

2.ユーザー自身で登録しているメールアドレス宛に検証メールを送信する

[設定] > [右上のアイコン] > [設定] > [高度なユーザーの設定] を押下する。 該当のユーザーを選択してメール横の[検証]リンクを押下する。 スクリーンショット 2024-10-12 14.33.34.png

▼ユーザのメールアドレスの検証 https://help.salesforce.com/s/articleView?id=release-notes.rn_security_verify_user_email_addresses.htm&release=244&type=5

3.未検証ユーザーの抽出

未検証ユーザーを抽出するには TwoFactorMethodsInfo オブジェクトを使用して、UserId 項目、HasUserVerifiedEmailAddress 項目を取得します。 HasUserVerifiedEmailAddress 項目はユーザーが未検証の場合に false を返すので、それらのユーザーのUserIdを取ってくることで未検証ユーザーを抽出できます。

SELECT UserId, HasUserVerifiedEmailAddress FROM TwoFactorMethodsInfo WHERE HasUserVerifiedEmailAddress = 'false'

サポートされているコールが describeSObjects()、query() のため、Salesforce inspector で Export はできないので注意してください。

▼オブジェクトリファレンス[TwoFactorMethodsInfoオブジェクト] https://developer.salesforce.com/docs/atlas.ja-jp.object_reference.meta/object_reference/sforce_api_objects_twofactormethodsinfo.htm

4.メール送信クラス

Apexからメール送信を行うには UserManagementクラスを使用します。sendAsyncEmailConfirmationメソッドはパラメーターでメールテンプレートやExperience Cloud サイトの情報を受取ることができます。

パラメーター 説明
userId 確認メールを受信するユーザーの ID
emailTemplateId 検証リンクが定義されているメールテンプレートの ID
networkId Experience Cloud サイトの ID
startUrl ユーザーは検証後にこのページにリダイレクトされ、パラメーターとして成功メッセージまたはエラーメッセージが渡されます。null の場合、ユーザーはログインページにリダイレクトされます。

Salesforce 経由でメールを送信するためにメールアドレスを確認する

https://help.salesforce.com/s/articleView?id=release-notes.rn_security_email_verification.htm&release=246&type=5

▼非同期メールでメールアドレスを確認する

https://help.salesforce.com/s/articleView?id=sf.emailadmin_async_email_verification.htm&type=5

5.メール送信クラスのサンプル

public class SendVerifyEmail implements Database.Batchable<sObject>{

    public SendVerifyEmail() {
    }

    public Database.QueryLocator start(Database.BatchableContext bc) {

        // 対象ユーザーの取得
        String strSql = '   SELECT Id' +
                        '        , Name' +
                        '        , UserName' +
                        '     FROM User' +
                        '    WHERE IsActive = true';

        return(Database.getQueryLocator(strSql));

    }

    public void execute(Database.BatchableContext bc,  List<User> activeUserList) {

            List<TwoFactorMethodsInfo> emailNotVerifiedUserList = [
                SELECT UserId
                FROM TwoFactorMethodsInfo
                WHERE HasUserVerifiedEmailAddress = false
                AND UserId IN :activeUserIds
            ];

            // 検証メール送信
            for(TwoFactorMethodsInfo emailNotVerifiedUser : emailNotVerifiedUserList) {
                System.UserManagement.sendAsyncEmailConfirmation(emailNotVerifiedUser.UserId, null, null, null);
            }

    }

    public void finish(Database.BatchableContext bc) {
        //メール送信処理終了
    }

}

6.テストクラスのサンプル

@isTest
public class SendVerifyEmailTest {
    
    @isTest
    static void SendVerifyEmailTest() {
        // テストデータの作成
        User unverifiedUser = new User(
            Username = 'testuser001@example.com',
            Email = 'testuser002@example.com',
            ProfileId = [SELECT Id FROM Profile WHERE Name = 'システム管理者' LIMIT 1].Id,
            IsActive = true
        );
        insert unverifiedUser;
    }
}

7.まとめ

いかがでしたでしょうか。 未検証ユーザーへ一度に検証メールを送信したい場合には是非活用してみてください。