mcpField
F
field-service-prework-brief-deployer-configure

field-service-prework-brief-deployer-configure

github.com/forcedotcom其他1.1k73 次安装1 天前更新
代码仓库

关于 field-service-prework-brief-deployer-configure

这项 Salesforce skill 为 Field Service Mobile 部署并激活 Einstein Pre-Work Brief 的确定性配置。它面向已完成必要 org 设置、许可和试点技术员准备的管理员,自动处理元数据、权限、布局及测试工单步骤。

主要功能

  1. 01部署并激活提示模板
  2. 02启用 Lightning Data Service
  3. 03分配许可证和权限集
  4. 04将字段添加到 Work Order 布局
  5. 05创建或更新已排期的测试工单

适用场景

  1. 01为试点技术员配置 Pre-Work Brief
  2. 02准备测试工单以验证移动端呈现
  3. 03重跑配置并仅应用缺失更改

安装

复制下面的命令,在终端中运行。

$npx skills add https://github.com/forcedotcom/sf-skills --skill field-service-prework-brief-deployer-configure

安装后在 Claude Code 中输入/field-service-prework-brief-deployer-configure确认已加载。

详细介绍

管理 Fs Prework Brief 部署程序

何时使用此技能

通过 dispatch 将 Einstein Pre-Work Brief 的确定性部分部署到目标组织的 Field Service Mobile:包括提示词模板、Lightning Data Service、许可证和权限集、Work Order 布局字段,以及一个已安排时间的测试 Work Order。

工作流

Field Service Pre-Work Brief 部署程序

通过 dispatch 部署 Einstein Pre-Work Brief 在 Field Service Mobile 上的确定性部分。

此技能针对已配置 Einstein for Field Service 附加组件的组织,执行搭建 Pre-Work Brief 所需的所有可自动化步骤:部署三个元数据构件、分配许可证和权限集、在技术人员的布局中显示 Work Order 字段、设置一个安排在今天时间窗口内的测试 Work Order,并通过 Connect API 激活提示词模板。整个工作流可端到端完全自动化——不存在无法自动化的手动步骤。设备上的渲染验证(确认 grounding 会生成特定于作业的内容)由协调 Pre-Work Brief 的技能负责,而非此部署原语。

此技能是无需判断的部署原语。组织诊断/路由(如果尚未配置则停止)、技术人员选择,以及使用全新还是现有测试数据的决策,均由协调技能处理,并作为输入/选择点传入。

功能

  • 提示词模板 — 将 einstein_gpt__fieldServicePreWorkBrief GenAiPromptTemplate(Pre_Work_Brief)部署为已发布状态。
  • Lightning Data Service — 在 Field Service 设置中启用 Lightning Data Service(lsdkForFieldServiceMobilePref 组织偏好设置;如果未启用 LDS,简报将在移动端显示为空白)。
  • 许可证 + 权限集 — 为管理员和试点技术人员分配 Einstein for Field Service PSL + 权限集。
  • 字段级访问权限 — 部署 PreWorkBrief_Field_Access,并通过 FLS 在技术人员的布局中显示 WorkOrder.PreWorkBriefPromptTemplate。
  • 测试数据 — 创建一个安排在今天时间窗口内、指向已部署模板的测试 Work Order + Service Appointment + Assigned Resource(或将现有 Work Order 指向该模板)。

输入

  • 目标组织 — 已配置 Einstein for Field Service 附加组件的组织。如果缺少该附加组件,则说明组织尚未配置——停止;由协调技能负责处理路由。
  • 试点技术人员 — 由协调技能选定的用户名 + 用户 ID,且具有一个有效的 ServiceResource。
  • 测试数据模式 — fresh(创建全新的测试 Work Order 链)或 existing(将提供的真实 Work Order 指向模板,并将其 Service Appointment 移至今天的时间窗口)。默认值为 fresh。

前提条件

  • 已配置 Einstein for Field Service 附加组件(存在 Einstein for Field Service 权限集许可证)。
  • 管理员拥有 Customize Application + Manage Profiles and Permission Sets 权限。
  • 已在组织中完成 Einstein 生成式 AI 基础设置(包括 Data 360 grounding)。
  • 已选定试点技术人员,且其具有一个有效的 ServiceResource。

顺利路径(全新测试数据)

验证配置 → 分配许可证和权限集 → 验证权限集分配 → 检测现有模板 → 部署提示词模板 → 验证模板已部署 → 读取 Field Service 设置 → 部署 LDS 设置 → 验证已启用 LDS → 部署字段访问权限集 → 读取 Work Order 布局 → 将字段添加到布局 → 验证字段已添加到布局 → 创建测试 Work Order → 创建 Service Appointment → 分配资源 → 验证测试 Work Order 已安排 → 解析模板版本 → 激活提示词模板(Connect API)→ 验证已激活。

现有测试 Work Order 分支 — 当 test-data-mode 为 existing 时,跳过 create-test-workorder / create-service-appointment / assign-resource,改为运行 point-existing-workorder:更新真实 Work Order 的 PreWorkBriefPromptTemplate,并将其 Service Appointment 调整到今天的时间窗口内。

每个步骤都是幂等的 — 写入前会检查组织状态,因此重新运行时不会应用任何更改。

执行顺序至关重要

  • 在部署提示词模板之前,先分配管理员权限集(包括 EinsteinGPTPromptTemplateManager)。 如果管理员缺少此权限,Pre-Work Brief 模板类型会在 Prompt Builder 中悄然消失,导致部署失败且不显示错误消息 — 只会缺少一个下拉选项。
  • 在设备上使用之前启用 LDS(lsdkForFieldServiceMobilePref), 否则简报在移动设备上会显示为空白。

