mcpField
E
experience-search-coordinate

experience-search-coordinate

github.com/forcedotcom创意与媒体1.1k228 次安装1 天前更新
代码仓库

关于 experience-search-coordinate

面向需要查找既有 CMS 内容或媒体的搜索协调技能,可按内容类型分派到内容或媒体流程,并处理混合请求。它不用于生成、编辑或发布内容。

主要功能

  1. 01识别内容与媒体搜索意图
  2. 02按类型路由搜索流程
  3. 03混合请求先处理媒体
  4. 04搜索前解析渠道范围

适用场景

  1. 01查找文章、新闻或常见问题
  2. 02查找图片、视频或文档
  3. 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.json
references/
content-route.md
content-type-discovery.md
media-route.md
scope-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,请立即停止并先返回此处。

  1. 明确给出了 channelId——直接使用,跳过发现步骤,继续执行下方的范围规则。
  2. 指定了 uiBundle/app,但未给出 channelId——解析该 uiBundle 的 channelId;如果无法解析,则按未指定名称处理,转到第 3 点。
  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 无条件执行——即使是没有明显渠道关联的普通关键词搜索也不例外,不仅限于请求中指定了渠道或应用的情况。

  1. 识别请求中一个或多个不同的搜索意图(例如,“找一篇关于我们发布会的新闻稿和一张用于页面的主视觉图”包含两个意图)。针对每个意图分别执行本步骤的其余内容。

    **如果意图跨越两种路由,必须依次执行路由——绝不可交错或合并:**先为每个媒体意图完整执行媒体路由(步骤 4),再为每个内容意图执行内容路由(步骤 3)。参见步骤 5。

  2. 在 metadata-grounding MCP 服务上调用 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” 作为最后手段。

  1. 调用 query_metadata,传入第 2 点中每个候选项的标识符(最多取前 5 个,批量放入一个 metadataIdentifiers 数组):

    query_metadata({ metadataIdentifiers: ["00DVW00000CfBQP2A3::0T1VW000008sow60AA::ContentTypeBundle"] })
    

    阅读每个条目的 typeDetails[].properties——mixins、typeClasses 和 apiName 都在其中,而不在 search_metadata 响应中。

  2. 先筛选受支持的类型,再决定幸存者应走哪条路径——不要混为一谈:

    • 丢弃任何 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 值传入,而不是按类型分别搜索。
  3. 传递 Step 1 中的范围(仅公共渠道,或公共渠道加一个 channelId)到所调用的路径——参见下文各路径中的 channelIds 字段。

如果意图本身有歧义(例如“帮我找些内容”,但没有提供类型或主题线索)→ 在调用 search_metadata 前,阅读 references/content-type-discovery.md § “Intent itself is ambiguous”,查看准确的问题/选项。不要猜测。

步骤 3:内容路由

