mcpField
F
field-service-objective-designer-configure

field-service-objective-designer-configure

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

field-service-objective-designer-configure について

Salesforce Field Service のスケジューリングポリシー向けに、構造化されたトレードオフ形式の聞き取りでサービス目標を設計するスキルです。Minimize Travel を基準に重みを算出し、レコード作成工程に渡す serviceObjectives[] を出力します。

主な機能

  1. 01カテゴリ別の目標選択を案内
  2. 02トレードオフ質問から重みを算出
  3. 03ペナルティ率の計算を説明
  4. 04目標固有のパラメーターを記録
  5. 05serviceObjectives[] ブロックを生成

活用シーン

  1. 01ポリシーのスケジューリング目標設定
  2. 02移動時間と他の目標の比較
  3. 03レコード作成用の目標データ準備

インストール

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

$npx skills add https://github.com/forcedotcom/sf-skills --skill field-service-objective-designer-configure

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

詳細

Sfs サービス目標の管理

このスキルを使用する場面

構造化されたトレードオフのヒアリングを通じて、Salesforce Field Service のスケジューリングポリシー向けサービス目標を設計します。目標の選択をユーザーに案内し、基準となる「移動時間の最小化」との交差点での等価性を定めて重みを導き出します。ペナルティ率の解釈と、平易な言葉でのポリシー概要を含む、最終的な重みの表を作成します。作業ルールの設計後に呼び出され、レコードの作成は sfs-sobject-create に委任します。

ワークフロー

Salesforce Field Service – サービス目標の設計

スケジューリングポリシーのサービス目標を設計します。 このスキルでは、目標を選択し、構造化されたトレードオフのヒアリングを通じて、質問を1つずつ行いながら各目標の重みを導き出し、出力として serviceObjectives[] ブロックを生成します。Salesforce のレコードは作成しません。完了後、sfs-sobject-create に委任します。

ヒアリングの段階: スケジューリングポリシー → 作業ルール → サービス目標(このスキル)→ レコード作成

このスキルが扱うのは、サービス目標の段階のみです。sfs-scheduling-policy-designer からの policy ブロックと、sfs-work-rule-designer からの workRules[] ブロックがコンテキストとして渡されます。このスキルでは serviceObjectives[] を追加し、設計全体を sfs-sobject-create に渡します。


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

オプティマイザーは、各候補スケジュールにペナルティポイントを割り当てます。合計ペナルティが小さいほど、スケジュールの質が高くなります。各サービス目標は、その重みと固有の尺度(その目標における最悪のケース)に基づいてペナルティポイントに寄与します。

「移動時間の最小化」の重みを基準とします。 その値は1000に設定され、その他すべての重みはこれを基準に導き出されます。すべての目標に共通する一般式は次のとおりです(ここに一度だけ示し、目標ごとには導き直しません)。

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

共通の内部倍率 ×1000 から、次の2つのことが導かれます。

  • 2つの目標間で導出する場合、この倍率は相殺されます。そのため、ヒアリングで使用する連続近似は有効なままであり、「weight_X を weight_Y から導く」式に ×1000 の項は含まれません。ただし、丸め関数が整数の重みに対して厳密でない場合(ASAP の round、スキルレベル/優先度の roundInt)、わずかなずれが生じる可能性があります。詳細は各目標の説明に記載します。
  • max(1, …) の下限により、重みが非常に低くても、含めたすべての目標が何らかの効果を持ちます。

例外が1つあります。 同一サイトの最終倍率(×0.01)は実質的に ×10 となります。これは「別の目標の率で割るだけ」という近道を調整する必要がある唯一のケースです。


目標別のペナルティ式

計算を示し、重みを逆算する際には、これらを使用してください。一般的な仕組み(×1000、下限、導出が有効な理由)は上記のとおりであり、ここでは繰り返しません。

移動時間の最小化(基準) — 尺度120分(2時間=デフォルトの MaxGrade__c)、round5、×1/60(分あたりから秒あたりへの換算)。penaltyPerViolation_travel = max(1, round5(1000×weight_travel/120)) × (1/60)。重みが1000の場合、8333.33333 → 138.88889ポイント/秒(秒単位の粒度)です。連続近似:travel_penalty(X_min) ≈ X × weight_travel/120 — 重みが1000の場合、約8.333ポイント/分です。 精度: 移動時間は秒単位、残業時間は(同じ尺度ですが)分単位です。61秒の移動は60秒の移動よりもコストが高くなります。下限が適用されるのは重みが約0.12を下回る場合のみです。

同一サイト — 尺度1(二値)、round5、×0.01 → 実質的に ×10(例外)。penaltyPerViolation_same_site = max(1, round5(1000×weight_same_site)) × 0.01。重みが50の場合、500ポイント/違反です。 精度: 整数の重みに対して round5 は厳密です。時間にかかわらず、イベントごとのペナルティは一定です。実質的な倍率は ×1000 ではなく ×10 です。 残業の最小化 — 移動の最小化と同じ仕組みです(スケール 120、round5、weight 1000 → 8333.33333)。次の 2 点だけ異なります。 最終乗数は ×1/60 ではなく ×1.0 なので、レートは分あたりです(8333.33333 pts/min)。また、ペナルティは分単位でまとめられます — overtime_penalty(X_sec) = ceil(X_sec/60) × penaltyPerViolation — つまり、61 秒のブロックも 120 秒のブロックも同じコストです。penaltyPerViolation_overtime = max(1, round5(1000×weight_overtime/120)) × 1.0。連続値では ≈ Z × weight_overtime/120 となり、移動と同程度です。 精度: weight が約 0.12 未満では下限値が適用されます。

優先リソース — スケール 1(二値)、roundInt、×1.0。penaltyPerViolation_preferred = max(1, roundInt(100 × 10.0 × weight_preferred)) × 1.0(100×10.0 = 1000 を分解しただけです)。weight 375 の場合、違反 1 件あたり 375,000 pts です。導出式:weight_preferred = T_equiv × (weight_travel/120) — weight_travel が 1000 の場合:= T_equiv × 8.333。 精度: 整数の weight では丸め誤差はありません(正確な形式は X_violations × 1000 × weight_preferred)。下限値が適用されるのは weight が 0.001 未満の場合のみです。

リソースの優先度 — スケール 10(優先度 0~10。0 = 最高、10 = 最低)、丸めなし(生の小数値)。penaltyPerViolation_resource_priority = max(1, (weight_resource_priority/10.0) × 1000)。weight 1875 の場合、優先度 1 点あたり 187,500 pts です。優先度 P の合計:P × penaltyPerViolation。P=0 → 0、P=10 → 最大値です。 精度: 浮動小数点値をそのまま使用し、丸めはありません。線形で、優先度 5 は最大値の 50% です。

