关于 experience-search-coordinate
面向需要查找既有 CMS 内容或媒体的搜索协调技能,可按内容类型分派到内容或媒体流程,并处理混合请求。它不用于生成、编辑或发布内容。
主要功能
- 01识别内容与媒体搜索意图
- 02按类型路由搜索流程
- 03混合请求先处理媒体
- 04搜索前解析渠道范围
适用场景
- 01查找文章、新闻或常见问题
- 02查找图片、视频或文档
- 03同时查找页面内容与媒体
MCP 相关
依赖 metadata-grounding MCP 服务的 search_metadata 和 query_metadata 来发现并分类可搜索的内容类型,随后将请求路由至相应搜索流程。
安装
复制下面的命令,在终端中运行。
npx skills add https:/ / github.com/ forcedotcom/ sf-skills --skill experience-search-coordinate安装后在 Claude Code 中输入/experience-search-coordinate确认已加载。
包含的文件
除 SKILL.md 外,安装时一起下载的参考资料和脚本。
assets/search-payload-template.jsonreferences/content-route.mdcontent-type-discovery.mdmedia-route.mdscope-resolution.md详细介绍
Search Coordinator
将搜索请求路由到正确的子工作流——结构化 CMS 内容或媒体;如果请求同时涉及两者,则两种工作流都运行。
下方的 → Read 指示是必需操作,不是供参考的信息——继续越过该指示之前,先打开相应文件;此前曾因凭记忆执行步骤而导致本技能内容过时。
范围
本技能用于搜索现有内容或媒体,而非创建新内容或图像。
用户想要以下内容时,请使用本技能:
- 搜索文章、博客帖子、新闻、常见问题、活动、产品或自定义内容
- 搜索图像、徽标、图标、照片、图形、横幅、主视觉图、音频片段、视频或文档
- 复用现有资源而非创建新资源,或在同一个请求中提出上述任意组合
用户想要以下内容时,请勿使用本技能:
- 生成新内容(使用
experience-cms-content-generate) - 生成新图像(使用图像生成工具)
- 定义内容类型架构(使用
experience-cms-content-type-generate) - 应用品牌指南(使用
experience-cms-brand-apply) - 创建、编辑或发布内容记录
步骤 1:确定渠道范围
每次执行到此处都必须完成——绝不可直接跳到步骤 2。 请求的任何措辞(“只帮我找篇文章”、普通关键词搜索等)都不能免除本步骤的本地文件扫描。如果你准备在本轮尚未扫描 uiBundles/*/public/content-metadata.json 的情况下于步骤 2 调用 search_metadata,请立即停止并先返回此处。
- 明确给出了
channelId——直接使用,跳过发现步骤,继续执行下方的范围规则。 - 指定了 uiBundle/app,但未给出
channelId——解析该 uiBundle 的channelId;如果无法解析,则按未指定名称处理,转到第 3 点。 - 否则,从本地项目中发现——扫描每个
uiBundles/*/public/content-metadata.json文件(绝不可根据服务端部署状态决定是否扫描),并收集其中的channelId。数量为零 → 仅公共渠道。恰好一个 → 公共渠道加该渠道。多于一个 → 使用ask_followup_question询问用户选择哪一个。
→ 行动前,请阅读 references/scope-resolution.md §「步骤 1 的第 1–3 点」,了解完整的发现机制。确定范围后继续执行步骤 2。
**范围规则(无论 channelId 是用户提供还是发现所得,均适用):**范围始终包括公共渠道(contentAccessScope: "Public",不含 channelIds 字段)以及该 channelId(单独调用,并将 channelIds 设为该值,不含 contentAccessScope 字段)——绝不可解析另一个 ID 来代表“公共”,也绝不可在同一次调用中同时发送 contentAccessScope 和 channelIds。**此范围决定步骤 3 和步骤 4 要发起多少次搜索调用:**范围中没有 channelId → 发起一次搜索调用(仅公共渠道)。范围中有 channelId → 发起两次搜索调用(先搜索公共渠道,再搜索指定的 channelId),并在展示前合并结果。
步骤 2:确定内容类型并进行路由
前提:本轮必须已执行步骤 1(步骤 1 得出的渠道范围将在下方第 5 点传递到步骤 3/4)。步骤 1 无条件执行——即使是没有明显渠道关联的普通关键词搜索也不例外,不仅限于请求中指定了渠道或应用的情况。
-
识别请求中一个或多个不同的搜索意图(例如,“找一篇关于我们发布会的新闻稿和一张用于页面的主视觉图”包含两个意图)。针对每个意图分别执行本步骤的其余内容。
**如果意图跨越两种路由,必须依次执行路由——绝不可交错或合并:**先为每个媒体意图完整执行媒体路由(步骤 4),再为每个内容意图执行内容路由(步骤 3)。参见步骤 5。
-
在
metadata-groundingMCP 服务上调用search_metadata,将意图本身作为查询。不支持filters数组——metadataType是唯一的缩小范围参数,作为顶层参数传入:search_metadata({ query: "<the user's intent>", metadataType: "ContentTypeBundle", limit: 100 })
每个候选项都已包含完整的复合标识符(例如 00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle)——按返回结果使用;切勿重新构造。
先检查结果,再进行任何其他操作:
metadata-grounding不可用(调用报错/超时) — 必须先调用get_content_types_for_workspace(工作区内容类型回退),再使用任何 Step 3/4 搜索工具;不要尝试query_metadata,因为没有可查询的内容;也不要在假定 FQN 的情况下直接跳到搜索工具。- 返回零个候选项 — 向用户提出一个澄清问题,以缩小意图范围(使用本步骤末尾“意图本身有歧义”情况中的同一问题/选项),然后使用澄清后的查询重试一次
search_metadata:- 重试后仍返回零个候选项 — 使用工作区内容类型回退。不要再问第二个澄清问题。
- 重试后返回一个或多个候选项 — 继续执行第 3 点。
- 首次调用返回一个或多个候选项 — 继续执行第 3 点。
当 search_metadata 未能返回可用候选项列表时的回退链 → 运行前阅读 references/content-type-discovery.md:“Workspace content-type fallback” 优先,然后才将 “Wording fallback” 作为最后手段。
-
调用
query_metadata,传入第 2 点中每个候选项的标识符(最多取前 5 个,批量放入一个metadataIdentifiers数组):query_metadata({ metadataIdentifiers: ["00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle"] })阅读每个条目的
typeDetails[].properties——mixins、typeClasses和apiName都在其中,而不在search_metadata响应中。 -
先筛选受支持的类型,再决定幸存者应走哪条路径——不要混为一谈:
- 丢弃任何
properties.mixins不含sfdc_cms:deliveryApiEnabled的候选项——只有启用了交付 API 的类型才可搜索。 - 在剩余项中,丢弃任何
properties.typeClasses不含sfdc_cms__structured的候选项。这是唯一的“支持搜索”门槛,不是内容/媒体划分——sfdc_cms__structured同时出现在结构化内容类型和媒体类型中,因此它只用于区分可搜索类型与不可搜索类型(例如,sfdc_cms__email包含sfdc_cms__content,但不包含sfdc_cms__structured,因此必须丢弃)。 - 没有幸存项 — 告知用户找到了哪些类型,但这些类型不受支持,然后停止(不要猜测路径)。部分幸存 — 告知用户哪些类型被筛除(直接陈述,不要提问),然后继续处理幸存项。全部幸存 — 无需消息。→ 阅读
references/content-type-discovery.md§ “Filter-result messages”,查看准确措辞。 - 根据排名最高的幸存项的
properties.typeClasses选择路径(顺序以search_metadata返回结果为准):包含sfdc_cms__media→ Media Route(Step 4);否则 → Content Route(Step 3)。 - 收集 FQN——排名最高的幸存项的
properties.apiName。如果其他幸存项与同一意图明显相关且分类相同,也加入它们的 FQN;将所有 FQN 作为一个逗号分隔的contentTypeFqn值传入,而不是按类型分别搜索。
- 丢弃任何
-
传递 Step 1 中的范围(仅公共渠道,或公共渠道加一个
channelId)到所调用的路径——参见下文各路径中的channelIds字段。
如果意图本身有歧义(例如“帮我找些内容”,但没有提供类型或主题线索)→ 在调用 search_metadata 前,阅读 references/content-type-discovery.md § “Intent itself is ambiguous”,查看准确的问题/选项。不要猜测。
步骤 3:内容路由
对于步骤 2 路由到这里的每个意图,都按以下顺序操作。内容类型 FQN 已在步骤 2 中确定。
-
构建搜索查询:
- 提取关键词——提取会出现在内容标题或正文中的具体名词和专有主题。使用领域特定的同义词,最多 10 个词,并用
OR连接。例如,“关于可持续商业实践的博客文章” →sustainable OR sustainability OR business OR environmental。 - 提取分类标签——仅提取描述性特征、类别或主题标签(形容词、类别、属性,不包括具体名词)。将其序列化为
{"OR": ["term1", "term2"]};如果没有适用项,则使用"{}"。例如,“可持续商业实践和环境责任” →{"OR": ["Business", "Corporate", "Environment", "Responsibility"]};“关于密码重置的常见问题” →"{}"(没有描述性或分类术语)。 - 加载
assets/search-payload-template.json,了解请求负载结构。将步骤 2 收集的 FQN 列表(以逗号分隔)设为contentTypeFqn。contentAccessScope和channelIds互斥——同一次调用中绝不能同时发送二者,因此应按下方第 3 点所述,在每次调用中分别设置。
- 提取关键词——提取会出现在内容标题或正文中的具体名词和专有主题。使用领域特定的同义词,最多 10 个词,并用
-
向用户确认——必须采用两条消息的模式(先发送带标签参数的聊天消息,再使用
ask_followup_question,选项为Yes - search now/Edit search/Cancel)。绝不要把带标签的列表放进ask_followup_question的文本中——格式会被移除。→ 阅读references/content-route.md§“搜索前确认查询”,查看模板。 -
根据步骤 1 中的范围执行搜索调用——仅限公开范围时调用一次;范围中包含
channelId时调用两次(公开范围 + 按频道限定)。→ 在解析结果前,阅读references/content-route.md§“执行搜索调用”,了解数组与合并规则。 -
最多展示 5 个不重复结果(仅在展示时按
managedContentKey去重——同一项即使在多个频道中找到,也只在此处显示一次),按内容类型分组;如果还有更多结果,则提供Load More选项。每项都显示其 Channel 字段——绝不能用频道作为分组键:I found 3 content items: Editorial (c__Editorial): 1. <title> — Channel: Public 2. <title> — Channel: <uiBundle> Blog (sfdc_cms__blog): 3. <title> — Channel: Public如果范围内只有一个内容类型 FQN,可以省略标题(仍视为一个分组);但始终要显示每项的 Channel 字段。如果某项存在于多个频道中,此处显示其第一个/主要频道——频道选择应在用户选择后进行(第 5 点),而不是在展示时进行。
-
用户选择一项、多项或所有项。对于每个已选项,如果其交付详情数组包含多个条目,则在显示详情前询问用户要使用哪个频道的副本——每个条目提供一个
ask_followup_question选项,选项名称取自managedContentChannelDetails.name;条目数为零或一的项则直接显示。显示managedContentId、managedContentKey、language、频道,以及——仅当条目为PUBLIC_UNAUTHENTICATED/COMMUNITY且包含contentUrl时才显示,WEB_APP条目除外——完整的 Unauthenticated URL(包含所有查询参数)。→ 提问前,阅读references/content-route.md§“选择时消除频道歧义”——将频道名称作为ask_followup_question的options数组传入,绝不要将其内联写入问题文本。 -
如果范围内的
channelId来自 uiBundle 发现(仅限公开范围或用户提供且没有 uiBundle 的channelId时,完全跳过此步骤),则使用相同的两条消息模式,提供experience-cms-content-render交接选项。传入每项的title、内容类型 FQN、所选条目的managedContentChannelDetails.id(频道 ID)(仅在该项有交付条目时传入;零条目的项应省略,绝不能使用占位符),以及 Unauthenticated URL;如果不存在,则传入其managedContentKey作为contentKey(接收技能的字段名称)。→ 阅读references/content-route.md§“提供渲染交接选项”,了解完整流程。
→ 阅读 references/content-route.md,查看完整的端到端示例。
第 4 步:媒体路由
对于路由到此处的每个意图,按以下顺序执行。媒体内容类型 FQN(例如 sfdc_cms__image、sfdc_cms__audio、sfdc_cms__video、sfdc_cms__document)已在第 2 步中确定——绝不要假设它是图像;请使用第 2 步为此意图发现的 FQN。
- 通过
ask_followup_question展示可用的搜索来源——暂不调用其他工具。 只列出你实际拥有的工具(通过内省确认)对应的来源,以及“其他”,每项各自作为一个选项。等待用户选择后再调用任何工具。→ 查看references/media-route.md§“展示可用的搜索来源”中的完整模板。 - 构建查询,然后在搜索前向用户确认——必须采用两条消息的模式,并在同一轮中完成:
- 使用关键词搜索(
search_media_cms_channels):提取关键词(具体名词,最多 10 个词)和分类标签(描述、风格、情绪、类别等词——查询中实际包含的每一个都要提取;不要仅仅因为找到了关键词就默认分类标签为空)。例如,“带河景的豪华公寓”→ 关键词apartment OR villa OR penthouse OR residence,分类标签{"OR": ["Luxury", "Premium", "Waterfront", "Riverside"]};“汽车”→ 分类标签"{}"(没有描述性词语)。构建请求载荷,将contentTypeFqn设为第 2 步中的 FQN(或多个 FQN),将searchLanguage设为区域设置(en_US、es_MX……默认en_US;必填,绝不能为 null 或省略),searchLimit: 5。→ 查看references/media-route.md中的完整示例。 - 使用 Data 360 混合搜索(
search_electronic_media):使用原样的searchQuery;仅当架构列出了searchLanguage时才使用纯 ISO 格式的searchLanguage;先向用户确认。若结果为零或出现语言错误,询问用户是否愿意在不带该参数的情况下重试一次;如果是服务器错误,绝不重试 →references/media-route.md。 - 其他——询问用户提供直接 URL、资产库路径,或要检查的特定系统(无需确认步骤,因为不会调用工具)。
→ 查看
references/media-route.md§“确认搜索前的查询”,了解确认消息模板(先发送带标签参数的聊天消息,再使用ask_followup_question,选项为Yes - search now/Edit search/Cancel)。
- 使用关键词搜索(
- 根据步骤 1 的范围执行搜索调用——仅搜索公开内容时调用一次;
channelId在范围内时调用两次(公开内容 + 特定频道);Data 360 混合搜索完全忽略频道范围。→ 解析结果前,先查看references/media-route.md§“执行搜索调用”,了解数组与合并规则。 - 通过
ask_followup_question展示所有不重复的结果,并按内容类型分组——每个结果对应一个选项(仅用于展示时按唯一标识符去重——同一项即使在多个频道中找到,也只在此处显示一次),选项标注标题、内容类型和频道(例如Product Launch Hero Banner (sfdc_cms__image) — Public)。与 Content Route 一致:内容类型是分组依据,频道是每项的字段。绝不自动选择。 - 由用户选择一项。 如果其交付详情数组中有多个条目,先询问用户要使用哪个频道的副本——每个条目对应一个选项,以
managedContentChannelDetails.name命名——然后再显示详情。显示内容名称、内容类型、频道;仅当条目属于PUBLIC_UNAUTHENTICATED/COMMUNITY且具有contentUrl时,显示完整的 Unauthenticated URL(包含所有查询参数);WEB_APP条目则不显示。→ 提问前先查看references/media-route.md§“在选择时消除频道歧义”——将频道名称作为ask_followup_question的options数组传入,绝不要将其直接写在问题文本中。 - 始终提供渲染交接选项,不受 uiBundle 限制(与 Content Route 不同,后者仅在范围内包含 uiBundle 频道时才提供此选项)。使用相同的两条消息模式:先以纯文本提及该项,再通过
ask_followup_question提供Yes/No。选择Yes时,通过mcp__skill_bridge__load_skill("experience-cms-content-render")分发——绝不要使用内置 Skill 工具(它使用独立注册表,会返回Unknown skill)——并传入所选项的title、内容类型 FQN、所选条目的managedContentChannelDetails.id(频道 ID);仅当该项有交付条目时才传入频道 ID——此路由明确支持零条目项,因此这类项应省略频道 ID,不要尝试从不存在的对象读取.id;再传入其 Unauthenticated URL,若没有则传入其managedContentKey,字段名为contentKey(接收技能使用的字段名)。如果加载调用失败,告知用户交接失败并停止。选择No时停止——搜索已完成;不要自行将 URL 用作备用方案。
→ 查看 references/media-route.md,了解完整示例。
步骤 5:展示合并结果
如果请求同时包含媒体和内容意图,按顺序完整执行两条路由,不要交错执行,也不要合并为一次展示:
- 先执行 Media Route——对每个媒体意图,完整执行步骤 4:展示来源、构建并确认查询、搜索、展示结果、让用户选择,并(第 6 点)提供
experience-cms-content-render交接选项(Yes/No)。完成整条路由(包括交接选项)后,再开始 Content Route。 - 再执行 Content Route——对每个内容意图,完整执行步骤 3:构建并确认查询、搜索、按内容类型分组展示结果(每项注明频道)、让用户选择,并在范围内包含 uiBundle 频道时(第 6 点)提供
experience-cms-content-render交接选项。
每条路由的结果都应单独标注和展示(例如,先显示 Media Route 的“图片:”,再显示 Content Route 的“文章:”)——绝不要将两者合并成一份没有区分的列表,也不要扣留 Media Route 的结果,等 Content Route 完成后再一并展示。
错误处理
| 错误 | 响应 |
|---|---|
通过自省未找到 search_content_cms_channels/search_media_cms_channels 工具 | “未配置内容搜索连接。请连接一个只读内容 MCP 服务。” |
| 两种媒体工具均不可用 | “目前未配置自动媒体搜索源。请提供直接 URL 或素材库路径。” |
metadata-grounding 不可用或没有匹配的内容类型 | 应用 references/content-type-discovery.md 中的回退链——不要阻止操作或要求提供 FQN |
| 没有搜索结果(任一路径) | “未找到[内容/媒体]。请尝试使用更宽泛的关键词、移除描述性词语,或改用其他[内容类型/来源]。” |
| 用户选择无效 | 重新显示选项并再次询问 |
| 工具返回错误(输入无效、缺少参数或其他原因) | 显示错误消息,提供使用更正后的参数重试的选项,且必须使用同一个工具——绝不能将另一条路径的搜索工具作为变通方案(见关键原则 3);混合语言错误:征得用户同意后,提供一次不指定语言的重试选项(见媒体路径) |
| 已连接服务上的搜索工具返回服务端错误(5xx、超时、连接失败——不包括“没有结果”) | 调用失败——不要静默改用其他服务重试。 告知用户检查该服务的连接/会话,并等待后再重试。 |
绝不要对用户要求的路径静默失败。 如果混合请求中的一条路径没有结果,仍要展示另一条路径的结果,并明确说明情况。这并不意味着在已连接的服务报错时改用其他服务——见上方服务端错误行。
关键原则
- 先确定频道范围——在进行任何内容类型发现或搜索之前,先确定频道范围(步骤 1)
- 先搜索,再选路径——由
search_metadata+query_metadata决定路径;只有在没有结果时才回退到get_content_types_for_workspace,然后再参考意图表述(步骤 2) - 绝不跨路径,包括出错时——内容请求绝不调用媒体搜索工具,反之亦然;如果某条路径的搜索工具因任何原因报错,应修正问题并重试同一个工具,或者停止并报告错误,绝不能将另一条路径的工具作为变通方案
- 内容搜索必须以元数据为依据——动态发现内容类型 FQN,绝不硬编码
- 媒体路径从纯文本开始——先展示来源,并等待用户回复,再调用任何工具
- 搜索前须确认(两条路径均如此)——执行前由用户批准查询参数
- 绝不自动选择——频道范围和两条路径均须等待用户选择
- 按顺序处理混合请求,先处理媒体路径——先完整完成媒体路径(包括渲染提议),再开始内容路径;将每条路径的结果作为单独的标记集合展示,绝不合并
- 妥善处理错误——按路径提供清晰的反馈和替代方案
- 提供渲染选项,不要擅自决定——两条路径都须在交接前征得用户 Yes/No 确认;区别仅在于触发条件——内容路径仅在范围内确实包含 uiBundle 频道时才提供该选项,媒体路径则始终提供
- 服务端错误时绝不切换到其他服务——调用失败,并告知用户检查该服务的会话;不要静默改用其他服务重试
- 按内容类型分组,绝不按频道分组——两条路径中,频道始终是单项字段,绝不能作为分组标题
- 在选择时消除频道歧义,而非提前处理——无论其交付详情数组有多长,展示时都只显示一次该项目;仅在用户选择后,才询问要使用哪个频道的副本,并在两条路径中按
managedContentChannelDetails.name为每个条目提供一个选项 - 未经身份验证的 URL 仅限公开频道——仅限
PUBLIC_UNAUTHENTICATED/COMMUNITY条目;两条路径中均不得使用WEB_APP(uiBundle)条目的contentUrl