AgentforceとRAG: AIエージェントを向上させるベストプラクティス

非構造化データと長い自由テキストフィールドでAgentforceを強化するためのベストプラクティスガイド

比較表

機能 LLMベースの解析 Docling解析の違い
主なアプローチ セマンティック分析とビジュアル推論 コンピュータビジョンとレイアウト分析
最適なデータソース 不規則で乱雑、またはスキャンされたドキュメント 構造化された、デジタル作成されたPDF
テーブル処理 コンテンツの意味を解釈 構造レイアウトを再構築
パフォーマンス 遅い(推論が多い) 高速/最適化

 
SELECT 
    v.Hybrid_score__c        AS Score, 
    c.Chunk__c               AS Chunk, 
    c.SourceRecordId__c      AS SourceRecordId, 
    c.DataSource__c          AS DataSource, 
    c.DataSourceObject__c    AS DataSourceObject
FROM hybrid_search(
        TABLE(KA_Agentforce_Default_Library_index__dlm), 
        '{!$_SEARCH_STRING}', 
        'Language__c=''{!$_LANGUAGE}'' 
         AND KnowledgePublicationStatus__c=''Online'' 
         AND DataSource__c IN (''FAQ_Internal_Comments_c__c'', ''AssignmentNote__c'')', 
        30
    ) v
INNER JOIN KA_Agentforce_Default_Library_chunk__dlm c 
    ON c.RecordId__c = v.RecordId__c
INNER JOIN ssot__KnowledgeArticleVersion__dlm kav 
    ON c.SourceRecordId__c = kav.ssot__Id__c
ORDER BY Score DESC
LIMIT 10
 

「PLAIN」「QUESTION」「METADATA」チャンク

チャンクの種類 説明
PLAIN 元のチャンクテキストを含みます。元のドキュメントからの未加工のコンテンツチャンクです。
QUESTION チャンクが回答できる質問が含まれています。

LLM生成の質問のセットを含みます。 関連する元のチャンクは、これらの質問への回答を提供します。 すべての生成された質問は、ベクトル化の前に1つのチャンクにまとめられます。 これは、会話からのユーザーの意図(質問として表現)と、元のチャンクに保存されたコンテキスト(回答として表現)との間で発生し得る意味的な不一致を最小限に抑えます。 質問チャンクは、特にQ&A関連のAIエージェントシナリオにおいて、検索のリコールと精度を向上させます。 

質問チャンクに属するベクトルは取得されますが、プロンプト拡張は対応する元のチャンクを使用して自動的に行われます。 したがって、質問自体がプロンプトに拡張されることは決してありません。
METADATA 元のチャンクにもとづいてLLMが生成したメタデータのセットを含みます。 以下は、インデックス化中に生成されるメタデータです。
- キーワード(最大10件) 
- エンティティ(チャンクのコンテンツに出現する主要なエンティティ)
- トピック(最大5つの主要なトピック)
- センチメント(ポジティブ/ネガティブ/ニュートラルをチャンクで指定済み)
- タイトル(簡潔で情報量の多いタイトル)
- 概要(通常100~250語の概要)

 
please answer this question:
{!$Input:question}

using this information:
{!$EinsteinSearch:ArticleRetriever_1Cx_Q8Qa1857028.results}
 

###
指示

 1. クエリを分析する: 質問セクションからユーザーの質問や問題を注意深く読み、理解してください。
 2. ナレッジを検索する: 提供された会社のナレッジを確認して、関連情報を見つけてください。
 3. 情報を評価する: ナレッジセクションにある利用可能な情報が質問に答えるのに十分かどうかを判断してください。
 4. 応答を作成する: ユーザーへの返信 
<generated_response> を生成するには、これらのルールに従う必要があります
 a.
 ユーザーの問い合わせにもっとも関連性の高い記事チャンクを見つけ、その記事のIDをそのまま抽出して、レスポンスJSONの
<source_id>フィールドに設定します。
 該当する記事が見つからない場合は、