スキルレベル — スケール 10(10 段階のスキルスケール)、roundInt。penaltyPerViolation_skill_level = max(1, roundInt(1000×weight_skill_level/10))。スキルレベル S の合計:S × penaltyPerViolation。 適用条件: 複数のスキル要件がある場合、SFS はスコアを平均します。スキル要件がない場合、影響はありません。最も適格/最も適格な候補のモード(インタビューで処理されます)によって優先方向は反転しますが、式は変わりません。 精度: 整数の weight では丸め誤差はありません。weight が約 0.01 未満の場合、下限値の影響はわずかです。

スキルの優先度 — スケール 10(スキルの優先度 1~10。1 = 最も優先、10 = 最も優先度が低い。null → 10)、roundInt。penaltyPerViolation_skill_preference = max(1, roundInt(1000×weight_skill_preference/10))。スキルの優先度 SP の合計:SP × penaltyPerViolation。適用条件(同じ Skill Type、または対応する Match Skills ルールとの一致)はインタビューで処理されます。 精度: 整数の weight では丸め誤差はありません。SP に対して線形です。

近隣グループ化 — スケール 1(二値 — クラスター内かどうか)、round5、×1.0(Same Site の ×0.01 とは異なり、×1000 をそのまま適用します)。penaltyPerViolation_group_nearby = max(1, round5(1000×weight_group_nearby)) × 1.0。weight 167 の場合、違反 1 件あたり 167,000 pts です。導出式:weight_group_nearby = T_equiv × (weight_travel/120) — weight_travel が 1000 の場合:= T_equiv × 8.333。 精度: イベントごとに一定です — クラスター外の各予約に対して、距離にかかわらず同じコストが発生します。

空き時間の最小化 — スケール 1(条件を満たす空き時間ごとに違反 1 件。最小時間は 30 分~24 時間の範囲で設定可能で、それより短い時間はカウントされません)、round。penaltyPerViolation_gaps = max(1, round(1000×weight_gaps))。合計:Σ over routes/shifts [ clump_counter(route) × penaltyPerViolation ]。 精度: 整数の weight では丸め誤差はありません(正確な形式は clump_count × 1000 × weight_gaps)。weight が 0.001 未満の場合、下限値の影響はわずかです。ルートごとの空き時間です — 1 つのリソースに空き時間が 3 つある場合、レートの 3 倍になります。


会話フロー

以下のすべてに適用される 2 つの全体ルール:

フェーズ 1:**導出した weight はすべて、次の値に切り上げてください...
フェーズ 2:目的間で生の weight 値を比較しないでください、...

ステップ 1:目的の選択

まず、目的の概念を説明します。サービス目的は、作業ルールを満たした候補者を評価するためのソフトな採点基準です。作業ルールとは異なり、目的によって候補者が不採用になることはありません。適格な候補者の中から、最適化エンジンが誰を優先するかに影響するだけです。移動時間の最小化は、基準となる目的として常に自動的に含まれ、固定ウェイトは 1000 であることも伝えます。他のすべての目的のウェイトは、トレードオフの計算を通じてこの値を基準に導出されるため、ユーザーが選択する必要はありません。

残り 9 つの目的については、以下の固定順序でカテゴリーを 1 つずつ提示し、次のカテゴリーに進む前にユーザーの選択を待ちます。

1. 顧客体験の目的 — 以下の 3 つを提示し、選択するものがあるか尋ねます。

  • ASAP — できるだけ早く顧客にサービスを提供する
  • 同一サイト — 2 件の作業が同じ場所にある場合、連続して実施する
  • 近隣の作業をグループ化 — 地理的に近い作業をまとめる

2. コスト/効率の目的 — 以下の 3 つを提示し、残り 2 つのうち選択するものを尋ねます(移動時間はすでに含まれています)。

  • 移動時間の最小化 (自動的に含まれる — 基準、固定ウェイト 1000)
  • 空き時間の最小化 — 技術者が継続して稼働できるようにする
  • 残業の最小化 — 残業代の支払いを避ける

3. 人員配置/割り当て品質の目的 — 以下の 4 つを提示し、選択するものがあるか尋ねます。

  • 優先リソース — 可能な場合は、優先指定された/名前が指定された技術者を割り当てる
  • リソースの優先度 — 優先度の高いリソースを優先する(例:請負業者より自社スタッフ)
  • スキルレベル — 作業に適した専門知識のレベルを割り当てる
  • スキルの優先設定 — スキルの種類内で優先順位の設定を尊重する(例:言語の優先設定)

目的ごとの追加質問 — 次のカテゴリーに進む前に、該当するカテゴリーの回答を受けた直後に尋ねます。

  • 移動時間の最小化(常に — コスト/効率カテゴリーの最後に一度尋ねます):「移動時間の最小化では、リソースの拠点への往復区間も計算対象にしますか。つまり、自宅から最初の作業場所までの移動と、最後の作業場所から自宅までの移動も含めますか。それとも、これらの区間は採点対象から除外しますか?」独立した 2 つのフラグ excludeTravelFromHome と excludeTravelToHome として記録します(true = 除外)。デフォルトはどちらも false です。
  • 同一サイト(選択された場合のみ):「同一サイトは、キャンパスや農場などで便利な、緯度/経度が完全に一致する場合にのみ同じサイトとみなしますか。それとも、デフォルトのグループ化(移動時間が約 1 秒以内の作業を同じサイトとみなす)を使用しますか?」useExactLocation として記録します(true = 緯度/経度が完全に一致する場合のみ)。デフォルトは false です。
  • 空き時間の最小化(選択された場合のみ):会社が空き時間とみなす最短の非稼働時間を尋ねます(30 分〜24 時間)。params.minGapMinutes として記録します。デフォルトは 30 です。この質問をここで行うことで、「空き時間の基準」と「空き時間をどの程度解消するか」を分けて扱えます。

3 つのカテゴリーすべてに回答し、追加質問への回答も記録したら、ステップ 2 に進みます。


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

移動時間の最小化以外に選択された各目的について、トレードオフに関する質問をします。目指すのは、ユーザーが 2 つの選択肢を同じ程度に許容できると考える分岐点です。この同等性を使ってウェイトを計算します。ユーザーの回答後、2 つのペナルティ式を等しく設定して未知のウェイトを求め、グローバルルールに従って切り上げます。計算式は必ず提示します。

自由形式の質問とあわせて、具体的なプリセット回答を提示します。 質問のテンプレートの後に、弱い/中程度/強い優先度に対応する、すぐに選べる分岐点をいくつか提示します。数値を考える代わりに、ユーザーがその中から選べるようにします。また、独自の正確なトレードオフを説明することも、必ず案内してください。プリセットは利便性のためのものであり、別の計算方法ではありません。

ASAP のトレードオフ

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

プリセット:「移動時間15分 ≈ 遅延12時間」(弱め)、「移動時間30分 ≈ 遅延24時間」(中程度)、「移動時間60分 ≈ 遅延48時間」(強め)。

計算式(ユーザーが移動時間 X 分、遅延 Y 時間を指定):

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

weight_travel = 1000 の場合:weight_asap = X × 6000 / Y。