注意事项

  • GenAiPromptTemplate 不支持 SOQL/REST 查询。 要检测它并解析其以 0hf 开头的 Id,请按名称通过 Metadata 深度读取(type=GenAiPromptTemplate,fullName=Pre_Work_Brief),而不是执行查询。
  • 将模板部署正文作为 JSON 对象发送,不要发送序列化后的字符串。
  • 整条记录写入的陷阱 — 有两种不同机制,都不是原始 MDAPI 部署。 deploy-lds-setting 和 add-field-to-layout 都会写入已有的整条记录,但方式不同。LDS 通过 Field Service 设置控制器(saveFieldServiceSettingsConfig)处理:按字段使用 isChanged<Field> 进行部分更新,请求正文必须包装在顶层 userSettings 键下 — {"userSettings": {"lsdkForFieldServiceMobilePref": true, "isChangedLsdkForFieldServiceMobilePref": true}}。平铺的正文(字段位于顶层)会返回 500 CONTROLLER_ERROR NullPointerException(userSettings is null)。Work Order 布局采用 Tooling API Layout.Metadata 往返写入,且是整对象替换:先读取当前 Metadata(使用 Tooling Layout GET — read-workorder-layout 的 describe/layouts 仅用于检查字段位置),只添加新字段,然后将整个对象 PATCH 回去(省略的键会重置为默认值)。写入时,将 feedLayout 置为 null,并移除 ServiceReportRelatedList 相关列表,否则 PATCH 会返回 400(参见布局步骤的备注)。这既不是 SOAP MDAPI,也不是 /headless/metadata(未路由)— 而是通过 dispatch passthrough 使用 Tooling REST。

激活提示词模板(Connect API)

该模板部署后的状态为 Published(已发布),而非 Active(已激活)——激活之前,它不会出现在运行时目录中,移动应用也会报错 “我们遇到了问题。” 激活可以通过 Connect API 以编程方式完成(自 v65.0 / API 258 起提供):

PUT /services/data/v67.0/einstein/prompt-templates/{devName}/versions/{versionId}/status?action=activate&ignoreWarnings=false
Body: {}
  • 先解析 versionId。 GET /services/data/v67.0/einstein/prompt-templates/{devName},读取 childRelationships.GenAiPromptTemplateVersions[].fields.Id.value(以 3vN 开头的版本 Id)。即使模板未激活或未出现在目录中,此 GET 请求也有效。
  • 成功时,响应为 isSuccessful:true、statusCode:"200",且 additionalData.wrappedMap.summary.overallSeverity 为 SAFE。模板级别的 IsActive 会变为 true,并填入 ActiveVersionId(版本自身的 Status 仍为 Published——版本级别的“Published”即模板级别的“Active”)。
  • 无法从 Apex 调用。 该端点带有 @ConnectHidden(from=Apex);请通过 dispatch(或 sf api request rest --method PUT)调用,而不是使用 ConnectApi。这就是此前的 Apex ConnectApi.EinsteinLLM 和 Tooling/元数据尝试失败的原因之一——此外,URL 格式也错误(用了 /activate,而非 /versions/{id}/status?action=activate),API 版本也过早(v62–v66)。
  • 随后会立即接续执行 verify-activation——读取运行时提示词模板目录(GET /einstein/prompt-templates?pageSize=200,确认现在已出现 Pre_Work_Brief)——以确认激活已生效。

于 2026-07-22 在非生产 org 中完成实时验证:IsActive 从 False 变为 True,ActiveVersionId 从 null 变为已填入,模板也在同一次调用中出现在运行时目录中。

范围边界——设备端渲染

设备端验证(技术人员打开 Field Service 移动应用,并确认简报在“概览”选项卡中显示了特定于作业的内容)不属于此部署基本操作的范围,由协调此流程的 Pre-Work Brief 技能负责。请注意,verify-activation 只能确认模板已激活;只有设备端渲染才能确认 Data 360 基于地面数据实际生成了特定于作业的内容——这是另一项检查,由协调技能负责。没有无头接口可以返回技术人员在设备上看到的内容。

来源

根据 sf-skills-internal 协调技能 field-service-prework-brief-configure(及其 references/ 文件)和 Salesforce Help 中关于 Einstein Pre-Work Brief 的文档编写。已在调度员 org 中完成实时验证。每次运行时,执行框架都会根据此技能生成有序、类型化的 SOR。

───── 运行时上下文(Headless 360 / agentic): 在 Headless 360 / agentic 上下文中运行此技能时,尽可能优先使用 dispatch 工具,而不是 CLI 工具(sf project deploy、sfdx、shell 命令)。下方 steps: 中列出了可用操作;每项操作都已针对实时 org 验证。请通过 dispatch 调用规范路径。仅当某项能力没有对应的 API 路径时,才可使用 CLI 作为后备方案。

关键约束

前置条件:

  • 已配置 EinsteinFieldServicePsl(检查:GET /services/data/v67.0/query?q=SELECT Id FROM PermissionSetLicense WHERE DeveloperName='EinsteinFieldServicePsl' returns totalSize>=1)
  • 管理员拥有 Customize Application + Manage Profiles and Permission Sets 权限(检查:session user is a Setup admin)
  • 已完成 Einstein 生成式 AI 基础设置(包括 Data 360 grounding)(检查:org-level Einstein Pref enabled; grounding sources reachable)
  • 已选定一个拥有活跃 ServiceResource 的试点技术人员(检查:GET /services/data/v67.0/query?q=SELECT Id, RelatedRecordId FROM ServiceResource WHERE IsActive=true AND RelatedRecordId='<pilot-user-id>' returns totalSize=1)

验证清单

每次写入操作后,都要重新读取状态以确认操作结果:

  • 执行 (write) 后:调用 verify-provisioning(GET /services/data/v67.0/query)
  • 执行 assign-perms-admin 后:调用 verify-permset-admin(GET /services/data/v67.0/query)
  • 执行 verify-showpreworkbrief-perm 后:调用 detect-existing-template(GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName})
  • 执行 deploy-prompt-template-header 后:调用 verify-template-deployed(GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName})
  • 执行 deploy-lds-setting 后:调用 verify-lds-enabled(GET /headless/invoke/platform/document-builder)
  • 执行 add-field-to-layout 后:调用 verify-field-on-layout(GET /services/data/v67.0/sobjects/WorkOrder/describe/layouts)
  • 执行 assign-resource 后:调用 verify-workorder-scheduled(GET /services/data/v67.0/query)
  • 执行 activate-prompt-template 后:调用 verify-activation(GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName})
  • 执行 assign-fs-einstein-user-admin 后:调用 verify-showpreworkbrief-perm(GET /services/data/v67.0/query)

操作参考

按用途分组的操作。请将这些操作作为上述工作流的构建模块。