对于步骤 2 路由到这里的每个意图,都按以下顺序操作。内容类型 FQN 已在步骤 2 中确定。

  1. 构建搜索查询:

    • 提取关键词——提取会出现在内容标题或正文中的具体名词和专有主题。使用领域特定的同义词,最多 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 点所述,在每次调用中分别设置。
  2. 向用户确认——必须采用两条消息的模式(先发送带标签参数的聊天消息,再使用 ask_followup_question,选项为 Yes - search now / Edit search / Cancel)。绝不要把带标签的列表放进 ask_followup_question 的文本中——格式会被移除。→ 阅读 references/content-route.md §“搜索前确认查询”,查看模板。

  3. 根据步骤 1 中的范围执行搜索调用——仅限公开范围时调用一次;范围中包含 channelId 时调用两次(公开范围 + 按频道限定)。→ 在解析结果前,阅读 references/content-route.md §“执行搜索调用”,了解数组与合并规则。

  4. 最多展示 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 点),而不是在展示时进行。

  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 数组传入,绝不要将其内联写入问题文本。

  6. 如果范围内的 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。

  1. 通过 ask_followup_question 展示可用的搜索来源——暂不调用其他工具。 只列出你实际拥有的工具(通过内省确认)对应的来源,以及“其他”,每项各自作为一个选项。等待用户选择后再调用任何工具。→ 查看 references/media-route.md §“展示可用的搜索来源”中的完整模板。
  2. 构建查询,然后在搜索前向用户确认——必须采用两条消息的模式,并在同一轮中完成:
    • 使用关键词搜索(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)。
  3. 根据步骤 1 的范围执行搜索调用——仅搜索公开内容时调用一次;channelId 在范围内时调用两次(公开内容 + 特定频道);Data 360 混合搜索完全忽略频道范围。→ 解析结果前,先查看 references/media-route.md §“执行搜索调用”,了解数组与合并规则。
  4. 通过 ask_followup_question 展示所有不重复的结果,并按内容类型分组——每个结果对应一个选项(仅用于展示时按唯一标识符去重——同一项即使在多个频道中找到,也只在此处显示一次),选项标注标题、内容类型和频道(例如 Product Launch Hero Banner (sfdc_cms__image) — Public)。与 Content Route 一致:内容类型是分组依据,频道是每项的字段。绝不自动选择。
  5. 由用户选择一项。 如果其交付详情数组中有多个条目,先询问用户要使用哪个频道的副本——每个条目对应一个选项,以 managedContentChannelDetails.name 命名——然后再显示详情。显示内容名称、内容类型、频道;仅当条目属于 PUBLIC_UNAUTHENTICATED/COMMUNITY 且具有 contentUrl 时,显示完整的 Unauthenticated URL(包含所有查询参数);WEB_APP 条目则不显示。→ 提问前先查看 references/media-route.md §“在选择时消除频道歧义”——将频道名称作为 ask_followup_question 的 options 数组传入,绝不要将其直接写在问题文本中。
  6. 始终提供渲染交接选项,不受 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:展示合并结果

如果请求同时包含媒体和内容意图,按顺序完整执行两条路由,不要交错执行,也不要合并为一次展示:

  1. 先执行 Media Route——对每个媒体意图,完整执行步骤 4:展示来源、构建并确认查询、搜索、展示结果、让用户选择,并(第 6 点)提供 experience-cms-content-render 交接选项(Yes/No)。完成整条路由(包括交接选项)后,再开始 Content Route。
  2. 再执行 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. 先确定频道范围——在进行任何内容类型发现或搜索之前,先确定频道范围(步骤 1)
  2. 先搜索,再选路径——由 search_metadata + query_metadata 决定路径;只有在没有结果时才回退到 get_content_types_for_workspace,然后再参考意图表述(步骤 2)
  3. 绝不跨路径,包括出错时——内容请求绝不调用媒体搜索工具,反之亦然;如果某条路径的搜索工具因任何原因报错,应修正问题并重试同一个工具,或者停止并报告错误,绝不能将另一条路径的工具作为变通方案
  4. 内容搜索必须以元数据为依据——动态发现内容类型 FQN,绝不硬编码
  5. 媒体路径从纯文本开始——先展示来源,并等待用户回复,再调用任何工具
  6. 搜索前须确认(两条路径均如此)——执行前由用户批准查询参数
  7. 绝不自动选择——频道范围和两条路径均须等待用户选择
  8. 按顺序处理混合请求,先处理媒体路径——先完整完成媒体路径(包括渲染提议),再开始内容路径;将每条路径的结果作为单独的标记集合展示,绝不合并
  9. 妥善处理错误——按路径提供清晰的反馈和替代方案
  10. 提供渲染选项,不要擅自决定——两条路径都须在交接前征得用户 Yes/No 确认;区别仅在于触发条件——内容路径仅在范围内确实包含 uiBundle 频道时才提供该选项,媒体路径则始终提供
  11. 服务端错误时绝不切换到其他服务——调用失败,并告知用户检查该服务的会话;不要静默改用其他服务重试
  12. 按内容类型分组,绝不按频道分组——两条路径中,频道始终是单项字段,绝不能作为分组标题
  13. 在选择时消除频道歧义,而非提前处理——无论其交付详情数组有多长,展示时都只显示一次该项目;仅在用户选择后,才询问要使用哪个频道的副本,并在两条路径中按 managedContentChannelDetails.name 为每个条目提供一个选项
  14. 未经身份验证的 URL 仅限公开频道——仅限 PUBLIC_UNAUTHENTICATED/COMMUNITY 条目;两条路径中均不得使用 WEB_APP(uiBundle)条目的 contentUrl

评论

支持 Markdown