<source_id> を「なし」に設定してください。
 b. 関連する記事チャンクを使用して、ユーザーの質問に正確に回答する応答を生成し、
<generated response>フィールドに設定します。
 c. ユーザーのリクエストが提供されたナレッジで回答できない場合、
<source_id> を 「なし」に設定し、
<generated_response> を「申し訳ありませんが、利用可能な記事にもとづいて回答を見つけることができません」に設定してください。
 5. 改善し、提供する: 回答が丁寧かつ専門的、簡潔で、{language}のみであることを徹底してください。
 6. 返信を見直す: 上記のすべての指示に従っていることを確認し、希望する出力形式で回答し、ナレッジのみに厳密にもとづいて回答を作成してください。

 ###
ナレッジ:
{!$EinsteinSearch:sfdc_ai__DynamicRetriever.results}

 ###
質問:
{!$Input:Query}

 
以下のコンテキストから論理的に導き出される根拠のみにもとづき、ユーザーのクエリに対して明確かつ直接的に回答してください。 
その後、ユーザーのクエリに対して、コンテキストにもとづき異なる視点を強調しつつ、詳細で慎重な推論をもって、論理的かつ体系的、思慮深く、多角的な視点から綿密に応答してください。 
回答には、整理された構成で詳細を提供してください。 現在の推論の道筋に異議を唱える可能性のある別の視点やアプローチを検討してください。 
質問に回答するための十分な情報が見つからない場合、または質問への回答方法がわからない場合は、「申し訳ありませんが、ご質問にお答えするための十分な情報が見つかりませんでした」と回答してください。
推論を裏付ける証拠やデータを評価し、不足や矛盾がないか特定してください。 
最後に、ユーザーのインテントを明確にするために質問をしつつ、ユーザーのクエリについて批判的思考と自己発見を促してください。 
事実と、意見または信念の違いを、詳細を交えて明確に説明してください。 
答えがわからない場合は、ユーザーのインテントを明確にするために質問してください。 
ユーザーのクエリで言及されているエンティティに注意し、そのエンティティに関する情報がコンテキストに含まれていることを確認してください。 

コンテキスト:
{!$EinsteinSearch:ArticleRetriever_1Cx_Q8Qa1857028.results}

クエリ:
{!$Input:question}

指示の形式を設定する: 
以下のマークダウン構造で回答の形式を設定してください。 
トピックの概要から始めてください。  
主なポイントをリスト形式で挙げ、重要な用語は太字で強調してください。 
以降のセクションでは、サブクエリを暗黙的に組み込んだ見出しおよび小見出しを作成してください。 
手順や連続したデータがある場合は、順序付きリストで提示してください。 
結論で締めくくってください。




public static List
<Response> searchSimilarCases(List
<Request> requests) {
List
<Response> responses = new List
<Response>();
Response response = new Response();

String caseDescription = requests[0].RelatedEntity.Description;

ConnectApi.CdpQueryInput input = new ConnectApi.CdpQueryInput();
input.sql = 'SELECT DISTINCT v.score__cScore__c, c.ssot__Id__cId__c, c.ssot__Subject__c
Subject__c"+
'FROM vector_search(\case_chunk_vector__dlm\;\" + caseDescription + '\', \'\', 200) v ' +
'JOIN Case_Chunks__dlm cc ON v.chunk_id__c = cc.chunkid__c ' +
'JOIN ssot__Case__dlm c ON cc.parentid__c = c.ssot__Id__c ' +
WHERE cc.column__c != \'ssot__Subject__c\' AND c.ssot__DataSourceId__c = \'CRM\' ' +
'LIMIT 10';

ConnectApi.CdpQueryOutput output = ConnectApi.CdpQuery.queryANSISql(input);

List Object> data = output.data;
String scs = '';
for (Object searchRecord : data) {

Map
<String, Object>myMap = (Map
<String, Object>) JSON.deserializeUntyped(JSON.serialize(searchRecord));
// check for access of case record for the current user
if (SimilarCasesSearch.getUserRecordAccess((String) myMap.get('Id__c'))) {
Map
<String, String> sc = new Map
<String, String>();
sc.put('Id', (String) myMap.get('Id__c'));
sc.put('Similar_Case__c', (String) myMap.get('Id__c'));
sc.put('Name', (String) myMap.get('Subject__c'));
sc.put('Score__c', String.valueOf(myMap.get('Score__c')));
scs = scs + JSON.serialize(sc);
}
}
response.Prompt = scs;
responses.add(response);
return responses;
}