Summary
OperationPurposeStatusCallDepends on
verify-provisioningverify—GET /services/data/v67.0/query—
assign-psl-adminwrite—POST /services/data/v67.0/sobjects/PermissionSetLicenseAssignverify-provisioning
assign-perms-adminwrite—POST /services/data/v67.0/sobjects/PermissionSetAssignmentassign-psl-admin
verify-permset-adminverify—GET /services/data/v67.0/queryassign-perms-admin
detect-existing-templateverify—GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}verify-showpreworkbrief-perm
deploy-prompt-template-headerwrite—PATCH /headless/invoke/platform/einstein-prompt-studio/insert-prompt-templatedetect-existing-template
verify-template-deployedverify—GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}deploy-prompt-template-header
read-fieldservice-settingsread—GET /headless/invoke/platform/document-builderverify-template-deployed
deploy-lds-settingwrite—PATCH /headless/invoke/platform/document-builder-rlm/save-field-service-settings-configread-fieldservice-settings
verify-lds-enabledverify—GET /headless/invoke/platform/document-builderdeploy-lds-setting
deploy-field-access-permsetwrite—POST /services/data/v67.0/sobjects/PermissionSetverify-lds-enabled
assign-perms-technicianwrite—POST /services/data/v67.0/sobjects/PermissionSetAssignmentdeploy-field-access-permset
read-workorder-layoutread—GET /services/data/v67.0/sobjects/WorkOrder/describe/layoutsassign-perms-technician
verify-field-on-layoutverify—GET /services/data/v67.0/sobjects/WorkOrder/describe/layoutsadd-field-to-layout
create-test-workorderwrite—POST /services/data/v67.0/sobjects/WorkOrderverify-field-on-layout
create-service-appointmentwrite—POST /services/data/v67.0/sobjects/ServiceAppointmentcreate-test-workorder
assign-resourcewrite—POST /services/data/v67.0/sobjects/AssignedResourcecreate-service-appointment
verify-workorder-scheduledverify—GET /services/data/v67.0/queryassign-resource
point-existing-workorderwrite—PATCH /services/data/v67.0/sobjects/WorkOrder/{Id}verify-field-on-layout
resolve-template-versionread—GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}verify-workorder-scheduled, point-existing-workorder
activate-prompt-templatewrite—PUT /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}/versions/{versionId}/status?action=activate&ignoreWarnings=falseresolve-template-version
verify-activationverify—GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}activate-prompt-template
assign-fs-einstein-psl-adminwrite—POST /services/data/v67.0/sobjects/PermissionSetLicenseAssignverify-permset-admin
assign-fs-einstein-user-adminwrite—POST /services/data/v67.0/sobjects/PermissionSetAssignmentassign-fs-einstein-psl-admin
verify-showpreworkbrief-permverify—GET /services/data/v67.0/queryassign-fs-einstein-user-admin
add-field-to-layoutwrite—PATCH /services/data/v67.0/tooling/sobjects/Layout/{layoutId}read-workorder-layout
Dependency graph
graph TD
  verify_provisioning["verify-provisioning (verify)"]
  assign_psl_admin["assign-psl-admin (write)"]
  assign_perms_admin["assign-perms-admin (write)"]
  verify_permset_admin["verify-permset-admin (verify)"]
  detect_existing_template["detect-existing-template (verify)"]
  deploy_prompt_template_header["deploy-prompt-template-header (write)"]
  verify_template_deployed["verify-template-deployed (verify)"]
  read_fieldservice_settings["read-fieldservice-settings (read)"]
  deploy_lds_setting["deploy-lds-setting (write)"]
  verify_lds_enabled["verify-lds-enabled (verify)"]
  deploy_field_access_permset["deploy-field-access-permset (write)"]
  assign_perms_technician["assign-perms-technician (write)"]
  read_workorder_layout["read-workorder-layout (read)"]
  verify_field_on_layout["verify-field-on-layout (verify)"]
  create_test_workorder["create-test-workorder (write)"]
  create_service_appointment["create-service-appointment (write)"]
  assign_resource["assign-resource (write)"]
  verify_workorder_scheduled["verify-workorder-scheduled (verify)"]
  point_existing_workorder["point-existing-workorder (write)"]
  resolve_template_version["resolve-template-version (read)"]
  activate_prompt_template["activate-prompt-template (write)"]
  verify_activation["verify-activation (verify)"]
  assign_fs_einstein_psl_admin["assign-fs-einstein-psl-admin (write)"]
  assign_fs_einstein_user_admin["assign-fs-einstein-user-admin (write)"]
  verify_showpreworkbrief_perm["verify-showpreworkbrief-perm (verify)"]
  add_field_to_layout["add-field-to-layout (write)"]
  verify_provisioning --> assign_psl_admin
  assign_psl_admin --> assign_perms_admin
  assign_perms_admin --> verify_permset_admin
  verify_showpreworkbrief_perm --> detect_existing_template
  detect_existing_template --> deploy_prompt_template_header
  deploy_prompt_template_header --> verify_template_deployed
  verify_template_deployed --> read_fieldservice_settings
  read_fieldservice_settings --> deploy_lds_setting
  deploy_lds_setting --> verify_lds_enabled
  verify_lds_enabled --> deploy_field_access_permset
  deploy_field_access_permset --> assign_perms_technician
  assign_perms_technician --> read_workorder_layout
  add_field_to_layout --> verify_field_on_layout
  verify_field_on_layout --> create_test_workorder
  create_test_workorder --> create_service_appointment
  create_service_appointment --> assign_resource
  assign_resource --> verify_workorder_scheduled
  verify_field_on_layout --> point_existing_workorder
  verify_workorder_scheduled --> resolve_template_version
  point_existing_workorder --> resolve_template_version
  resolve_template_version --> activate_prompt_template
  activate_prompt_template --> verify_activation
  verify_permset_admin --> assign_fs_einstein_psl_admin
  assign_fs_einstein_psl_admin --> assign_fs_einstein_user_admin
  assign_fs_einstein_user_admin --> verify_showpreworkbrief_perm
  read_workorder_layout --> add_field_to_layout
Read operations
read-fieldservice-settings