整数式の検証: penaltyPerViolation = max(1, round(1000 × weight_asap / 43200)) を確認し、そのうえで effective_weight = penaltyPerViolation × 43200 / 1000 を確認します。effective_weight に大きな差(5%超)がある場合は、その旨を記載します。 低い重みへの警告: 算出された重みが 22 未満の場合、SFS は1分あたりのペナルティを1(下限値)に固定することを警告します。重みが1~21の場合、最適化の動作はすべて同じになります。

同一拠点のトレードオフ

説明上の注意: 同一拠点の予約はすでに同じ場所にあるため、同一拠点と移動時間を比較しないでください。選択されている場合は ASAP または優先リソースと比較します。

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

「同じ拠点での2つの予約を想像してください。最適化ツールは、次のどちらかを選べます。(A)両方を同じリソースに割り当てますが、可能な時刻より[H]時間遅く予約されます。(B)別々のリソースに分けて、今すぐ予約します。どの程度の予約の遅れなら『分けてしまおう』と思いますか?」

プリセット:「最大1時間遅れても一緒にする」(弱)、「最大4時間遅れても一緒にする」(中程度)、「最大8時間遅れても一緒にする」(強)。 計算式(遅延のしきい値 D_equiv(分)):weight_same_site = D_equiv × (weight_asap / 43200)

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

「優先リソースへの割り当てを尊重するために、同一拠点の予約を別々のリソースに分けますか?それとも、優先リソースを無視することになっても、予約を同じリソースにまとめますか?」

プリセット:「同じくらい重要」(F = 1)、「同一拠点を2倍重視」(F = 2)、「同一拠点を半分の重さで重視」(F = 0.5)。 計算式:weight_same_site = F × weight_preferred

どちらも選択されていない場合 — 移動時間との比較に切り替え:

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

プリセット:「追加10分」、「追加20分」、「追加30分」。 計算式:weight_same_site = T_equiv × (weight_travel / 120)

残業最小化のトレードオフ

「今すぐ予約できますが残業が[Z]分発生する場合と、通常勤務時間内の[H]時間後に予約する場合、どのあたりで同じくらい受け入れやすいと感じますか?」

プリセット:「残業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 が選択されていない場合): weight_overtime = T_equiv × weight_travel / Z。T は、Z 分の残業よりユーザーが望む移動時間の相当分(分)です。

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

「予約に優先リソースが割り当てられている場合、その優先を尊重することはどれくらい重要ですか?優先リソースは利用可能ですが、移動時間が[X]分余計にかかると想像してください。どのあたりで『近いリソースを使おう』と思いますか?」

プリセット:「追加15分」(軽め)、「追加30分」(中程度)、「追加60分」(強め)。 計算式(移動時間のしきい値 T_equiv):weight_preferred = T_equiv × (weight_travel / 120)

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

説明のポイント: 近隣グループ化と移動の最小化は、自然に競合します。グループをまとめたままにすると、分割するより移動の合計が増える場合があります。トレードオフとなるのは、グループをまとめておくためにユーザーが許容する全体の移動時間の最大追加量です。

「クラスター内のすべての予約をまとめておくために、スケジュールに全体の移動時間を最大で何分追加してもよいですか?クラスターを維持するコストがその時間を超える場合、オプティマイザーはクラスターを分割して移動時間を節約します。」

プリセット:「追加10分」(弱)、「追加20分」(中)、「追加30分」(強)。 計算式(T_equiv):weight_group_nearby = T_equiv × (weight_travel / 120)

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

共有する背景情報:

「リソース優先度の目的では、サービスリソースを0~10の尺度でランク付けできます。0は最優先(最適な候補)、10は最低優先度を意味します。たとえば、社内スタッフには優先度1、契約業者には優先度5以上を設定できます。オプティマイザーは、リソースの優先度の値に比例したペナルティを適用します。優先度5のリソースには目的の重みの50%に相当するペナルティが発生し、優先度0のリソースにはペナルティがまったく発生しません。」

「利用可能なリソースが2つあるとします。リソースAはスタッフの技術者(優先度1)ですが、移動時間が[X]分長くなります。リソースBは契約業者(優先度[P])で、より近くにいます。どの時点で『契約業者を使えばいい』と言いますか?」

プリセット:「追加30分」(弱)、「追加60分」(中)、「追加90分」(強)。 計算式(T_equiv、スタッフの優先度 P_high = 1、契約業者の優先度 P_low):weight_resource_priority = T_equiv × weight_travel / (12 × (P_low - P_high)) 例(スタッフ1、契約業者5、T_equiv = 90):= 90 × 1000 / (12 × 4) = 1,875

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

共有する背景情報:

「スキルレベルの目的では、予約に必要なスキル要件を満たすリソースのうち、最も資格の低いリソースまたは最も資格の高いリソースを優先するよう、オプティマイザーを誘導します。ペナルティは、リソースのスキルレベルの生の値に目的の重みを掛けて計算されます。そのため、スキルレベル8のリソースには、スキルレベル1のリソースと比べて、重みの8倍のペナルティが発生します。」

ステップ1 — 希望するモードを尋ねます:

「どのモードを使いますか?

  • 最も資格の低いリソース — 要件を満たすリソースのうち、スキルが最も低いリソースを優先します。複雑な作業にシニアリソースを確保したい場合や、コストを抑えたい場合に適しています。
  • 最も資格の高いリソース — 利用可能なリソースのうち、スキルが最も高いリソースを優先します。初回訪問での修理完了率を最大化したい場合や、成果の品質を優先する場合に適しています。」

最も資格の低いリソースの場合:

「条件を満たすリソースが2つあるとします。ジュニア技術者(スキルレベル[S_low])とシニア技術者(スキルレベル[S_high])です。シニア技術者のほうが近くにいます。最も資格の低いリソースを選ぶモードでは、より難しい作業にシニア技術者を確保するため、オプティマイザーはジュニア技術者を優先します。ジュニア技術者を割り当てるために、移動時間が何分長くなってもよいですか?」

最も資格の高いリソースの場合:

「条件を満たすリソースが2つあるとします。ジュニア技術者(スキルレベル[S_low])とシニア技術者(スキルレベル[S_high])です。ジュニア技術者のほうが近くにいます。最も資格の高いリソースを選ぶモードでは、オプティマイザーはシニア技術者を優先します。シニア技術者を割り当てるために、移動時間が何分長くなってもよいですか?」

プリセット:「追加15分」(弱)、「追加30分」(中)、「追加45分」(強)。 計算式(モードにかかわらず同じ):weight_skill_level = T_equiv × weight_travel / (120 × (S_high - S_low)) 例 — 最も資格の低いリソース(S_low=4、S_high=8、T_equiv=30):= 30 × 1000 / (120 × 4) = 62.5 → 63に切り上げ

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

共有する背景情報:

