mcpField
F
field-service-scheduling-policy-designer-query

field-service-scheduling-policy-designer-query

github.com/forcedotcomその他1.1k82 回インストール1 日前に更新
リポジトリ

field-service-scheduling-policy-designer-query について

Salesforce Field Service のスケジューリングポリシーを設計する対話型スキルです。作業ルール、関連グループ、サービス目標と整合性のある重みを設計し、別のスキルがレコードを作成するための仕様を出力しますが、レコード操作は行いません。

主な機能

  1. 01要件を収集するインタビュー
  2. 02フィルターと評価目標の区別
  3. 03作業ルールと関連グループの設計
  4. 04整合性のある目標重みの算出
  5. 05構造化された構築仕様の出力

活用シーン

  1. 01Field Service のスケジューリングポリシー設計
  2. 02トレードオフの質問による目標重みの調整
  3. 03レコード作成に向けたポリシー仕様の準備

インストール

コマンドをコピーしてターミナルで実行します。

$npx skills add https://github.com/forcedotcom/sf-skills --skill field-service-scheduling-policy-designer-query

インストール後、Claude Code で/field-service-scheduling-policy-designer-queryを入力して読み込まれたか確認します。

詳細

Sfs Scheduling Policy Designer の管理

このスキルを使用するタイミング

このスキルは、Salesforce Field Service(SFS)のスケジューリングポリシー全体(ポリシーの意図、候補リソースを絞り込む作業ルール、ルールと目的の適用範囲を定める関連性グループ、数学的に整合性のある最適化の重み値を持つサービス目的)を検討・設計する際に使用します。ユーザーがスケジューリングポリシー、作業ルール、サービス目的、関連性グループ、In-Day Optimization、Commit Mode、フィールドサービス最適化、ペナルティポイントについて言及した場合や、Salesforce Field Service のスケジューリングポリシーの設定または調整を希望した場合は、いつでも起動してください。要件(作業ルール)、適用範囲(関連性グループ)、トレードオフに関する質問を順に確認し、各目的に一貫した重みを導き出してから、別のデータレイヤースキルがレコードに変換するための構造化されたビルド仕様を出力します。このスキルはポリシーを設計するものであり、レコードの作成、読み取り、更新、削除は行いません。

ワークフロー

Salesforce Field Service – スケジューリングポリシー設計者(ビジネスロジック、要約版)

このスキルは、ユーザーがSalesforce Field Serviceのスケジューリングポリシー全体を最初から最後まで設計できるよう支援し、その設計を構造化されたビルド仕様として引き渡します。すべてのレコード操作は**sfs-sobject-create**スキルに委任します。このスキルが担当するのは、ポリシーの内容とその理由です。レコードを作成する方法には一切関与しません。

インタビューの順序(必須): 以下の順序でインタビューを行い、質問を飛ばさず、次の項目に進む前にそれぞれを完了してください。