读取 Field Service 设置单例(userSettings 映射 — 移动端的 LDS 使用 lsdkForFieldServiceMobilePref,以及其他偏好设置)。结果是 deploy-lds-setting 在 verify-lds-enabled 中执行读回契约时所用的写入前快照。复用自 FieldServiceSettings SOR。

调用: GET /headless/invoke/platform/document-builder

依赖: verify-template-deployed

输出: 位于 body.body 的 FieldServiceSettingsSerializer 映射。关键字段:lsdkForFieldServiceMobilePref(Field Service Mobile 的 Lightning Data Service — 若缺少此项,Pre-Work Brief 在设备上将显示空白)。此步骤为 verify-lds-enabled 提供快照;deploy-lds-setting 不会使用整个映射(参见其输入 — 控制器使用 isChanged<Field> 部分补丁语义)。

read-workorder-layout

枚举 WorkOrder 页面布局和记录类型。结果用于 add-field-to-layout / verify-field-on-layout。响应较大(约 200KB)。

调用: GET /services/data/v67.0/sobjects/WorkOrder/describe/layouts

依赖: assign-perms-technician

输出: layouts[] 数组,包含 layoutSections/detailLayoutSections 和 buttonLayoutSection;recordTypeMappings[] 将 recordTypeId 映射到 layoutId。扫描技术人员布局中的 detailLayoutSections[].layoutRows[].layoutItems[].layoutComponents[].value,检查是否存在 'PreWorkBriefPromptTemplate'。

resolve-template-version

从已部署的 Pre_Work_Brief 模板中提取 3vN 前缀的版本 Id。与 detect-existing-template / verify-template-deployed 使用相同的 GET — 此步骤使用独立 ID 重新读取,因为激活操作明确需要版本 Id。读取 childRelationships.GenAiPromptTemplateVersions[].fields.Id.value;选择 fields.Status.value == 'Published' 的条目(激活前的版本)。

调用: GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}

输入:

  • promptTemplateDevName (String) — 此工作流中固定为 'Pre_Work_Brief'。

依赖: verify-workorder-scheduled、point-existing-workorder

输出: childRelationships.GenAiPromptTemplateVersions[].fields.Id.value — activate-prompt-template 使用的 3vN 前缀版本 Id。即使模板处于非激活状态/未出现在运行时目录中,GET 也能正常工作。

备注: branch_join:两个测试数据分支的汇合点。verify-workorder-scheduled 是新建分支的末端;point-existing-workorder 是现有分支的末端。这两者互斥,因此恰好有一个前置步骤会运行 — 无论哪种模式,都会触发激活。此处的 depends_on 应视为 OR 汇合,而非屏障。

写入操作
assign-psl-admin

通过 PermissionSetLicenseAssign 创建操作,将 Einstein-for-Field-Service PSL 授予管理员用户。具备幂等性:将 DUPLICATE_VALUE(STATUSCODE)视为已分配成功并忽略。

调用: POST /services/data/v67.0/sobjects/PermissionSetLicenseAssign

输入:

  • AssigneeId (String) — 18 位 User Id(以 005 为前缀)。
  • PermissionSetLicenseId (String) — 来自 verify-provisioning 的 0PL 前缀 PSL Id。
    • 来源: verify-provisioning 的输出

依赖: verify-provisioning

回滚: DELETE /sobjects/PermissionSetLicenseAssign/{id}

备注: 幂等性:DUPLICATE_VALUE -> 成功(已分配)

assign-perms-admin

为管理员授予 EinsteinGPTPromptTemplateManager 权限集,以获得 Prompt Studio 编写权限。注意(2026-08-25 更正):此权限集不会让 Pre-Work Brief 模板类型显示出来;该类型受 UserPermission ShowPreWorkBriefGA 控制(参见 assign-fs-einstein-user-admin)。在完整的 FS 评估组织中,ShowPreWorkBriefGA 已默认启用,因此看起来只需此权限集即可,但在全新组织中并非如此。此操作具有幂等性:将 DUPLICATE_VALUE 视为已分配并忽略。

调用: POST /services/data/v67.0/sobjects/PermissionSetAssignment

输入:

  • AssigneeId (String)
  • PermissionSetId (String) — 以 0PS 开头的 PermissionSet ID。通过 SELECT Id FROM PermissionSet WHERE Name='EinsteinGPTPromptTemplateManager' 查询。

依赖: assign-psl-admin

回滚: DELETE /sobjects/PermissionSetAssignment/{id}

备注: 幂等性:DUPLICATE_VALUE → 成功;skill_gotcha:部署 deploy-prompt-template-header 前跳过此操作会导致静默失败(下拉选项缺失,且无错误提示)。

deploy-prompt-template-header

通过一次 insertPromptTemplate 调用创建 Pre_Work_Brief GenAiPromptTemplate(类型为 einstein_gpt__fieldServicePreWorkBrief),并通过 childRelationships 内联传入版本 + WorkOrder 输入 + Flow DataProvider。insertPromptTemplate 会拒绝不带版本的模板头(“Prompt Template[null] has no versions”),而 createPromptTemplateVersion 只会向已存在的模板添加版本,因此模板头 + 版本 + 输入 + provider 会在同一事务中一并创建。如果 detect-existing-template 返回 200,则跳过。

根据技能原先假设通过 MDAPI 部署进行了调整:此 dispatcher 未路由 /headless/metadata;einstein-prompt-studio 是规范替代方案。

调用: PATCH /headless/invoke/platform/einstein-prompt-studio/insert-prompt-template