「スキル優先度の目的は、作業指示に同じスキル種別のスキル要件が複数あり、あるスキルが別のスキルより優先される場合に適用されます。各スキル要件には、1~10 のスキル優先度が設定されます。1 が最も優先度が高く、10 が最も低くなります。オプティマイザーは、その優先度に比例するペナルティを割り当てます。」

スキル種別(この目的を機能させるために必須)も取得してください。スキル優先度は 1 つのスキル種別、つまり、連携する「スキルを照合」作業ルールで「少なくとも 1 つのスキルが一致(OR)」を使って評価されるスキルのグループ(例:言語)を対象とします。質問:

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

skillType パラメーターとして記録します(スキル種別の開発者名。目標の {ns}Skill_Type__c に対応)。そのスキル種別に OR ロジックを使う「スキルを照合」ルールが定義されていない場合は、ユーザーに知らせてください。

「顧客には、[スキル A] を話す技術者(スキル優先度 [SP_high_pref])と [スキル B] を話す技術者(スキル優先度 [SP_low_pref])のどちらでも対応できますが、顧客は [スキル A] を希望しているとします。[スキル A] を話す技術者のほうが遠くにいます。その技術者を割り当てるために、移動時間が何分増えることまで許容しますか?」

プリセット:「15 分余分にかかる」(弱)、「30 分余分にかかる」(中)、「45 分余分にかかる」(強)。 計算式(T_equiv、SP_high_pref = 優先度が高い、SP_low_pref = 優先度が低い):weight_skill_preference = T_equiv × weight_travel / (12 × (SP_low_pref - SP_high_pref)) 例(スペイン語の優先度 1、英語の優先度 6、T_equiv = 45):= 45 × 1000 / (12 × 5) = 750

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

最小ギャップ時間は、ステップ 1 で params.minGapMinutes(デフォルト 30)としてすでに取得しています。再度質問しないでください。このセクションでは重みの計算方法のみを扱います。

ASAP が選択されている場合 — ギャップ最小化と ASAP の比較:

「オプティマイザーには、次のどちらかの選択肢があります。(A) 技術者のスケジュールにギャップを残し、新しい予約を [H] 時間早く設定する。(B) スケジュールを詰めてギャップをなくす代わりに、その予約が [H] 時間遅く設定される。『ギャップを残して早く予約する』と判断するのは、予約がどれくらい遅れる場合ですか?」

プリセット:「最大 2 時間遅くなるならギャップを残す」(弱)、「最大 4 時間遅くなるなら」(中)、「最大 8 時間遅くなるなら」(強)。 計算式(遅延しきい値 D_equiv、単位は分):weight_gaps = D_equiv × weight_asap / 43200 例(weight_asap = 250、D_equiv = 240 分):= 240 × 250 / 43200 = 1.389 → ceil to 2

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

「ギャップを残すために、スケジュール全体の移動時間が何分増えるまで許容できますか?技術者の勤務シフトを詰めるより、ギャップを残すほうがよいと判断する基準を教えてください。」

プリセット:「10 分余分にかかる」、「20 分余分にかかる」、「30 分余分にかかる」。 計算式:weight_gaps = T_equiv × weight_travel / 7200

整数式の検証: penaltyPerViolation = max(1, round(1000 × weight_gaps)) であることを確認します。算出した重みが非常に小さい場合(約 1 未満)、max(1,…) の下限によりペナルティが固定の最小値に切り上げられることを警告します。


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

トレードオフに関する質問への回答がすべて揃ったら、対象に含めた目標ごとに 1 行を設け、重みと、ユーザーが示したトレードオフに基づく 1 行の根拠を記載した、簡潔な表を提示します(例:ASAP なら、ユーザーが示した「移動時間 X 分 ≈ 遅延 Y 時間」の分岐点、Same Site なら「1 回の分割 ≈ ASAP の遅延 D 分」など)。Minimize Travel は常に基準となる 1000 です。

次に、生の重みではなく、単位あたりのペナルティポイントを使って相対的な優先度を説明します。目標ごとに倍率が異なるためです。次のように各レートを計算します。Travel は 1 分あたり weight_travel / 120、ASAP は遅延 1 分あたり weight_asap / 43200、Overtime は 1 分あたり weight_overtime / 120、Same Site はイベント 1 件あたり weight_same_site × 10(×10 であり、×1000 ではありません)、Preferred Resource と Group Nearby はイベント 1 件あたり weight × 1000、Resource Priority はリソース 1 件あたり weight × (P / 10)(関連する優先度ティアをいくつか示します)、Skill Level はリソース 1 件あたり S × weight × 100(関連するスキルティアを示します)、Skill Preference はスキルの割り当て 1 件あたり weight × (SP / 10)(関連する優先度を示します)、Minimize Gaps はギャップ 1 件あたり weight_gaps × 1000(一定)です。

これらのレートを使い、ユーザーが示した元のトレードオフに基づいて、相対的な優先度を平易な言葉で説明します。たとえば、「これは、移動時間 30 分が遅延 12 時間に相当するという、あなたが示した優先度を反映しています」のように説明します。推定された優先度がユーザーの期待と異なる場合は、トレードオフに関する質問を再度行うことを提案します。


方針の概要(2~3 文のビジネス向け要約)

重みとペナルティレートを示した後、このスケジューリング方針で何が最適化されるのかを説明する、経営層向けの 2~3 文の概要を作成します。数式に関心のない関係者向けの、簡潔な説明です。

作成方法:

フェーズ 3:目標をカテゴリに分類する

顧客体験(ASAP、Same Site、Group Nearby)、コスト/効率性(Minimize Travel、Minimize Gaps、Minimize Overtime)、従業員/割り当ての品質(Preferred Resource、Resource Priority、Skill Level、Skill Preference)。

フェーズ 4:ペナルティレートを比較し、どのカテゴリが優勢かを判断する...
フェーズ 5:分岐点から、重要なトレードオフの緊張関係を特定する...
フェーズ 6:主要な優先事項と副次的な優先事項を示す 2~3 文を書く...

ペナルティの計算ではなく、ユーザーが実際に示した価値観(例:「車で最大 2 時間移動する」「最大 24 時間待つ」)に基づいたビジネス用語を使います。この要約は、スケジューリング方針レコードの Description フィールドに記載する内容としても適しています。引き継ぎ時に、ユーザーへその旨を提案してください。


Build Spec を出力する(引き継ぎ契約)

このスキルの最終成果物であり、sfs-sobject-create との唯一の契約です。設計(方針設定、作業ルール、関連性グループ、重み)が確定したら、JSON オブジェクトとして構造化された Build Spec を 1 つ出力します。sfs-sobject-create はこれをそのまま使用し、意図を再推定しないため、完全で自己完結した内容にする必要があります。

厳格な境界:出力するのは設計値のみです。Salesforce オブジェクト名、フィールド API 名、レコード ID、Composite API の参照構文、作成順序は含めません。これらはデータレイヤーのスキルに任せてください。ルール/目標は、この文書で使用している正規の型名(例:Match Skills、Minimize Travel、Resource Priority)をそのまま使用し、業務パラメーターを付けて記述します。不明なパラメーターは省略するか null とし、API フィールド名を決して捏造しないでください。