標準Flowアクション

Flowアクション 説明
言語を検出 クエリの言語を検出し、その言語をフィルター値としてリトリーバーノードに引き渡すことで、動的なフィルタリング(言語別)が可能になります。
{ケース/メール/会話}のクエリを変換 これらの3つのノードはそれぞれ、ケース、メール、会話を検索向けに最適化されたクエリへと変換するLLM変換を呼び出します。 これにより、リトリーバーが検索インデックスに引き渡すクエリが改善されます。 たとえば、会話からクエリへのアクションは、「ご用件をお伺いします」や「お世話になっております」のような関連性のないメッセージで検索インデックスをクエリすることを回避します。 同様に、ケースからクエリおよびメールからクエリは、テキストから関連情報を抽出し、挨拶や検索に使用すべきでないその他のテキストを除去します。

 global with sharing class RetrieverProcessor {

 
    @InvocableMethod
    class public static List
<String> GetWebProduct(List
<Requests> queryResults)
       {
            List
<String> resultsList = new List
<String>()
            for (Requests queryResult : queryResults) {
                List<String> segments = new List
<String>();
               for (ConnectApi.MlRetrieverQueryResultDocumentRepresentation document: queryResult.queryResult.searchResults) {
               for (ConnectApi.MlRetrieverQueryResultDocumentContentRepresentation content: document.result) {
                    if (content.fieldName.equals('Chunk')) {
                        segments.add(content.value.toString());
                   }
                }}
              if

 if (segments.size() == 0) {
                    resultsList.add('No results');
                } else {
                    resultsList.add(String.join(segments, ','));
                }  
    }
return resultsList;
       }      
    global class Requests {
        @InvocableVariable
        global ConnectApi.MlRetrieverQueryResultRepresentation queryResult;
        
    }
}
  
SELECT 'INDEX' AS Location, COUNT(DISTINCT rc.SourceRecordId__c) AS ArticleCount, now() AS Timestamp 
FROM 
<chunk DMO of the Search Index> rc
UNION
SELECT 'DMO' AS Location, COUNT(DISTINCT  kav.Id__c)  AS ArticleCount, now() AS Timestamp 
FROM 
<DMO that was indexed, e.g. Knowledge Article Version> kav
ORDER BY Location;
  

指標

指標 回答 定義 できること
コンテキストの関連性 取得されたコンテンツはクエリにどの程度関連しているか? LLMベースの評価 検索の問題を切り離す
忠実性 取得したコンテンツに対して、応答はどれほどグラウンディングされているか? LLMベースの評価 LLM生成問題を切り離す
回答の関連性 この回答はクエリにどの程度関連しているか? LLMベースの評価 回答に関する全体的な応答指標。 コンテキストの関連性および忠実性と組み合わせて使用するとさらに有用。
ConnectApi.CdpQueryInput input = new ConnectApi.CdpQueryInput();
input.sql = 'SELECT r.Label_c__c Label, COUNT(r.Label_c__c) AS counter FROM vector_search(table(Intent_Training_index__dlm), topic,'' , 50) v JOIN Intent_Training_chunk__dlm c ON v.RecordId__c = c.RecordId__c JOIN Intent_Training__dlm r ON r.Id__c = c.SourceRecordId__c GROUP BY r.Label_c__c ORDER BY counter DESC LIMIT 1;
                
ConnectApi.CdpQueryOutput output = ConnectApi.CdpQuery.queryANSISql(input);