输入:

  • promptTemplate (object) — 完整的 GenAiPromptTemplate,作为单次调用插入,并以 JSON 对象发送(不是序列化字符串)。准确的嵌套结构(每个节点使用包装形式 {apiName, id:null, isStandard:false, isOverridable:false, additionalFields:{}, fields:{...}, childRelationships:{...}})。 apiName 必须是节点的实体类型名称(不能为 null):顶层节点为 'GenAiPromptTemplate',嵌套节点依次为 'GenAiPromptTemplateVersion'、'GenAiPromptTemplateInput'、'GenAiPromptTemplateDataProvider'、'GenAiPromptTemplateDataProviderParam'。apiName 为 null 会抛出 CONTROLLER_ERROR(“Cannot invoke String.equals ... getApiName() is null”)。Flow 引用(DataProvider Definition/ReferenceName 和版本 Content 合并字段)必须使用完整的命名空间限定 Flow 名称 sfdc_fieldservice__ModifyPWB — 仅使用 FlowDefinitionView.ApiName(ModifyPWB)会导致 flow-accessibility 错误(“ModifyPWB is not accessible”):

    promptTemplate.fields: DeveloperName='Pre_Work_Brief', MasterLabel='Pre-Work Brief', Type='einstein_gpt__fieldServicePreWorkBrief', Visibility='Global'

    promptTemplate.childRelationships.GenAiPromptTemplateVersions[0]: .fields: Content='{!$Flow:sfdc_fieldservice__ModifyPWB.Prompt}', PrimaryModel='sfdc_ai__DefaultOpenAIGPT4OmniMini' .childRelationships.GenAiPromptTemplateInputs[0].fields: ApiName='WorkOrder' (必须与模板类型的架构输入名称一致), ReferenceName='Input:WorkOrder', MasterLabel='Work Order', Definition='SOBJECT://WorkOrder', IsRequired=true .childRelationships.GenAiPromptTemplateDataProviders[0]: .fields: Definition='flow://sfdc_fieldservice__ModifyPWB', ReferenceName='Flow:sfdc_fieldservice__ModifyPWB' .childRelationships.GenAiPromptTemplateDataProviderParams[0].fields: Definition='SOBJECT://WorkOrder', ParameterName='WorkOrder', IsRequired=true, ValueExpression='{!$Input:WorkOrder}'

必须显式提供 Flow DataProvider:启用已配置操作的门控后,只有 DataGraph provider 会自动派生;Flow provider 由客户端管理。前提条件:目标 org 必须具有 sfdc_fieldservice__ModifyPWB flow(ProcessType=PromptFlow,且处于激活状态),并带有 WorkOrder 输入变量;否则 MissingTemplateInputs / provider 验证错误会反复出现。

依赖: detect-existing-template

输出: 已创建的模板记录,Id 以 0hf 开头,包含 DeveloperName、MasterLabel、IsActive=false,以及内联版本(LastCreatedVersionId 以 3vN 开头,LastUsedVersionNumber=1)。

deploy-lds-setting

通过 saveFieldServiceSettingsConfig 为 Field Service Mobile 启用 Lightning Data Service。请求格式(至关重要):PATCH 请求体必须在顶层 "userSettings" 键下封装变更内容 — {"userSettings": {"lsdkForFieldServiceMobilePref": true, "isChangedLsdkForFieldServiceMobilePref": true}}。使用平铺请求体(字段位于顶层)会导致 500 CONTROLLER_ERROR NullPointerException("userSettings is null")。在 userSettings 中,控制器使用 isChanged 部分补丁语义:同时传入 lsdkForFieldServiceMobilePref=true 和 isChangedLsdkForFieldServiceMobilePref=true — 如果省略 isChanged 标志,将返回 200 和 body:true,但不会实际保存任何内容。在 userSettings 中仅发送变更内容及其对应的 isChanged 标志,而非完整的回读映射。仅凭 200 和 body:true 无法证明数据已保存 — 始终通过 getFieldServiceSettingsConfig 回读验证。

调用: PATCH /headless/invoke/platform/document-builder-rlm/save-field-service-settings-config

输入:

  • lsdkForFieldServiceMobilePref (boolean) — Lightning-SDK-for-Field-Service-Mobile 组织偏好的目标值(设为 true 以启用)。由 ORG_PREFERENCE_LSDK_FOR_FIELD_SERVICE_MOBILE 提供支持。
  • isChangedLsdkForFieldServiceMobilePref (boolean) — 要保存 lsdkForFieldServiceMobilePref,必须设为 true。控制器按字段应用 isChanged 门控;如果缺少对应标志或标志为 false,则会静默跳过该字段 — 返回 200 且响应体为 {body:true} 并不能确认值已生效。始终通过 getFieldServiceSettingsConfig 回读验证。

依赖: read-fieldservice-settings

回滚: 使用 lsdkForFieldServiceMobilePref= 和 isChangedLsdkForFieldServiceMobilePref=true 重新发送 PATCH。

备注: partial_patch_semantics:按字段应用 isChanged 门控。返回 200 且 body:true 并不能确认写入成功;始终回读验证。

deploy-field-access-permset

创建 PermissionSet 'PreWorkBrief_Field_Access' 和 FieldPermissions 行,为 WorkOrder.PreWorkBriefPromptTemplate 授予读取权限。使用原生 CRUD 取代技能中假定的 MDAPI 部署路径。具备幂等性:PermissionSet 插入时出现 DUPLICATE_DEVELOPER_NAME 会被捕获并忽略,随后复用已有权限集。

调用: POST /services/data/v67.0/sobjects/PermissionSet

输入:

  • Name (String) — 'PreWorkBrief_Field_Access'
  • Label (String) — 'Pre-Work Brief Field Access'

依赖: verify-lds-enabled

输出: 已创建的 PermissionSet Id(以 0PS 开头);将其用作后续 POST /sobjects/FieldPermissions 的 ParentId,并设置 SobjectType='WorkOrder'、Field='WorkOrder.PreWorkBriefPromptTemplate'、PermissionsRead=true。

回滚: DELETE /sobjects/PermissionSet/{id}

备注: composite:后续操作:使用 ParentId=、SobjectType='WorkOrder'、Field='WorkOrder.PreWorkBriefPromptTemplate'、PermissionsRead=true,发送 POST /sobjects/FieldPermissions。

assign-perms-technician

向试点技术人员授予 PSL、FieldServiceMobileStandardPermSet 和 PreWorkBrief_Field_Access。共执行三次写入(PermissionSetLicenseAssign + 两次 PermissionSetAssignment)。具备幂等性:逐行捕获并忽略 DUPLICATE_VALUE。

调用: POST /services/data/v67.0/sobjects/PermissionSetAssignment

输入:

  • AssigneeId (String) — 试点技术人员的 User Id。
  • PermissionSetId (String) — 每个权限集分别调用一次。PSL 通过使用 PermissionSetLicenseId 的 POST /sobjects/PermissionSetLicenseAssign 授予。

依赖: deploy-field-access-permset

