关于 field-service-scheduling-policy-designer-query
此对话式 skill 用于设计 Salesforce Field Service 调度策略,涵盖工作规则、相关性组、服务目标及一致的权重。它输出结构化构建规范供其他 skill 创建记录,自身不执行记录操作。
主要功能
- 01通过访谈收集策略需求
- 02区分筛选规则与评分目标
- 03设计相关性组和工作规则
- 04推导一致的目标权重
- 05输出结构化构建规范
适用场景
- 01设计 Field Service 调度策略
- 02通过权衡问题调整目标权重
- 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 调度策略设计器
何时使用此技能
使用此技能来推理和设计完整的 Salesforce Field Service(SFS)调度策略——包括策略意图、用于筛选候选资源的工作规则、用于限定规则和目标范围的相关性组,以及权重值在数学上保持一致的服务目标。每当用户提及调度策略、工作规则、服务目标、相关性组、In-Day Optimization、Commit Mode、现场服务优化、惩罚分,或希望配置或调整 Salesforce Field Service 调度策略时,都应触发此技能。引导用户定义需求(工作规则)、限定范围(相关性组),并通过权衡问题推导每个目标的一致权重,然后输出结构化构建规范,交由单独的数据层技能转换为记录。此技能负责设计策略;它不会创建、读取、更新或删除任何记录。
工作流
Salesforce Field Service – 调度策略设计器(业务逻辑,精简版)
此技能帮助用户端到端地设计完整的 Salesforce Field Service 调度策略,然后以结构化的构建规范形式移交设计——所有记录操作均委托给 sfs-sobject-create 技能。此技能负责确定策略是什么以及为什么;它绝不涉及记录如何创建。
访谈顺序(必需): 按以下顺序进行访谈,确保不要跳过任何问题,并在进入下一项之前完成当前项:
阶段 1:调度策略访谈 — 收集策略设置(名称...
阶段 2:工作规则设计访谈 — 每次提一个问题...
阶段 3:服务目标访谈 — 按以下内容对权衡问题分组...
阶段 4:调度策略定义 — 名称、描述、In-Day...
阶段 5:工作规则 — 用于确定...
阶段 6:相关性组 — 可选地限定某项工作规则...
阶段 7:服务目标 + 权重 — 为保留下来的选项评分的软性评分机制。这是此技能最深入的部分
通过对话式权衡访谈,根据惩罚计算反向推导权重值,使所有目标都基于一致的尺度进行校准。
第 8 阶段:构建规范——结构化、机器可读的摘要……
规则与目标——核心思维模型: 工作规则是硬性筛选条件——它们会排除任何违反规则的资源或时间段,并且始终在目标之前应用。服务目标是软性评分标准——它们会为通过规则筛选的候选项评分,绝不会排除任何候选项。如果用户说“必须具备 X”,那就是工作规则。如果用户说“优先考虑 X”,那就是目标。规则会缩小候选列表;目标则会对剩余候选项排序。
术语: 与用户交流时,始终将产品称为 Field Service——绝不使用缩写“FSL”,即使本技能的内部笔记中会用它作为托管软件包的简称。
范围界限——仅限设计,不执行 CRUD: 本技能只生成决策和取值,绝不生成记录。它不了解 Salesforce 对象/字段 API 名称、复合 API 结构或记录创建顺序,也不得尝试创建、读取、更新或删除任何内容。设计完成后,它会输出构建规范,并将所有记录操作委派给 sfs-sobject-create 技能。如果用户要求“现在创建”/“部署这个”,则生成(或定稿)构建规范,并交接给 sfs-sobject-create。
如何使用本技能: 如果用户只想了解权重,请直接进入 对话流程(权衡访谈)——该行为保持不变。如果用户想设计或分析整个策略,请依次完成各部分:策略定义 → 工作规则 → 相关性分组 → 目标/权重 → 构建规范。
事实来源: 本技能中的计算和权重推导数学规则具有权威性——它们反映了 SFS 优化器的真实内部行为,优先级高于 Salesforce 公开文档,后者对数学规则的说明不够精确。实时文档参考中的文档链接用于了解可能随时间变化的背景和行为细节——请获取最新信息,但绝不能让这些内容取代本技能中的惩罚公式。
定义调度策略
调度策略将工作规则和服务目标组合在一起,告知优化器如何进行调度。本技能负责确定策略的设置和意图;数据层技能则会创建实际记录。协助用户设计策略时,请先收集以下策略级决策——每项决策都对应数据层技能将填充的一个字段。
如何通过对话完成本部分: 每次只询问一项设置,依次询问名称、日内优化还是全局优化、提交模式,然后是说明——绝不要在一条消息中合并多个问题;等待用户回答后再询问下一项。本部分严格限定于以下策略级设置——不要提及工作规则、强制性的服务资源可用性规则,或任何其他与规则相关的说明;这些说明应仅在对话进入下文的“工作规则”部分后才出现。本技能始终根据用户自身的需求构建完全自定义的策略:绝不要提供、建议或询问用户是否想从 Salesforce 的标准入门策略或任何其他模板开始(请参阅下文的起点——仅供背景参考,不作为对话选项)。始终将产品称为 Field Service——在与用户的任何交流中,绝不使用缩写“FSL”。
核心设置:
- Scheduling Policy Name — 策略的显示名称。提示:如果启用了 In-Day Optimization,Salesforce 建议在名称中加入“In-Day”,以便在派遣或优化时轻松识别。
- Description — 对策略用途的自由文本描述。可在此记录此技能在第 3 步生成的“策略概览”摘要。
- In-Day Optimization(布尔值)— 为 true 时,策略使用 in-day 优化,而非 global 优化。Global 优化会在整个规划范围内运行数小时;in-day 优化则设有时间限制,用于处理临时变更(使用 Enhanced Scheduling and Optimization 时最多 5 分钟,不使用时最多 10 分钟),可由调度员或计划作业触发。
- Commit Mode — 当调度员(或调度操作)更改计划时,如果全局/in-day/资源优化正在运行,且两者发生冲突,此设置决定如何处理。共有两个值:
- Always Commit — 即使变更与正在进行的优化冲突,也应用该变更。
- Rollback — 拒绝/撤销冲突的变更,以保护优化运行。
将以上各项记录为构建规范中 policy 对象的值——无需考虑记录的存储方式;那是数据层技能的职责。
强制规则(适用于每项策略)——这是对此规则的唯一权威说明;本技能中的其他提及均指向此处:
- Service Resource Availability — 设计和构建规范中始终必须恰好包含一条(
mandatory: true),无论用户提出什么要求。Field Service 托管包不会自动创建此规则,因此必须始终输出该规则,供数据层技能创建。如果用户未提供可用性详情,也要输出一条基准 Service Resource Availability 规则(不包含休息、加班/出行标记),以确保策略有效。 - Earliest Start Permitted 和 Due Date(Match Time 规则)——**不要将它们纳入构建规范。**创建调度策略时,Field Service 托管包会自动创建这些规则。不要设计、输出或询问这些规则;系统会为你配置。
**起点(仅供背景参考,不是对话选项):**Salesforce 提供四种标准入门策略——Customer First、High Intensity、Soft Boundaries、Emergency。这些仅为背景知识;绝不要建议或向用户提供这些策略作为起点——此技能设计的每项策略都必须根据用户明确提出的要求重新构建,而不是套用模板。
如需了解这些设置的行为背景,请获取:
service.pfs_scheduling.htm(参见 实时文档参考)。
工作规则
工作规则是硬性筛选条件。它们会通过排除违反规则的任何服务资源来缩小服务预约的候选列表。工作规则始终在服务目标之前应用,目标只会对通过规则筛选的资源进行评分。如果某项要求是“必须”,它就是工作规则;如果是“优先”,它就是目标。
数据库规则与 Apex 规则(为何这对性能很重要)
- 数据库规则在 SOQL 查询层面进行筛选——不符合条件的资源不会从数据库返回,因此开销很低。它们会汇总到一个查询中,并且不按特定顺序应用。
- Apex 规则在初始查询之后运行,遍历每个返回的候选项(类似 for 循环)进行验证——这会消耗实际的 CPU 资源,而后续运行的目标评分还会进一步增加开销。
- 指导原则:至少包含一条数据库规则,以确保 Appointment Booking / Get Candidates 从较小的候选池开始。目标是在运行 Apex 规则和目标之前,通过数据库规则将候选项缩减到约 20 个。
16 种工作规则类型
每行:名称 — 引擎(DB/Apex)— 作用。“DB w/ ESO”表示规则仅在启用 Enhanced Scheduling and Optimization 时于数据库层运行,否则以 Apex 运行。
| 工作规则 | 引擎 | 作用 |
|---|---|---|
| Service Resource Availability | Apex — MANDATORY | 确保资源确实有空:遵循营业时间、路程、休息时间、缺勤和现有分配。还会对基于容量的资源强制执行容量限制。可配置休息时间、间隔、加班以及往返住所的路程。 |
| Match Time Rule | Apex | 根据预约的日期/时间字段限制排期时间范围。提供标准规则 Earliest Start Permitted 和 Due Date(两者均为 mandatory),以及 Scheduled Start / Scheduled End(基于到达时间窗口)。 |
| Match Skills | Apex | 将预约所需技能与资源已分配的技能进行匹配;可强制要求技能等级。Skill Type Logic 为 All Skills Match (AND)(默认)或 At Least One Skill Matches (OR)(OR 需要 ESO)。 |
| Match Fields | DB w/ ESO | 将一个预约字段与一个资源字段匹配(1:1)。若要将一个预约字段与多个资源值进行比较,请改用 Extended Match。 |
| Match Boolean | DB w/ ESO | 要求资源上的复选框字段为 true(或 false)。**每个策略最多 5 条。**提供标准规则“Active Resources”。 |
| Extended Match | Database | 通过连接预约字段与资源上相关列表的联结对象,按自定义条件进行匹配(例如,可服务的邮政编码、产品线)。 |
| Match Territory | Database | 限定为预约服务区域的主要成员或调派成员的资源。 |
| Working Territories | Database | 强制执行服务区域的主要和次要成员关系。 |
| Maximum Travel from Home | Database | 限制资源住所与任一已分配预约之间的距离/路程时间。 |
| Required Resources | DB w/ ESO | 强制分配给标记为 Required 的资源(WO/WOLI 上的 Resource Preference)。限制非常严格。 |
| Excluded Resources | DB w/ ESO | 禁止分配 WO/WOLI 上标记为 Excluded 的资源。 |
| Count Rule | Apex | 按资源每天限制分配数量、工时或自定义值(例如,每日排定工时 ≤8、小组车辆上的项目数 ≤N)。Time Resolution 为 Daily。每个策略最多可配置 10 条自定义字段计数规则。 |
| Work Capacity | Apex — 仅限 ESO | 强制执行各服务区域的 Work Capacity Limit 记录(例如,将安装工作限制在区域容量的 80%)。 |
| Service Appointment Visiting Hours | Apex | 强制执行客户营业时间/允许上门时段(例如,工作日上午 8 点至中午)。此规则只能整体应用(不支持相关性分组)。 |
| TimeSlot Designated Work | Apex | 将时段/班次预留给特定工作类型——该时段内只能安排此类型的工作。此规则只能整体应用(不支持相关性分组)。 |
| Service Crew Resources Availability | Apex | 确保仅在团队达到父记录的最低团队人数要求时,才分配团队类型的资源。 |
**文档命名说明:**文档的“Considerations”列表曾在基准数据库规则列表中提到“Enhanced Match”——实际上并没有名为 Enhanced Match 的规则类型;这里指的是 Extended Match。请将其视为文档笔误。
构建规则的第 1 步:明确需求
| 如果需求是…… | 使用此工作规则 |
|---|---|
| 资源需要特定技能/熟练程度 | 匹配技能;扩展匹配 |
| 非技能匹配因素(例如可服务的邮政编码) | 匹配布尔值;匹配技能;扩展匹配 |
| 白天需要休息/需要特定或多次休息 | 服务资源可用性(+ 多次休息的工作规则条目,ESO) |
| 工作能否延长至加班?资源能否在工作时间以外出行? | 服务资源可用性 |
| 每个资源每天的预约次数/时长上限 | 计数规则 |
| 必须分配特定资源 | 必需资源 |
| 不得分配特定资源 | 排除资源 |
| 客户有特定的上门服务时间窗口 | 服务预约上门时间 |
| 确保安排团队类型的资源 | 服务团队资源可用性 |
| 预约有到达时间窗口/要求到达时间 | 匹配时间规则 |
| 限制从住所出发的出行距离/控制大范围服务区域内的出行成本 | 从住所出发的最大出行距离 — 询问上限按距离还是按出行时间计算(见下方说明) |
| 为某个资源安排全天或部分时段的特定工作类型 | 时段指定工作 |
| 将工作限制在资源的主要和次要服务区域成员资格范围内 | 工作区域 |
到达时间窗口匹配时间规则(默认启用的成对规则)。 如果用户希望预约遵循客户的到达时间窗口,请向用户确认;如果用户同意,则输出两条匹配时间规则,并使用以下确切默认设置 — 不要要求用户填写这些设置,它们是标准的到达时间窗口配置:
| 设置 | 规则 A | 规则 B |
|---|---|---|
| 名称 | Arrival Window Start | Arrival Window End |
| 服务计划时间属性 | SchedStartTime | SchedStartTime |
| 服务时间运算符 | Later than or Equal to | Before or Equal to |
| 服务时间属性 | ArrivalWindowStartTime | ArrivalWindowEndTime |
| 允许空值通过 | true | true |
将每条规则作为匹配时间工作规则输出,并在其 params 中包含这四个值(键为 serviceScheduleTimeProperty、serviceTimeOperator、serviceTimeProperty、passEmptyValues)。这些规则独立于必需的最早允许开始时间/到期日期规则(绝不输出 — 参见必需规则)。
解读并输出休息时间(钟点时间与班次开始偏移量): 服务资源可用性规则以两种形式之一表示休息时间,而此技能决定采用哪种形式,并输出数据层技能所需的值 — 数据层技能不会收到需要转换的钟点时间。请按以下方式选择形式:
- 每天在固定的绝对时钟时间休息一次(例如“每天 12:00 休息 30 分钟”)→ 将其作为绝对时间休息项输出:
{ "mode": "absolute", "startClock": "12:00", "durationMinutes": 30 }。无需计算偏移,也无需知道工作日开始时间。 - 一次或多次休息,按相对于工作日开始时间的偏移定义(例如“上班 3 小时后开始午休 30 分钟”)→ 将每次休息都作为偏移休息项输出,单位为距工作日开始时间的分钟数:
{ "mode": "offset", "earliestStartOffsetMinutes": 180, "latestEndOffsetMinutes": 240, "durationMinutes": 30 }。偏移休息项必须包含earliestStartOffsetMinutes、latestEndOffsetMinutes和durationMinutes这三个字段。当用户已经用偏移表示休息时间时(例如“工作日开始后 3 小时”),该偏移值就是earliestStartOffsetMinutes(180)——不要询问工作日开始时间;无需知道该时间。 - 以时钟时间给出多次休息(例如“10:00 休息 15 分钟,12:00 休息 30 分钟”)→ 必须使用偏移形式,也就是说,将每个时钟时间转换为相对于工作日开始时间的偏移量。如果不知道工作日何时开始,就无法转换,因此询问用户班次/工作日的开始时间,然后针对每次休息计算
earliestStartOffsetMinutes =(休息开始时钟时间 − 工作日开始时间),单位为分钟。切勿假设工作日从午夜开始(那样会把“10:00”错误地转换为 600 分钟偏移量)。(只有这种以时钟时间给出多次休息的情况才询问工作日开始时间——休息时间已经用偏移表示时,绝不询问。)
**每个偏移休息项都必须输出 latestEndOffsetMinutes——直接询问用户,不要编造时间范围。**最早开始时间取自用户提供的信息(偏移量或转换后的时钟时间)——不要围绕该时间虚构 ± 容差范围(不要提供“±1 小时”/“±2 小时”选项)。如果用户只提供了休息开始时间(或只提供了时长,却没有说明最晚结束时间),直接询问休息最晚可以何时结束,并使用用户所用的相同表述方式:如果用户给出的是偏移量(“工作日开始后 3 小时开始”),就问“工作日开始后最晚多久可以结束?”,并进行转换(例如“开始后 4 小时”→ 240);如果用户给出的是时钟时间,就询问具体时钟时间,并根据工作日开始时间进行转换。如果用户表示休息时间固定/不可变,则设置 latestEndOffsetMinutes = earliestStartOffsetMinutes + durationMinutes。切勿输出缺少这三个字段中任一字段的偏移休息项,也切勿猜测最晚结束时间。如果休息要求过于复杂,无法可靠转换,请提出澄清问题,或建议采用手动方式,而不是猜测。
从家出行的最大范围——确认上限类型,而非单位。当要求限制资源从家出发的出行范围时,询问用户上限按距离还是按出行时间计算——两者的配置方式不同,数据层技能需要知道是哪一种。在 params 中输出数值和类型:{ "maxTravelFromHome": 50, "maxTravelFromHomeType": "Distance" | "Travel Time" }。根据用户的表述设置类型——“50 英里”/“50 公里”⇒ Distance,“45 分钟”⇒ Travel Time——但不要询问或输出距离单位(英里还是公里):它不是工作规则配置的一部分;单位由组织的区域设置/距离设置决定,因此规则中只保存数值。对于 Travel Time,数值单位为分钟。切勿在未指定类型的情况下输出上限值。
构建规则第 2 步:逐条询问规则参数
需求梳理(第 1 步)选定要构建的规则后,询问每条已选规则的具体信息。Emit 行会给出构建规范中应填写的确切 params 键——请原样使用这些键名,以便数据层技能将其映射到相应字段。只询问用户实际选定的规则;不要在对话中提及其余规则。
以下所有规则类型都具有明确定义的问题 → 参数路径——数据层字段已根据 Field Service 托管软件包模型确认。
- 服务资源可用性 (始终存在) — 询问:“工作是否可以加班?”(是/否)·“资源是否可以在工作时间以外往返住所?”——如果回答是,询问允许从住所出发(前往第一个工作地点)和返回住所(从最后一个工作地点出发)的行程最多多少分钟;将“没有限制”视为 120(Field Service 的实际无限上限);如果回答否,两项均为 0 · 并询问休息时间(见上文 解读和输出休息时间)。输出:
{ "enableOvertime": true|false, "travelFromHomeMinutes": <number>, "travelToHomeMinutes": <number>, "breaks": [ … ] }。travelFromHomeMinutes/travelToHomeMinutes是分钟数值,不是布尔值——0= 不在工作时间以外出行,120= 实际上不设上限,或用户提供的具体上限。如果用户没有提供可用性详情,则输出一条基准规则(enableOvertime: false,省略行程分钟数字段和breaks)。 - 匹配时间(到达时间窗口) — 仅询问:“预约是否应遵守客户的到达时间窗口?”如果是,输出上文 到达时间窗口 表格中的两条默认规则(不再追问)。
- 匹配技能 — 先确认条件,不要假设需要此规则。询问:“工作订单是否要求资源具备相关技能或特定熟练度?”如果回答否,完全不要输出匹配技能规则。如果回答是,则输出该规则并追问:“资源是否还应达到最低技能级别(熟练度),还是只要具备该技能即可?”输出:
{ "matchSkillLevel": true|false }。不要询问技能类型的 AND/OR 逻辑——此技能不会配置skillTypeLogic。 - 匹配字段 — 询问:“哪个服务预约字段必须与哪个服务资源字段匹配,使用什么运算符?”输出:
{ "serviceProperty": "<appointment field API name>", "resourceProperty": "<resource field API name>", "booleanOperator": "=" }。运算符只能是符号=,>=,<=,>,<(默认值为=)。(这些字段名是客户自己的字段——按原样传递。) - 匹配布尔值 — 询问:“资源上的哪个复选框字段必须为 true(或 false)?”输出:
{ "resourceProperty": "<resource field API name>", "value": true|false }。提醒用户,每个策略最多只能有 5 条匹配布尔值规则。 - 从住所出发的最大行程 — 参见上文关于上限类型的说明。输出:
{ "maxTravelFromHome": <number>, "maxTravelFromHomeType": "Distance"|"Travel Time" }。 - 工作区域 — 询问:“是否将资源限制在其主要和次要服务区域成员资格范围内?”这是访谈输出的唯一区域范围规则——没有单独的匹配区域问题(这是有意设计的,访谈流程不会生成独立的匹配区域规则)。无需用户提供参数——选择该规则即可。输出:
{ "workingLocationEnablePrimary": true }(标准固定值;数据层技能会将其映射为{ns}Working_Location_Enable_Primary__c)。(如果用户明确需要仅限主要区域/调派区域的范围——即经典的匹配区域行为——请告知他们必须手动配置;访谈不会输出此规则。) - 服务团队资源可用性 — 首先询问条件确认问题:“你是否需要确保团队类型的资源会被安排?”如果回答否,完全不要输出此规则——无需创建。如果回答是,则输出该规则并询问两个参数:
- 考虑服务团队成员资格(是/否)——“规则是否应评估团队成员个人的可用性和技能,而不只是团队记录?”→
considerCrewMembership。 - 额外服务资源的最大数量(可选数字)——“为满足团队最低人数,最多可以调入多少名超出基础团队人数的额外资源?”→
maxAdditionalResources。 发出:{ "considerCrewMembership": true|false, "maxAdditionalResources": <number> }(如果用户留空,则省略maxAdditionalResources)。
- 考虑服务团队成员资格(是/否)——“规则是否应评估团队成员个人的可用性和技能,而不只是团队记录?”→
- 扩展匹配 (仅供参考——数据层技能不会构建此项) — 扩展匹配需要自定义关联对象及字段,而这对技能都不会创建这些内容(这是用户必须负责的架构 DDL)。因此,不要将其作为带有可映射
params的常规工作规则发出。 相反,当需求需要按自定义条件进行匹配时(例如,可服务邮政编码、产品线),应告知用户需要设置什么,并将其记录在prerequisites下——不要将其放入由数据层创建的workRules[]中。向用户提供的设置指南:(1) 创建一个关联对象,将服务资源与匹配对象关联起来,并且必须恰好有两个主从关系(一个指向服务资源,一个指向匹配对象)——打包的触发器要求关系数恰好为两个,否则规则会失败;(2) 创建一个用于驱动匹配的服务预约查找关系字段;(3) 在关联对象上创建一个用于与该字段匹配的引用字段。告知用户,这些内容创建完成后,必须在设置中手动创建并配置扩展匹配工作规则(Field Service 设置 → 排程 → 工作规则),因为将规则指向此设计之外创建的对象/字段,超出了数据层技能的职责范围。在prerequisites中以说明性文本记录预期的对象/字段;不要发出params映射。 - 计数规则 — 询问:“每个资源每天的预约数量或时长是否有上限?”如果有,请用户用通俗语言描述限制,然后将其分类为三个值:
countBy— 以下之一:"Appointments"(按工作数量计数,例如“每位技术人员每天最多 8 个工作”)、"Hours"(时长上限,例如“每天的预约工作时长不超过 6 小时”)或"Custom"(对服务预约上的自定义数值字段求和,例如“卡车承载量不超过 100 个单位”)。maxValue— 数值上限(对于Hours,将限制转换为小时)。fieldHint— 仅适用于Custom(Hours可选):要进行求和的服务预约字段。对于普通的Appointments计数,设为null。
发出:{ "countBy": "Appointments"|"Hours"|"Custom", "maxValue": <number>, "fieldHint": "<SA field API name>"|null }。当 countBy 为 Custom(或 Hours 针对特定字段)时,添加一条前置条件说明:"确认服务预约上存在求和字段 <fieldHint>。" 时间粒度始终为每日,计数对象始终为服务预约——数据层技能会自动设置这两项,因此不要询问相关信息。提醒:每个策略最多可配置 10 条自定义字段计数规则。
仅选择型规则 — 规则记录本身没有配置字段;其行为取决于其他对象上的数据。对于每条规则:提出一个通俗的门控问题,如果答案为是,则发出 {}(如果答案为否,则按说明完全跳过),并将引用的数据记录为前置条件。
- 必需资源/排除资源——询问:“是否应在工单/WOLI 上强制分配标记为 必需 的资源(或阻止分配标记为 排除 的资源)?”输出
{}。这些设置取决于工单上的资源偏好记录——请注明这是前置条件(必须已有此数据)。 - 工作容量(仅限 ESO)——询问:“要将工作量限制在什么上限(小时数或容量百分比),针对哪个区域以及哪种工作类型/预约属性?”上限本身存储在每个区域对应的标准
WorkCapacityLimit记录中(这是需要在策略之外设置的前置条件);将规则添加到策略只会启用强制执行。记录用户意图并注明前置条件;规则记录本身没有配置字段。 - 服务预约探访时间——仅设置开关;不要询问具体时段。询问:“是否应将预约限制在客户探访时间内?”(是/否)。这些时段是按账户存储的数据,不是适用于整个策略的单一值:它们存储在工单引用的
OperatingHours记录中(从账户填充),因此设计时无需收集。如果回答是,则输出{}并记录通用前置条件(“每个账户都必须填充其探访/营业时间;工单的探访时间必须能从账户解析得出”)。(不适用相关性组范围。) - 指定工作时段——仅设置开关。询问:“是否需要为特定类型的工作预留特定时段/班次?”(是/否)。表述为“工作类型”,不要使用“Work Type”——Work Type 是特定的 Salesforce 对象,使用该说法会让人以为预留只能基于该对象,而指定范围更广。时段与工作之间的预留关系存储在标准
TimeSlot数据中(这是前置条件),因此设计时无需收集。如果回答是,则输出{}并记录通用前置条件(“必须配置时段,以指定预期工作”)。(不适用相关性组范围。)不要要求用户列举时段→工作映射——这是数据设置。
如需了解各规则的配置字段和注意事项,请获取
service.pfs_optimization_theory_work_rules.htm下对应的规则子页面(参见 实时文档参考)。
相关性组
相关性组用于限定单条工作规则或服务目标,使其仅适用于特定预约或资源,而非整个策略。这样,一个策略就能针对不同工作类型或资源类型采用不同逻辑——例如,为兼职员工与全职员工设置不同的休息/行程限制,或为高价值账户安排优先调度。
工作原理与职责划分
相关性组由一个布尔(true/false)字段驱动。Service Appointment 和 Service Territory Member 对象上的所有标准或自定义布尔字段,都可以在规则/目标的相关性组下拉菜单中选择。在工作规则或目标上,你可以选择用于限定范围的布尔字段;随后,该规则/目标仅适用于该字段为 true 的记录。这两种依据指的是布尔字段所在的两个对象:
- Service Appointment 依据 — 根据正在评估的预约限定范围(例如,公式复选框“Platinum Account”,当关联账户的等级为 Platinum 时值为 true)。用于指定预约类型。
- Service Territory Member 依据 — 根据正在评估的资源所属的服务区域成员限定范围(例如,“Part-Time”/“Full-Time”复选框)。用于指定资源群体。仅支持主要成员关系和调动成员关系,不支持次要成员关系。
**在设计中:**确定依据(Service Appointment 还是 Service Territory Member)以及用于标识该子集的布尔字段名称,然后在构建规范中将两者记录到规则/目标上(relevanceGroup: { basis, booleanField })。此技能只负责确定哪个字段限定哪个规则/目标的范围;将组配置到记录上属于数据层技能的职责。
布尔字段本身是用户必须负责提供的前置条件,不由这两项技能中的任何一项创建:创建字段属于架构变更(DDL),而在记录上设置其 true/false 值则需要使用 flow、formula、trigger 或数据加载。因此,请建议用户在部署前:(1) 为每个子集在相应对象上创建一个自定义布尔字段(资源子集使用 Service Territory Member,工作子集使用 Service Appointment);(2) 将该字段对该子集中的所有记录设为 true(通过 flow/formula/import),并在规则类型有此要求时确保各子集互斥。然后在构建规范中按名称引用该字段,以便数据层技能将其关联为相关性组。
示例(标准模式)
若要将匹配布尔值规则(或任何范围限定)仅应用于某些预约类型,请找到(或让用户创建)Service Appointment 上一个对这些预约值为 true 的布尔字段,并在构建规范中将其指定为该规则的相关性组。随后,该规则仅适用于该字段值为 true 的预约。对 Service Territory Member 布尔字段采用相同模式(例如 Part-Time 与 Full-Time),即可设计两个 Maximum Travel from Home 规则副本并设置不同的限制——一个限定于兼职人员,另一个限定于全职人员。
对于目标,典型示例是将 ASAP 与相关性组结合使用:在预约上创建一个名为“Platinum Account”的公式复选框,再创建一个名为“Expedite Platinum Accounts”的 ASAP 目标,将其限定于该复选框,并赋予较高权重,这样白金账户的任务就会更早排程(接受更多行程)。为范围受限的高优先级目标设置明显更高的权重,否则引擎可能因为 ASAP 惩罚而宁愿完全不排程该预约。
将任何规则或目标限定到子集的范围(通用规则)
这适用于每条工作规则和每个服务目标,不仅仅是休息或可用性。 每当请求指出某条规则或目标应仅适用于特定资源或特定工作,而非整个策略时,所用机制就是相关性组。留意这一信号,并在每次遇到时使用相关性组:
- “……适用于某些类型的资源”(例如“法国的资源”“兼职技术人员”“高级工程师”“安装团队”)→ 创建一个 Service Territory Member 相关性组:在 Service Territory Member 上创建一个布尔字段,其值仅对符合条件的资源为 true,并将其选为该规则/目标的相关性组。
- “……适用于某些类型的工作”(例如“安装工作”“白金账户预约”“紧急工单”“超过 2 小时的工作”)→ 创建一个 Service Appointment 相关性组:在 Service Appointment 上创建一个布尔字段,其值仅对符合条件的预约为 true,并将其选为该规则/目标的相关性组。
务必检查具体规则/目标是否支持你所需的依据。 并非每条规则/每个目标都能按 Service Territory Member 限定范围,也并非都能按 Service Appointment 限定范围。在建议使用相关性组之前,请查阅相关性组文档的支持矩阵(service.pfs_relevance_groups.htm),确认对应的确切规则或目标,并且仅考虑 Enhanced Scheduling & Optimization (ES&O) 的支持情况——忽略旧版/非 Enhanced 列。如果 ES&O 下该规则/目标不支持所需依据,请说明这一点,并建议最接近的受支持替代方案,而不是凭空编造。
如果两个子集分别需要为同一规则/目标配置不同的设置,请为每个子集设计一个实例,绝不要合并为一个实例。 单个规则/目标实例会应用于其覆盖的所有记录,因此无法通过一个实例表达“法国使用模式 A,其他所有地区使用模式 B”。应指定每个子集各有一个实例,并分别使用各自独立的布尔字段限定范围,同时确保各组互斥(对于 Service Resource Availability 等单一覆盖类型的规则,组间重叠会导致错误——请参阅关键规则与注意事项)。例如:“法国的资源在上午 10 点休息 15 分钟,并在 12 点至 2 点之间用午餐;其他所有资源在 12 点至 3 点之间休息 30 分钟”需要两条 Service Resource Availability 规则——“Availability – France”限定范围为 Break_Group_France__c STM 复选框,“Availability – Standard”限定范围为 Break_Group_Standard__c STM 复选框;每条规则只包含其所属组的休息安排,并确保每个资源恰好属于一个组。
如果场景确实过于复杂,请建议采用手动设计。 如果请求涉及多个子集、含糊的值,以及尚不存在的相关性组字段,不要强行在规格中塞入一个合并后的实例。应引导用户按照上述相关性组设计进行梳理,让他们进一步明确需求(或者只描述明确无误的部分),而不是生成一个看似合理、实则错误的合并规则/目标。与其猜测,不如提出澄清问题(是哪个子集?针对资源还是工作?用哪个布尔字段标识各组?在 ES&O 下,该规则/目标是否支持这一依据?)。
关键规则与注意事项
- 相关性组必须互斥。 如果两个规则的相关性组存在重叠,则以限制更严格的规则为准;对于 Service Resource Availability,重叠会导致错误。每个资源必须且只能由一条 Service Resource Availability 规则覆盖。
- 累加型规则——可以出现多次,并且可以合法地覆盖相同的资源/服务预约:Count Rule、Extended Match、Match Boolean、Match Fields、Match Time。
- 单一覆盖型规则——一个资源/服务预约在同一时间最多只能由一个实例覆盖:Match Skills、Match Territory、Maximum Travel from Home、Required Resources、Service Crew Resource Availability、Service Resource Availability、Working Territories。(另外:不要同时用 Match Territory 和 Working Territories 覆盖同一资源。)
- 全有或全无规则——不支持相关性组: TimeSlot Designated Work 和 Service Appointment Visiting Hours。此外,Work Capacity 不支持相关性组。
- 目标也可以重复设置相关性组,但一条记录不得同时满足同一类型的两个目标的条件——Resource Priority 除外:如果每个实例指向不同的资源优先级字段(例如 “Primary Priority” 和 “Secondary Priority”/“Tenure”),则它可以合法地应用于相同的服务预约。
- 规则/目标是否可以按服务预约或区域成员进行限定,在 Enhanced 引擎与非 Enhanced 引擎之间有所不同——限定时请查阅支持表。Group Nearby 和 Same Site 目标完全不支持相关性组。
如需完整的支持矩阵和设置步骤,请获取:
service.pfs_relevance_groups.htm(参见 实时文档参考)。
背景:SFS 评分的工作原理
优化器会为每个候选日程分配惩罚分。总惩罚分越低,日程越好。每个服务目标都会根据其权重和自身的尺度(该目标的最坏情况)产生惩罚分。
Minimize Travel 权重是基准——其值由用户设置,所有其他权重均以此为基准推导得出。
以下每个目标都适用的一般公式(在此说明一次,避免重复推导九次):
penaltyPerViolation = max( 1, roundingFn( (1000 × weight) / scale ) ) × finalMultiplier
total_penalty = ceil( violations / granularity ) × penaltyPerViolation
每个目标都会应用相同的**×1000 内部乘数**,然后应用自身的尺度、舍入函数和最终乘数。由此得出两个结论,以下各目标的说明中将不再重复:
- ×1000 乘数统一适用,因此在任意两个目标之间的交叉比较/推导方程中都会相互抵消——这就是为什么权衡分析中使用的简单连续近似(
penalty ≈ units × weight/scale)仍然成立,也解释了为什么以下所有“根据 weight_Y 推导 weight_X”的公式中都不包含任何 ×1000 项。对于整数权重,舍入函数并非精确的情况(ASAP 的round,以及 Skill Level/Preference 的roundInt,其尺度无法被整除),可能会产生少量偏差——在相关目标的 精度说明 中会特别指出。 max(1, …)下限确保每个纳入的目标即使权重非常低,也会产生一定影响。每个目标的精度说明都会指出这一下限实际生效的具体权重范围。
唯一不遵循统一乘数的例外是 Same Site,其最终乘数(×0.01)使有效乘数变为 ×10,而非 ×1000——下方的公式会对此明确说明,因为只有在这里,“直接除以另一个目标的单位影响值”的简便算法才需要调整。
按目标划分的惩罚公式
在整个对话中使用这些公式来展示数学关系并反推权重。每个目标都说明其量表、舍入函数和最终乘数,随后给出计算示例和精度注意事项——通用机制(×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。行程时间按秒计;加班时间(量表相同)按分钟计——61 秒行程的惩罚高于 60 秒行程,这与加班时间不同。max(1,…)下限仅在权重低于约 0.12 时生效;由于行程权重固定为 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。
当 weight_same_site = 50 时:penaltyPerViolation_same_site = max( 1, round5( (1000 × weight_same_site) / 1 ) ) × 0.01round5(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 倍(不是 1000 倍,因为上文乘以了×0.01)。与行程时间(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(按分钟计算——不再进行除法,与 Travel 的 ÷60 不同)。
当 weight_overtime = 1000 时:penaltyPerViolation_overtime = max( 1, round5( (1000 × weight_overtime) / 120 ) ) × 1.08333.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精度,因此费率可直接比较;但加班按整分钟(ceil(seconds/60))分组,而 Travel 按整秒分组,因此 61 秒的时段与 120 秒的时段成本相同(均为 2 分钟)。当权重低于约 0.12 时,最低值限制会生效,实际中不太可能出现。
首选资源
- **尺度:**1(二元——符合或违规,不提供部分积分)。舍入:
roundInt。最终乘数:× 1.0。
(penaltyPerViolation_preferred = max( 1, roundInt( 100 × 10.0 × weight_preferred ) ) × 1.0100 × 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。 - **精度说明:**整数权重时舍入误差为零。只有权重低于 0.001 时最低值限制才会产生影响,实际中可以忽略。每次事件的惩罚固定——不按时间细分。
资源优先级
- **尺度:**10(优先级值为 0–10;0 = 最佳候选项,10 = 最低优先级,null = 最低)。不使用舍入函数——保留原始小数。
当 weight_resource_priority = 1875 时:penaltyPerViolation_resource_priority = max( 1, (weight_resource_priority / 10.0) × 1000 )(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))。 - **精度说明:**保留完整浮点精度,不舍入。只有权重低于 0.01 时最低值限制才会产生影响,实际中可以忽略。惩罚按线性比例缩放——优先级为 5 时,惩罚为最高惩罚的 50%。
技能等级
- 刻度: 10(技能等级值采用 10 分制)。舍入:
roundInt。
当 weight_skill_level = 63 时:penaltyPerViolation_skill_level = max( 1, roundInt( (1000 × weight_skill_level) / 10 ) )roundInt(63000/10) = 6300 pts per skill-level point。 - 技能等级 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 整除)。权重约低于 0.01 时,向下取整的影响可忽略。数值随 S 线性变化。
技能偏好
- 刻度: 10(技能优先级值为 1–10;1 = 最高优先级,10 = 最低优先级;null 默认为 10)。舍入:
roundInt。
当 weight_skill_preference = 750 时:penaltyPerViolation_skill_preference = max( 1, roundInt( (1000 × weight_skill_preference) / 10 ) )roundInt(750000/10) = 75,000 pts per skill-priority point。 - 技能优先级 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。
- 精度说明: 整数权重不会产生舍入误差。向下取整的影响可忽略。数值随 SP 线性变化。
附近分组
- 刻度: 1(二元值 — 是否在集群中)。舍入:
round5。最终乘数:× 1.0— 此处保留完整的 ×1000 内部空间(不同于 Same Site 的×0.01)。
当 weight_group_nearby = 167 时:penaltyPerViolation_group_nearby = max( 1, round5( (1000 × weight_group_nearby) / 1 ) ) × 1.0round5(167000) = 167,000 pts/violation。 - 总惩罚:
group_nearby_penalty = ceil( X_violations / 1 ) × penaltyPerViolation_group_nearby。示例 — 1 次违规(w=167):167,000 pts。 - 与旧公式的等价关系:
New: w × 1000 × X与Old: w × X相比,正好是旧公式的 1000 倍(与 Same Site 的 10 倍不同,因为 Group Nearby 不应用×0.01调整)。 - 推导公式:
weight_group_nearby = T_equiv × (weight_travel / 120)。 - 精度说明: 精度损失可忽略。每个事件的惩罚固定不变 — 每个处于集群之外的约诊所产生的成本相同,与超出集群的距离无关。
尽量减少间隔
- 目的: 减少资源在一个班次内连续预约之间的空闲时间——这是一项软目标,空闲间隔会受到惩罚,但不会因此直接拒绝原本不错的排班。
- 量级: 1(每个符合条件的空闲间隔 = 1 次违规,按路线/班次计数)。可配置的最短间隔时长: 介于 30 分钟和 24 小时之间——短于最短时长的间隔不计数。舍入:
round(无量级除数——有效量级 = 1)。
当 weight_gaps = 50 时:penaltyPerViolation_gaps = max( 1, round( 1000 × weight_gaps ) )round(50000) = 50,000 pts per gap violation。 - 总惩罚:
gaps_penalty = Σ over all routes/shifts [ clump_counter(route) × penaltyPerViolation_gaps ]——clump_counter(route)统计该资源班次中超过配置的最短时长的空闲间隔,并对所有路线求和。示例——一个资源有 2 个符合条件的间隔(w=50):2 × 50,000 = 100,000 pts。在整个排班中,3 个资源分别有 1/2/1 个间隔:(1+2+1) × 50,000 = 200,000 pts。 - 连续近似: 对整数权重精确成立(
round(1000×w) = 1000×w):gaps_penalty = clump_count × 1000 × weight_gaps。 - 精度说明: 整数权重的舍入误差为零。权重低于 0.001 时,向下取整的影响可忽略不计。惩罚按每条路线的每个间隔计算——一个资源有 3 个符合条件的间隔,惩罚为费率的 3 倍。最短间隔时长阈值是配置参数,不属于权重计算的一部分。
对话流程
两条全局规则(此处说明一次,适用于以下所有内容)
- 每个派生权重都向上舍入到下一个整数——采用向上取整,而不是四舍五入。Salesforce Field Service 排班策略不接受小数权重。以下公式后不再重复此规则;适用的例外情况(ASAP 和「尽量减少间隔」的低权重下限警告)会在相应位置明确指出。
- 切勿通过比较不同目标的原始权重值来推断优先级——应始终先计算并比较单位惩罚率(见步骤 3),因为每个目标都有自己的量级。
步骤 1:选择目标
首先向用户解释这一概念:服务目标是软性评分标准,用于评估已经通过工作规则筛选的候选项。与工作规则不同,目标绝不会淘汰任何人,只会影响优化器优先选择哪个符合条件的候选项。在同一段解释中,还要告诉用户,“最小化行程”始终会自动纳入,作为锚定目标,固定权重为 1000;其他所有目标的权重都会通过权衡计算相对于它推导得出——用户无需选择它。
然后按以下固定顺序,一次只展示一个类别,并询问用户是否选择其中的目标;收到用户的选择后再进入下一个类别——绝不要一次性展示全部九个目标:
1. 客户体验目标——展示以下三个目标,并询问用户想选择哪些(如有):
- 尽快服务 — 尽可能早地为客户提供服务
- 同一地点 — 如果两项工作在同一地点,则将它们安排为连续作业
- 就近分组 — 将地理位置相近的工作安排在一起
2. 成本/效率目标——展示以下三个目标(说明“最小化行程”已自动纳入),并询问用户想选择其余两个中的哪些:
- 最小化行程(已自动纳入——锚定目标,固定权重为 1000)
- 最小化空闲时间 — 让技术人员持续保持忙碌
- 最小化加班 — 避免支付加班费
3. 员工/分配质量目标——展示以下四个目标,并询问用户想选择哪些(如有):
- 优先资源 — 尽可能使用首选/指定的技术人员
- 资源优先级 — 优先安排高优先级资源(例如,正式员工优先于承包商)
- 技能等级 — 根据工作匹配合适的专业水平
- 技能偏好 — 遵循同一技能类型内的偏好排序(例如,语言偏好)
**各目标的后续问题——在用户回答所属类别后立即提问,再进入下一个类别。**这些问题用于收集配置事实(而不是权重计算),因此应在选择时提问,而不是推迟到步骤 2:
- 最小化行程 (始终询问——在成本/效率类别问题的末尾询问一次,因为该类别中会说明行程已自动纳入):“最小化行程是否也应计算往返资源常驻地点的路段——即从家到第一项工作的行程,以及从最后一项工作回家的行程?还是应将这些路段排除在评分之外?”将其记录为两个独立标志:
excludeTravelFromHome和excludeTravelToHome(true= 排除相应路段)。如果用户没有偏好,则两者默认均为false(计入两个路段)。 - 同一地点 (仅在用户选择该目标时询问,在客户体验类别问题的末尾提问):“同一地点是否应仅在两项预约的纬度/经度完全相同时才将它们视为同一地点——这适用于建筑物之间没有连通道路的园区或农场等场景?还是采用默认分组方式(行程时间相差约一秒以内的预约)?”将其记录为
useExactLocation(true= 仅精确纬度/经度匹配)。如果用户没有偏好,则默认false(标准的邻近分组)。 - 最小化空闲时间 (仅在用户选择该目标时询问,在成本/效率类别问题的末尾提问):询问用户,公司将多长的最短空闲时长视为一个空档(30 分钟至 24 小时;短于此时长的空闲时间不计罚分)——完整的说明/示例请参见下方的最小化空闲时间权衡。将其记录为
params.minGapMinutes;如果用户未提供数值,则默认 30。在权衡问题之前提问,可以将“多长时间算空档”与“优化器应多努力消除空档”区分开来。
完成三个类别的提问并记录所有后续问题的回答后,进入步骤 2,提出用于推导权重的权衡问题。
第 2 步:权衡访谈
针对每个选定的目标(“尽量减少出行”除外),提出一个权衡问题。目的是找到用户认为两个选项同样可以接受的临界点——这种等价关系可以用来计算权重。
用户回答后,将两个惩罚表达式设为相等,求解未知权重,然后按照上述全局规则向上取整。务必展示计算过程。
使用以下问题模板。调整问题中的具体数字,使其听起来自然——如果用户似乎不理解,可以调整示例数值,但计算过程必须保持一致。
除了开放式问题,还要提供具体的预设答案。 使用问题模板提问后,提供一小组可供选择的临界点示例——给出涵盖轻度/中度/强烈偏好的普通数字示例,这样用户可以直接选择,而不必从头编出数字。如果预设选项都不合适,也要邀请用户用自己的话描述确切的权衡;无论选择哪种方式,都使用下方相同的计算方法。预设选项只是为了方便作答,并非独立的计算方式。
仅进行一次合理性检查: 得出每个权重后,简单的验证方法是将其代回刚才求解的等式两边——两边应当相等。下方各目标的说明中默认已进行了这项检查,因此不会逐项重复,除非出现不太明显的情况,例如 SFS 的整数取整偏差(已针对“尽快安排”和“尽量减少间隔”明确标出),或低权重下限警告。
尽快安排权衡
问题模板:
“如果你可以现在就安排预约,但需要额外出行 [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(max(1,…) 下限),这意味着 1–21 范围内的任何权重都会产生相同的优化行为。建议用户接受这一限制,或将权重提高到至少 22,以便该目标产生与权重成比例的效果。
同地点权衡
重要说明: 不要将同地点与出行时间进行比较——同地点预约本来就在同一地点,因此出行并不是有意义的权衡项。相反,应将同地点与 尽快安排(如果已选择)或 首选资源(如果已选择)进行比较。关键问题是:你会拆分同地点预约,以便更早安排,还是为了遵循资源偏好?
如果已选择尽快安排——比较同地点与尽快安排:
问题模板:
“假设有两个预约在同一地点。优化器可以: (A) 将两者分配给同一资源,但安排时间会比原本可能的时间晚 [H] 小时。 (B) 将它们拆分——分配给不同资源或安排在不同时间——这样就能立即安排。 延迟多久时,你会觉得‘那就拆分吧’?”
例如:“如果这意味着要晚 8 小时安排,你还会让同地点预约保持在一起吗?晚 4 小时呢?晚 1 小时呢?”
可提供的预设选项(或让用户说明自己的延迟阈值):“最多愿意晚 1 小时安排,也要保持在一起”(稍微倾向于保持在一起),“最多愿意晚 4 小时安排,也要保持在一起”(中等倾向),“最多愿意晚 8 小时安排,也要保持在一起”(强烈倾向——如上例)。
需要确定的关键等价关系是:拆分一个同地点预约 = 尽快安排延迟多少分钟? 让用户给出一个延迟阈值 D_equiv(分钟),达到该阈值时,拆分就可以接受:
计算方法:
same_site_penalty = asap_penalty(D_equiv)
weight_same_site = D_equiv × (weight_asap / 43200)
如果未选择尽快安排,但已选择首选资源——比较同地点与首选资源:
问题模板:
“假设有两个预约在同一地点。你会拆分它们(分配给不同资源),以遵循其中一个预约的首选资源分配吗?还是即使这意味着忽略首选资源,也要让它们保持在一起?”
可提供的预设选项(或让用户说明自己的选择):“同等重要”(同地点 = 首选资源),“同地点重要性是首选资源的两倍”(F = 2),“同地点重要性是首选资源的一半”(F = 0.5)。
关键等价关系是:拆分一个同地点预约 = 违反一次首选资源要求? 如果用户认为两者同样不好:weight_same_site = weight_preferred。如果用户认为同地点的重要性比首选资源高 F 倍:weight_same_site = F × weight_preferred。
如果既未选择尽快安排,也未选择首选资源——退而比较出行时间:
仅在最后不得已时使用出行时间比较:
“额外出行多少分钟,才值得拆分同地点预约?”
可提供的预设选项(或让用户说明自己的选择):“额外 10 分钟”、“额外 20 分钟”、“额外 30 分钟”。
weight_same_site = T_equiv × (weight_travel / 120)
With weight_travel = 1000:
weight_same_site = T_equiv × 8.333
最小化加班权衡
问题模板:
“如果一个预约可以现在安排,但需要加班/延长工作时间 [Z] 分钟;与之相比,在正常工作时间内**[H] 小时后**安排——在什么情况下,你会觉得两种选择同样可以接受?”
例如:“你愿意加班 30 分钟,把某件事安排在现在,而不是 12 小时后吗?”
可提供的预设选项(或让用户说明自己的选择):“加班 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)
(注意:如果已选择尽快安排,则需先计算 weight_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 的资源则完全不会受到惩罚。”
权衡问题是:为了使用优先级较高而非较低的资源,你愿意接受多少额外行程?
问题模板:
“假设有两个可用于预约的资源:
- 资源 A 是一名员工技术人员(优先级 1),但距离远 [X] 分钟
- 资源 B 是一名承包商(优先级 [P]),距离较近
到什么程度时,你会说‘那就用承包商吧’?换句话说,要多出多少分钟的行程,你才愿意放弃优先级较高的资源?”
使用符合用户场景的具体优先级值。上面的示例使用员工(优先级 1)与承包商(优先级 5),但可根据需要调整。
可提供的预设选项(或让用户自行描述):“多出 30 分钟”(轻微偏好高优先级资源)、“多出 60 分钟”(中等偏好)、“多出 90 分钟”(强烈偏好高优先级资源,即下方示例)。
让用户给出行程阈值 T_equiv(即他们会改用优先级较低资源时所能接受的额外行程分钟数):
**数学计算:**两种资源之间的惩罚差必须等于临界点处的行程惩罚:
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 — 询问用户想使用哪种模式:
“在计算权重之前,我需要知道你想使用哪种模式:
- 最低资历 — 优先选择仍满足要求的技能水平最低的资源。适合将资深资源留给复杂工作,或尽可能使用成本较低的初级员工来控制成本。
- 最高资历 — 优先选择可用资源中技能水平最高的资源。适合最大限度提高一次修复率,或优先保证结果质量的情况。
你想使用哪种模式?”
如果选择「最低资历」——问题模板:
“假设有两个资源都符合预约条件——他们都满足最低技能要求。一个是初级技术人员(技能水平 [S_low]),另一个是资深技术人员(技能水平 [S_high])。资深技术人员距离更近。
在「最低资历」模式下,优化器会优先选择初级技术人员,以便将资深技术人员留给更难的工作。为了安排初级技术人员而不选择附近的资深技术人员,你最多愿意接受多少分钟的额外行程?”
如果选择「最高资历」——问题模板:
“假设有两个资源都符合预约条件——他们都满足最低技能要求。一个是初级技术人员(技能水平 [S_low]),另一个是资深技术人员(技能水平 [S_high])。初级技术人员距离更近。
在「最高资历」模式下,优化器会优先选择资深技术人员,以最大限度提高一次修复的可能性。为了安排资深技术人员而不选择附近的初级技术人员,你最多愿意接受多少分钟的额外行程?”
使用符合用户组织实际情况的具体技能水平值(以下示例中,初级为 4,资深为 8)。
可提供的 T_equiv 预设选项(也可以让用户自行描述):“额外 15 分钟”(轻微)、“额外 30 分钟”(中等——接近下方示例)、“额外 45 分钟”(强烈偏好目标技能水平)。
步骤 2 — 收集两个技能水平值(S_low、S_high)和 T_equiv——即仍愿意安排优先资源的最大额外行程时间。
计算方法(两种模式的公式相同)——两种资源之间的惩罚差必须等于交叉点处的行程惩罚:
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
解读:优化器会安排技能水平为 4 的技术人员,即使其距离远出最多 30 分钟;超过这一限度后,才会改派附近技能水平为 8 的技术人员。
示例——最高资历(技能水平相同,T_equiv = 45 分钟):
weight_skill_level = 45 × 1000 / 480 = 93.75 → round up to 94
解读:优化器会安排技能水平为 8 的技术人员,即使其距离远出最多 45 分钟;超过这一限度后,才会改派附近技能水平为 4 的技术人员。
技能偏好权衡
向用户说明的背景:
“当一个工单有多个相同技能类型的技能要求,且某项技能优先于另一项技能时,适用技能偏好目标。每项技能要求都有一个 1 到 10 的技能优先级值,其中 1 表示最优先,10 表示最不优先。优化器会分配与该优先级值成正比的惩罚:优先级为 1 的技能会受到完整目标权重 10% 的惩罚,而优先级为 10 的技能会受到完整权重的惩罚。如果某项资源具备优先级更高的技能,其受到的惩罚更低,因此优化器会优先选择该资源。”
权衡问题是:为了分配具备更高优先级技能的资源,而不是具备较低优先级技能的资源,你愿意接受增加多少出行时间?
问题模板:
“假设有一个工单,客户可以由会说 [Skill A] 的技术人员(技能优先级为 [SP_high_pref])或会说 [Skill B] 的技术人员(技能优先级为 [SP_low_pref])提供服务,但客户更倾向于 [Skill A]。会说 [Skill A] 的技术人员距离更远。
为了安排会说 [Skill A] 的技术人员,而不是会说 [Skill B] 的技术人员,你最多愿意接受增加多少分钟的出行时间?”
请使用符合用户场景的具体技能名称和优先级值。以下示例使用语言技能类型中的西班牙语(优先级 1)和英语(优先级 6)。
还需记录技能类型(此目标正常运行所必需)。 技能偏好针对一种技能类型运行,即配套的 Match Skills 工作规则通过 **“至少一项技能匹配(OR)”**进行评估的技能类别(例如 Language)。询问用户:
“此偏好对哪种技能类型进行排序?请提供其 Developer Name——它必须与设置为*至少一项技能匹配(OR)*的 Match Skills 工作规则所用的技能类型相同,因为在 AND 匹配模式下,技能偏好不会生效。”
将答案记录为 skillType 参数(技能类型的 Developer Name,原样传递)。添加一条前置条件说明:策略还必须包含针对该技能类型、配置为 OR 逻辑的 Match Skills 规则;如果用户尚未定义此规则,请标记出来。(数据层技能会将 skillType 映射到目标上的 {ns}Skill_Type__c。)
为 T_equiv 提供的预设选项(也可以描述自定义选项):“额外 15 分钟”(轻度偏好)、“额外 30 分钟”(中度偏好)、“额外 45 分钟”(强烈偏好——以下示例)。
询问用户:正在比较的两个技能优先级值(SP_high_pref = 更优先,SP_low_pref = 较不优先)、T_equiv(仍会选择具备更优先技能的资源时所能接受的最大额外出行分钟数)以及 skillType。
数学关系: 两种技能选项之间的惩罚差必须等于交叉点处的出行惩罚:
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 到 2。
如果未选择 ASAP——改用行程时间比较:
问题模板:
“整个排班中,额外增加多少分钟的行程时间,才值得保留技术人员班次中的间隔,而不是压缩排班?”
可提供的预设选项(也可让用户自行描述):“额外 10 分钟”“额外 20 分钟”“额外 30 分钟”。
让用户给出 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)
使用这些比率,以通俗易懂的语言描述相对优先级。例如:
“Travel 每分钟累积 [X] 个惩罚分数,而 ASAP 每延迟一分钟累积 [Y] 个惩罚分数。因此,优化器认为 1 分钟的出行成本是 1 分钟排程延迟的 [X/Y] 倍——这意味着尽管 ASAP 的权重数值更大,出行实际上才是每分钟影响更大的因素。”
“优先级为 5 的承包商会产生 [Z] 个惩罚分数,相当于增加 [T] 分钟出行时间。因此,只有当员工技师的路线会增加超过 [T] 分钟的出行时间时,优化器才会使用承包商。”
“高级技师(技能等级 8)会产生 [Z] 个惩罚分数,而初级技师(技能等级 4)会产生 [Y] 个惩罚分数——相差 [Z-Y] 分,相当于增加 [T] 分钟出行时间。因此,在 Least Qualified 模式下,优化器会让初级技师多走最多 [T] 分钟,然后才改派附近的高级技师。”
“拥有首选技能的资源(优先级 1)会产生 [Y] 个惩罚分数,而拥有较不受偏好技能的资源(优先级 6)会产生 [Z] 个惩罚分数——相差 [Z-Y] 分,相当于增加 [T] 分钟出行时间。因此,在分配较不受偏好的资源之前,优化器会让拥有首选技能的资源最多多走 [T] 分钟。”
“技师班次中的每个空档都会产生 [Z] 个惩罚分数,相当于 ASAP 延迟 [D] 分钟。因此,只有当压缩空档会让预约时间推迟 [D] 分钟或更久时,优化器才会在排程中保留空档。这体现了你表达的偏好:如果能提前 [D] 分钟安排预约,你愿意接受空档。”
政策一览(2–3 句业务摘要)
展示权重和惩罚率后,请生成一段2–3 句的高层次业务摘要,说明此排程政策的优化目标——为不关心数学细节的利益相关者准备一句精炼的执行摘要。
生成方式:
第 9 阶段:**按类别对目标分类
客户体验(ASAP、Same Site、Group Nearby);成本/效率(Minimize Travel、Minimize Gaps、Minimize Overtime);员工队伍/分配质量(Preferred Resource、Resource Priority、Skill Level、Skill Preference)。
第 10 阶段:通过比较各类别的单次事件……确定主导类别
第 11 阶段:根据交叉点……找出关键权衡关系
阶段 12:写出 2–3 个句子,说明一个主要关注点以及……
摘要示例:
主要关注点:人员分配质量/次要关注点:成本效益:“此策略主要关注人员分配质量——优化器最多愿意绕行 2 小时,将首选技术人员派往现场,优先考虑服务连续性以及合适的人选。次要关注点是成本效益:对于资质相同的候选人,它会积极减少行驶距离,并且最多愿意延迟安排 24 小时,以避免多行驶 30 分钟。”
主要关注点:成本效益/次要关注点:客户体验:“此策略主要关注成本/效率——路线短且不加班是首要任务;如果能显著缩短行驶时间,优化器会跳过首选资源。次要关注点是客户体验:当出行成本相近时,它会尽量提前安排预约,并将同一地点的访问集中安排,而不是让客户等待。”
主要关注点:客户体验/次要关注点:人员分配质量:“此策略主要关注客户体验——优化器会积极将预约安排在最早可行的时间,并将同一地点的访问集中安排,即使这会导致行驶距离更长。次要关注点是人员分配质量:当时间相同时,它会优先选择首选技术人员和优先级更高的资源,而非普通候选人。”
均衡:“此策略在三个维度上保持均衡——没有哪一类占据主导地位。它不会为了小幅提升排程效果而牺牲首选技术人员,会接受适度延迟以保持路线高效,并将同一地点的访问集中安排作为次要的决胜因素。优化器会做出务实的权衡,而不是不惜一切代价优化某个目标。”
在步骤 3 输出的末尾呈现摘要,标签为 “策略一览”。
输出构建规范(移交给数据层技能)
这是此技能的最终交付物,也是与 sfs-sobject-create 技能之间唯一的约定。设计确定后——选择策略设置、工作规则、相关性组并推导权重——输出一个结构化的 构建规范 JSON 对象。sfs-sobject-create 会原样使用此规范创建记录;它不会重新推导意图,因此规范必须完整且独立自洽。
严格边界:此技能只输出设计值。不得包含 Salesforce 对象名称、字段 API 名称、记录 ID、Composite 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" 等赘词),不要原样追加完整策略名称。**例外:**两条 Arrival Window Match Time 规则已有各自的描述性名称(Arrival Window Start、Arrival Window End)——对于这两条规则,以相同方式追加缩写后的策略名称,而不是将名称替换为类型:例如,"Arrival Window Start - First test"、"Arrival Window End - First test"。- 始终将
Minimize Travel的权重设为 1000,并且恰好包含一条"mandatory": true的Service Resource Availability工作规则,即使用户从未提及它们。绝不要输出Earliest Start Permitted或Due Date(参见 Mandatory rules)。 - 每个子集一个实例。 如果某条规则/目标按相关性组拆分,则为每个组分别输出一个数组条目,每个条目都带有自己的
relevanceGroup。 - 权重必须为整数,且已按照 Conversation Flow 中的 global rules 向上取整。
- 参数是业务值,而不是字段映射。 将“在 12:00 至 15:00 之间某个时间安排 30 分钟的休息”作为时长/时间放入
params;不要将其转换为休息记录的字段布局——该映射由数据层技能负责。 - 明确列出前置条件。 设计所依赖的任何相关性组布尔字段、自定义优先级字段或技能类型依赖,都必须放入
prerequisites,以便用户(和数据层技能)了解这些条件必须预先存在。 - 不要尝试创建任何内容。 输出规范后,告知用户该规范已准备好交由
sfs-sobject-create创建记录。如果用户要求“构建/部署/创建”,你的任务是完成此规范,而不是执行 CRUD。
各目标的通俗业务意图
使用以下描述,帮助用户在深入了解计算方式之前,理解每个目标在业务上的实际作用。
客户体验目标
同一地点(在同一位置进行拜访) — “如果两个工作在同一个地点,就连续完成。”当多个预约位于同一物理地点(同一栋楼、同一个地址)时,将它们安排在同一位技术人员的路线中连续进行,避免技术人员离开后又返回。减少不必要的重复上门,提高现场工作效率。
将附近的预约安排在一起(同一地点半径) — “将地理位置相近的工作安排在一起。”这是“同一地点”目标的扩展版本——它不要求地点完全相同,而是将位于动态计算半径范围内的预约安排到同一条路线中。减少在同一区域内来回穿梭,让技术人员先完成一个本地片区的工作,再前往下一个片区。
成本 / 效率目标
尽量减少出行 — “少开车。”减少各条路线的总出行时间,即减少任务之间的往返次数,并缩短往返距离。这会直接降低燃油消耗、车辆磨损和无效的驾车时间。可配置是否计入首段(家 → 第一个任务)和末段(最后一个任务 → 家)的行程。
尽量减少空档 — “让技术人员持续忙碌。”对技术人员一天中的空闲时段进行惩罚,促使日程安排的工作尽可能连贯,任务之间不留空闲时间。请注意其机制:它计算每班次中彼此分开的工作时段数量(空档大于配置的最小值),而不是空闲时间的长度。因此,业务意图是“不要把技术人员一天的工作拆成互不相连的时段”,而非“尽量压缩每一分钟的空闲时间”。
加班 — “避免支付加班费。”避免安排让技术人员超出标准班次时长的日程,从而控制劳动力成本并遵守正常工作时限。
人员配置 / 指派质量目标
技能水平 — “为任务匹配合适的专业水平。”根据资源的资质水平来引导指派。可配置为优先选择具备胜任能力但资质最低的技术人员(不要让专家处理简单任务,把专家留给更需要他们的任务),或优先选择资质最高的技术人员(派最优秀的人负责)。此目标基于指定的技能类型运行。
技能偏好(属性排名) — “遵循偏好排名,例如患者 / 客户的偏好。”除了判断技能是否匹配外,还会根据偏好属性对资源进行排名,并优先选择排名较高的匹配项。代码中的典型用例是居家医疗患者偏好:将患者更喜欢的护理人员排在仅仅具备资质的其他人选之前。
首选资源(指定资源) — “尽可能使用首选 / 指定的技术人员。”优先将任务指派给标记为该任务首选的资源(例如安装设备的技术人员,或客户账户的常用代表),以提升服务连续性并让客户感到熟悉。
资源优先级 — “优先选择优先级较高的资源。”将工作倾向于优先级较高的资源(例如优先安排内部员工而非承包商,或优先安排绩效排名靠前的人员)。
文档背景:各目标的行为与配置
这些说明补充了 Salesforce 文档中关于行为和配置的背景信息,帮助解释各目标为何会有相应的表现。这些说明不是计算参考——权威的惩罚计算公式位于上文的各目标的惩罚公式,并始终优先适用(依据本技能开头的事实来源)。
通用评分机制(文档说明):优化器会使用策略中的目标对每个候选日程进行评分;每个目标的权重决定其对最终评分的影响程度。每个目标都有一个尺度,用于定义最佳 / 最差情况(例如,“尽量减少出行”的尺度为 120 分钟,因此 1 小时的出行时间相当于尺度的 50%)。分级值 × 权重 = 该目标的贡献值;所有目标的贡献值相加,即为日程评分。增强型排程与优化(ESO)通过惩罚值评分(总惩罚值最低者胜出)。旧版(非 ESO)引擎则通过奖励 / 加分评分(总分最高者胜出),使用的是相同目标,但方向相反。本技能中的公式描述的是 ESO 的惩罚行为。
- 尽早安排(ASAP) — “尽可能早的时间”准确来说是以下时间中最晚的一个:当前时间、预约的最早允许开始时间、到达时间窗口的开始时间,或优化时段的开始时间。延迟以该时间为基准,按 30 天的尺度衡量。文档建议在每个策略中都加入 ASAP,权重至少为 1,以便在平分时优先安排较早的时间。
- 最小化差旅 — 对新增差旅评分:衡量新旧日程总差旅时间的差值,而非绝对差旅时间。可配置为排除往返大本营的路段(ESO 下的
Exclude Travel To/From Home Base)。基于容量的资源假定差旅时间为零(差旅评分最佳)——应改用资源优先级来引导它们。 - 最小化加班 — 对新增加班时间评分(与之前日程相比的差值)。加班时间 = 紧接在普通时段/班次之后的延长时段/班次中的时间;普通工时之前或之间的延长时间不计入。延长时段内的差旅和休息时间也计入。
- 同一地点 — 可通过使用精确位置配置分组精度:默认将差旅时间不超过 1 秒的预约分为一组;勾选后则要求纬度/经度完全相同(适用于没有连通道路的校园/农场)。仅适用于 ESO;不支持相关性分组。这与预约捆绑(保证严格安排在同一趟行程中)不同;同一地点是一种软性偏好。
- 归组附近的预约 — 根据邻近程度动态聚类预约,让一名工作人员“负责”当天的某个区域。它与最小化差旅直接竞争,必须在两者之间取得平衡。仅适用于 ESO;不支持相关性分组。
- 最小化空档 — 必须配置最小空档时长(即贵公司对空档的定义)。差旅时间不计为闲置时间,因此,如果两项工作之间实际有 25 分钟的空档,但差旅时间为 35 分钟,那么在最小空档时长设为 30 分钟时,系统不会对其进行优化。仅适用于 ESO;仅支持日程/批量日程/日内/全局优化(不适用于预约、获取候选项、拖放、RSO);不支持多日安排,也不支持多个灵活休息时间。
- 首选资源 — “首选”是指账户、资产、位置、工作单或 WOLI 上类型为首选的资源偏好。该偏好为二元属性(首选或非首选,没有等级之分)。如果列出了多个首选资源,它们都将被视为同等首选。
- 资源优先级 — 通过标准的优先级字段或自定义数值字段设置优先级,然后将目标的资源优先级字段指向该字段。规则:值越低 = 业务优先级越高;0 = 最佳候选项;null = 最低优先级 / 最不受偏好;负值 = 按 null 处理。文档建议使用 1–10 的整数范围。支持相关性分组;如果每个实例都指向不同的优先级字段,也可以在同一批预约上使用两次。
- 技能等级 —
Prioritize Resource选项列表用于选择优先选择资质最低者(优先选择满足要求的最低技能等级——保留专家)或优先选择资质最高者(优先选择最高技能等级——提高首次修复率)。如果预约有多个技能要求,该目标会对这些要求取平均值。如果预约没有技能要求,该目标不起作用。此功能不要求启用“匹配技能”工作规则。 - 技能偏好 — 技能优先级范围为 1–10(1 = 最高偏好,10 = 最低偏好);超出范围的值会限制为 1 或 10,空值默认为 10。仅当具有相同技能类型的技能使用“匹配技能”**“至少一个技能匹配(OR)”**逻辑进行评估时才相关;在 AND 匹配下不起作用。
实时文档参考
根据此技能的设计,这里仅存储摘要——需要了解可能随时间变化的最新行为或概念细节时,请获取 Salesforce 实时文档。这些都是 help.salesforce.com 文章 ID(打开 https://help.salesforce.com/s/articleView?id=<ID>&type=5)。不要让文档覆盖此技能中的计算规则——这里的数学计算具有权威性。注意:记录级字段 API 名称、复合 API 结构和创建步骤不属于此技能的范围,而是由数据层技能负责——获取这些文档是为了了解意图和行为,而不是了解如何构建记录。
策略概念
service.pfs_scheduling.htm— 排程策略的概念和设置(名称、描述、日内优化、提交模式);标准策略;策略的应用方式。service.pfs_scheduling_policy_guidelines.htm— 策略设计指南,包括需求→工作规则映射,以及目标排名/权重指导。
工作规则
service.pfs_optimization_theory_work_rules.htm— 工作规则概览、DB 与 Apex 的性能考量,以及全部 16 种规则类型子页面的索引。各个子页面(追加规则 slug)介绍每条规则的行为和注意事项,例如..._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— 目标概览,以及一般的量表/权重/惩罚与奖励评分机制。各目标子页面(追加 slug)介绍行为和配置背景:..._asap、..._min_travel、..._min_overtime、..._pref_resource、..._resource_priority、..._skill_level、..._skill_preference、..._same_site、..._group_nearby、..._min_gaps,以及示例..._asap_example(ASAP + 相关性组)。
语气和风格指南
全局舍入和优先级比较规则位于 对话流程 顶部,此处不再重复。其余对话风格如下:
- 语气自然、内容具体。示例中使用具体数字,而非抽象公式。
- 每次回答后都展示计算过程,让用户放心并能够核验。
- 如果用户似乎不确定,请提供以惩罚率而非原始权重为依据的参照比较。
- 不要在对话中提及未选中的目标——不要询问这些目标。
─────
运行时上下文(Headless 360 / agentic): 此技能在 Headless 360 / agentic 上下文中运行时,尽可能优先使用平台调度工具(托管 Headless 360 MCP 中的 dispatch;本地开发 MCP 中的 dispatch),而非 CLI 工具(sf project deploy、sfdx、shell 命令)。可用操作如下方 steps: 所列;每项操作都已在实时 org 中验证。请针对规范路径调用调度工具。只有在某项功能不存在 API 路径时,才可使用 CLI 作为后备方案。
关键约束
前提条件:
- 已安装 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 字段,具体见技能正文(“Relevance Groups”)。此技能会在构建规范的
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 规则——这是此技能生成的唯一强制规则(见上文强制规则)。不要生成 Earliest Start Permitted 或 Due Date——它们会自动配置。
- 始终依据用户陈述的原始取舍来解读,而不只是依据权重数值。例如:“这体现了您所说的偏好:30 分钟的行程时间相当于 12 小时的延迟。”如果这种隐含的优先级与用户预期不符,请提出重新确认取舍问题。
- ASAP(Promptness)——“尽可能早地为客户提供服务。”此项会将每个预约尽量安排在其时间窗口内最早的合法开始时间,从而尽量缩短客户等待时间。延迟每增加一分钟,惩罚就会加重,因此预约被推迟得越久,成本越高。
操作参考
按用途对操作进行分组。将这些操作作为上述工作流的构建模块。
摘要
| 操作 | 用途 | 状态 | 调用 | 依赖项 |
|---|---|---|---|---|
list-starter-scheduling-policies | 读取 | — | GET /services/data/vXX.0/query | — |
read-starter-policy-work-rules | 读取 | — | GET /services/data/vXX.0/query | list-starter-scheduling-policies |
read-starter-policy-objectives | 读取 | — | GET /services/data/vXX.0/query | list-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 子记录。呈现给管理员前,过滤掉名为 'Earliest Start Permitted' 和 'Due Date' 的 Time_Rule_Service 行——这些由托管包自动创建,不得再次输出。
依赖: 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 服务目标记录一一对应。
写入操作
emit-build-spec
终端操作:输出完整的 JSON 构建规范(架构见 agent_guidance 中的 "Emitting the Build Spec"),并将其交给 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“纠正”这些公式;应将其视为权重推导的事实来源。