{
  "specVersion": "1.0",
  "policy": {
    "name": "scheduling policy name",
    "description": "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 naming convention below",
    "mandatory": false,
    "params": {},   // business params only. e.g. Service Resource Availability absolute break: breaks:[{mode:'absolute',startClock:'12:00',durationMinutes:30}]; offset break: breaks:[{mode:'offset',earliestStartOffsetMinutes:180,latestEndOffsetMinutes:210,durationMinutes:30}]; Maximum Travel from Home: maxTravelFromHome:45, maxTravelFromHomeType:'Travel Time'
    "relevanceGroup": { "basis": "Service Appointment | Service Territory Member | null", "booleanField": "scoping Boolean field name, or null for policy-wide" }
  }],
  "serviceObjectives": [{
    "type": "canonical objective type name (e.g. 'Minimize Travel')",
    "name": "instance display name — see naming convention below",
    "weight": 1000,
    "params": {},   // objective-specific business params. e.g. Skill Level mode:'Least Qualified'; Resource Priority priorityField concept; Minimize Gaps minGapMinutes; Skill Preference skillType; Minimize Travel excludeTravelFromHome/excludeTravelToHome; Same Site useExactLocation
    "relevanceGroup": { "basis": "Service Appointment | Service Territory Member | null", "booleanField": "scoping Boolean field name, 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 deployment — e.g. 'Create Boolean field Break_Group_France__c on Service Territory Member and set it true for French resources.'"],
  "notes": "caveats, low-weight-floor warnings, or unresolved ambiguities"
}

出力ルール:

フェーズ 7:命名規則(すべてのルールと目標)

name = 正式なタイプ名 + 省略したポリシー名を " - " で区切ったものです。例:ポリシー「First test on shorter」→「Match Skills - First test」、「Minimize Travel - First test」。ポリシー名は省略してください(「on shorter」や「policy」などの埋め草を削除します)。そのまま付け加えないでください。例外: 2 つの Arrival Window Match Time ルールは独自の名前(Arrival Window Start、Arrival Window End)を保持します。タイプ名に置き換えるのではなく、省略したポリシー名をそれらの名前に付け加えてください:「Arrival Window Start - First test」、「Arrival Window End - First test」。

フェーズ 8:必ず含める Minimize Travel(重み 1000)と、`"mandatory"

true` の Service Resource Availability ルールを 1 つだけ含めてください。話題に上がらなかった場合も同様です。Earliest Start Permitted または Due Date は出力しないでください。

フェーズ 9:サブセットごとに 1 インスタンス — ルール/目標が…
フェーズ 10:重みは整数で、小数点以下を切り上げた値にします(すでに切り上げ済みです)…
フェーズ 11:パラメーターは業務上の値であり、フィールドマッピングではありません — 「12

00 から 15:00 までの 30 分間の休憩」は期間/時刻として指定します。フィールドレイアウトのマッピングはデータレイヤースキルに記述します。

フェーズ 12:前提条件を明示する — 関連性グループの Boolean フィールドがある場合は…
フェーズ 13:何も作成しないでください。 出力した後、ユーザーに伝えてください…

引き継ぎ

ユーザーが重みと概要を確認したら、目標の設計フェーズは完了です。sfs-sobject-create に委任し、上記のビルド仕様(sfs-scheduling-policy-designer の policy ブロック、sfs-work-rule-designer の workRules、およびこのスキルの serviceObjectives を含む)を渡して、スケジューリングポリシー、作業ルール、サービス目標を依存関係の順序に従って作成できるようにします。

「目標の設計が完了しました。次にレコード作成へ引き継ぎ、Salesforce のすべてのレコード(スケジューリングポリシー、作業ルール、サービス目標)を正しい順序で作成します。進めてもよろしいですか?」

───── ランタイムコンテキスト(Headless 360 / エージェント型): このスキルを Headless 360 / エージェント型のコンテキストで実行する場合は、可能な限り CLI ツール(sf project deploy, sfdx, シェルコマンド)よりも dispatch ツールを優先してください。利用可能な操作は以下の steps: に記載されています。それぞれ実際の組織で検証済みです。正規のパスに対して dispatch を使用してください。特定の機能に API パスが存在しない場合に限り、CLI による代替を使用できます。

重大な制約事項

前提条件:

  • 組織に FSL 管理パッケージがインストールされている必要があります。(確認方法:Tooling API: GET /services/data/v68.0/tooling/query with q=SELECT SubscriberPackage.NamespacePrefix FROM InstalledSubscriberPackage. Filter client-side for a NamespacePrefix starting with FSL (matches FSL, FSLQA, FSLMPTEST, FSLMPPERF). No FSL* record — abort: this skill cannot design objectives for an org without FSL.)
  • Skill Preference 目標をデプロイするには、事前に組織に SkillType レコードが少なくとも 1 件存在する必要があります。このスキルの本文では、Skill Type Developer Name をユーザーに尋ねます。SkillType のレコードが 0 件の場合は、sfs-sobject-create が目標を書き込む前に、対象の Skill Type を作成する必要があることをユーザーに警告してください。(確認方法:SOQL: SELECT COUNT() FROM SkillType. Zero → surface in the interview if the user selects Skill Preference; the emitted spec should add a prerequisites[] entry naming the Skill Type Developer Name the user needs to create.)
  • 基準が Service Territory Member の Relevance Group では、対象範囲を絞り込むために使用する Boolean フィールドが、ServiceTerritoryMember 上の Boolean 型カスタムフィールドとして既に存在している必要があります(標準フィールドは IsDeleted のみです)。ユーザーが STM 基準の対象範囲の絞り込みに使用する Boolean フィールドを指定した場合、メタデータワークフローでデプロイ前に追加できるよう、出力する prerequisites[] エントリにそのフィールドを記載してください。(確認方法:Global describe on ServiceTerritoryMember; filter fields whose type equals boolean. On a fresh FSL install, only IsDeleted is present. Any STM-basis Relevance Group requires customer-authored Booleans; surface the dependency in the emitted prerequisites[] rather than blocking.)

運用ルール:

  • ASAP — スケールは 43,200 分(30 日)、丸め処理です。penaltyPerViolation_asap = max(1, round(1000×weight_asap/43200))。整数分単位の場合:asap_penalty(X_sec) = ceil(X_sec/60) × penaltyPerViolation。連続値の場合:asap_penalty(Y_min) ≈ Y × weight_asap/43200。 精度: 重み 1~21 は機能しません。round(1000×21/43200)=0 となり、最小値の 1 に固定されるため、すべて同一の 1 pt/分のレートになります。それを超えると誤差は小さくなります(重み 250 → 実効値 259.2、3.7%)。精度が重要な場合は整数式で検証してください。
  • ここでは Salesforce レコードを作成せず、作成フローも開始しないでください。Salesforce API 呼び出しはすべて sfs-sobject-create が担当します。引き継ぎメッセージ:

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

オペレーションは目的別にグループ化されています。上記のワークフローを構築する際に、これらを使用してください。

概要
オペレーション目的ステータス呼び出し依存関係
resolve-fsl-namespace検証—GET /services/data/v68.0/tooling/query—
enumerate-service-objective-record-types読み取り—GET /services/data/v68.0/queryresolve-fsl-namespace
describe-service-goal-schema読み取り—GET /services/data/v68.0/sobjects/FSLMPPERF__Service_Goal__c/describeresolve-fsl-namespace
list-scheduling-policy-goal-junction読み取り—GET /services/data/v68.0/sobjects/FSLMPPERF__Scheduling_Policy_Goal__c/describeresolve-fsl-namespace
list-skill-types読み取り—GET /services/data/v68.0/query—
list-service-appointment-boolean-fields読み取り—GET /services/data/v68.0/sobjects/ServiceAppointment/describe—
list-service-territory-member-boolean-fields読み取り—GET /services/data/v68.0/sobjects/ServiceTerritoryMember/describe—
list-existing-service-goals読み取り—GET /services/data/v68.0/queryresolve-fsl-namespace
emit-service-objectives-block書き込み——enumerate-service-objective-record-types、describe-service-goal-schema、list-scheduling-policy-goal-junction、list-skill-types、list-service-appointment-boolean-fields、list-service-territory-member-boolean-fields
handoff-to-sobject-create委任——emit-service-objectives-block
依存関係グラフ
graph TD
  resolve_fsl_namespace["resolve-fsl-namespace (validate)"]
  enumerate_service_objective_record_types["enumerate-service-objective-record-types (read)"]
  describe_service_goal_schema["describe-service-goal-schema (read)"]
  list_scheduling_policy_goal_junction["list-scheduling-policy-goal-junction (read)"]
  list_skill_types["list-skill-types (read)"]
  list_service_appointment_boolean_fields["list-service-appointment-boolean-fields (read)"]
  list_service_territory_member_boolean_fields["list-service-territory-member-boolean-fields (read)"]
  list_existing_service_goals["list-existing-service-goals (read)"]
  emit_service_objectives_block["emit-service-objectives-block (write)"]
  handoff_to_sobject_create["handoff-to-sobject-create (delegate)"]
  resolve_fsl_namespace --> enumerate_service_objective_record_types
  resolve_fsl_namespace --> describe_service_goal_schema
  resolve_fsl_namespace --> list_scheduling_policy_goal_junction
  resolve_fsl_namespace --> list_existing_service_goals
  enumerate_service_objective_record_types --> emit_service_objectives_block
  describe_service_goal_schema --> emit_service_objectives_block
  list_scheduling_policy_goal_junction --> emit_service_objectives_block
  list_skill_types --> emit_service_objectives_block
  list_service_appointment_boolean_fields --> emit_service_objectives_block
  list_service_territory_member_boolean_fields --> emit_service_objectives_block
  emit_service_objectives_block --> handoff_to_sobject_create
読み取りオペレーション
enumerate-service-objective-record-types

<NS>__Service_Goal__c 上の有効な RecordType.DeveloperName 値を列挙します。これは、組織が公開するサービス目標タイプの正規の列挙です。serviceObjectives[] JSON ブロックで出力されるすべての目標は、このセットの DeveloperName にマッピングされる必要があります。sfs-sobject-create は、正しい RecordTypeId を書き込むためにこれに依存しています。Group Nearby は対象組織上の独立した RecordType ではありません。インタビューでこれについて質問された場合、スキルはオプションを提示する前に、組織がそれを公開していることを確認する必要があります。

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

入力:

  • q (String) — SOQL: SELECT Id, DeveloperName, Name, IsActive FROM RecordType WHERE SObjectType='<NS>__Service_Goal__c' AND IsActive = true ORDER BY DeveloperName。resolve-fsl-namespace で検出された名前空間に置き換えます。

依存先: resolve-fsl-namespace

出力: DeveloperName(例: Objective_Asap、Objective_Minimize_Travel、Objective_Same_Site、Objective_Skill_Level、Objective_Skill_Preferences、Objective_PreferredEngineer、Objective_Resource_Priority、Objective_Minimize_Gaps、Objective_Minimize_Overtime、Objective_Custom_Logic)、Name(人間が読める Field Service ラベル)、および Id を含むレコード一覧。スキルは、ユーザーの会話上の目標選択を DeveloperName にマッピングします。対象組織 sfs-headless-skill-building では 10 件の有効なレコードが想定されます。

備考: 対象組織で 10 件の有効な DeveloperName 値をプローブ検証済みです。スキルの インタビューで提示されない目標タイプ(Objective_Custom_Logic)も 引き続き列挙されるため、スキルはデプロイ時に暗黙的にブロックされるのではなく、 それらを明示的に辞退できます。

describe-service-goal-schema

Service_Goal__c sObject をイントロスペクトして、インタビューが駆動する設定フィールドを公開します。ピックリスト列挙(Custom_Type__c は 14 値、Prioritize_Resource__c は Skill Level モードの質問用に Least Qualified / Most Qualified)、スキルパラメータを支える文字列カラム(Skill_Type__c、Resource_Priority_Field__c、Object_Group_Field__c、Resource_Group_Field__c)、および Gap_Duration__c や Ignore_Home_Base_Coordinates__c などのパラメータ用の数値/真偽値カラムを読み込みます。sObject パスは検出された名前空間プレフィックスを使用します。表示されているパスは対象組織 sfs-headless-skill-building 用です。実行時には解決された <NS> に置き換えてください。

呼び出し: GET /services/data/v68.0/sobjects/FSLMPPERF__Service_Goal__c/describe

依存先: resolve-fsl-namespace

出力: describe.fields[] から名前で抽出: Custom_Type__c(ピックリストドメイン — 目標タイプを網羅する 14 値)、Prioritize_Resource__c(ピックリスト、"Least Qualified" がデフォルト、"Most Qualified" と対比)、Gap_Duration__c(double、分; params.minGapMinutes のターゲット)、Ignore_Home_Base_Coordinates__c(boolean; 帰宅移動フラグのターゲット)、Skill_Type__c(string; Skill Preference のターゲット)、Resource_Priority_Field__c(string、デフォルト "fsl__priority__c"; Resource Priority のターゲット)、Object_Group_Field__c および Resource_Group_Field__c(strings; Relevance Group のスコープ設定フィールド)、Custom_Logic_Data__c(long textarea; Objective_Custom_Logic 専用)。

備考: describe レスポンスには 10 件すべての RecordTypeId も含まれます。利用者は RecordTypeId をこの describe から、または enumerate-service-objective-record-types ステップの SOQL クエリから取得できます。健全な インストールでは両者は一致します。

list-scheduling-policy-goal-junction

Weight で Service_Goal を Scheduling_Policy に関連付ける Scheduling_Policy_Goal__c ジャンクションを調査します。後続の sfs-sobject-create は、出力された serviceObjectives[] ブロック内の(ポリシー、目標、Weight)の各組につき 1 件ずつ、このジャンクションにレコードを書き込みます。主な列:FSLMPPERF__Scheduling_Policy__c(主従関係)、Service_Goal__c(主従関係)、Weight__c(Number(9,0)、null 不可)。この (9,0) の精度により、スキルのグローバルルール #1(整数の Weight)がスキーマ層で適用されます。sObject パスには、検出された名前空間プレフィックスを使用します。

呼び出し: GET /services/data/v68.0/sobjects/FSLMPPERF__Scheduling_Policy_Goal__c/describe

依存関係: resolve-fsl-namespace

出力: describe.fields[] で、FSLMPPERF__Weight__c の type=double、precision=9、scale=0、nillable=false を確認します。また、2 つの主従関係参照項目が存在し、それぞれ Scheduling_Policy__c と Service_Goal__c を参照することを確認します。対象外の組織で Weight__c が nillable=true の場合は、差異を記録してください。スキルの上限切り上げによる整数化ルールは引き続き正しいものの、スキーマ層での適用は行われていません。

備考: この describe は、設計スキル向けのスキーマ可視性の読み取りです。実際のジャンクション行の書き込みは sfs-sobject-create で行われます。ここで Weight__c(9,0) の制約を明らかにすることで、上限切り上げを忘れる将来のスキルの差異を検出できます。

list-skill-types

既存の SkillType レコードを読み取ります。Skill Preference の目標には Skill Type の Developer Name が必要です。インタビューでは、列挙された選択肢を提示するか、存在しない場合はユーザーに警告できる必要があります。対象組織 sfs-headless-skill-building では、SkillType のレコードは 0 件です。プラットフォームのメタデータ上は Skill Preference を提示できますが、ユーザーが SkillType レコードを作成するまでは機能しません。

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

入力:

  • q (String) — SOQL:SELECT Id, DeveloperName, MasterLabel FROM SkillType ORDER BY DeveloperName。標準 sObject のため、名前空間の置換は不要です。

出力: DeveloperName と MasterLabel を含む SkillType のレコード一覧。空の場合も、インタビューではユーザーが意図する Developer Name 文字列を取得する必要があります。その値を出力された prerequisites[] ブロックに含めて、sfs-sobject-create が書き込みにフラグを付けるか、保留できるようにします。

備考: スキルの本文では、「この設定が順位付けする Skill Type はどれですか? Developer Name を入力してください」と明示的に尋ねます。このステップにより、実際のレコードが存在する場合、インタビューで候補を自動補完できます。

list-service-appointment-boolean-fields

ServiceAppointment sObject の Boolean 項目をすべて一覧表示します。Service Appointment を基準とする Relevance Group のために、インタビューで使用します。グループのスコープ設定に使う Boolean 項目は、このオブジェクトに存在する必要があります。応答の項目を type == "boolean" でフィルタリングし、カスタム名と標準名の両方を提示します。兄弟スキルの sfs-work-rule-designer と同じ調査です。両方のスキルで SA-boolean ドメインを共有します。

呼び出し: GET /services/data/v68.0/sobjects/ServiceAppointment/describe

出力: describe 応答から、type == "boolean" の fields[] を取得し、name + label + custom + defaultValue を抽出します。対象組織のベースラインは Boolean 項目 18 件です。うちカスタム項目が 13 件(すべて FSLMPPERF__ で始まる項目:Auto_Schedule__c、Emergency__c、InJeopardy__c、IsFillInCandidate__c、IsMultiDay__c、Pinned__c、Prevent_Geocoding_For_Chatter_Actions__c、Same_Day__c、Same_Resource__c、Schedule_over_lower_priority_appointment__c、UpdatedByOptimization__c、Use_Async_Logic__c、Virtual_Service_For_Chatter_Action__c)、標準項目が 5 件(IsBundle、IsBundleMember、IsDeleted、IsManuallyBundled、IsOffsiteAppointment)です。

備考: Service Appointment をスコープとする Relevance Group では、指定された Boolean 項目がここにすでに存在している必要があります。インタビューの参加者がこの一覧にない項目を指定した場合は、続行する前に(メタデータのワークフローを通じて)その項目を作成するよう促すか、不足している項目を出力された prerequisites[] ブロックに含めます。

list-service-territory-member-boolean-fields

ServiceTerritoryMember sObject のすべての Boolean フィールドを一覧表示します。Service Territory Member を基準とする Relevance Groups でのみ使用します。グループのスコープ設定用 Boolean はこのオブジェクトに存在する必要があります。レスポンスのフィールドを type == "boolean" でフィルタリングします。兄弟スキル sfs-work-rule-designer と同じプローブです。

呼び出し: GET /services/data/v68.0/sobjects/ServiceTerritoryMember/describe

出力: 新規の FSL インストールでは、ServiceTerritoryMember にカスタム Boolean フィールドはありません(標準の IsDeleted のみ)。したがって、ServiceTerritoryMember を基準とするすべての Relevance Group は、ユーザーが先に作成するカスタム Boolean に依存します。カスタム Boolean の結果が空の場合、スキルは出力する prerequisites[] ブロックにこの依存関係を示す必要があります(スキル本文の例:「Service Territory Member に Break_Group_France__c Boolean フィールドを作成し、フランスのリソースでは true に設定してください。」)。

注記: ServiceAppointment とは対照的です(プローブで、対象組織に FSL パッケージのカスタム Boolean が 13 個あることを確認済み)。ServiceTerritoryMember をスコープとする Relevance Group は、すべてユーザーが作成するものです。

list-existing-service-goals

既存の <NS>__Service_Goal__c レコードを読み取り、インタビュー中の参考例として使用します。上流の sfs-scheduling-policy-designer が変更の開始点を示した場合に便利です。インタビューでは、ポリシー上の現在の目標をベースラインとして提示し、ユーザーがそこから変更できるようにします。

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

入力:

  • q (String) — SOQL:SELECT Id, Name, RecordType.DeveloperName FROM <NS>__Service_Goal__c ORDER BY RecordType.DeveloperName。resolve-fsl-namespace で検出した名前空間を代入します。

依存関係: resolve-fsl-namespace

出力: 既存の目標の一覧。対象組織には 9 個のシードレコードがあり、Objective_Custom_Logic を除く 10 個の RecordType を網羅しています。参考資料としてのみ使用してください。出力する serviceObjectives[] ブロックには、この一覧をそのままコピーせず、インタビューで設計した内容を反映してください。

注記: 種別の判別には RecordType.DeveloperName を使用します。Service_Goal__c には FSLMPPERF__Type__c フィールドはありません(兄弟スキルの Work_Rule__c と同様です)。

書き込み操作
emit-service-objectives-block

インタビューの最終ステップです。ユーザーが目標のセットと重みを確認すると、このスキルは会話チャネルに、{ serviceObjectives: [ { type, name, weight, params, relevanceGroup, rationale } ] } 形式の JSON ブロックと、ユーザーが作成する依存関係(STM 基準の Relevance Group 用カスタム Boolean、SkillType Developer Name、カスタム Resource Priority フィールド)を含むトップレベルの prerequisites[] リストを出力します。デリゲートスキル(sfs-sobject-create)がこれを使用して、対応する Service_Goal + Scheduling_Policy_Goal ジャンクションレコードを書き込みます。これは API 呼び出しではなく、クライアント側のワークフロー境界です。

依存関係: enumerate-service-objective-record-types、describe-service-goal-schema、list-scheduling-policy-goal-junction、list-skill-types、list-service-appointment-boolean-fields、list-service-territory-member-boolean-fields

その他の操作
resolve-fsl-namespace

インストール済みの FSL 管理パッケージの名前空間プレフィックスを解決します。本番環境でのプレフィックスは FSL、内部組織では FSLQA / FSLMPTEST / FSLMPPERF です。このスキルで以降に実行するすべての SOQL、describe、JSON ブロックの出力では、検出したプレフィックスを使用した <NS>__Service_Goal__c と <NS>__Scheduling_Policy_Goal__c を使ってください。FSLMPPERF をハードコードしないでください。

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

入力:

  • q (String) — SOQL: SELECT SubscriberPackage.NamespacePrefix, SubscriberPackage.Name FROM InstalledSubscriberPackage。WHERE 句は指定しません。Tooling API は NamespacePrefix に対するフィルターを拒否するためです。フィルターを指定せずにクエリし、クライアント側で絞り込んでください。

出力: records 配列から、SubscriberPackage.NamespacePrefix が FSL で始まる最初のレコードを選びます(大文字と小文字を区別)。その NamespacePrefix が、この組織で使用する実行時の <NS> です。結果が空の場合は FSL がインストールされていないため、FSL パッケージのインストールをユーザーに案内するメッセージを表示して中止してください。

注: sfs-scheduling-policy-designer と sfs-work-rule-designer の兄弟スキルで実証済みのパターンです。現在の対象組織である sfs-headless-skill-building で検出されたプレフィックスは FSLMPPERF です。

handoff-to-sobject-create

emit-service-objectives-block の後、sfs-sobject-create に引き継ぎます。 委譲先には、蓄積された設計コンテキストとして、sfs-scheduling-policy-designer の親ポリシー、sfs-work-rule-designer の workRules[] ブロック、このスキルの serviceObjectives[] ブロックに加え、最終的な書き込みに正しいプレフィックスを使用できるよう、検出した FSL 名前空間も引き継がれます。Salesforce API の呼び出しはすべて sfs-sobject-create が担当します。このスキルは設計値を出力するだけで、レコードは作成しません。

依存関係: emit-service-objectives-block

メモ

  • 構成: これは、3人のデザイナーと1人のデプロイ担当者で構成される連鎖の3人目のデザイナーです。 sfs-scheduling-policy-designer → sfs-work-rule-designer → THIS → sfs-sobject-create 各デザイナーはJSONのサブブロックを出力します。デプロイ担当者は、連鎖全体が完了し、ユーザーが承認した後にSalesforceレコードを書き込みます。このスキルのサブブロックはserviceObjectives[]で、設計から必要と判断されるprerequisites[]エントリも含みます。
  • 権威性: インタビューで提示する10種類の目標タイプは、ステップ enumerate-service-objective-record-typesで列挙されるRecordType DeveloperNameの値に基づきます。組織がFSLのインストールをカスタマイズして追加のレコードタイプ(新しいバージョンのGroup Nearbyなど)を追加している場合、インタビューではそれらを提示します。一部を無効にしている場合は、提示しません。
  • 冪等性: このスキルはデータを出力しますが、書き込みは行いません。下流の冪等性に関する考慮事項は、sfs-sobject-createの書き込み処理が担います(Service_Goalは(policy, RecordType)または名前で重複排除、Scheduling_Policy_Goalジャンクションは(Scheduling_Policy, Service_Goal)で重複排除)。ジャンクションのスキーマにあるWeight__c(9,0) NOT NULL制約を満たすには、書き込み処理で整数の重みを指定する必要があります。スキルのグローバルルール #1(導出した重みをすべて切り上げて丸める)により、この制約を満たします。
  • 重みの導出: 重みの導出に関するすべての計算(基準値としてのMinimize Travelの1000、ASAPとの交差点、Same SiteとASAP/Preferred Resourceの比較、Same Siteの実効×10乗数に対する×0.01の例外など)は、エージェントがクライアント側で行う決定論的な演算です。ワイヤー操作は関与しません。 出力前に整数への切り上げを行うことで、デプロイ時にScheduling_Policy_Goal.Weight__c(9,0) NOT NULL制約が満たされます。
  • ワイヤー形状の参照: 現在のsObjectのdescribeから読み取った、インタビューと下流のデプロイで遵守すべきフィールド形状の事実:
  • FSLMPPERF__Service_Goal__c.FSLMPPERF__Custom_Type__c — 有効な値が14個ある選択リストです。このスキルが提示する10種類の目標はRecordType.DeveloperNameで対応付けられます。選択リストの値の範囲はさらに広く、Objective_Task_Priorityと、保存文字列にASCII以外の結合文字を含む旧式の距離ベース優先度も含まれます(手入力しないでください。必要な場合はdescribeから読み取ってください)。
  • FSLMPPERF__Service_Goal__c.FSLMPPERF__Prioritize_Resource__c — 選択リスト(2)で、値はLeast Qualified(デフォルト)とMost Qualifiedです。Skill Level目標のmodeパラメーターに対応します。
  • FSLMPPERF__Service_Goal__c.FSLMPPERF__Gap_Duration__c — Number(4,0)、 nillable=trueです。スキルのminGapMinutesです。
  • FSLMPPERF__Service_Goal__c.FSLMPPERF__Resource_Priority_Field__c — String(255)で、デフォルトの数式は"fsl__priority__c"です。
  • FSLMPPERF__Scheduling_Policy_Goal__c.FSLMPPERF__Weight__c — Number(9,0) NOT NULLです。したがって、上限は999,999,999です。スキルが計算する基準値相対の重みは、この上限を大幅に下回ります。
  • ジャンクションの不変性: FSLMPPERF__Scheduling_Policy_Goal__cには、両方の主従関係参照 (FSLMPPERF__Service_Goal__cとFSLMPPERF__Scheduling_Policy__c)があり、 updateable=falseに設定されています。ジャンクション行の親レコードとの関連付けは作成後に変更できません。別のポリシーに目標を付け替える場合、またはポリシーと目標を入れ替える場合、デプロイ担当者はジャンクション行を削除して再作成する必要があります。両方の参照でcascade-delete=trueに設定されているため、どちらかの親レコードを削除するとジャンクションも削除されます。FSLMPPERF__Weight__c自体は更新可能で、作成後もその値を調整できます。

コメント