回滚: 对每一行执行 DELETE /services/data/v67.0/sobjects/PermissionSetAssignment/{id}(PSL 则执行 DELETE /services/data/v67.0/sobjects/PermissionSetLicenseAssign/{id})。

备注: 幂等性:逐行将 DUPLICATE_VALUE 视为成功

create-test-workorder

创建一个测试 WorkOrder,通过 PreWorkBriefPromptTemplate 指向 Pre_Work_Brief。test-data-mode=existing 时跳过(由 point-existing-workorder 分支接管)。使用 Subject 幂等性标签 'B4Expertise_<run_id>_create-test-workorder',以便在运行后清理。

调用: POST /services/data/v67.0/sobjects/WorkOrder

输入:

  • Subject (String) — 使用 'B4Expertise_<run_id>_create-test-workorder' 作为标签,以便在运行后清理。
  • Status (String) — 例如 'New'
  • PreWorkBriefPromptTemplate (String) — WorkOrder.PreWorkBriefPromptTemplate 是指向 GenAiPromptTemplate 的查找字段——传入 0hf 前缀的模板 Id,而不是 DeveloperName。从 verify-template-deployed 的响应正文(GET /einstein/prompt-templates/Pre_Work_Brief)中解析 Id:读取顶层的 "id",或 "fields" 对象下的 "Id" 值。传入 DeveloperName 'Pre_Work_Brief' 会返回 400 INVALID_INPUT "Enter a valid Pre-Work Brief prompt template ID"。

依赖于: verify-field-on-layout

回滚: DELETE /sobjects/WorkOrder/{id}

create-service-appointment

为测试 WorkOrder 创建 ServiceAppointment,并将 SchedStartTime/SchedEndTime 设在今天的时间窗口内(以便技术人员今天可以在设备上调出该记录)。

调用: POST /services/data/v67.0/sobjects/ServiceAppointment

输入:

  • ParentRecordId (String) — 来自 create-test-workorder 的 WorkOrder Id。
    • 来源: create-test-workorder 的输出
  • SchedStartTime (String) — 今天时间窗口内的 ISO 日期时间。
  • SchedEndTime (String)

依赖于: create-test-workorder

回滚: DELETE /sobjects/ServiceAppointment/{id}

assign-resource

创建 AssignedResource,将试点技术人员的 ServiceResource 与测试 ServiceAppointment 关联起来。通过 SELECT Id FROM ServiceResource WHERE RelatedRecordId='' AND IsActive=true 解析 ServiceResource。

调用: POST /services/data/v67.0/sobjects/AssignedResource

输入:

  • ServiceAppointmentId (String)
  • ServiceResourceId (String)

依赖于: create-service-appointment

回滚: DELETE /sobjects/AssignedResource/{id}

point-existing-workorder

使用现有测试数据的分支:将某个真实 EXISTING WorkOrder 的 PreWorkBriefPromptTemplate PATCH 为 Pre_Work_Brief 模板 Id,并将其 ServiceAppointment 的 SchedStartTime/SchedEndTime PATCH 到今天的时间窗口内。test-data-mode=existing 时,替代 create-test-workorder -> create-service-appointment -> assign-resource。

调用: PATCH /services/data/v67.0/sobjects/WorkOrder/{Id}

输入:

  • Id (String) — 路径参数 — 协调技能提供的 WorkOrder Id。
  • PreWorkBriefPromptTemplate (String) — 0hf 前缀的 GenAiPromptTemplate Id(不是 DeveloperName)。通过 verify-template-deployed 的响应正文解析。PATCH 时传入 DeveloperName 'Pre_Work_Brief' 会返回 400 INVALID_INPUT。

依赖于: verify-field-on-layout

回滚: PATCH 前保存原有的 PreWorkBriefPromptTemplate 和 SA SchedStart/End;回滚时 PATCH 恢复。

备注: composite:后续操作:使用今天时间窗口内的 SchedStartTime + SchedEndTime PATCH /sobjects/ServiceAppointment/{SA-Id}。;分支:仅当 test-data-mode=='existing' 时执行。与 create-test-workorder / create-service-appointment / assign-resource 互斥。

activate-prompt-template

通过 einstein-gpt-connect-api 版本状态端点,将 Pre_Work_Brief 从 Published 切换为 Active。请求正文为空。成功时,响应为 isSuccessful:true、statusCode:"200"、additionalData.wrappedMap.summary.overallSeverity:"SAFE";模板级别的 IsActive 会切换为 true,并填充 ActiveVersionId(版本自身的 Status 仍为 Published——在模板级别,版本的 "Published" 即 "Active")。幂等:重新激活已处于 Active 状态的版本会返回 200 SAFE。端点为 @ConnectHidden(from=Apex)——请通过 dispatch / 原生 REST 调用,而非 ConnectApi。

调用: PUT /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}/versions/{versionId}/status?action=activate&ignoreWarnings=false

输入:

  • promptTemplateDevName (String) — 路径参数。此工作流中固定为 'Pre_Work_Brief'。
  • versionId (String) — 路径参数。来自 resolve-template-version 的 3vN 前缀版本 Id。
    • 来源: resolve-template-version 的输出
  • action (String) — 查询参数。'activate'(此步骤)或 'deactivate'(回滚)。无默认值。
  • ignoreWarnings (boolean) — 查询参数。默认值为 false。设为 true 可接受 WARNING 严重级别的安全变更评估并强制激活;如果要让平台的安全变更规则阻止有风险的激活,请保持为 false。

依赖: resolve-template-version

输出: isSuccessful:true、statusCode:"200"、errorMessages:[]、hasWarning:false、warningMessages:[]、回显的 templateId、templateType:"einstein_gpt__fieldServicePreWorkBrief"、回显的 versionId、additionalData.wrappedMap.summary.overallSeverity:"SAFE"(或 "WARNING"/"ERROR")。写入后的副作用:fields.IsActive 从 false 切换为 true;fields.ActiveVersionId 填充为传入的 versionId。

回滚: PUT /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}/versions/{versionId}/status?action=deactivate