Phase 1: Scheduling Policy Interview — ポリシー設定を収集します(名前...
Phase 2: Work Rule Design Interview — 質問を1つずつ尋ねます...
Phase 3: Service Objective Interview — トレードオフに関する質問を...
Phase 4: The scheduling policy definition — 名前、説明、In-Day...
Phase 5: Work rules — 適格なリソースを決定するハードフィルター...
Phase 6: Relevance groups — 作業ルールの適用範囲を必要に応じて指定します...
Phase 7: Service objectives + weights — 絞り込み後の候補を評価するソフトスコアリングです。このスキルで最も深く掘り下げる部分です

対話形式のトレードオフインタビューを通じて、ペナルティ計算から重み値を逆算し、すべての目的を一貫した尺度で調整します。

フェーズ 8:ビルド仕様 — 構造化された機械可読の概要……

ルールと目標 — 基本となる考え方: 作業ルールは厳格なフィルターです。違反するリソースや時間枠はすべて除外し、常に目標より先に適用されます。サービス目標は柔軟な採点基準です。ルールを通過した候補を評価し、候補を除外することはありません。「X が必須」と望む場合は作業ルールです。「X を優先」と望む場合は目標です。ルールによって候補リストが絞り込まれ、目標によって残った候補に順位が付けられます。

用語: ユーザーと話すときは、製品を必ず Field Service と呼んでください。このスキルの内部メモでマネージドパッケージの略称として「FSL」が使われていても、略称は決して使用しないでください。

範囲の境界 — 設計のみ、CRUD は行わない: このスキルが生成するのは判断と値であり、レコードではありません。Salesforce のオブジェクト/項目の API 名、コンポジット API の構造、レコード作成の順序についての知識はありません。また、何かを作成、読み取り、更新、削除しようとしてはなりません。設計が完了したら、ビルド仕様を出力し、すべてのレコード操作を sfs-sobject-create スキルに委任します。ユーザーが「今すぐ作成して」/「これをデプロイして」と依頼した場合は、ビルド仕様を作成(または完成)して、sfs-sobject-create に引き継いでください。

このスキルの使い方: ユーザーが重み付けだけを求めている場合は、会話フロー(トレードオフの聞き取り)に進んでください。その動作に変更はありません。ポリシー全体の設計や検討を希望している場合は、各セクションを順番に進めてください。ポリシーの定義 → 作業ルール → 関連性グループ → 目標/重み付け → ビルド仕様。

信頼できる情報源: このスキルの計算と重み導出の計算式は、正式な情報源です。これらは SFS オプティマイザーの実際の内部動作を反映しており、計算式の精度が低い Salesforce の公開ドキュメントよりも優先されます。最新ドキュメントの参照先にあるドキュメントへのリンクは、時間の経過とともに変わる可能性がある背景情報や動作の詳細を確認するためのものです。最新の詳細を確認するには参照してください。ただし、このスキルに記載されたペナルティの計算式より優先させてはいけません。


スケジューリングポリシーの定義

スケジューリングポリシーは作業ルールとサービス目標をまとめ、オプティマイザーにスケジューリング方法を指示します。このスキルが決めるのはポリシーの設定と意図であり、データレイヤーのスキルが実際のレコードを作成します。ユーザーによるポリシー設計を支援するときは、まずポリシーレベルの判断事項を収集してください。それぞれが、データレイヤーのスキルが入力する項目に対応します。

このセクションの会話での進め方: 各設定について、一度に 1 つずつ質問してください。名前、次に「日中の最適化」か「グローバル」か、次にコミットモード、最後に説明の順です。複数の質問を 1 つのメッセージにまとめず、次の質問をする前にユーザーの回答を待ってください。このセクションでは、以下のポリシーレベルの設定だけを扱ってください。作業ルール、必須の「サービスリソースの可用性」ルール、その他のルールに関する説明には触れないでください。その説明は、この後の「作業ルール」セクションに進んでから行います。このスキルでは、ユーザー独自の要件に基づいて、常に完全にカスタムのポリシーを作成します。Salesforce の標準スターターポリシーやその他のテンプレートを使い始めるかどうかを、提案、推奨、質問してはいけません(背景情報のみを示す以下の開始点を参照してください。会話上の選択肢として提示するものではありません)。製品は必ず Field Service と呼び、ユーザーとのやり取りで「FSL」という略称は決して使用しないでください。

コア設定:

  • スケジューリングポリシー名 — ポリシーの表示名です。ヒント: In-Day Optimization が有効になっている場合、Salesforce では名前に「In-Day」を含めることを推奨しています。ディスパッチや最適化の際に識別しやすくなります。
  • 説明 — ポリシーの意図を自由記述する説明です。このスキルがステップ 3 で生成する「Policy at a Glance」の要約を記録するのに適した場所です。
  • In-Day Optimization (boolean) — true の場合、ポリシーは global 最適化ではなく in-day 最適化を使用します。Global は全期間にわたって数時間実行されますが、in-day は直前の変更のために時間制限付きで実行され (Enhanced Scheduling and Optimization ありで最大 5 分、なしで最大 10 分)、ディスパッチャーまたはスケジュールされたジョブによってトリガーできます。
  • Commit Mode — ディスパッチャー (またはスケジューリング操作) が global/in-day/resource 最適化の実行中にスケジュールを変更し、両者が競合した場合の動作を制御します。2 つの値があります:
    • Always Commit — 進行中の最適化と競合しても変更を適用します。
    • Rollback — 競合する変更を拒否/取り消して、最適化の実行を保護します。

これらをそれぞれ、ビルド仕様の policy オブジェクトの値としてキャプチャしてください。レコードがどのように保存されるかは気にする必要はありません。それはデータレイヤースキルの役割です。

必須ルール (すべてのポリシー) — これがこのルールの唯一の正規の記述です。このスキル内の他のすべての言及は、ここを参照するだけです:

  • Service Resource Availability — 必ずちょうど 1 つを設計とビルド仕様に含めてください (mandatory: true)。ユーザーが何を求めても関係ありません。Field Service マネージドパッケージはこのルールを自動作成しないため、データレイヤースキルが作成できるように常に出力する必要があります。ユーザーが可用性の詳細を何も指定しない場合でも、ポリシーが有効になるように、ベースラインの Service Resource Availability ルールを出力してください (休憩なし、残業/移動フラグなし)。
  • Earliest Start Permitted と Due Date (Match Time ルール) — これらをビルド仕様に含めないでください。 Field Service マネージドパッケージは、スケジューリングポリシーの作成時に自動的にこれらを作成します。設計、出力、または質問をしないでください。これらは自動的にプロビジョニングされます。

出発点 (背景情報のみ — 会話の選択肢ではありません): Salesforce には 4 つの標準スターターポリシーが用意されています — Customer First、High Intensity、Soft Boundaries、Emergency。これは背景知識のみです。これらを出発点としてユーザーに提案したり提示したりしないでください — このスキルが設計するすべてのポリシーは、テンプレートをモデルにするのではなく、ユーザー自身が述べた要件から新しく構築されます。

これらの設定の動作に関する背景については、次を取得してください: service.pfs_scheduling.htm (Live Documentation References を参照)。


作業ルール

作業ルールはハードフィルターです。 これらは、ルールに違反するサービスリソースを拒否することで、サービスアポイントメントの候補リストを絞り込みます。これらは常にサービス目標の前に適用され、目標はルールを通過したリソースのみをスコアリングします。要件が「必須」であれば作業ルールであり、「優先」であれば目標です。

データベースルールと Apex ルール (パフォーマンスにとってなぜ重要か)
  • データベースルールは SOQL クエリレベルでフィルタリングします — 失格となったリソースはデータベースから返されることがないため、低コストです。これらは 1 つのクエリに集約され、特定の順序なしで適用されます。
  • Apex ルールは初期クエリの後に実行され、返されたすべての候補を (for ループのように) 反復して検証します — これには実際の CPU コストがかかり、その後実行される目標の採点によってさらに増大します。
  • ガイダンス: 少なくとも 1 つのデータベースルールを含めて、Appointment Booking / Get Candidates が小さな候補プールから開始できるようにしてください。Apex ルールと目標が実行される前に、データベースルールでおよそ ~20 候補まで絞り込むことを目指してください。
16 種類の作業ルール

各行の内容:名前 — エンジン(DB/Apex)— 機能。「DB(ESO 有効時)」は、Enhanced Scheduling and Optimization が有効な場合にのみデータベースレベルでルールが実行され、それ以外の場合は Apex で実行されることを意味します。

作業ルールエンジン機能
サービスリソースの稼働状況Apex — 必須リソースが実際に利用可能であることを確認します。営業時間、移動時間、休憩、欠勤、既存の割り当てを考慮します。また、容量ベースのリソースについて容量を適用します。休憩、空き時間、時間外労働、自宅との往復移動を設定できます。
時間の一致ルールApex予約の日時フィールドに基づいて、スケジュール可能な時間枠を制約します。標準ルールとして、最早開始可能日時と期限日(どちらも必須)、および「開始予定日時」/「終了予定日時」(到着時間枠に基づく)が用意されています。
スキルの一致Apex予約に必要なスキルと、リソースに割り当てられたスキルを照合します。スキルレベルも適用できます。スキルタイプロジックは、すべてのスキルが一致(AND)(デフォルト)または**少なくとも 1 つのスキルが一致(OR)**です(OR には ESO が必要です)。
フィールドの一致DB(ESO 有効時)予約の 1 つのフィールドをリソースの 1 つのフィールドと照合します(1:1)。予約のフィールドを複数のリソース値と比較する場合は、代わりに「拡張一致」を使用してください。
ブール値の一致DB(ESO 有効時)リソースのチェックボックスフィールドが true(または false)であることを要求します。**ポリシーあたり最大 5 個です。**標準の「アクティブなリソース」が用意されています。
拡張一致データベース予約フィールドをリソースの関連リストにリンクする中間オブジェクトを使って、カスタム条件で照合します(例:対応可能な郵便番号、製品ライン)。
テリトリーの一致データベース予約のサービステリトリーの主要メンバーまたは移動メンバーであるリソースに限定します。
稼働テリトリーデータベースサービステリトリーの主要および副次メンバーシップを適用します。
自宅からの最大移動距離データベースリソースの拠点と、割り当てられたすべての予約との間の距離または移動時間に上限を設定します。
必須リソースDB(ESO 有効時)必須とマークされたリソースへの割り当てを強制します(WO/WOLI のリソース設定)。制約が非常に厳しくなります。
除外リソースDB(ESO 有効時)WO/WOLI で除外とマークされたリソースへの割り当てを禁止します。
件数ルールApexリソースあたり 1 日の割り当て数、時間数、またはカスタム値に上限を設定します(例:スケジュールされた時間が 8 時間以下、トラックに積む品目が N 個以下)。時間分解能は日次です。ポリシーあたり最大 10 個のカスタムフィールド件数ルールを設定できます。
作業容量Apex — ESO 専用テリトリーごとの作業容量上限レコードを適用します(例:設置作業をテリトリー容量の 80% までに制限)。
サービス予約の訪問可能時間Apex顧客の営業時間/訪問可能時間枠を適用します(例:平日の午前 8 時から正午まで)。例外グループのない、全か無かのルールです。
時間枠指定作業Apex特定の作業タイプ用に時間枠/シフトを確保します。その時間枠には、その作業タイプのみをスケジュールできます。例外グループのない、全か無かのルールです。
サービスクルーリソースの稼働状況Apexクルータイプのリソースを、親レコードに設定された最小クルー人数を満たす場合にのみ割り当てられるようにします。

ドキュメントの名称に関する注意:ドキュメントの「考慮事項」リストでは、ベースラインのデータベースルール一覧に「拡張一致」ではなく「Enhanced Match」と記載されている箇所があります。「Enhanced Match」というルールタイプは存在しません。「拡張一致」を指すものであり、ドキュメントの誤記として扱ってください。

ルール作成のステップ 1:要件を定義する
要件が以下の場合この作業ルールを使用
リソースに特定のスキル/習熟度レベルが必要Match Skills;Extended Match
スキル以外のマッチング要因(例:対応可能な郵便番号)Match Boolean;Match Skills;Extended Match
日中の休憩/特定または複数の休憩Service Resource Availability(複数の休憩、ESO には作業ルールのエントリを追加)
残業が可能かどうか/リソースが勤務時間外に移動できるかどうかService Resource Availability
リソースごとの1日あたりの予約の最大数/時間Count Rule
特定のリソースを必ず割り当てる必要があるRequired Resources
特定のリソースを割り当ててはならないExcluded Resources
顧客に特定のサービス訪問時間帯があるService Appointment Visiting Hours
クルータイプのリソースが確実にスケジュールされるようにするService Crew Resources Availability
予約に到着時間帯/必須の到着時刻があるMatch Time Rule
自宅からの移動距離に上限を設ける/広いテリトリー全体の移動コストを管理するMaximum Travel from Home — 上限を距離で設けるか、移動時間で設けるかを確認します(以下の注を参照)
1日の一部または全体で、特定の作業タイプをリソースに割り当てるTimeSlot Designated Work
リソースの主および副のサービステリトリーへの所属に作業を限定するWorking Territories

到着時間帯の Match Time ルール(オプトイン時のデフォルトのペア)。 顧客の到着時間帯を予約に反映するようユーザーが希望する場合は、確認を求めます。ユーザーが同意したら、以下の正確なデフォルト設定で Match Time ルールを2つ出力します。これらは標準の到着時間帯設定なので、ユーザーに値の入力を求めないでください。

設定ルール Aルール B
名前Arrival Window StartArrival Window End
サービススケジュール時刻プロパティSchedStartTimeSchedStartTime
サービス時刻演算子Later than or Equal toBefore or Equal to
サービス時刻プロパティArrivalWindowStartTimeArrivalWindowEndTime
空の値を許可truetrue

それぞれを Match Time 作業ルールとして出力し、params にこれら4つの値(キー serviceScheduleTimeProperty、serviceTimeOperator、serviceTimeProperty、passEmptyValues)を含めます。これらは必須の Earliest Start Permitted/Due Date ルールとは別のものです(出力されません。「Mandatory rules」を参照)。

休憩時刻の解釈と出力(時刻とシフト開始からのオフセット): Service Resource Availability ルールでは、休憩を2種類の形式で表します。このスキルが形式を決定し、データレイヤースキルに必要な値を出力します。データレイヤースキルが変換する必要のある時刻を受け取ることはありません。次のように形式を選択します。

  • 絶対時刻による 1 つの固定された毎日の休憩(例: 「毎日 12:00 に 30 分」)→ これを絶対休憩として出力します: { "mode": "absolute", "startClock": "12:00", "durationMinutes": 30 }。オフセットの計算や稼働日の開始は不要です。
  • 稼働日の開始からのオフセットとして定義された 1 つ以上の休憩(例: 「シフト開始から 3 時間後に始まる 30 分の昼食」)→ それぞれをオフセット休憩として、稼働日の開始からの分単位で出力します: { "mode": "offset", "earliestStartOffsetMinutes": 180, "latestEndOffsetMinutes": 240, "durationMinutes": 30 }。オフセット休憩では earliestStartOffsetMinutes、latestEndOffsetMinutes、durationMinutes の 3 つすべてが必須です。ユーザーがすでに休憩をオフセットの用語で述べている場合(例: 「稼働日の開始から 3 時間後」)、そのオフセットがそのまま earliestStartOffsetMinutes(180)です — 稼働日の開始を尋ねないでください。それは必要ありません。
  • 休憩が時刻で指定されているが複数ある場合(例: 「10:00 に 15 分と 12:00 に 30 分」)→ オフセットの形を使う必要があり、それは各時刻を稼働日の開始からのオフセットに変換することを意味します。稼働日の開始がわからなければそれはできません。したがって、シフト / 稼働日の開始をユーザーに尋ね、その後、各休憩について earliestStartOffsetMinutes = (休憩開始時刻 − 稼働日の開始) を分単位で計算します。稼働日が真夜中に始まると決して仮定しないでください(それは「10:00」を誤った 600 分のオフセットにしてしまいます)。(稼働日の開始を尋ねるのは、この時刻の場合のみです — 休憩がすでにオフセットで表現されている場合は決して尋ねないでください。)

オフセット休憩ごとに常に latestEndOffsetMinutes を出力してください — それを直接尋ね、期間を勝手に作らないでください。 最早開始はユーザーが述べたもの(オフセット、または変換された時刻)から得られます — その周りに ± の許容範囲をでっち上げないでください(「±1 時間」/「±2 時間」の選択肢はなし)。ユーザーが休憩の開始(または終了予定が示されていない期間)しか与えていない場合は、休憩が終了してもよい最も遅い時刻をはっきりと尋ねてください。その際はユーザーが使ったのと同じ用語で表現します。ユーザーがオフセット(「稼働日の開始から 3 時間後に始まる」)を与えた場合は、「稼働日の開始から何時間後として、最も遅くいつ終了してもよいですか?」と尋ねて変換します(例: 「開始から 4 時間後」→ 240)。ユーザーが時刻を与えた場合は、時刻を尋ねて稼働日の開始で変換します。ユーザーが休憩は固定/ピン留めで柔軟性がないと言った場合は、latestEndOffsetMinutes = earliestStartOffsetMinutes + durationMinutes と設定します。オフセット休憩を 3 つのフィールドのいずれかが欠けた状態で決して出力せず、最新終了を決して推測しないでください。休憩の要件が確実に変換するには複雑すぎる場合は、推測するのではなく、明確化のための質問をするか、手動アプローチを勧めてください。

自宅からの最大移動 — 単位ではなく上限の種類を確定します。 要件がリソースが自宅からどれだけ移動できるかの上限を定める場合、その上限が距離によるものか移動時間によるものかをユーザーに尋ねてください — この 2 つは設定方法が異なり、データ層スキルはどちらなのかを知る必要があります。値と種類を params に出力します: { "maxTravelFromHome": 50, "maxTravelFromHomeType": "Distance" | "Travel Time" }。ユーザーの言い回しを解釈して種類を設定します — 「50 マイル」/「50 km」⇒ Distance、「45 分」⇒ Travel Time — ただし距離の単位(マイルか km か)を尋ねたり出力したりしないでください。それは作業ルール設定の一部ではありません。単位は組織のロケール / 距離設定の別の場所で管理されるため、ルールは数値のみを保存します。Travel Time の場合、値は分です。上限の値をその種類なしで決して出力しないでください。

ルール構築のステップ 2: ルールごとのパラメーターの質問

要件ファネル(ステップ 1)が構築するルールを選択したら、選択された各ルールの詳細を尋ねます。Emit 行は、構築仕様に入れる正確な params キーを示します — データ層スキルがそれらをフィールドにマッピングできるよう、これらのキー名をそのまま使用してください。ユーザーが実際に選択したルールについてのみ尋ね、残りは会話に出さないでください。 以下のすべてのルールタイプには、完全に定義された質問 → パラメーターのパスがあります。データレイヤーのフィールドは、Field Service マネージドパッケージのモデルに照らして確認済みです。

  • Service Resource Availability (常に存在します) — 次のように質問します:「作業で残業が発生する可能性はありますか?」(はい/いいえ)・「リソースは勤務時間外に自宅との間を移動できますか?」— はいの場合、自宅から(最初の作業場所まで)および自宅まで(最後の作業場所から)の移動にそれぞれ何分まで許可されるかを質問します。「制限なし」は Field Service の実質的な上限である 120 として扱い、いいえの場合は両方を 0 とします · さらに、休憩に関する聞き取りを行います(上記の休憩時間の解釈と出力を参照)。次を出力します:{ "enableOvertime": true|false, "travelFromHomeMinutes": <number>, "travelToHomeMinutes": <number>, "breaks": [ … ] }。travelFromHomeMinutes/travelToHomeMinutes はブール値ではなく、分単位の値です — 0 は勤務時間外の移動なし、120 は実質的に無制限、またはユーザーが指定した上限を表します。ユーザーから稼働可能時間について詳細が示されない場合は、基本ルールを出力します(enableOvertime: false。移動時間のキーと breaks は省略します)。
  • Match Time(到着時間帯) — 「予約で顧客の到着時間帯を尊重する必要がありますか?」とのみ質問します。はいの場合、上記の到着時間帯の表にある2つのデフォルトルールを出力します(追加の質問はしません)。
  • Match Skills — まず必要性を確認し、必要だと決めつけないでください。 「作業指示には、関連スキルまたは特定の習熟度を持つリソースが必要ですか?」と質問します。いいえの場合、Match Skills ルールは出力しません。はいの場合、ルールを出力し、続けて「リソースは最低スキルレベル(習熟度)も満たす必要がありますか、それともスキルを 持っている だけで十分ですか?」と質問します。次を出力します:{ "matchSkillLevel": true|false }。スキルタイプの AND/OR ロジックについては質問しないでください — このスキルでは skillTypeLogic を設定しません。
  • Match Fields — 「どのService Appointment フィールドを、どのService Resource フィールドと、どの演算子で一致させますか?」と質問します。次を出力します:{ "serviceProperty": "<appointment field API name>", "resourceProperty": "<resource field API name>", "booleanOperator": "=" }。演算子には =, >=, <=, >, < のいずれかを指定します(デフォルトは =)。これらのフィールド名は顧客独自のフィールド名なので、指定されたとおりに渡します。
  • Match Boolean — 「リソース上のどのチェックボックスフィールドを true(または false)にする必要がありますか?」と質問します。次を出力します:{ "resourceProperty": "<resource field API name>", "value": true|false }。1つのポリシーにつき Match Boolean ルールは最大5つであることをユーザーに伝えます。
  • Maximum Travel from Home — 上記の上限タイプに関する注記を参照してください。次を出力します:{ "maxTravelFromHome": <number>, "maxTravelFromHomeType": "Distance"|"Travel Time" }。
  • Working Territories — 「リソースを、主要および副次のサービステリトリーへの所属に限定しますか?」と質問します。この聞き取りで出力するテリトリー範囲指定ルールはこれのみです — 独立した Match Territory の質問はありません(設計上、聞き取りのどの経路でも単独の Match Territory ルールは生成されません)。ユーザー指定のパラメーターはありません — ルールを選択するだけで十分です。次を出力します:{ "workingLocationEnablePrimary": true }(標準の固定値。データレイヤーのスキルが {ns}Working_Location_Enable_Primary__c にマッピングします)。(ユーザーが主要テリトリーのみ/再配置のみの範囲指定(従来の Match Territory の動作)を明示的に必要とする場合は、手動で設定する必要があると伝えてください。聞き取りではこのルールを出力しません。)
  • Service Crew Resources Availability — まず必要性を確認する質問をします:「クルータイプのリソースが確実にスケジュールされるようにする必要がありますか?」いいえの場合、このルールは出力しません — 作成するものはありません。はいの場合、ルールを出力し、次の2つのパラメーターを質問します:
    • Consider Service Crew Membership(はい/いいえ) — 「ルールでクルーのレコードだけでなく、個々のクルーメンバーの稼働状況とスキルも評価しますか?」→ considerCrewMembership。
    • Maximum Additional Service Resources(数値、省略可) — 「最小クルー人数を満たすために追加できる、基本クルーを超えるリソースの最大数はいくつですか?」→ maxAdditionalResources。 出力: { "considerCrewMembership": true|false, "maxAdditionalResources": <number> }(ユーザーが空欄のままにした場合は maxAdditionalResources を省略します)。
  • 拡張マッチ (助言のみ — データレイヤーのスキルはこれを作成しません) — 拡張マッチには、このペアのどのスキルも作成しないカスタムジャンクションオブジェクトと項目が必要です(これはユーザーが管理する必要があるスキーマ DDL です)。したがって、マッピング可能な params を持つ通常の作業ルールとして出力しないでください。 代わりに、要件がカスタム条件のマッチング(例: サービス可能な郵便番号、製品ライン)を求める場合は、何を設定すべきかをユーザーに助言し、それを prerequisites に記録してください — データレイヤーが作成するために workRules[] に入れないでください。ユーザーに提供する設定ガイダンス: (1) Service Resource をマッチ対象オブジェクトにリンクするジャンクションオブジェクトを作成し、正確に 2 つの Master-Detail 関係(1 つは Service Resource へ、もう 1 つはマッチ対象オブジェクトへ)を持たせます — パッケージ化されたトリガーは正確に 2 つを要求し、そうでないとルールは失敗します。(2) マッチを駆動する Lookup 型の Service Appointment 項目。(3) ジャンクション上の、それと照合される参照項目。これらが存在したら、拡張マッチ作業ルールはセットアップで手動で(Field Service Settings → Scheduling → Work Rules)作成および設定する必要があることをユーザーに伝えてください。この設計の外部で作成されたオブジェクト/項目にルールを向けることは、データレイヤーのスキルが行う範囲を超えているためです。意図するオブジェクト/項目を助言テキストとして prerequisites に記録してください。params マッピングは出力しないでください。
  • カウントルール — 質問: 「リソースごと 1 日あたりの予約の最大数または最大時間はありますか?」ある場合は、ユーザーにその制限を平易な言葉で説明してもらい、それを 3 つの値に分類します:
    • countBy — "Appointments"(ジョブの件数、例: 「技術者 1 人あたり 1 日最大 8 件のジョブ」)、"Hours"(時間の上限、例: 「1 日あたり予約の作業時間 ≤ 6 時間」)、または "Custom"(Service Appointment 上のカスタム数値項目の合計、例: 「トラック上の ≤ 100 ユニット」)のいずれか。
    • maxValue — 数値の上限(Hours の場合は、制限を時間に変換します)。
    • fieldHint — Custom の場合のみ(およびオプションで Hours): 合計する Service Appointment 項目。単純な Appointments カウントの場合は null を設定します。

出力: { "countBy": "Appointments"|"Hours"|"Custom", "maxValue": <number>, "fieldHint": "<SA field API name>"|null }。countBy が Custom の場合(または特定の項目に対する Hours の場合)、前提条件の注記を追加します: 「合計対象の項目 <fieldHint> が Service Appointment に存在することを確認してください。」 Time Resolution は常に Daily で、カウント対象オブジェクトは常に Service Appointment です — データレイヤーのスキルが両方を自動的に設定するため、これらについて尋ねないでください。注意: ポリシーごとに最大 10 個のカスタム項目カウントルールです。

選択のみのルール — ルールレコード自体には設定項目がなく、動作は他のオブジェクトのデータからもたらされます。それぞれについて: 平易なゲート質問をし、はいの場合は {} を出力し(いいえの場合は、注記がある箇所では完全にスキップし)、参照されるデータを前提条件として記録します。

  • 必須リソース/除外リソース — 「作業指示/WOLIで、必須としてマークされたリソースの割り当てをポリシーで強制しますか?(または、除外としてマークされたリソースの割り当てを防止しますか?)」と尋ねます。{} を出力します。これらの設定は作業指示上のリソース設定レコードを参照するため、前提条件として記録します(データが存在している必要があります)。
  • 作業容量(ESO のみ)— 「作業をどの上限(時間または容量に対する割合)で、どのテリトリーとどの作業種別/予約属性に対して制限しますか?」と尋ねます。上限は、テリトリーごとの標準 WorkCapacityLimit レコードに保存されます(ポリシーの外部で設定する前提条件)。ポリシーにルールを追加すると、適用が有効になります。意図を記録し、前提条件を明記します。ルールレコード自体に設定フィールドはありません。
  • サービス予約の訪問可能時間 — ゲートのみ。時間帯について尋ねないでください。 「予約を顧客の訪問可能時間に制限しますか?」(はい/いいえ)と尋ねます。時間帯はアカウントごとのデータであり、ポリシー全体で共通する値ではありません。作業指示から参照される OperatingHours レコードに保存されます(アカウントから設定されます)。そのため、設計時に収集する情報はありません。はいの場合は {} を出力し、一般的な前提条件を記録します(「各アカウントに訪問可能時間/営業時間が設定されている必要があります。作業指示の訪問可能時間はアカウントから引き継がれる必要があります」)。許可する時間帯をユーザーに尋ねてはいけません。アカウントごとに異なるためです。(関連性グループによるスコープ設定はありません。)
  • 時間枠の指定作業 — ゲートのみ。 「特定の作業種別のために、特定の時間枠/シフトを確保する必要がありますか?」(はい/いいえ)と尋ねます。「Work Type」ではなく「作業種別」と表現してください。Work Type は特定の Salesforce オブジェクトであり、そのように表現すると、予約の対象がそのオブジェクトに限られると誤解されますが、指定の範囲はより広いものです。時間枠と作業の対応付けは標準の TimeSlot データに保存されるため(前提条件)、設計時に収集する情報はありません。はいの場合は {} を出力し、一般的な前提条件を記録します(「時間枠に、対象となる作業を指定する必要があります」)。(関連性グループによるスコープ設定はありません。)時間枠と作業の対応付けをユーザーに列挙させないでください。これはデータ設定で行います。

ルールごとの設定フィールドと注意点については、service.pfs_optimization_theory_work_rules.htm 内の該当するルールのサブページを取得してください(ライブドキュメントの参照先を参照)。


関連性グループ

関連性グループは、単一の作業ルールまたはサービス目標の適用対象を特定の予約またはリソースだけに限定するもので、ポリシー全体には適用されません。これにより、パートタイムとフルタイムの従業員で休憩/移動の上限を変えたり、価値の高いアカウントの予約を優先したりするなど、異なる作業種別またはリソース種別に異なるロジックを1つのポリシーに設定できます。

仕組みと役割分担

関連性グループは、Boolean(true/false)項目によって制御されます。Service Appointment オブジェクトおよび Service Territory Member オブジェクト上のすべての標準またはカスタムの Boolean 項目を、ルール/目的の「関連性グループ」ドロップダウンから選択できます。作業ルールまたは目的では、制限に使う Boolean 項目を選択します。すると、そのルール/目的は、その項目が true のレコードにのみ適用されます。基準は、その Boolean 項目が存在するオブジェクトに応じた2種類です。

  • Service Appointment 基準 — 評価対象の予約に基づいて範囲を限定します(例:関連アカウントのティアが Platinum のときに true になる数式チェックボックス「Platinum Account」)。予約の種類を対象にする場合に使います。
  • Service Territory Member 基準 — 評価対象のリソースのサービステリトリーメンバーに基づいて範囲を限定します(例:「Part-Time」/「Full-Time」チェックボックス)。リソースの集団を対象にする場合に使います。サポートされるのはプライマリおよび転属後のメンバーシップのみです。セカンダリは対象外です。

設計時には、 基準(Service Appointment または Service Territory Member)と、対象のサブセットを識別する Boolean 項目名を決め、両方をビルド仕様のルール/目的に記録します(relevanceGroup: { basis, booleanField })。このスキルで決定するのは、どの項目でどのルール/目的の範囲を限定するかだけです。レコードへのグループの設定は、データレイヤースキルの担当範囲です。

Boolean 項目そのものは、ユーザーが用意する前提条件であり、ここで取り上げるどちらのスキルでも作成しません。項目の作成はスキーマの変更(DDL)であり、レコードの true/false 値の設定にはフロー、数式、トリガー、またはデータロードが必要です。そのため、デプロイ前に次の対応をユーザーに案内してください。(1) サブセットごとに、適切なオブジェクト上にカスタム Boolean 項目を1つ作成する(リソースのサブセットなら Service Territory Member、作業のサブセットなら Service Appointment)。(2) そのサブセットに該当するレコードだけが true になるよう値を設定する(フロー/数式/インポートを使用)。ルールの種類によって必要な場合は、サブセット同士が重複しないようにします。続いて、ビルド仕様でその項目名を参照すれば、データレイヤースキルが関連性グループとして設定できます。

例(基本パターン)

Match Boolean ルール(またはその他の範囲限定)を特定の予約の種類だけに適用するには、該当する予約で true になる Service Appointment 上の Boolean 項目を特定する(またはユーザーに作成してもらう)必要があります。その項目をビルド仕様でルールの関連性グループとして指定します。これにより、ルールはその項目が true の予約にのみ適用されます。Service Territory Member の Boolean 項目(例:Part-Time と Full-Time)を使って同じ方法を適用すると、Maximum Travel from Home ルールを2つ作成し、それぞれ異なる上限を設定できます。一方はパートタイム勤務者、もう一方はフルタイム勤務者に範囲を限定します。

目的の典型例としては、ASAP と関連性グループを組み合わせる方法があります。予約に「Platinum Account」という数式チェックボックスを作成し、それを関連性グループとして設定した、高い重みの ASAP 目的「Expedite Platinum Accounts」を設定します。これにより、Platinum の作業はより早くスケジュールされます(移動時間が長くなることを許容します)。範囲を限定した優先度の高い目的には、明確に高い重みを設定してください。そうしないと、エンジンが ASAP のペナルティを理由に、予約自体をスケジュールしないことがあります。

任意のルールまたは目標を一部の対象に限定する(一般ルール)

これは、休憩や稼働率だけでなく、すべての作業ルールとサービス目標に適用されます。 ルールまたは目標をポリシー全体ではなく、特定のリソースのみまたは特定の作業のみに適用するというリクエストの場合、その仕組みは関連グループです。この兆候を見逃さず、該当する場合は毎回これを使用してください。

  • 「特定の種類のリソースに適用する」(例:「フランスのリソース」「パートタイムの技術者」「シニアエンジニア」「設置作業チーム」)→ Service Territory Member の関連グループを作成します。Service Territory Member 上に Boolean フィールドを作成し、該当するリソースのみが true になるようにして、ルールまたは目標の関連グループとして選択します。
  • 「特定の種類の作業に適用する」(例:「設置作業」「プラチナアカウントの予約」「緊急作業指示」「2 時間を超える作業」)→ Service Appointment の関連グループを作成します。Service Appointment 上に Boolean フィールドを作成し、該当する予約のみが true になるようにして、ルールまたは目標の関連グループとして選択します。

必要な基準が、その特定のルールまたは目標でサポートされているか必ず確認してください。 Service Territory Member で範囲を限定できないルールまたは目標もあれば、Service Appointment で範囲を限定できないものもあります。関連グループを提案する前に、該当するルールまたは目標について、関連グループのドキュメントにあるサポートマトリックス(service.pfs_relevance_groups.htm)を確認し、Enhanced Scheduling & Optimization(ES&O)のサポートのみを考慮してください。レガシー版または Enhanced 版以外の列は無視してください。ES&O でそのルールまたは目標に必要な基準がサポートされていない場合は、その旨を伝え、存在しない機能を作り上げずに、最も近いサポート対象の代替案を提案してください。

2 つの対象グループに、同じルールまたは目標の異なる設定が必要な場合は、グループごとに 1 つずつインスタンスを設計し、決して 1 つのインスタンスにまとめないでください。 ルールまたは目標の単一インスタンスは、その対象となるすべてのレコードに適用されるため、「フランスにはパターン A、それ以外にはパターン B」を 1 つのインスタンスで表現することはできません。代わりに、対象グループごとに 1 つのインスタンスを指定し、それぞれ別個の Boolean フィールドで範囲を限定して、グループが相互排他的になるようにします(Service Resource Availability など、対象範囲を 1 つに限定するルールタイプでは、重複するとエラーになります。Key rules and gotchas を参照してください)。例:「フランスのリソースには午前 10 時に 15 分の休憩と、12 時から 2 時までの昼休みを適用し、それ以外のリソースには 12 時から 3 時までの 30 分の休憩を適用する」場合、Service Resource Availability ルールを 2 つ用意する必要があります。1 つは Break_Group_France__c STM チェックボックスで範囲を限定した「Availability – France」、もう 1 つは Break_Group_Standard__c STM チェックボックスで範囲を限定した「Availability – Standard」です。それぞれのルールには、そのグループ固有の休憩のみを設定し、すべてのリソースが必ずどちらか一方のグループにのみ属するようにします。

シナリオが本当に複雑すぎる場合は、手動で設計するよう提案してください。 リクエストに複数の対象グループ、曖昧な値、まだ存在しない関連グループ用フィールドが組み合わさっている場合、仕様に単一の統合インスタンスを無理に押し込まないでください。ユーザーに上記の関連グループ設計を説明して詳細を詰めてもらうか、曖昧さのない部分だけを仕様化します。もっともらしく見えても誤った統合ルールまたは目標を提示するよりも、確認の質問をすること(どの対象グループか、リソースか作業か、各グループを識別する Boolean フィールドは何か、ES&O でそのルールまたは目標にその基準がサポートされているか)を優先してください。

主なルールと注意点
  • 関連グループは相互に排他的でなければなりません。 関連グループが重複する2つのルールがある場合、より制限の厳しいルールが優先されます。ただし、サービスリソースの利用可能性では、重複するとエラーが発生します。各リソースは、サービスリソースの利用可能性ルールで必ず1つだけカバーされている必要があります。
  • 加算型のルールタイプ — 複数回指定でき、同じリソース/予約を正当にカバーできます:件数ルール、拡張マッチ、ブール値マッチ、フィールドマッチ、時間マッチ。
  • 単一カバレッジのルールタイプ — リソース/予約は、同時に最大1つのインスタンスでカバーされる必要があります:スキルマッチ、テリトリーマッチ、自宅からの最大移動距離、必須リソース、サービスクルーのリソース利用可能性、サービスリソースの利用可能性、作業テリトリー。(また、リソースをテリトリーマッチと作業テリトリーの両方で同時にカバーしないでください。)
  • 全か無かのルール — 関連グループなし: タイムスロット指定作業とサービス予約の訪問時間です。また、作業容量は関連グループをサポートしていません。
  • 目的も関連グループを使って繰り返し指定できますが、レコードが同じタイプの2つの目的の条件を同時に満たしてはなりません。ただし、リソース優先度は例外で、それぞれのインスタンスが異なるリソース優先度フィールド(例:「主優先度」と「副優先度」/「在籍期間」)を参照する場合、同じ予約に正当に2回適用できます。
  • ルール/目的を予約単位とテリトリーメンバー単位のどちらで適用範囲指定できるかは、Enhanced エンジンと非 Enhanced エンジンで異なります。適用範囲を指定する際は、サポート表を確認してください。近隣グループ化と同一サイトの目的は、関連グループを一切サポートしていません。

サポート一覧と設定手順の全容については、service.pfs_relevance_groups.htm を取得してください(ライブドキュメントの参照先を参照)。


背景:SFS のスコアリングの仕組み

オプティマイザーは、各スケジュール候補にペナルティポイントを割り当てます。合計ペナルティが小さいほど、スケジュールの評価は高くなります。各サービス目的は、重みと独自の尺度(その目的で想定される最悪のケース)に基づいてペナルティポイントを加算します。

移動の最小化の重みが基準です — 値はユーザーが設定し、他のすべての重みはこれを基準に算出されます。

以下のすべての目的に共通する一般式(9回も導出し直さなくて済むよう、ここに一度だけ記載します):

penaltyPerViolation = max( 1, roundingFn( (1000 × weight) / scale ) ) × finalMultiplier
total_penalty       = ceil( violations / granularity ) × penaltyPerViolation

すべての目的で同じ**×1000の内部乗数**が適用され、その後に目的ごとの尺度、丸め関数、最終乗数が適用されます。これには次の2つの意味があり、以下の各目的の説明では繰り返しません。

  • ×1000の乗数は一律なので、2つの目的間のクロスオーバー/導出方程式では相殺されます。これが、トレードオフのヒアリング全体で使われる単純な連続近似(penalty ≈ units × weight/scale)が有効である理由であり、以下の「weight_X を weight_Y から導出する」各式に×1000の項がない理由です。整数の重みに対して丸め関数が厳密でない場合(ASAP の round、均等に割り切れない尺度に対するスキルレベル/優先度の roundInt)は、わずかなずれが生じる可能性があります。重要な場合は、各目的の精度に関する注記で説明します。
  • max(1, …) の下限により、重みが非常に小さい場合でも、含まれる各目的には何らかの効果が保証されます。この下限が実際に適用される具体的な重みの範囲は、各目的の精度に関する注記で示します。

一律の乗数の唯一の例外は同一サイトです。最終乗数(×0.01)により実質的な乗数は×10となります。これは、他の目的の率で単純に割るという方法を調整なしでは使えない唯一のケースであるため、以下の式で明記します。


目的別のペナルティ計算式

会話全体を通じて、数式の提示と重みの逆算には以下の計算式を使用します。各目的には、スケール、丸め関数、最終乗数を示した後、計算例と精度に関する注意点を記載しています。一般的な仕組み(×1000 の乗数、max(1,…) の下限、導出が有効であり続ける理由)については上で一度説明しているため、ここでは繰り返しません。

移動時間の最小化(基準)
  • スケール: 120 分(2 時間 = デフォルトの MaxGrade__c)。丸め: round5(小数点以下 5 桁)。最終乗数: × 1/60(分単位のレートを秒単位に変換)。

    penaltyPerViolation_travel = max( 1, round5( (1000 × weight_travel) / 120 ) ) × (1/60)
    

    weight_travel = 1000 の場合:round5(1000×1000/120) = 8333.33333 → × 1/60 = 138.88889 pts/second。

  • 移動時間 X 秒に対するペナルティ: travel_penalty(X_seconds) = ceil( X_seconds / 1 ) × penaltyPerViolation_travel(秒単位の粒度。ASAP の分単位の粒度とは異なります)。

例 — 移動時間が 30 分(1800 秒)の場合:1800 × 138.88889 = 250,000 pts。スケールの上限である 2 時間(7200 秒)では、7200 × 138.88889 = 1,000,000 pts です。

  • 連続近似(重みの導出用): travel_penalty(X_minutes) ≈ X × (weight_travel / 120) — weight_travel = 1000 の場合、約 8.333 pts/min です。

  • 精度に関する注意: round5 は、ASAP の整数 round よりもはるかに高い精度を保ちます。Travel は秒単位ですが、同じスケールの Overtime は分単位です。そのため、61 秒の移動は 60 秒の移動よりもコストが高くなります。これは Overtime とは異なります。max(1,…) の下限が適用されるのは重みが約 0.12 を下回る場合のみで、Travel は 1000 に固定されているため関係ありません。

ASAP
  • スケール: 43,200 分(30 日 = 2,592,000 秒)。丸め: round(整数)。
    penaltyPerViolation_asap = max( 1, round( (1000 × weight_asap) / 43200 ) )
    
  • 未来の時刻 X 秒に対するペナルティ: asap_penalty(X_seconds) = ceil( X_seconds / 60 ) × penaltyPerViolation_asap(分単位の粒度)。
  • 連続近似: asap_penalty(Y_minutes) ≈ Y × (weight_asap / 43200)。
  • 精度に関する注意: 約 22 未満の重みは実質的に機能しません。round(1000×21/43200) = round(0.486) = 0 は下限によって 1 に制限されるため、重みが 1~21 の場合、1 pt/min のレートは同じです。それを超えると誤差は小さくなります。重みが 250 の場合、round(250000/43200) = 6 → 実効重みは 6×43200/1000 = 259.2(3.7% の誤差)です。精度が重要な場合は、最終的な重みを整数の計算式で検証してください。
同一サイト
  • スケール: 1(二値 — 同じ場所にまとめるか、分割するか)。丸め: round5。最終乗数: × 0.01 — これは上で示した例外で、実効乗数は ×10 となり、×1000 ではありません。
    penaltyPerViolation_same_site = max( 1, round5( (1000 × weight_same_site) / 1 ) ) × 0.01
    
    weight_same_site = 50 の場合:round5(50000) = 50000 → × 0.01 = 500 pts/violation。
  • 合計ペナルティ: same_site_penalty = ceil( X_violations / 1 ) × penaltyPerViolation_same_site。例 — 1 件の分割(w=50):1 × 500 = 500 pts。
  • 旧来の概念式との同等性: New: w × 10 × X と Old: w × X — 新しい内部計算式は、旧式の 10 倍 です(上記の ×0.01 があるため、1000 倍ではありません)。Travel(w=1000 → 138.89 pts/sec)または ASAP と内部的に比較する場合、両者に同じ「×1000 の後にスケールを適用する」パターンがあるため、クロスオーバーの計算は引き続き同じ連続形式になります。
    same_site_penalty = asap_penalty(D_equiv)  →  weight_same_site = D_equiv × (weight_asap / 43200)
    
  • 精度に関する注意: 整数の重みでは round5 による精度の損失は実質的にありません。ここでの実効乗数は ×10 です。×1000 を基準とした倍率と異なるのは、この目的だけであることを覚えておいてください。時間の次元に関係なく、イベントごとに一定のペナルティが適用されます。
超過勤務を最小限にする
  • スケール: 120 分(Travel と同じ)。丸め: round5。最終乗数: × 1.0(1 分あたり — Travel の ÷60 とは異なり、これ以上の除算はありません)。
    penaltyPerViolation_overtime = max( 1, round5( (1000 × weight_overtime) / 120 ) ) × 1.0
    
    weight_overtime = 1000 の場合:8333.33333 pts/minute。
  • X 秒の超過勤務に対するペナルティ: overtime_penalty(X_seconds) = ceil( X_seconds / 60 ) × penaltyPerViolation_overtime。例 — 30 分(1800 秒):ceil(1800/60) × 8333.33333 = 30 × 8333.33333 = 250,000 pts。
  • 連続近似: overtime_penalty(Z_minutes) ≈ Z × (weight_overtime / 120)。
  • 精度に関する注記: Travel と同じスケールと round5 の精度を使用するため、レートを直接比較できます。ただし、Overtime は Travel の 1 秒単位ではなく、分単位(ceil(seconds/60))でグループ化されるため、61 秒のブロックと 120 秒のブロックのコストは同じです(どちらも 2 分)。weight ≈0.12 未満ではフロアが適用されますが、実際にはほぼ該当しません。
優先リソース
  • スケール: 1(二値 — 遵守または違反で、部分点はありません)。丸め: roundInt。最終乗数: × 1.0。
    penaltyPerViolation_preferred = max( 1, roundInt( 100 × 10.0 × weight_preferred ) ) × 1.0
    
    (100 × 10.0 = 1000 — 内部の乗数は同じで、分解して示しているだけです。)weight_preferred = 375 の場合:roundInt(375000) = 375,000 pts/violation。
  • 合計ペナルティ: preferred_penalty = ceil( X_violations / 1 ) × penaltyPerViolation_preferred。例 — 1 件の違反(w=375):375,000 pts。
  • 連続近似: 整数の重みでは正確です(roundInt(1000×w) = 1000×w、丸め誤差ゼロ):preferred_resource_penalty = X_violations × 1000 × weight_preferred。
  • 精度に関する注記: 整数の重みでは丸め誤差はありません。weight が 0.001 未満の場合にのみフロアが適用されますが、実際には無関係です。イベントごとの固定ペナルティであり、時間単位の粒度はありません。
リソースの優先度
  • スケール: 10(優先度の値は 0~10。0 = 最適候補、10 = 最低優先度、null = 最低)。丸め関数なし — 小数値をそのまま使用します。
    penaltyPerViolation_resource_priority = max( 1, (weight_resource_priority / 10.0) × 1000 )
    
    weight_resource_priority = 1875 の場合:(1875/10.0) × 1000 = 187,500 pts per priority point。
  • 優先度 P のリソースに対する合計ペナルティ: resource_priority_penalty = ceil( P / 1 ) × penaltyPerViolation_resource_priority — P は違反数として直接使用されます(÷10 のスケール除算はすでにレートに組み込まれています)。例 — P=5(w=1875):5 × 187,500 = 937,500 pts。P=0(最適):0 pts。P=10(最低):10 × 187,500 = 1,875,000 pts。
  • 旧式との同等性: New: P × (w/10) × 1000 = Old: w × (P/10)。一律の ×1000 を除けば同じです。÷10 はリソースごとの評価時ではなく、レートの算出時に適用するだけです。
  • 導出式: weight_resource_priority = T_equiv × weight_travel / (12 × (P_low - P_high))。
  • 精度に関する注記: 浮動小数点精度をそのまま使用し、丸めはありません。weight が 0.01 未満の場合にのみフロアが適用されますが、無関係です。ペナルティは線形に増加し、優先度 5 では最大ペナルティの 50% が課されます。
スキルレベル
  • 尺度: 10(10段階のスキルレベル値)。丸め: roundInt。
    penaltyPerViolation_skill_level = max( 1, roundInt( (1000 × weight_skill_level) / 10 ) )
    
    weight_skill_level = 63 の場合:roundInt(63000/10) = スキルレベル1ポイントあたり6300 pts。
  • スキルレベル S の合計ペナルティ: skill_level_penalty = ceil( S / 1 ) × penaltyPerViolation_skill_level — S は違反件数としてそのまま使用します。例 — S=4(w=63):4 × 6300 = 25,200 pts。S=8(w=63):8 × 6300 = 50,400 pts。
  • 概念式との関係: S × weight は内部で S × (1000 × weight)/10 になります。整数の重みでは (1000×w)/10 が正確に割り切れるため、roundInt による誤差はありません。
  • 導出式: weight_skill_level = T_equiv × weight_travel / (120 × (S_high - S_low))。
  • モードによって解釈は変わりますが、式は変わりません: モードにかかわらず、式は同じです。Least Qualified — 通常のロジックでは、ペナルティが低いほど優先されます(S=4 の 25,200 は S=8 の 50,400 より優先されます)。Most Qualified — SFS はこの目的に限り優先順位を反転させるため、ペナルティが高いほど優先されます(スコアが高くても、S=8 の 50,400 は S=4 の 25,200 より優先されます)。スキル要件が複数ある場合、SFS はそれらのスキルレベルスコアを平均します。予約にスキル要件がない場合、スキルレベルは影響しません。重みを導出する前に、ユーザーがどちらのモードを使用しているか確認してください — トレードオフの捉え方が異なります。
  • 精度に関する注記: 整数の重みでは丸め誤差はありません(1000×w を 10 で割る計算が正確です)。Floor による誤差は、重みが約0.01未満の場合は無視できます。S に比例して増加します。
スキルの優先度
  • 尺度: 10(スキル優先度は1~10。1=最優先、10=最低優先。null の場合は10が既定値です)。丸め: roundInt。
    penaltyPerViolation_skill_preference = max( 1, roundInt( (1000 × weight_skill_preference) / 10 ) )
    
    weight_skill_preference = 750 の場合:roundInt(750000/10) = スキル優先度1ポイントあたり75,000 pts。
  • スキル優先度 SP の合計ペナルティ: skill_preference_penalty = ceil( SP / 1 ) × penaltyPerViolation_skill_preference。例 — SP=1(w=750):75,000 pts。SP=6:450,000 pts。SP=10:750,000 pts。
  • 概念式との関係: weight × (SP/10) は内部で SP × (1000×weight)/10 になります。評価時にスキルごとに適用していた ÷10(SP/10)が、現在はレートに組み込まれており、SP は割らずに違反件数として使用します。代数的に等価です。
  • 導出式: weight_skill_preference = T_equiv × weight_travel / (12 × (SP_low_pref - SP_high_pref))。
  • 同じ Skill Type のスキル間で、Match Skills の作業ルールが "At Least One Skill Matches (OR)" の場合にのみ適用されます。AND マッチングでは効果はありません。空欄の Skill Priority は10が既定値です。
  • 精度に関する注記: 整数の重みでは丸め誤差はありません。Floor による誤差は無視できます。SP に比例して増加します。
Group Nearby
  • 尺度: 1(2値 — クラスター内かどうか)。丸め: round5。最終乗数: × 1.0 — ここでは内部の ×1000 の全範囲が維持されます(Same Site の ×0.01 とは異なります)。
    penaltyPerViolation_group_nearby = max( 1, round5( (1000 × weight_group_nearby) / 1 ) ) × 1.0
    
    weight_group_nearby = 167 の場合:round5(167000) = 1違反あたり167,000 pts。
  • 合計ペナルティ: group_nearby_penalty = ceil( X_violations / 1 ) × penaltyPerViolation_group_nearby。例 — 1違反(w=167):167,000 pts。
  • 旧式との等価性: 新:w × 1000 × X 対 旧:w × X — 旧式の1000倍です(Group Nearby には ×0.01 の調整が適用されないため、Same Site の10倍とは異なります)。
  • 導出式: weight_group_nearby = T_equiv × (weight_travel / 120)。
  • 精度に関する注記: 精度損失は無視できます。イベントごとに一定のペナルティが適用されます。クラスターからどれほど離れていても、クラスター外の予約には同じコストがかかります。
ギャップの最小化
  • 目的: シフト内で、リソースの連続する予定の間に生じるアイドル時間を短縮します。これはソフト目標であり、アイドル時間が続くとペナルティが発生しますが、それ以外は良好なスケジュールを不適切として却下することはありません。
  • スケール: 1(条件を満たすアイドルギャップごとに違反1件としてカウントし、ルート/シフトごとに集計します)。設定可能なギャップ最小時間: 30分から24時間までです。最小時間より短いギャップはカウントされません。丸め: round(スケール除数なし。有効スケール=1)。
    penaltyPerViolation_gaps = max( 1, round( 1000 × weight_gaps ) )
    
    weight_gaps = 50 の場合:round(50000) = 50,000 pts per gap violation。
  • ペナルティ合計: gaps_penalty = Σ over all routes/shifts [ clump_counter(route) × penaltyPerViolation_gaps ] — clump_counter(route) は、そのリソースのシフトで設定された最小時間を超えるアイドルギャップを数え、すべてのルートにわたって合計します。例 — 条件を満たすギャップが2つあるリソース1つ(w=50):2 × 50,000 = 100,000 pts。スケジュール全体で、ギャップが1/2/1個あるリソースが3つの場合:(1+2+1) × 50,000 = 200,000 pts。
  • 連続近似: 整数の重みでは正確です(round(1000×w) = 1000×w):gaps_penalty = clump_count × 1000 × weight_gaps。
  • 精度に関する注意: 整数の重みでは丸め誤差はゼロです。重みが0.001未満では切り捨て誤差は無視できます。ペナルティはルートごとのギャップ単位です。1つのリソースに条件を満たすギャップが3つある場合、レートの3倍が加算されます。ギャップ最小時間のしきい値は設定パラメーターであり、重みの計算には含まれません。

会話の流れ

2つのグローバルルール(ここで一度だけ示し、以下すべてに適用)
  • 導出した重みはすべて、次の整数に切り上げてください — 四捨五入ではなく切り上げです。Salesforce Field Service のスケジューリングポリシーでは、小数の重みは使用できません。このルールは以下の各式の後には繰り返しません。例外(ASAP とギャップの最小化に関する低い重みの下限の警告)は、該当する箇所で明示します。
  • 優先度を推測するために、目的間で重みの値をそのまま比較しないでください — それぞれの目的でスケールが異なるため、必ず単位あたりのペナルティレートを算出して比較してください(ステップ3を参照)。
ステップ 1:目的の選択

まず、サービス目的の概念をユーザーに説明してください。サービス目的は、すでに勤務ルールをクリアした候補者を評価する「ソフトなスコアリング」基準です。勤務ルールとは異なり、目的によって候補者が除外されることはありません。適格な候補者のうち、オプティマイザーがどの候補者を優先するかに影響するだけです。この説明の中で、Minimize Travel は常にアンカー目的として自動的に含まれ、固定ウェイトは 1000 であることも伝えてください。他のすべての目的のウェイトは、トレードオフの計算を通じてこれを基準に導出されます。ユーザーがこれを選択する必要はありません。

続いて、残りの 9 つの目的を、以下の固定順序で 1 カテゴリーずつ提示し、ユーザーが選択するまで次のカテゴリーに進まないでください。9 つすべてを一度に提示してはいけません。

1. 顧客体験の目的 — 以下の 3 つを提示し、どれを選択するか(または選択しないか)を尋ねてください。

  • ASAP — できるだけ早く顧客にサービスを提供する
  • Same Site — 2 件のジョブが同じ場所にある場合、連続して実施する
  • Group Nearby — 地理的に近いジョブをまとめる

2. コスト/効率の目的 — 以下の 3 つを提示し(Minimize Travel はすでに自動的に含まれていることに触れてください)、残りの 2 つのうちどれを選択するか尋ねてください。

  • Minimize Travel(すでに自動的に含まれています — アンカー、固定ウェイト 1000)
  • Minimize Gaps — 技術者が継続して稼働できるようにする
  • Minimize Overtime — 時間外労働の支払いを避ける

3. 人員配置/アサイン品質の目的 — 以下の 4 つを提示し、どれを選択するか(または選択しないか)を尋ねてください。

  • Preferred Resource — 可能な場合、希望する/指名された技術者を利用する
  • Resource Priority — 優先度の高いリソースを優先する(例:契約業者よりスタッフ)
  • Skill Level — ジョブに適したレベルの専門知識を割り当てる
  • Skill Preference — スキル種別内の優先順位を尊重する(例:言語の優先順位)

目的ごとの追加質問 — 該当するカテゴリーへの回答を受けた直後、次のカテゴリーに進む前に質問してください。 これらは設定に関する情報(ウェイト計算ではありません)を収集する質問なので、ステップ 2 に先送りせず、選択時に尋ねてください。

  • Minimize Travel (必ず質問します。Cost/Efficiency カテゴリーの質問の最後に 1 回尋ねてください。Travel がすでに含まれていることをここで伝えるためです):「Minimize Travel では、リソースの拠点との往復区間、つまり自宅から最初のジョブまでの移動と、最後のジョブから自宅までの移動もカウントしますか。それとも、これらの区間をスコアリングから除外しますか?」excludeTravelFromHome と excludeTravelToHome の 2 つの独立したフラグとして記録してください(true = その区間を除外)。ユーザーに希望がない場合は、既定値を両方とも false(両方の区間を含める)にしてください。
  • Same Site (選択された場合のみ。Customer-Experience カテゴリーの質問の最後に尋ねてください):「Same Site では、緯度/経度が完全に一致する場合に限って 2 件の予約を同じ場所とみなしますか(建物間をつなぐ道路のないキャンパスや農場に便利です)。それとも、既定のグルーピング(移動時間が約 1 秒以内の予約を同じ場所とみなす)を使いますか?」useExactLocation として記録してください(true = 緯度/経度の完全一致のみ)。ユーザーに希望がない場合は、既定値を false(標準の近接性によるグルーピング)にしてください。
  • Minimize Gaps (選択された場合のみ。Cost/Efficiency カテゴリーの質問の最後に尋ねてください):会社がギャップとして扱う最小の空き時間をユーザーに尋ねてください(30 分から 24 時間の間。これより短い時間はペナルティの対象になりません)。詳しい説明と例については、以下の Minimize Gaps Trade-Off を参照してください。params.minGapMinutes として記録してください。ユーザーが値を指定しない場合は、既定値を 30 にしてください。トレードオフの質問より前にここで尋ねることで、「何分以上をギャップとみなすか」と「ギャップを埋めるためにオプティマイザーにどれほど積極的に働かせるか」を切り分けられます。

3 つすべてのカテゴリーについて質問し、追加質問への回答を記録したら、ステップ 2 に進み、ウェイトを導出するためのトレードオフの質問を行ってください。


ステップ 2:トレードオフのヒアリング

選択された各目的(「移動時間の最小化」を除く)について、トレードオフに関する質問をします。目標は、ユーザーが2つの選択肢を同じくらい許容できると感じる境目を見つけることです。この同等性を使って重みを計算します。

ユーザーの回答を受けたら、2つのペナルティ式を等しくして未知の重みを求め、上記の全般ルールに従って切り上げます。計算過程は必ず示してください。

以下の質問テンプレートを使用してください。質問に含める具体的な数値は、自然に感じられるよう調整してください。ユーザーが混乱しているようなら、例の値を変更してもかまいませんが、計算の整合性は保ってください。

自由回答の質問とあわせて、具体的な選択肢を提示してください。 質問テンプレートを提示した後、すぐに選べる少数の境目の例をユーザーに示します。弱め・中程度・強めの好みを幅広く示す、単純な数値例を使ってください。こうすれば、ユーザーは数値を一から考えずに選べます。どの選択肢も合わない場合は、自分に合った正確なトレードオフを自分の言葉で説明してもらうよう、必ず促してください。どちらの方法でも、以下の同じ計算に進みます。選択肢は回答しやすくするためのものであり、別の計算方法ではありません。

妥当性確認は一度だけ: 各重みを導出した後、導出した等式の両辺にその重みを代入して確認します。両辺は一致するはずです。この確認は全体を通して暗黙に行うものであり、以下の目的ごとに繰り返し示すことはありません。ただし、SFS の整数丸めによるずれ(ASAP と「ギャップの最小化」では明示的に注意喚起します)や、重みが下限値に達する場合の警告など、重要な点がある場合は除きます。


ASAP のトレードオフ

質問テンプレート:

「今すぐ予約できるものの、移動時間が**[X]分増える場合と、移動時間は増えないものの、[Y]時間後**に予約する場合では、どのあたりならほぼ同等だと感じますか?」

例:「移動時間が30分増えても今すぐ予約する場合と、移動時間は増えないものの24時間後に予約する場合では、どちらがよいですか?」

提示する選択肢(最も近い境目を選ぶか、自分の案を説明してもらいます):「移動時間15分 ≈ 遅延12時間」(即時性をやや重視)、「移動時間30分 ≈ 遅延24時間」(中程度。上記の例)、「移動時間60分 ≈ 遅延48時間」(即時性を強く重視)。

計算(ユーザーが境目として、移動時間 X 分、遅延 Y 時間を示した場合):

travel_penalty(X) = asap_penalty(Y × 60)

X × (weight_travel / 120) = (Y × 60) × (weight_asap / 43200)

Solve for weight_asap:
weight_asap = X × (weight_travel / 120) × (43200 / (Y × 60))
            = X × weight_travel × 6 / Y

注:weight_travel = 1000 の場合、これは weight_asap = X × 6000 / Y に簡略化できます。

整数式の検証(導出後に確認):

weight_asap を計算したら、SFS が実際に使用する整数ペナルティ率を確認してください。

penaltyPerViolation = max( 1, round( 1000 × weight_asap / 43200 ) )
effective_weight    = penaltyPerViolation × 43200 / 1000

effective_weight が導出した weight_asap と大きく異なる場合(通常はずれが 5% を超える場合)、ユーザーにその差を伝え、SFS 内部の丸めによって実際の境目がわずかに変わることを説明してください。

重みが小さい場合の警告: 導出した重みが 22 未満の場合は、SFS が1分あたりのペナルティを1に固定する(max(1,…) の下限値)ことをユーザーに警告してください。つまり、重みが1~21の範囲では、どの値でもオプティマイザーの動作は同じです。この下限値を受け入れるか、目的を重みに比例して反映させるため、重みを少なくとも22まで上げることを推奨してください。


同一拠点のトレードオフ

重要な前提: 同一拠点を移動時間と比較しないでください。同一拠点での予約はすでに同じ場所にあるため、移動は意味のあるトレードオフではありません。代わりに、同一拠点をASAP(選択されている場合)または優先リソース(選択されている場合)と比較してください。重要な問いは、より早く予約するため、またはリソースの希望を尊重するために、同一拠点の予約を分けるかどうかです。

ASAPが選択されている場合 — 同一拠点とASAPを比較します:

質問のテンプレート:

「同じ拠点で2件の予約があるとします。オプティマイザーは次のどちらかを選べます。 (A)両方を同じリソースに割り当てますが、予約時間は本来可能な時間より**[H]時間遅く**なります。 (B)予約を分けて、別のリソースまたは時間に割り当てることで、今すぐ予約します。 予約がどのくらい遅れるなら、『分けてしまおう』と思いますか?」

例:「同一拠点の予約をまとめるために、予約時間が8時間遅くなってもよいですか? 4時間ならどうですか? 1時間ならどうですか?」

提示する選択肢(または、希望する遅延のしきい値を説明してもらいます):「最大1時間遅れてもまとめる」(まとめることへの弱い選好)、「最大4時間遅れてもまとめる」(中程度)、「最大8時間遅れてもまとめる」(強い選好 — 上記の例)。

確認すべき重要な等価関係は、**同一拠点の予約を1回分けること = ASAPの遅延何分分か?**です。分けてもよいと考える遅延のしきい値 D_equiv(分)をユーザーに答えてもらいます。

計算:

same_site_penalty = asap_penalty(D_equiv)

weight_same_site = D_equiv × (weight_asap / 43200)

ASAPが選択されておらず、優先リソースが選択されている場合 — 同一拠点と優先リソースを比較します:

質問のテンプレート:

「同じ拠点で2件の予約があるとします。そのうち1件に優先リソースを割り当てるため、予約を分けますか(別のリソースにしますか)? それとも、優先リソースを無視することになっても、予約をまとめたままにしますか?」

提示する選択肢(または、希望を説明してもらいます):「重要度は同じ」(同一拠点 = 優先リソース)、「同一拠点を2倍重視する」(F = 2)、「同一拠点を半分の重要度とする」(F = 0.5)。

重要な等価関係は、**同一拠点の予約を1回分けること = 優先リソース違反1回分か?**です。ユーザーが両者を同程度に望ましくないと考える場合:weight_same_site = weight_preferred。同一拠点を優先リソースよりも係数Fだけ重要だと考える場合:weight_same_site = F × weight_preferred。

ASAPも優先リソースも選択されていない場合 — 移動時間との比較に切り替えます:

最後の手段としてのみ、移動時間との比較を使用します。

「同一拠点の予約を分ける価値があると感じるのは、移動時間が何分増える場合ですか?」

提示する選択肢(または、希望を説明してもらいます):「移動時間が10分増える」、「20分増える」、「30分増える」。

weight_same_site = T_equiv × (weight_travel / 120)

With weight_travel = 1000:
weight_same_site = T_equiv × 8.333

残業の最小化に関するトレードオフ

質問のテンプレート:

「ある予約を今すぐ入れられるものの、[Z]分の残業/延長時間が必要になる場合と、通常の営業時間内に今から[H]時間後に入れる場合、どの時点で両者が同程度に許容できると感じますか?」

例:「何かを12時間後ではなく今すぐ予約するためなら、30分の残業を受け入れますか?」

提示する選択肢(または、希望を説明してもらいます):「残業15分 ≈ 6時間の遅延」(残業を強く避ける)、「残業30分 ≈ 12時間の遅延」(中程度 — 上記の例)、「残業60分 ≈ 24時間の遅延」(すぐに対応するためなら残業を受け入れる)。

計算(ユーザーが残業Z分、遅延H時間と答えた場合):

overtime_penalty(Z) = asap_penalty(H × 60)

Z × (weight_overtime / 120) = (H × 60) × (weight_asap / 43200)

weight_overtime = weight_asap × H / (Z × 6)

(注:ASAPが選択されている場合は、先に weight_asap を計算する必要があります。ASAPが選択されていない場合は、残業と移動時間のトレードオフを尋ね、移動時間を基準に計算してください。)

残業と移動時間の比較(ASAPが選択されていない場合):

Z × (weight_overtime / 120) = T × (weight_travel / 120)

weight_overtime = T × weight_travel / Z

With weight_travel = 1000:
weight_overtime = T × 1000 / Z

Tは、ユーザーが残業Z分の代わりに選びたいと考える移動時間(分)です。


優先リソースのトレードオフ

質問テンプレート:

「予約に優先リソースが割り当てられている場合、その希望を尊重することはどの程度重要ですか?優先リソースは利用可能ですが、別のリソースと比べて移動時間が [X] 分余計にかかるとします。どの時点で『近いほうのリソースを使いましょう』と言いますか?」

提示する選択肢(または独自に説明してください):「15 分余計」(弱い優先度)、「30 分余計」(中程度)、「60 分余計」(指定リソースを強く優先)。

希望を覆す移動時間のしきい値 T_equiv(分)をユーザーに設定してもらいます。

preferred_resource_penalty = travel_penalty(T_equiv)

weight_preferred = T_equiv × (weight_travel / 120)

With weight_travel = 1000:
weight_preferred = T_equiv × 8.333

近隣グループ化のトレードオフ

説明:「近隣グループ化」と「移動時間の最小化」は、自然に競合する目標です。「近隣グループ化」の目標は、地理的に近い予約をまとめることです。ただし、あるグループ内の予約をすべてまとめておくと、グループを分けた場合よりもスケジュール全体の移動時間が長くなることがあります。トレードオフとなるのは、予約をグループ内にまとめておくために、ユーザーが許容するスケジュール全体の追加移動時間の上限です。グループを維持するための移動コストがそのしきい値を超える場合は、グループを分けて移動時間を節約するほうが適切です。

質問テンプレート:

「『近隣グループ化』の目標では、地理的に近い予約をまとめます。別のグループ分けのほうが効率的な場合、予約を同じグループにまとめておくことで、スケジュール全体の移動時間が長くなることがあります。

予約を同じグループにまとめておくために、スケジュール全体の追加移動時間を最大でどの程度許容できますか?グループの維持にそれ以上のコストがかかる場合、オプティマイザーはグループを分けて移動時間を節約するべきです。」

例:「グループを維持するために、スケジュール全体の移動時間が 10 分増えてもよいですか?20 分や 30 分ではどうですか?」

提示する選択肢(または独自に説明してください):「10 分余計」(グループ化の優先度は低い)、「20 分余計」(中程度)、「30 分余計」(グループ化を強く優先)。

グループを維持するために許容するスケジュール全体の最大追加移動時間 T_equiv をユーザーに設定してもらいます。この値を超える場合は、グループを分けるほうが望ましいです。

group_nearby_penalty = travel_penalty(T_equiv)

weight_group_nearby = T_equiv × (weight_travel / 120)

With weight_travel = 1000:
weight_group_nearby = T_equiv × 8.333

リソース優先度のトレードオフ

ユーザーと共有する背景情報:

「リソース優先度の目的では、サービスのリソースに0〜10の範囲で順位を付けられます。0は最優先(最適な候補)、10は最低優先度を意味します。たとえば、社内スタッフには優先度1、請負業者には優先度5以上を割り当てることができます。オプティマイザーは、リソースの優先度に比例したペナルティを適用します。優先度5のリソースには目的関数の重みの50%に相当するペナルティが発生しますが、優先度0のリソースにはペナルティがまったく発生しません。」

トレードオフに関する質問:優先度の低いリソースではなく優先度の高いリソースを使うために、移動時間がどれだけ余分にかかっても許容できますか?

質問のテンプレート:

「予約に利用できるリソースが2つあるとします。

  • リソースAはスタッフ技術者(優先度1)ですが、移動距離が**[X]分遠い**です
  • リソースBは請負業者(優先度[P])で、こちらのほうが近いです

どの時点で『請負業者を使えばよい』と判断しますか?つまり、優先度の高いリソースを使わずに済ませるために、移動時間が何分余分にかかると許容できますか?」

ユーザーの状況に合った具体的な優先度の値を使ってください。上の例ではスタッフ(優先度1)と請負業者(優先度5)を使っていますが、必要に応じて調整してください。

提示する選択肢(または独自の値を説明してください):「移動時間が30分余分」(優先度をやや重視)、「移動時間が60分余分」(優先度を中程度に重視)、「移動時間が90分余分」(優先度を強く重視。下の例を参照)。

優先度の低いリソースを使うことにする移動時間のしきい値 T_equiv(分)をユーザーに指定してもらいます。

**計算式:**2つのリソース間のペナルティ差は、切り替え点での移動ペナルティと等しくなります。

penalty_difference = resource_priority_penalty(P_low) - resource_priority_penalty(P_high)
                   = weight_resource_priority × ((P_low - P_high) / 10)

travel_penalty(T_equiv) = T_equiv × (weight_travel / 120)

Solve for weight_resource_priority:
weight_resource_priority = T_equiv × weight_travel / (12 × (P_low - P_high))

例(スタッフの優先度1、請負業者の優先度5、T_equiv = 90分):

weight_resource_priority = 90 × 1000 / (12 × (5 - 1))
                         = 90,000 / 48
                         = 1,875

スキルレベルのトレードオフ

ユーザーに共有する背景情報:

「スキルレベルの目標は、予約のスキル要件を満たすリソースのうち、最もスキルレベルが低いもの、または最も高いものを選ぶようオプティマイザーを誘導します。これは「最も低いスキルレベル」/「最も高いスキルレベル」の設定で構成できます。ペナルティは、リソースのスキルレベルの生の値に目標の重みを乗じて計算されます。スキルレベルが8のリソースには重みの8倍、スキルレベルが3のリソースには重みの3倍のペナルティが発生します。」

ステップ1 — どちらのモードを使うかユーザーに尋ねます:

「重みを算出する前に、どちらのモードを使うか確認する必要があります。

  • 最も低いスキルレベル — 要件を満たすリソースのうち、最もスキルレベルの低いリソースを優先します。複雑な作業に備えて上級リソースを確保したい場合や、可能な限り費用を抑えるために、より低コストの若手スタッフを活用したい場合に適しています。
  • 最も高いスキルレベル — 利用可能なリソースのうち、最もスキルレベルの高いリソースを優先します。初回修理率を最大化したい場合や、成果の品質を優先する場合に適しています。

どちらを使いますか?」

「最も低いスキルレベル」の場合 — 質問テンプレート:

「2つのリソースがどちらも予約の対象になり、最低スキル要件を満たしているとします。一方は若手技術者(スキルレベル [S_low])、もう一方は上級技術者(スキルレベル [S_high])です。上級技術者のほうが近くにいます。

「最も低いスキルレベル」モードでは、上級技術者をより難しい作業に備えて確保するため、オプティマイザーは若手技術者を優先します。近くにいる上級技術者ではなく、若手技術者を割り当てるために、移動時間が何分増えるまで許容できますか?」

「最も高いスキルレベル」の場合 — 質問テンプレート:

「2つのリソースがどちらも予約の対象になり、最低スキル要件を満たしているとします。一方は若手技術者(スキルレベル [S_low])、もう一方は上級技術者(スキルレベル [S_high])です。若手技術者のほうが近くにいます。

「最も高いスキルレベル」モードでは、初回修理の可能性を最大化するため、オプティマイザーは上級技術者を優先します。近くにいる若手技術者ではなく、上級技術者を割り当てるために、移動時間が何分増えるまで許容できますか?」

ユーザーの組織に合った具体的なスキルレベルの値を使ってください(以下の例では、若手に4、上級に8を使用します)。

T_equiv のプリセット候補(または独自の値を提示): 「移動時間が15分増加」(弱い選好)、「移動時間が30分増加」(中程度 — 以下の例に近い)、「移動時間が45分増加」(対象のスキルレベルを強く優先)。

ステップ2 — 2つのスキルレベルの値(S_low、S_high)と T_equiv を確認します — 優先するリソースを割り当てるために許容できる、移動時間の最大増加分です。

計算(どちらのモードでも同じ式) — 2つのリソース間のペナルティ差は、切り替え点における移動ペナルティと等しくなる必要があります。

penalty_difference = (S_high - S_low) × weight_skill_level

travel_penalty(T_equiv) = T_equiv × (weight_travel / 120)

Solve for weight_skill_level:
weight_skill_level = T_equiv × weight_travel / (120 × (S_high - S_low))

どちらのモードでも同じ式になる理由: ペナルティ差は、どちらの場合もオプティマイザーが移動時間の短縮によって埋めようとする差を表しており、優先する方向が逆になるだけです。「最も低いスキルレベル」モードでは、オプティマイザーはスキルレベルの低いリソースを選ぶために、最大 T_equiv 分の追加移動を許容します。「最も高いスキルレベル」モードでは、SFS がこの目標に限って内部的に優先順位を反転させるため、スキルレベルの高いリソースに到達するために、最大 T_equiv 分の追加移動を許容します。どちらの場合も、重みが差の大きさを制御します。

例 — 「最も低いスキルレベル」(若手のスキルレベル4、上級のスキルレベル8、T_equiv = 30分):

weight_skill_level = 30 × 1000 / (120 × (8 - 4)) = 30,000 / 480 = 62.5 → round up to 63

解釈:オプティマイザーは、近くにいるスキルレベル8の技術者を割り当てることに切り替えるまで、最大30分離れたスキルレベル4の技術者を割り当てます。

例 — 「最も高いスキルレベル」(同じスキルレベル、T_equiv = 45分):

weight_skill_level = 45 × 1000 / 480 = 93.75 → round up to 94

解釈:オプティマイザーは、近くにいるスキルレベル4の技術者を割り当てることに切り替えるまで、最大45分離れたスキルレベル8の技術者を割り当てます。


スキルの優先度とトレードオフ

ユーザーに共有する背景情報:

「スキルの優先度の目標は、作業指示に同じスキル種別の要件が複数あり、あるスキルが別のスキルより優先される場合に適用されます。各スキル要件には、1〜10 のスキル優先度が設定されます。1 が最も優先度が高く、10 が最も低くなります。オプティマイザーは、その優先度に比例したペナルティを割り当てます。優先度が 1 のスキルには目標の重み全体の 10% のペナルティが課され、優先度が 10 のスキルには重み全体が課されます。リソースがより優先度の高いスキルを持つ場合、ペナルティが小さくなるため、オプティマイザーで優先されます。」

トレードオフについての質問:優先度の低いスキルを持つリソースではなく、優先度の高いスキルを持つリソースを割り当てるために、移動時間がどれだけ長くなっても許容できますか?

質問のテンプレート:

「顧客への対応を、**[Skill A]を話す技術者(スキル優先度 [SP_high_pref])でも、[Skill B]**を話す技術者(スキル優先度 [SP_low_pref])でも行えるものの、顧客が[Skill A]を希望している作業指示を想像してください。[Skill A]を話す技術者のほうが遠くにいます。

[Skill B]を話す技術者ではなく、[Skill A]を話す技術者を割り当てるために、移動時間が何分長くなっても許容できますか?」

ユーザーのシナリオに合った具体的なスキル名と優先度を使用してください。以下の例では、Language スキル種別内で、スペイン語(優先度 1)と英語(優先度 6)を使用しています。

また、この目標が機能するために必要なスキル種別を確認します。 スキルの優先度は1 つのスキル種別、つまり、付随するスキルの照合作業ルールが「少なくとも 1 つのスキルが一致(OR)」で評価するスキルのグループ(例:Language)を対象とします。ユーザーに次のように尋ねてください。

「この優先度で順位付けするスキル種別はどれですか?その開発者名を教えてください。スキルの優先度は AND 照合では効果がないため、「少なくとも 1 つのスキルが一致(OR)」に設定されたスキルの照合作業ルールで使用するものと同じスキル種別である必要があります。」

回答は skillType パラメーターとして記録します(スキル種別の開発者名をそのまま渡します)。前提条件として、そのポリシーに該当するスキル種別の OR ロジックで設定されたスキルの照合ルールも含める必要があることを記載してください。ユーザーがまだ定義していない場合は、その旨を明示してください。(データレイヤーのスキルは、目標の skillType を {ns}Skill_Type__c にマッピングします。)

T_equiv に提示するプリセット(または独自の値を説明してください):「移動時間が 15 分長くても許容」(弱い優先)、「30 分長くても許容」(中程度)、「45 分長くても許容」(強い優先。以下の例)。

ユーザーには、比較する 2 つのスキル優先度の値(SP_high_pref = より優先度の高いスキル、SP_low_pref = より優先度の低いスキル)、より優先度の高いスキルを持つリソースを割り当てる場合に許容する移動時間の最大追加分 T_equiv、および skillType を尋ねてください。

計算式: 交差点で、2 つのスキル選択肢のペナルティの差は、移動ペナルティと等しくなる必要があります。

penalty_difference = weight_skill_preference × ((SP_low_pref - SP_high_pref) / 10)

travel_penalty(T_equiv) = T_equiv × (weight_travel / 120)

Solve for weight_skill_preference:
weight_skill_preference = T_equiv × weight_travel / (12 × (SP_low_pref - SP_high_pref))

例(スペイン語の優先度 1、英語の優先度 6、T_equiv = 45 分):

weight_skill_preference = 45 × 1000 / (12 × (6 - 1)) = 45,000 / 60 = 750

解釈:オプティマイザーは、英語を話す技術者に切り替える前に、スペイン語を話す技術者が最大 45 分余計に移動する経路を選択します。


ギャップ最小化のトレードオフ

ユーザーに共有する背景情報:

「ギャップ最小化の目的は、リソースの勤務シフト内で連続する予約の間の待機時間を短縮することです。技術者の作業をまとめて、訪問の合間に待機しなくて済むようにします。これはソフトな目的です。オプティマイザーはギャップの圧縮を試みますが、それ以外は良好なスケジュールを却下することはありません。最小ギャップ時間(30分~24時間)を設定できます。このしきい値を超えるギャップのみが違反としてカウントされます。」

最小ギャップ時間はステップ1ですでに取得済みです(params.minGapMinutes。指定がない場合のデフォルトは30で、{ns}Gap_Duration__cにマッピングされます)。会話の途中でユーザーが目的の選択を変更した場合を除き、ここで再度質問しないでください。このセクションでは重みの計算のみを扱います。重みはオプティマイザーがギャップを埋めるためにどれだけ強く取り組むかを設定し、minGapMinutesは何をギャップとして数えるかを設定します。

**自然なトレードオフは、ギャップ最小化とASAPの間にあります。**予約を後の時間にすることになっても、スケジュールを圧縮(ギャップを減らす)しますか?

ASAPが選択されている場合 — ギャップ最小化とASAPを比較します(推奨):

質問テンプレート:

「技術者のスケジュールにギャップがあるとします。オプティマイザーには次のいずれかを選択できます。 (A) ギャップを残し、新しい予約を**[H]時間早くスケジュールする(別のリソースまたは時間枠に割り当てる)。 (B) ギャップをなくすようにスケジュールを圧縮する。ただし、その予約は可能な時間よりも[H]時間遅く**スケジュールされます。

どれくらいの遅延までなら、『ギャップはそのままにして、早い時間にスケジュールする』と判断しますか?」

例:「予約が4時間遅くなるとしても、ギャップを最小限に保ちますか?2時間ならどうですか?8時間ならどうですか?」

提示するプリセットの選択肢(または独自の選択肢を説明します):「最大2時間遅くなるならギャップを残す」(ギャップ最小化の優先度は低い)、「最大4時間遅くなるならギャップを残す」(中程度。上記の例)、「最大8時間遅くなるならギャップを残す」(ギャップ圧縮を強く優先)。

ユーザーに、ギャップを残してより早くスケジュールすることを許容する遅延しきい値D_equiv(分)を入力してもらいます。

計算:

gaps_penalty(1 gap) = asap_penalty(D_equiv)

Using the continuous approximation:
1000 × weight_gaps = D_equiv × (1000 × weight_asap / 43200)

Solve for weight_gaps:
weight_gaps = D_equiv × weight_asap / 43200

注:weight_asap = 250、D_equiv = 240(4時間)の場合:weight_gaps = 240 × 250 / 43200 = 1.389 → ceil to 2。

ASAPが選択されていない場合 — 移動時間との比較に切り替えます:

質問テンプレート:

「ギャップを圧縮するのではなく、技術者のシフトにギャップを残す価値があると判断するのは、スケジュール全体で移動時間が何分増える場合ですか?」

提示するプリセットの選択肢(または独自の選択肢を説明します):「移動時間が10分増える」、「20分増える」、「30分増える」。

ユーザーに、ギャップを1つなくすために許容するスケジュール全体の移動時間T_equiv(分)を入力してもらいます。

gaps_penalty(1 gap) = travel_penalty(T_equiv)

1000 × weight_gaps = T_equiv × (1000 × weight_travel / 120) × (1/60)

Solve for weight_gaps:
weight_gaps = T_equiv × weight_travel / 7200

With weight_travel = 1000:
weight_gaps = T_equiv / 7.2

**整数式の検証(導出後に確認):**SFSが実際に使用する整数のレートpenaltyPerViolation = max( 1, round( 1000 × weight_gaps ) )を確認します。導出した重みが非常に小さい場合(約1未満)、重みの値に関係なくmax(1,…)の下限によってペナルティが固定の最小効果に制限されることをユーザーに警告してください。

ステップ 3:結果を出力する

すべてのトレードオフに関する質問に回答したら、見やすい要約表を提示します。

| Service Objective     | Weight | Rationale                                                           |
|-----------------------|--------|---------------------------------------------------------------------|
| Minimize Travel       | 1000   | Anchor (fixed)                                                      |
| ASAP                  | <weight> | [X min travel ≈ Y hours delay]                                      |
| Same Site             | <weight> | [1 split ≈ D min of ASAP delay]                                     |
| Minimize Overtime     | <weight> | [Z min OT ≈ H hours delay / T min travel]                           |
| Preferred Resource    | <weight> | [1 override ≈ T min travel]                                         |
| Group Nearby          | <weight> | [1 cluster break ≈ T min extra travel]                              |
| Resource Priority     | <weight> | [priority P_low vs P_high crossover ≈ T min extra travel]           |
| Skill Level           | <weight> | [skill S_low vs S_high crossover ≈ T min extra travel]              |
| Skill Preference      | <weight> | [skill priority SP_high_pref vs SP_low_pref ≈ T min extra travel]   |
| Minimize Gaps         | <weight> | [1 gap ≈ D min of ASAP delay / T min extra travel]                  |

続いて、優先度の解釈を、単位あたりのペナルティポイントを使って示します(上記の全体ルールに従い、生の重みの値は使いません。各目的の尺度が異なるため、生の重みは目的間で比較できません)。選択した各目的のペナルティ率を計算して示してください。

Travel:              penalty per minute             = weight_travel / 120
ASAP:                penalty per minute of delay    = weight_asap / 43200
Overtime:            penalty per minute             = weight_overtime / 120
Same Site:           penalty per event              = weight_same_site × (1 / 1)
Preferred Res.:      penalty per event              = weight_preferred × (1 / 1)
Group Nearby:        penalty per event              = weight_group_nearby × (1 / 1)
Resource Priority:   penalty per resource           = weight_resource_priority × (P / 10)
                     (show for each relevant priority tier, e.g. P=1 and P=5)
Skill Level:         penalty per resource           = S × weight_skill_level
                     (show for each relevant skill level tier, e.g. S=4 and S=8)
Skill Preference:    penalty per skill assignment   = weight_skill_preference × (SP / 10)
                     (show for each relevant skill priority, e.g. SP=1 and SP=6)
Minimize Gaps:       penalty per gap                = weight_gaps × 1 (flat per-gap)

これらの率を使って、相対的な優先度を平易な言葉で説明します。例:

「移動には1分あたり[X]ペナルティポイントが加算され、ASAPでは遅延1分あたり[Y]ペナルティポイントが加算されます。つまり、オプティマイザーは移動1分をスケジュールの遅延1分の[X/Y]倍のコストとして扱います。したがって、ASAPの重みの数値のほうが大きくても、1分あたりでは移動のほうが実際に強い要因です。」

「優先度5の契約技術者には[Z]ペナルティポイントが発生します。これは移動時間[T]分の増加に相当します。そのため、スタッフ技術者の経路で移動時間が[T]分を超えて増える場合に限り、オプティマイザーは契約技術者を使用します。」

「スキルレベル8の上級技術者には、初級技術者の[Y]に対して[Z]ペナルティポイントが発生します。その差は[Z-Y]ポイントで、移動時間[T]分の増加に相当します。そのため、最小資格モードでは、近くにいる上級技術者に切り替える前に、オプティマイザーは初級技術者の移動時間を最大[T]分増やします。」

「優先スキルを持つリソース(優先度1)には、あまり優先されないスキルを持つリソース(優先度6)の[Z]に対して[Y]ペナルティポイントが発生します。その差は[Z-Y]ポイントで、移動時間[T]分の増加に相当します。そのため、優先スキルを持つリソースを割り当てる際、優先度の低いリソースを割り当てる前に、オプティマイザーは移動時間を最大[T]分増やします。」

「技術者のシフトにアイドル時間が1つ生じるごとに[Z]ペナルティポイントが発生します。これはASAPの遅延[D]分に相当します。そのため、予約を[D]分(またはそれ以上)先にずらすことになる場合に限り、オプティマイザーはスケジュールに空き時間を残します。これは、[D]分早く予約できるのであれば空き時間を許容するという、お客様の意向を反映しています。」

ポリシーの概要(ビジネス向け要約、2~3文)

重みとペナルティ率を示した後、このスケジューリングポリシーが最適化する対象をまとめた、経営層向けの2~3文の概要を作成してください。数式に関心のない関係者に向けた、簡潔な説明です。

生成方法:

フェーズ 9:**目的をカテゴリ別に分類する

顧客体験(ASAP、同じ場所、近くでまとめる)、コスト/効率(移動の最小化、空き時間の最小化、残業の最小化)、人員配置/割り当ての品質(優先リソース、リソースの優先度、スキルレベル、スキルの優先度)。

フェーズ 10:どのカテゴリが優勢かを判断するには、イベントごとの値を比較します…
フェーズ 11:重要なトレードオフの緊張関係を特定するには、クロスオーバーから判断します…
フェーズ 12:2~3 文で、主要な重点項目を挙げてください……

要約の例:

主要項目:要員の割り当て品質/副次項目:コスト効率: 「このポリシーは主に要員の割り当て品質を重視しており、オプティマイザーは最大 2 時間遠回りしてでも希望する技術者を割り当て、継続性と適切な担当者の選定を優先します。副次的な重点項目はコスト効率です。同等の資格を持つ候補者の中では移動時間を積極的に最小化し、余分な運転を 30 分減らすために、スケジュールを最大 24 時間遅らせます。」

主要項目:コスト効率/副次項目:顧客体験: 「このポリシーは主にコスト/効率を重視しており、短いルートと残業なしを最優先し、大幅な運転時間の削減につながる場合は、希望するリソースを外すこともあります。副次的な重点項目は顧客体験です。移動コストが同程度の場合は、予約を早め、顧客を待たせるのではなく、同じ場所への訪問をまとめます。」

主要項目:顧客体験/副次項目:要員の割り当て品質: 「このポリシーは主に顧客体験を重視しており、オプティマイザーは移動時間が長くなっても、予約を可能な限り早い時間に積極的に設定し、同じ場所への訪問をまとめます。副次的な重点項目は要員の割り当て品質です。時間が同じ場合は、希望する技術者や優先度の高いリソースを一般的な候補者より優先します。」

バランス型: 「このポリシーは 3 つの観点すべてでバランスが取れており、特定のカテゴリが優先されることはありません。わずかなスケジュール上の利点のために希望する技術者を外すことはなく、ルート効率を保つためにある程度の遅れを許容し、同じ場所への訪問のグループ化は、あくまで軽いタイブレーク条件として扱います。オプティマイザーは、1 つの目標を何としても最適化するのではなく、現実的なトレードオフを行います。」

ステップ 3 の出力の最後に、**「ポリシーの概要」**というラベルを付けて要約を示してください。


ビルド仕様の出力(データレイヤースキルへの引き継ぎ)

これは、このスキルの最終成果物であり、sfs-sobject-create スキルとの唯一の契約です。設計が確定したら、つまりポリシー設定、作業ルール、関連性グループ、導出した重みを決定したら、JSON オブジェクトとして構造化されたビルド仕様を 1 つ出力してください。sfs-sobject-create はこの内容をそのまま使用してレコードを作成し、意図を再導出することはありません。そのため、仕様は完全で自己完結している必要があります。

**厳格な境界:**このスキルが出力するのは設計値のみです。Salesforce のオブジェクト名、フィールド API 名、レコード ID、コンポジット API の参照構文、作成順序を含めてはいけません。これらはすべてデータレイヤースキルの担当範囲です。ルールと目的は、正規のタイプ名(このスキルで使用されているとおりの正確な名前。例:Match Skills、Minimize Travel、Resource Priority)と、その業務パラメーターで記述してください。パラメーターが不明な場合は、省略するか null と記載してください。API フィールド名を推測してはいけません。

ビルド仕様のスキーマ
{
  "specVersion": "1.0",
  "policy": {
    "name": "string — the scheduling policy name",
    "description": "string — free text; include the Policy at a Glance summary",
    "inDayOptimization": true,
    "commitMode": "Always Commit | Rollback"
  },
  "workRules": [
    {
      "type": "canonical work rule type name (e.g. 'Match Skills')",
      "name": "instance display name — see the naming convention below (e.g. 'Match Skills - First test')",
      "mandatory": false,
      "params": {
        "// business parameters for this rule — see the per-type param conventions below": "",
        "// example (Service Resource Availability, absolute break)": "breaks: [{ mode: 'absolute', startClock: '12:00', durationMinutes: 30 }]",
        "// example (Service Resource Availability, offset break)": "breaks: [{ mode: 'offset', earliestStartOffsetMinutes: 180, latestEndOffsetMinutes: 210, durationMinutes: 30 }]",
        "// example (Maximum Travel from Home)": "maxTravelFromHome: 45, maxTravelFromHomeType: 'Travel Time'"
      },
      "relevanceGroup": {
        "basis": "Service Appointment | Service Territory Member | null",
        "booleanField": "the name of the scoping Boolean field, or null for policy-wide"
      }
    }
  ],
  "serviceObjectives": [
    {
      "type": "canonical objective type name (e.g. 'Minimize Travel')",
      "name": "instance display name — see the naming convention below (e.g. 'Minimize Travel - First test')",
      "weight": 1000,
      "params": {
        "// objective-specific business parameters": "e.g. mode: 'Least Qualified' for Skill Level; priorityField concept for Resource Priority; minGapMinutes for Minimize Gaps; skillType for Skill Preference; excludeTravelFromHome/excludeTravelToHome for Minimize Travel; useExactLocation for Same Site"
      },
      "relevanceGroup": {
        "basis": "Service Appointment | Service Territory Member | null",
        "booleanField": "the name of the scoping Boolean field, or null for policy-wide"
      },
      "rationale": "one-line human trace of how the weight was derived (the stated crossover)"
    }
  ],
  "prerequisites": [
    "human-readable notes the user must satisfy before/around deployment — e.g. 'Create Boolean field Break_Group_France__c on Service Territory Member and set it true for French resources.'"
  ],
  "notes": "any caveats, low-weight-floor warnings, or unresolved ambiguities"
}
仕様を出力する際のルール
  • name の命名規則(すべての作業ルールとサービス目標に適用): name は、ルール/目標の正規の種類名(type と同じ文字列)に、短縮したスケジューリングポリシー名を「 - 」で区切って付けたものにします。例:ポリシーが「First test on shorter」の場合は "Service Resource Availability - First test"、"Match Skills - First test"、"Minimize Travel - First test" とします。ポリシー名は、簡潔で分かりやすい形に短縮してください(「on shorter」や「policy」などの補助的な語句は省きます)。例外: 2つの Arrival Window Match Time ルールには、すでに説明的な名前(Arrival Window Start、Arrival Window End)が付いています。これらは type の名前に置き換えず、同じように短縮したポリシー名を付けます。例:"Arrival Window Start - First test"、"Arrival Window End - First test"。
  • Minimize Travel を必ずウェイト 1000 で含め、"mandatory": true を設定した Service Resource Availability 作業ルールを必ず1つだけ含めてください。 ユーザーがそれらについて一切話していない場合も同様です。Earliest Start Permitted や Due Date は絶対に出力しないでください(Mandatory rules を参照)。
  • サブセットごとに1つのインスタンスを出力します。 ルール/目標が関連性グループにまたがって分割されている場合は、それぞれに個別の relevanceGroup を設定し、配列エントリを分けて出力してください。
  • ウェイトは整数とし、Conversation Flow のglobal rulesに従って、すでに切り上げた値を使います。
  • パラメーターは業務上の値であり、フィールドマッピングではありません。 「12:00~15:00の間に30分の休憩を取る」は、休憩レコードのフィールド構成に変換せず、時間と時刻として params に入れてください。このマッピングはデータレイヤースキルで行います。
  • 前提条件を明示します。 設計で必要となる関連性グループの Boolean フィールド、カスタム優先度フィールド、またはスキルタイプへの依存関係は、ユーザー(およびデータレイヤースキル)が事前に用意する必要があることが分かるよう、prerequisites に含めてください。
  • 何も作成しないでください。 仕様を出力した後、レコード作成のために sfs-sobject-create に渡せる状態であることをユーザーに伝えてください。「構築する」「デプロイする」「作成する」よう求められた場合も、あなたの役割は仕様を完成させることであり、CRUD を実行することではありません。

目標ごとの平易な業務上の意図

各目標が業務上どのような働きをするのか、計算の説明に入る前にユーザーが理解できるよう、以下の説明を使用してください。

顧客体験に関する目標

同じ場所(同じロケーションへの訪問) — 「2つの作業が同じ場所にある場合は、続けて実施します。」複数の予約が同じ物理的な場所(同じ建物、同じ住所)にある場合、技術者がその場を離れて戻ることがないよう、1人の技術者のルート上で連続して訪問できるようにします。再訪による無駄を減らし、現地での作業効率を高めます。

近隣の予約をまとめる(同一ロケーションの範囲) — 「地理的に近い作業をまとめます。」同じ場所のより広範なバージョンで、完全に同じ場所であることを求めず、動的に計算された半径内にある予約を同じルートにまとめます。担当区域内を行き来する無駄を減らし、次の場所へ移動する前に、技術者が近隣エリアの作業に集中できるようにします。

コスト/効率性の目標

移動の最小化 — 「移動を減らす」。ルート全体の移動時間を短縮します。ジョブ間の移動が減り、距離も短くなります。燃料費、車両の摩耗、フロントガラスを眺めているだけの非生産的な時間を直接削減します。最初の移動区間(自宅 → 最初のジョブ)と最後の移動区間(最後のジョブ → 自宅)を含めるか除外するかを設定できます。

空き時間の最小化 — 「技術者が継続して稼働できるようにする」。技術者の勤務日における待機時間をペナルティとして扱い、ジョブ間に空き時間がない連続した作業スケジュールに近づけます。注意点として、待機時間の長さではなく、1 シフトあたりの別々の作業クラスターの数(設定した最小値を超える空き時間で区切られたもの)をカウントします。そのため、業務上の意図は「技術者の勤務日をつながりのない時間に分断しない」であり、「わずかな空き時間もすべて削る」ではありません。

時間外労働 — 「時間外労働の支払いを避ける」。技術者が通常のシフト時間を超えるスケジュールを避け、人件費を抑え、通常の勤務時間の制限を守ります。

人員配置/割り当て品質の目標

スキルレベル — 「ジョブに適したレベルの専門知識を割り当てる」。リソースの資格レベルに基づいて割り当てを調整します。能力のある技術者のうち最も資格レベルが低い人を優先する(簡単なジョブに熟練者を使わず、必要なときのために確保する)か、最も資格レベルが高い人を優先する(最も優秀な人材を割り当てる)かを設定できます。定義済みのスキルタイプを基準に動作します。

スキルの優先度(属性ランキング) — 「患者/顧客の希望など、優先度ランキングを尊重する」。スキルの合否判定にとどまらず、優先度属性に基づいてリソースを順位付けし、より上位の候補を優先します。コードにおける代表的な用途は、在宅医療における患者の希望です。患者が希望する介護者を、単に資格要件を満たすだけの候補より上位に順位付けします。

優先リソース(必須リソース) — 「可能な場合は、優先または指定された技術者を使う」。ジョブの優先リソースとして指定されたリソースの割り当てを優先します(例:機器を設置した技術者や、取引先が通常利用している担当者)。これにより、継続性と顧客との信頼関係を高めます。

リソースの優先度 — 「優先度の高いリソースを優先する」。優先度の高いリソースに作業が割り当てられるよう調整します(例:請負業者より社内スタッフを優先する、または評価の高い担当者を先に割り当てる)。


ドキュメントの背景情報:目標ごとの動作と設定

このメモでは、各目標がそのように動作する理由を説明するため、Salesforce のドキュメントに基づく動作と設定に関する背景情報を補足します。これは計算方法の参考資料ではありません。権威あるペナルティ計算式は上記の 目標別ペナルティ計算式 に記載されており、このスキルの冒頭にある 信頼できる情報源 に従い、常にそちらが優先されます。

一般的なスコアリングの仕組み(ドキュメントより): オプティマイザーは、ポリシーの目標を使用して、候補となるすべてのスケジュールを評価します。各目標の重みによって、最終スコアに対する影響の大きさが決まります。各目標には最良/最悪のケースを定義する尺度があります(たとえば、「移動の最小化」の尺度は 120 分なので、移動時間が 1 時間の場合、尺度の 50% になります)。評価値 × 重みがその目標のスコアへの寄与分となり、すべての目標の寄与分を合計したものがスケジュールのスコアになります。Enhanced Scheduling and Optimization (ESO) はペナルティでスコアを算出し、合計ペナルティが最小のものが最適です。従来の(ESO 以外の)エンジンは報酬/ボーナスでスコアを算出し、合計が最大のものが最適です。使用する目標は同じですが、評価の方向が逆になります。このスキルの計算式は、ESO のペナルティの動作をモデル化しています。

  • ASAP —「最も早い時刻」とは、現在時刻、予定の最早開始可能時刻、到着時間帯の開始時刻、または最適化期間の開始時刻のうち、最も遅い時刻を正確に指します。遅延は、この基準時刻から30日スケールで測定されます。ドキュメントでは、同点の場合により早いスケジュールが選ばれるよう、すべてのポリシーに重み1以上でASAPを含めることを推奨しています。
  • Minimize Travel — 追加の移動をスコア化します。絶対的な移動量ではなく、旧スケジュールと新スケジュールの総移動量の差です。ホームベースへの移動区間を除外するよう設定できます(ESOのExclude Travel To/From Home Base)。容量ベースのリソースは移動がゼロとみなされます(最良の移動評価)。代わりにリソース優先度を使用して誘導してください。
  • Minimize Overtime — 追加の残業をスコア化します(前のスケジュールとの差)。残業とは、通常の枠/シフトの直後に続く延長枠/シフト内の時間です。通常時間の前または間の延長時間は対象になりません。延長時間内に含まれる移動時間と休憩時間も含みます。
  • Same Site — Use Exact Locationでグループ化の精度を設定できます。デフォルトでは、移動時間が最大1秒以内の予定をグループ化します。これをオンにすると、緯度/経度が完全一致する必要があります(接続道路のないキャンパスや農場向け)。ESOのみ対応。関連グループには対応していません。厳密に同じ移動を保証するAppointment Bundlingとは異なり、Same Siteはソフトな優先設定です。
  • Group Nearby Appointments — 近接性に基づいて予定を動的にクラスタリングし、1人の作業者がその日のエリアを「担当」するようにします。Minimize Travelと直接競合するため、両者のバランスを取る必要があります。ESOのみ対応。関連グループには対応していません。
  • Minimize Gaps — 最小ギャップ時間(会社が定義するギャップ)を設定する必要があります。移動時間はアイドル時間に含まれないため、作業間の実際のギャップが25分で、移動に35分かかる場合、最小値が30分ならギャップは最小化されません。ESOのみ対応。スケジュール/一括スケジュール/当日/グローバル最適化のみ対応(Book Appointment、Get Candidates、ドラッグ&ドロップ、RSOには非対応)。複数日にまたがる予定および複数の柔軟な休憩には対応していません。
  • Preferred Resource —「優先」とは、アカウント、資産、場所、作業指示、またはWOLIに対して、タイプがPreferredのリソース優先設定を指します。二択(優先かどうかで、段階はありません)。複数のリソースが優先として登録されている場合、すべて同等に優先されます。
  • Resource Priority — 標準のPriority項目またはカスタムの数値項目で優先度を設定し、目的のResource Priority Fieldでその項目を指定します。規則:値が小さいほど業務上の優先度が高い、0 = 最適な候補、null = 最低の優先度/優先度が最も低い、負の値 = nullとして扱われる。ドキュメントでは、1~10の整数スケールを推奨しています。関連グループに対応しており、各インスタンスが異なる優先度項目を参照する場合は、同じ予定に対して2回使用できます。
  • Skill Level — Prioritize Resource選択リストで、Least Qualified(必要十分なスキルのうち最も低いスキルを優先し、熟練者を温存する)またはMost Qualified(最も高いスキルを優先し、初回訪問での修理を目指す)を選択します。予定に複数のスキル要件がある場合、目的はそれらの平均を取ります。予定にスキル要件がない場合、この目的は効果がありません。機能させるためにMatch Skills作業ルールは必要ありません。
  • Skill Preference — Skill Priorityの範囲は1~10です(1 = 最も優先、10 = 最も優先度が低い)。範囲外の値は1または10に丸められ、空欄の場合は10がデフォルトになります。同じSkill Typeに属するスキルに対して、Match Skillsの**「At Least One Skill Matches (OR)」**ロジックを評価する場合にのみ関連します。ANDマッチングでは効果がありません。

ライブドキュメントの参照先

このスキルの設計上、ここには概要のみを保存しています。時間の経過とともに変わる可能性のある最新の動作や概念の詳細が必要な場合は、Salesforce のライブドキュメントを取得してください。いずれも help.salesforce.com の記事 ID です(https://help.salesforce.com/s/articleView?id=<ID>&type=5 として開きます)。このスキルの計算をドキュメントで上書きしないでください。ここに記載された計算が正となります。注:レコードレベルの項目 API 名、複合 API の構造、作成手順はこのスキルの対象外であり、データレイヤースキルに記載されています。これらのドキュメントは、レコードの作成方法ではなく、意図と動作を確認するために取得してください。

ポリシーの概念

  • service.pfs_scheduling.htm — スケジューリングポリシーの概念と設定(Name、Description、In-Day Optimization、Commit Mode)、標準ポリシー、ポリシーの適用方法。
  • service.pfs_scheduling_policy_guidelines.htm — 要件から作業ルールへのマッピング、目的の順位付けや重み付けに関する指針など、ポリシー設計のガイドライン。

作業ルール

  • service.pfs_optimization_theory_work_rules.htm — 作業ルールの概要、DB と Apex のパフォーマンスに関する考慮事項、16 種類すべてのルールタイプのサブページ一覧。個別のサブページ(ルールのスラッグを末尾に追加)には、各ルールの動作や注意点が記載されています。例:..._match_fields、..._match_skills、..._match_time、..._service_resource_availability、..._capacity_limit(作業容量)、..._count、..._match_boolean、..._required_resources。

関連性グループ

  • service.pfs_relevance_groups.htm — 関連性グループの概要、ブール型項目の仕組み、具体例、相互排他ルール、ならびにルールおよび目的ごとの完全な対応マトリックス(ESO とレガシーで許可される基準)。

サービス目的(背景情報のみ。計算はこちらが正となります)

  • service.pfs_optimization_theory_service_objectives.htm — 目的の概要と、スケール/重み/ペナルティ対報酬によるスコアリングの仕組み。目的ごとのサブページ(スラッグを末尾に追加)には、動作や設定に関する背景情報が記載されています。..._asap、..._min_travel、..._min_overtime、..._pref_resource、..._resource_priority、..._skill_level、..._skill_preference、..._same_site、..._group_nearby、..._min_gaps、および具体例の ..._asap_example(ASAP と関連性グループ)。

トーンとスタイルのガイドライン

全体的な丸め処理と優先順位の比較ルールは、会話の流れの冒頭に記載されており、ここでは繰り返しません。その他の会話のトーンについては、以下のとおりです。

  • 会話調で、具体的にしてください。抽象的な数式ではなく、例では具体的な数値を使ってください。
  • 回答の後には、必ず計算を示してください。ユーザーの信頼につながり、自分で確認できるようになります。
  • ユーザーが迷っている様子であれば、生の重みではなくペナルティ率を基準に比較できるよう提案してください。
  • 選択されていない目的は会話に含めないでください。ユーザーに尋ねることも避けてください。

───── ランタイムコンテキスト(Headless 360/エージェント型): このスキルが Headless 360/エージェント型コンテキストで実行される場合、可能であれば CLI ツール(sf project deploy、sfdx、シェルコマンド)よりも、プラットフォームのディスパッチツール(ホスト型 Headless 360 MCP の dispatch、ローカル開発用 MCP の dispatch)を優先してください。利用可能な操作は以下の steps: に記載されており、ライブ組織で検証済みです。正規パスに対してディスパッチツールを呼び出してください。CLI によるフォールバックは、該当機能に API パスが存在しない場合に限り許容されます。

重要な制約事項

前提条件:

  • FSL マネージドパッケージ(FSLMPPERF 名前空間)がインストールされているか、組織で Enhanced Scheduling & Optimization(ESO)が有効になっており、InBusinessSchedulingUiApiFamily 機能ゲートがオンになっていること。(確認方法:Global describe: expect Scheduling_Policy__c with FSL namespace prefix. Resolve namespace via Tooling API query: GET /services/data/v67.0/tooling/query?q=SELECT SubscriberPackage.NamespacePrefix FROM InstalledSubscriberPackage. Filter client-side for NamespacePrefix starting with FSL (matches FSL, FSLQA, FSLMPTEST, FSLMPPERF). Use discovered prefix (e.g., FSL__Scheduling_Policy__c).)
  • 実行ユーザーに、Scheduling Policy レコードの読み取り権限を付与する権限セットが割り当てられていること(マネージドパッケージを使用する組織では FSL Admin 権限セット、ESO を使用する組織では SchedulingPolicy の読み取り権限など)。(確認方法:Attempt the list-starter-scheduling-policies step; a permission failure returns INSUFFICIENT_ACCESS_OR_READONLY.)
  • ビルド仕様で参照されるスコープ付きの作業ルール/目標について、スキル本文の「関連性グループ」で示すとおり、呼び出し元が Service Appointment および/または Service Territory Member に必要なカスタム Boolean フィールドが存在することを確認する必要があります。このスキルはビルド仕様の prerequisites[] 配列に要件を記録します。sfs-sobject-create はレコードを検証して作成しますが、カスタムフィールドは作成しません。(確認方法:Populate build-spec prerequisites[] with any Boolean field names referenced by relevanceGroup entries; admin creates DDL out-of-band.)

運用ルール:

  • ルールリストから始めず、ビジネス上のニーズから始めて、各要件を満たすルールに対応付けます。この対応付けをユーザーと一緒に進めます。
  • Service Resource Availability ルールを必ずちょうど 1 つ含めます。これは、このスキルが出力する唯一の必須ルールです(上記の「必須ルール」を参照)。Earliest Start Permitted または Due Date は出力しないでください。これらは自動的にプロビジョニングされます。
  • 解釈は、重みの数値だけではなく、ユーザーが述べた元のトレードオフに必ず基づかせます。たとえば、「これは、移動時間 30 分が遅延 12 時間に相当するという、あなたのご希望を反映しています」のように説明します。推定される優先順位がユーザーの期待と一致しない場合は、トレードオフに関する質問を再度確認することを提案します。
  • ASAP(迅速性) — 「お客様にできるだけ早く対応します」。すべての予約を時間枠内で最も早い、実施可能な開始時刻に近づけ、お客様の待ち時間を最小限にします。遅延 1 分ごとにペナルティが増えるため、予約が遅くなるほどコストも増加します。

オペレーションリファレンス

オペレーションを目的別に分類しています。上記のワークフローを構成する要素として使用してください。

概要
オペレーション目的ステータス呼び出し依存関係
list-starter-scheduling-policies読み取り—GET /services/data/vXX.0/query—
read-starter-policy-work-rules読み取り—GET /services/data/vXX.0/querylist-starter-scheduling-policies
read-starter-policy-objectives読み取り—GET /services/data/vXX.0/querylist-starter-scheduling-policies
emit-build-spec書き込み——list-starter-scheduling-policies
resolve-fsl-namespace検証—GET /services/data/v67.0/tooling/query—
依存関係グラフ
graph TD
  list_starter_scheduling_policies["list-starter-scheduling-policies (read)"]
  read_starter_policy_work_rules["read-starter-policy-work-rules (read)"]
  read_starter_policy_objectives["read-starter-policy-objectives (read)"]
  emit_build_spec["emit-build-spec (write)"]
  resolve_fsl_namespace["resolve-fsl-namespace (validate)"]
  list_starter_scheduling_policies --> read_starter_policy_work_rules
  list_starter_scheduling_policies --> read_starter_policy_objectives
  list_starter_scheduling_policies --> emit_build_spec
読み取りオペレーション
list-starter-scheduling-policies

提供されているスターターのスケジューリングポリシー(Customer First、High Intensity、Soft Boundaries、Emergency)と、組織内にすでに存在するカスタムポリシーを読み取ります。トレードオフのヒアリングのステップ 1 の前に使用し、管理者がモデルにするスターターを選べるようにします。

呼び出し: GET /services/data/vXX.0/query

入力:

  • q (string) — SOQL クエリ。管理パッケージの組織:SELECT Id, Name, FSLMPPERF__Description__c FROM FSLMPPERF__Scheduling_Policy__c ORDER BY Name。 ESO が有効な組織:SELECT Id, MasterLabel, DeveloperName, SchedulingCategory FROM SchedulingPolicy ORDER BY MasterLabel。まず FSL を試し、INVALID_TYPE の場合はネイティブにフォールバックします。

出力: REST クエリの応答です。records[] には、各ポリシーの Id と表示名、説明が含まれます。提供されている 4 つのスターターの表示名は、スキル本文に記載されたとおりです。

read-starter-policy-work-rules

選択したスターターポリシーに関連付けられた作業ルールのエントリを読み取り、カスタマイズする前に、テンプレートに含まれるルールの種類とパラメーターを管理者が確認できるようにします。

呼び出し: GET /services/data/vXX.0/query

入力:

  • q (string) — SOQL。管理パッケージ:SELECT Id, Name, FSLMPPERF__Work_Rule__r.Name, FSLMPPERF__Work_Rule__r.RecordType.DeveloperName FROM FSLMPPERF__Scheduling_Policy_Work_Rule__c WHERE FSLMPPERF__Scheduling_Policy__c = ''。ESO:SchedulingPolicy Id を参照する SchedulingConstraint / SchedulingRule 子レコードをクエリします。管理パッケージによって自動作成され、決して再出力してはならない Time_Rule_Service 行(名前が 'Earliest Start Permitted' および 'Due Date')は、管理者に提示する前に除外してください。

依存関係: list-starter-scheduling-policies

出力: REST クエリの応答です。各行には、ポリシーと作業ルールの中間レコードと、具体的なルールタイプ名および説明が含まれます。

read-starter-policy-objectives

選択したスターターポリシーに関連付けられたサービス目標のエントリを読み取ります。提供されているスターターで各目標が有効か無効か、およびその重みを表示します。トレードオフのヒアリングを進める際の参考になります。

呼び出し: GET /services/data/vXX.0/query

入力:

  • q (string) — SOQL。管理パッケージ:SELECT Id, Name, FSLMPPERF__Weight__c, FSLMPPERF__Service_Goal__r.Name FROM FSLMPPERF__Scheduling_Policy_Goal__c WHERE FSLMPPERF__Scheduling_Policy__c = ''。ESO:ネイティブのインターフェースで SchedulingObjective と SchedulingPolicyObjective の中間レコードをクエリします。

依存関係: list-starter-scheduling-policies

出力: REST クエリの応答です。各行には、目標名と重みが含まれます。スキル本文に記載された 10 個の目標は、それぞれ FSL Service Goal レコードに 1 対 1 で対応します。

書き込み操作
emit-build-spec

最終アクション:完成した JSON ビルド仕様(「Emitting the Build Spec」に記載された agent_guidance 内のスキーマ)を出力し、レコード作成のために sfs-sobject-create スキルに引き渡します。

依存関係: list-starter-scheduling-policies

その他の操作
resolve-fsl-namespace

インストール済みの FSL 管理パッケージの名前空間プレフィックスを動的に解決します。本番環境のプレフィックスは FSL、内部組織では FSLQA/FSLMPTEST/FSLMPPERF です。以降のすべてのクエリでは、検出したプレフィックスを使用して __Scheduling_Policy__c を指定する必要があります。

呼び出し: GET /services/data/v67.0/tooling/query

入力:

  • q (String) — SOQL:SELECT SubscriberPackage.NamespacePrefix FROM InstalledSubscriberPackage。WHERE 句は指定しません(Tooling API では NamespacePrefix によるフィルターが拒否されます)。すべてのパッケージをクエリし、NamespacePrefix が FSL で始まる最初のレコードをクライアント側でフィルタリングします。

出力: フィルターしていない結果から、SubscriberPackage.NamespacePrefix が FSL で始まる最初のレコードを選択します。その NamespacePrefix 値が、実行時に使用する です(例:FSL、FSLMPPERF)。フィルター後の結果が空の場合、FSL はインストールされていないため、中止します。

注記: fs-worktypes-deployer のパターンに基づいています。このスキルのすべての SOQL クエリでは、検出した名前空間を使用する必要があります:__Scheduling_Policy__c、__Work_Rule__c など。FSLMPPERF をハードコードしないでください。

注記

  • 構成: 最後のステップでは、兄弟スキル sfs-sobject-create に引き継ぎます。このスキルは出力された build-spec JSON を受け取り、組織に適した managed-pkg(FSLMPPERF__)または ESO(ネイティブ)のサーフェスを使用して、CRUD(SchedulingPolicy、Rule、Objective、ジャンクションレコード)を実行します。
  • 優先される情報源: スキル本文に記載されたペナルティ計算式(タイプ 1~5)は、異なる場合は公開ドキュメントより優先されます。help.salesforce.com の情報に基づいて計算式を「修正」せず、重みの導出における信頼できる情報源として扱ってください。

コメント