备注: empty_body:请求正文为空({})。没有字段。;apex_hidden:端点为 @ConnectHidden(from=Apex)——无法从 ConnectApi.EinsteinLLM 调用。请通过 dispatch / sf api request rest --method PUT / 任意原生 REST 客户端调用。;access_check:x-sfdc.org-access-check: EinsteinGPT.orgHasEinsteinGPTEnabled——组织必须启用 Einstein GPT。;idempotency:重新激活已处于 Active 状态的版本会无错误地返回 200 SAFE。可安全重复执行。;api_version:自 v65.0(API 258 起)提供。更早的版本(v62-v66 部分版本)或错误的 URL 形式(/activate,而非 /versions/{id}/status?action=activate)会返回非 2xx。

assign-fs-einstein-psl-admin

向管理员授予 EinsteinFieldServicePsl PSL(“Einstein for Field Service”)。此 PSL 为 UserPermission ShowPreWorkBriefGA 授权,该权限是 fieldServicePreWorkBrief 模板类型的门控条件。幂等:将 DUPLICATE_VALUE 视为已分配成功并忽略。

调用: POST /services/data/v67.0/sobjects/PermissionSetLicenseAssign

输入:

  • AssigneeId (String) — 18 字符的管理员 User Id(以 005 开头)。
  • PermissionSetLicenseId (String) — 以 0PL 开头的 PSL Id。通过 SELECT Id FROM PermissionSetLicense WHERE DeveloperName='EinsteinFieldServicePsl' 查询获取。

依赖: verify-permset-admin

回滚: DELETE /sobjects/PermissionSetLicenseAssign/{id}

备注: 幂等:DUPLICATE_VALUE -> 成功(已分配)

assign-fs-einstein-user-admin

向管理员授予 EinsteinFieldServiceUser 权限集(PermissionsShowPreWorkBriefGA=true)。这会授予 UserPermission ShowPreWorkBriefGA,使 fieldServicePreWorkBrief 模板类型在 getPromptTemplateTypes / insertPromptTemplate 中有效。关键依赖:必须在 deploy-prompt-template-header 之前完成,否则该类型会显示为“无效”。支持幂等操作。

调用: POST /services/data/v67.0/sobjects/PermissionSetAssignment

输入:

  • AssigneeId (String)
  • PermissionSetId (String) — 以 0PS 开头的 PermissionSet Id。通过 SELECT Id FROM PermissionSet WHERE Name='EinsteinFieldServiceUser' 查询。

依赖: assign-fs-einstein-psl-admin

回滚: DELETE /sobjects/PermissionSetAssignment/{id}

备注: 幂等性:DUPLICATE_VALUE → 成功;技能陷阱:缺少此项会导致部署失败,并显示“Prompt Template Type is not valid”(在具有完整 FS 评估权限的组织中,此权限可能已隐式具备,因此不会报错)。

add-field-to-layout

通过 Tooling API Layout.Metadata 往返,将 WorkOrder.PreWorkBriefPromptTemplate 添加到技师的 Work Order 页面布局中(这不是缺口——此前关于“没有整版布局写入 API”的说明不正确;对于 dispatch 接口,/headless/metadata 未路由,但 Tooling REST 可直接传递,而且这不是 SOAP MDAPI)。往返流程:(1) 解析布局 Id — GET /tooling/query?q=SELECT Id, Name, TableEnumOrId FROM Layout WHERE TableEnumOrId='WorkOrder'(Id 以 00h 开头);(2) 读取完整元数据 — GET /tooling/sobjects/Layout/{id},保留 Metadata 对象;(3) 将 layoutItem {behavior:'Edit', field:'PreWorkBriefPromptTemplate'} 插入 layoutSections[].layoutColumns[].layoutItems[] 中(例如 “Information” 分区);(4) 使用 {"Metadata": } PATCH /tooling/sobjects/Layout/{id} — HTTP 204 = 成功。Tooling Metadata 写入采用整对象替换,因此必须往返整个 Metadata 对象,只修改新增项。写入时有两个容易踩坑的地方(每个都会导致 PATCH 返回 400):(a) feedLayout.rightComponents[].componentType 读回时是字符串,但写入器会拒绝("Cannot deserialize instance of complexvalue from VALUE_STRING")— 写入时设置 feedLayout:null(将 Chatter 动态消息流视图布局重置为默认值);(b) ServiceReportRelatedList 相关列表无法通过写入验证("Mass quick actions don't support ServiceReport")— 删除该 relatedLists 条目。这两项都是整对象替换的副作用(动态消息流视图重置;Service Reports 相关列表移除),为了显示该字段可以接受,并且可在 Layout Editor 中恢复。字段级安全性需通过 deploy-field-access-permset(FieldPermissions)单独授予——此步骤只负责布局放置。

调用: PATCH /services/data/v67.0/tooling/sobjects/Layout/{layoutId}

输入:

  • layoutId (String) — 以 00h 开头的 Layout Id。通过 GET /tooling/query?q=SELECT Id FROM Layout WHERE TableEnumOrId='WorkOrder' 解析。
  • Metadata (object) — 从 GET /tooling/sobjects/Layout/{id} 往返获得的完整 Layout Metadata 对象,并在某个 layoutSections 列中新增 layoutItem {behavior:'Edit', field:'PreWorkBriefPromptTemplate'}。设置 feedLayout:null,并删除 ServiceReportRelatedList relatedLists 条目(参见说明中的易踩坑事项)。采用整对象替换语义——省略的键会重置为默认值。

依赖: read-workorder-layout

输出: HTTP 204 No Content = 布局已更新。通过 verify-field-on-layout(扫描 describe/layouts)确认。

回滚: 使用步骤 (2) 中捕获的写入前 Metadata 快照重新 PATCH /tooling/sobjects/Layout/{id}。

备注: transport:通过 dispatch 上的原生直通方式使用 Tooling REST——不是 Metadata API SOAP。/headless/metadata 仍未路由。;full_replace:Tooling Metadata 写入会替换整个布局;往返整个 Metadata 对象,只修改新增项。;write_footguns:必须将 feedLayout 设为 null(写入器会拒绝往返得到的 componentType 字符串);必须删除 ServiceReportRelatedList("Mass quick actions don't support ServiceReport")。两者都是整对象替换的副作用。

验证操作
verify-provisioning

读取 EinsteinFieldServicePsl 的 PermissionSetLicense。返回零行表示组织尚未预配;协调技能会转入 STOP。幂等读取。

调用: GET /services/data/v67.0/query

输入:

  • q (String) — SOQL:SELECT Id, DeveloperName FROM PermissionSetLicense WHERE DeveloperName='EinsteinFieldServicePsl'

输出: records[0].Id = 用于 assign-psl-admin / assign-perms-technician 的、以 0PL 开头的 PSL Id。

verify-permset-admin

在提示词模板部署之前,通过 SOQL 确认管理员拥有 EinsteinGPTPromptTemplateManager。防止因下拉列表静默缺失而导致的失败。

调用: GET /services/data/v67.0/query

输入:

  • q (String) — SELECT Id FROM PermissionSetAssignment WHERE AssigneeId='' AND PermissionSet.Name='EinsteinGPTPromptTemplateManager'

依赖项: assign-perms-admin

输出: totalSize>=1 -> 检查通过。

detect-existing-template

通过运行时目录读取 Pre_Work_Brief。有两个用途:(1) 幂等性检查:200 且 fields.DeveloperName='Pre_Work_Brief' -> 跳过 deploy-prompt-template-header / -version;404 -> 继续;(2) 激活前预读取:捕获 fields.IsActive + fields.ActiveVersionId。GenAiPromptTemplate 无法通过 SOQL/Tooling 查询;此 Connect API 是规范的读取路径。

调用: GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}

输入:

  • promptTemplateDevName (String) — 此工作流固定为 'Pre_Work_Brief'。

依赖项: verify-showpreworkbrief-perm

输出: fields.Id = 以 0hf 开头的模板 Id;fields.IsActive 布尔值;fields.ActiveVersionId(处于 Active 状态时有值,否则为 null);childRelationships.GenAiPromptTemplateVersions[].fields.Id.value = 以 3vN 开头的版本 Id(即使模板为 Inactive,版本仍为 Published)。

注意: skill_gotcha:对 GenAiPromptTemplate 执行 SOQL/Tooling 会返回 INVALID_TYPE 400。此 Connect API 是唯一的读取路径。

verify-template-deployed

部署后回读 Pre_Work_Brief。确认 fields.Id 已填充,且 childRelationships.GenAiPromptTemplateVersions 包含 Status='Published' 的已部署版本。与 detect-existing-template 使用同一端点——这是部署前后契约。输出后续步骤所需的 0hf 前缀模板 Id(create-test-workorder / point-existing-workorder 会从此响应读取 fields.Id)。

调用: GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}

输入:

  • promptTemplateDevName (String)

依赖项: deploy-prompt-template-header

输出: fields.Id 存在(0hf 前缀——后续 WorkOrder.PreWorkBriefPromptTemplate 查找会使用此值);childRelationships.GenAiPromptTemplateVersions[].fields.Status == 'Published';激活前 fields.IsActive == false(将在 activate-prompt-template 中切换)。

verify-lds-enabled

回读 Field Service 设置;确认写入后 body.body.lsdkForFieldServiceMobilePref==true。与 read-fieldservice-settings 使用同一端点——这是写入前后契约。此项为必需,因为 deploy-lds-setting 无论其配套的 isChanged 标志是否已设置,都会返回 200。

调用: GET /headless/invoke/platform/document-builder

依赖项: deploy-lds-setting

输出: 写入后 body.body.lsdkForFieldServiceMobilePref == true。此处出现偏差,说明 deploy-lds-setting 漏设了配套的 isChangedLsdkForFieldServiceMobilePref 标志。

verify-field-on-layout

扫描 WorkOrder 布局描述(GET /sobjects/WorkOrder/describe/layouts),检查技师布局分区中是否有 value='PreWorkBriefPromptTemplate' 的 layoutComponent。存在 -> add-field-to-layout 已生效;不存在 -> Tooling Metadata PATCH 未生效(重新检查往返结果和两个写入陷阱)。

调用: GET /services/data/v67.0/sobjects/WorkOrder/describe/layouts

依赖项: add-field-to-layout

输出: 布尔值:技师的布局中是否存在 PreWorkBriefPromptTemplate?如果为 false 且 add-field-to-layout 尚未解决,则向人工操作人员提供布局编辑器深层链接,而不是直接失败。

verify-workorder-scheduled

SOQL 冒烟测试:SELECT Id, SchedStartTime FROM ServiceAppointment WHERE WorkOrderId='' AND SchedStartTime = TODAY。幂等性 + 已落入今天时间窗口的约定。

调用: GET /services/data/v67.0/query

输入:

  • q (String)

依赖: assign-resource

输出: totalSize==1 -> 链路已落入今天。结果为 0 -> SA 不在今天的时间窗口内,或不存在。

verify-activation

读取运行时提示词模板目录,确认激活已生效。两种等效读取方式,任选其一。(a)详情读取 GET /einstein/prompt-templates/{devName},确认 fields.IsActive.value==true 且 fields.ActiveVersionId.value 有值。(b)目录列表 GET /einstein/prompt-templates?pageSize=200,确认 promptRecords[] 中包含一项,其 fields.DeveloperName.value=='Pre_Work_Brief' 且 fields.IsActive.value==true。目录列表方式可证明模板已出现在运行时目录中(Agent / 移动客户端现在可以看到它)。

调用: GET /services/data/v67.0/einstein/prompt-templates/{promptTemplateDevName}

输入:

  • promptTemplateDevName (String)

依赖: activate-prompt-template

输出: fields.IsActive.value==true 且 fields.ActiveVersionId.value 有值 -> 激活已生效。可选的目录列表回退方式 GET /einstein/prompt-templates?pageSize=200 -> promptRecords[?fields.DeveloperName.value=='Pre_Work_Brief'].fields.IsActive.value==true,可确认其在运行时目录中可见。

verify-showpreworkbrief-perm

确认管理员在部署提示词模板前已有效持有 UserPermission ShowPreWorkBriefGA —— 这是 fieldServicePreWorkBrief 类型的真正门槛。可避免出现 “Prompt Template Type is not valid” 故障。

调用: GET /services/data/v67.0/query

输入:

  • q (String) — SELECT Id FROM PermissionSetAssignment WHERE AssigneeId='' AND PermissionSet.Name='EinsteinFieldServiceUser'

依赖: assign-fs-einstein-user-admin

输出: totalSize>=1 -> 门槛已通过(已授予 ShowPreWorkBriefGA)。

评论

支持 Markdown