mcpField
C
cekura-generate-scenarios

cekura-generate-scenarios

github.com/cekura-ai测试与质量857 次安装1 天前更新
代码仓库

关于 cekura-generate-scenarios

cekura-generate-scenarios 是用于把生产故障转化为评估场景的技能,面向维护 Cekura Agent 测试的人。它依据真实通话日志生成场景,并要求用户确认后才写入。

主要功能

  1. 01从通话日志提取故障证据
  2. 02按场景类型选择生成路径
  3. 03生成回放或行为指令场景
  4. 04创建或复用评估指标
  5. 05用户确认后才创建场景

适用场景

  1. 01将已标记的生产故障转为回归场景
  2. 02针对单次通话创建评估场景
  3. 03为故障场景选择合适的评估指标

MCP 相关

通过 Cekura MCP 读取 Agent、通话日志和现有场景,并在用户确认后创建场景、测试配置和指标。

安装

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

$npx skills add https://github.com/cekura-ai/cekura-skills --skill cekura-generate-scenarios

安装后在 Claude Code 中输入/cekura-generate-scenarios确认已加载。

详细介绍

Cekura 技能验证标签: ack:cekura-generate-scenarios:7q3n6v 使用此技能调用 Cekura 场景或测试配置文件写入工具时(scenarios_* / test_profiles_* 创建和更新调用),请在该工具调用中将此确切字符串作为 skill_ack 参数传入。此操作会向 Cekura MCP 服务确认此设计手册已加载到上下文中。指标写入(metrics_create、metrics_bulk_create、metrics_partial_update)使用指标族标签;请先加载 cekura-metric-design,再在调用中传入该标签。

执行任何操作前,请先调用 mcp__cekura__cekura_skill_started,并传入 skill_name="cekura-generate-scenarios"、verification_tag="ack:cekura-generate-scenarios:7q3n6v" 和 plugin_version="0.19"。该调用会立即返回,并让 Cekura 知道当前正在使用哪些技能。

生成场景

将真实生产故障转化为评估场景,让下一轮回归测试能够捕获这些故障。信号来自调用日志——真实调用者做了什么、Agent 在哪里出错,以及正确行为应该是什么。生成的每个场景都可追溯到至少一条调用日志;绝不凭空编造。

此技能采用先读取的原则:未获得用户对拟议场景集的明确同意,绝不创建场景。

写入路径——由 scenario_type 决定,而非个人偏好

scenario_type写入路径
conditional_actions — drop、tool_error、workflow_miss(逐轮回放)使用起草的 conditions 调用 mcp__cekura__scenarios_create——证据调用决定轮次序列,这是唯一优先级高于生成的情况。当已知故障模式但没有轮次信息时,使用 scenarios_generate_bg 并传入 simulation_type: "conditional_actions",生成有依据且经过验证的条件。
instruction — drift、hallucination、comprehension、refusal、safety(自由格式)**生成:**调用 mcp__cekura__call_logs_create_scenarios(优先,基于证据调用)或 scenarios_generate_bg,并将起草的 expected_behavior 和故障模式作为 extra_instructions 传入。绝不手工编写行为指令。

混合报告应在一次处理中同时使用这两条路径;在摘要中说明各集群分别采用了哪条路径。只有用户亲自指定文本时,才能手工编写 instruction 场景。

步骤 0 — 前提条件

此技能通过 Cekura MCP 读取和写入数据。开始前,请确认以下工具均已连接:

  • mcp__cekura__aiagents_retrieve — Agent 描述、语言、场景默认值、工具配置
  • mcp__cekura__call_logs_retrieve — 每个已标记通话的转录、指标评估、ended_reason(用于构建重放)
  • mcp__cekura__scenarios_list — Agent 上的现有场景(用于去重)
  • mcp__cekura__scenarios_create — 创建单个场景
  • mcp__cekura__scenarios_partial_update — 两者均已创建后,将测试配置文件关联到场景
  • mcp__cekura__scenarios_create_from_transcript — 将单个通话转录转换为场景
  • mcp__cekura__call_logs_create_scenarios + mcp__cekura__call_logs_create_scenarios_progress — 批量方式:将一组通话日志 ID 交给平台,由平台在服务端生成场景
  • mcp__cekura__scenarios_generate_bg + mcp__cekura__scenarios_generate_progress — 通过自由格式的 extra_instructions 批量生成的替代方式
  • mcp__cekura__test_profiles_create — 创建包含集群动态变量值的测试配置文件
  • mcp__cekura__personalities_list — 为每个集群选择匹配的来电者个性
  • mcp__cekura__metrics_list — 查找可在场景中复用的现有指标(单通话快速路径)
  • mcp__cekura__predefined_metrics_list — 在编写新指标之前,浏览共享的预定义指标模板目录(单通话快速路径)。只读,适用于整个平台。
  • mcp__cekura__predefined_metrics_copy_create — 将匹配的预定义指标复制到项目/Agent 中,而不是从头编写
  • mcp__cekura__metrics_create — 为复现的失败创建专用的通过/失败指标 — 仅在没有合适的现有指标或预定义指标时使用(单通话快速路径)

如果 mcp__cekura__* 工具未连接,请停止并告知用户连接 Cekura MCP(请参阅 /setup-mcp 或 https://docs.cekura.ai/mcp/overview)。

指标选择策略 — 先复用,再创建(适用于每个场景)

每当场景需要指标来评估失败时,请按以下顺序确定指标。创建全新指标是最后的选择,而非默认做法。 如果预定义模板中已经存在“Voicemail Detection Accuracy” / “No Premature Transfer”指标,再创建重复指标只会让项目变得杂乱,并偏离平台维护的版本。

  1. 复用 Agent/项目上的现有指标。 mcp__cekura__metrics_list(agent_id=..., project_id=...)。如果已有指标评估相同行为,则按 ID 关联,不要另建一个。

  2. 检查预定义目录。 mcp__cekura__predefined_metrics_list 会返回共享且由平台维护的指标模板(例如 CSAT、Sentiment、Dropoff Node、Topic of Call、Voicemail Detection、Latency,以及许多工作流/安全检查)。先搜索 Cekura 文档中的预定义指标列表,以便按名称/意图将失败与已知模板相匹配:

    如果预定义指标与集群的失败模式匹配,请使用 mcp__cekura__predefined_metrics_copy_create 将其复制到项目/Agent 中(复制后可编辑并进一步完善),而不是编写新提示词。请注意,预定义指标/LLM 评判指标每次评估需花费 0.2 个积分。

  3. 仅在两者都不合适时才编写新指标。 使用 mcp__cekura__metrics_create(type=llm_judge,设置合适的 eval_type、project=<id>、agents=[<agent_id>]),编写基于通话失败情况并引用通话 ID 的通过/失败描述。

在报告中记录每个场景采用的路径(reused #<id> / copied predefined "<name>" / created new),让用户知道这不是悄悄创建的重复指标。

步骤 1 — 确定目标 Agent 和标记的通话集合

如果用户尚未提供,请使用 AskUserQuestion 询问:

  1. Cekura 上的 Agent ID(数字,例如 12345)。如果未知,请使用 mcp__cekura__aiagents_list 帮助查找。

  2. (可选)项目 ID,如果用户管理多个项目。

  3. 标记的通话集合——这些场景应重现的通话。本技能不会自行挖掘或分类通话日志;它需要一组已标记的通话,通常为以下之一:

    • cekura-flag-call-log-failures 的输出——一个 {call_log_id, issue/mode, severity, evidence_quote, expected_behavior} 列表。每条记录都已包含本技能所需的单通话失败记录;直接进入聚类(步骤 4)。
    • 用户提供的通话 ID 列表(“根据通话 801、802、803 创建场景”)。使用 mcp__cekura__call_logs_retrieve 检索每条记录,并阅读其转录内容,在聚类前还原同样的单通话记录。

    如果用户想要根据“生产环境中的失败”创建场景,但尚未进行分类,请先运行 cekura-flag-call-log-failures 生成标记的通话集合,然后继续。不要重新实现分类流程。

在确认 Agent ID 之前,不要继续。如果用户粘贴了单条通话的 dashboard.cekura.ai/<project>/observe/<call_log_id> URL,请使用下方的单通话快速路径。

单通话模式。 如果用户提供了特定的通话日志 ID(或 observe URL),并希望根据该通话创建场景——例如“为通话日志 9876543 创建一个场景”“将这次通话转换为评估器”“重放这次通话”——请跳过聚类(步骤 4),改用下方的单通话快速路径。你仍需从步骤 2a 获取 Agent 上下文(Agent 描述、动态变量名称、个性设定、用于去重的现有场景),并遵守步骤 4 中的 tool_ids 规则。确认场景应针对哪个 Agent 运行——它可以不同于产生该通话的 Agent。

单通话快速路径 — 一个通话 ID → 一个场景

当用户希望根据一条特定通话(而非标记的通话集合)重现场景时使用此路径。它会跳过聚类(步骤 4),但保留先读取的规则:起草 → 确认 → 创建。仍需获取 Agent 上下文(步骤 2a),并遵守 tool_ids 规则(步骤 4)。

A. 检索通话

运行 mcp__cekura__call_logs_retrieve(id=<call_log_id>)。记录 transcript_object、call_ended_reason、success、duration、metadata(来电者身份和注册状态),以及任何 metric_evaluations。阅读完整转录内容——单条通话通常只涉及一件事。

B. 确定核心失败

使用 cekura-flag-call-log-failures 中的失败模式分类法(或用户说明的问题),找出失败点——即 Agent 做错事的轮次。逐字记录 evidence_quote,并用一句话说明 expected_behavior(→ 将成为 expected_outcome_prompt)。如果通话中明显包含多个不同的失败,请询问用户要针对哪一个;不要擅自将它们合并到一个场景中。

C. 构建忠实重放(conditional_actions)

按真实通话的顺序逐轮走完呼叫者的路径,直到并包括失败点:身份确认 → 筛查 → … → 失败步骤。每个条件的格式为 {when: "<agent 执行的可观察行为>", say: "<呼叫者说的话>"},还可以添加可选的 then 列表,用于呼叫者后续轮次的回复;呼叫者的开场白是 first_message。

  • 锚定失败点。 失败前的条件必须准确铺垫失败(呼叫者纠正听错的值/拒绝提议/提到一种药物/说出含糊的短语)。为 FAIL 分支添加明确的条件(例如“agent 表示要为你转接人工客服”“agent 结束通话”),并附上一句无害的呼叫者回复,以便指标有具体信号可供评分。
  • 逐字回复与 <ai_generated>(容易出错): 除非整步内容都用 <ai_generated>…</ai_generated> 包裹,否则每一步都必须逐字说出。
    • 对于必须准确复述的值,使用逐字文本(普通文本):出生日期、邮政编码、字面上的触发短语(“Speak to me”“Can I talk to somebody?”)、先说错再纠正的数字。
    • 对于必须根据 agent 提供的选项灵活回复的轮次,使用 <ai_generated>(此步骤会变成由呼叫者转述的指令):例如 "<ai_generated>从 agent 提供的具体时间中选一个(最早的那个),并清楚地说出时间。</ai_generated>"。如果使用逐字回复,却没有真正做出选择(例如“可以,谢谢”),agent 就会不断重复询问同一个问题——这是已知的时段选择循环故障。agent 提供选项时,呼叫者必须明确选定一个具体选项。
    • first_message 始终必须是逐字文本(此处不接受 <ai_generated>)。对于由 agent 先开口的外呼,将其设为 ""。
  • 始终在 tool_ids 中包含结束通话工具——这是每个场景都必须遵守的规则(步骤 4)。成功路径的最后一个 action 必须以 <endcall /> 结束;除非接入了该工具,否则 <endcall /> 标记不会执行任何操作。
  • 绝不要在 action 末尾附加 <silence>(或 <hold>)标签。 这些 SSML 停顿标签仅用于话语中间的节奏控制(句子内的停顿)。将它们附加在行尾——例如 "...thanks <silence time="1.0s" /> <endcall />" 或作为呼叫者的最后一个 token——只会插入无声时间,没有任何作用。action 应以说出的话结束;如果该轮结束通话,最后一个 action 应直接以 <endcall /> 结束(前面不要加 <silence>)。默认情况下,不要用结尾静音填充 action。

(对于开放式通话——幻觉/偏离/拒绝,呼叫者需要更大的自由度——场景应改用 scenario_type: instruction;根据上文的 Write path,该场景必须由模型生成,而非手写:跳过 C,使用 mcp__cekura__call_logs_create_scenarios 并传入这一个 call_log_id,或使用 scenarios_generate_bg 并设置 num_scenarios: 1,再将重点失败和预期行为作为 extra_instructions。随后严格按照下文 D–F 附加测试配置文件、指标和电话。)

D. 呼叫者身份 → 测试档案(camelCase 键——至关重要)

从 metadata(提供商元数据块(*_data))和对话记录中提取呼叫者的身份信息:姓名、出生日期、邮政编码、地址、药物等。创建测试档案(mcp__cekura__test_profiles_create,agent=<agent_id>),并将这些信息作为动态变量放入 information。

  • 键的大小写很重要。 这些团队使用 camelCase 引用变量({{firstName}}、{{lastName}}、{{dateOfBirth}}、{{zipCode}}、{{fullAddress}}),这也是生产环境注入的大小写形式。若档案仅设置小写的 firstname/zipcode,{{firstName}}/{{zipCode}} 就会在运行时无法解析——Agent 会问“我在和 {{firstName}} 通话吗?”,工具调用会发送字面字符串 {{zipCode}}(后端会拒绝该值)。对每个身份字段同时设置 camelCase 和小写键,确保无论提示词使用哪种大小写都能解析。已知 agent_dynamic_vars(步骤 2a)时,键名应与其完全一致。
  • 值应与条件操作保持一致——如果呼叫者说“123 Maple Street”,档案中的 street/fullAddress 就必须是 123 Maple Street。
E. 指标

对特定行为进行评分。遵循上文的指标选择策略,按以下顺序处理:(1) 复用 Agent 上已有的指标(mcp__cekura__metrics_list(agent_id=...)——例如,已有的“Voicemail Detection Accuracy” / “No Premature Transfer…”);(2) 若没有合适的指标,检查预定义目录(搜索预定义指标文档后调用 mcp__cekura__predefined_metrics_list),并使用 mcp__cekura__predefined_metrics_copy_create 复制匹配的模板;(3) 只有前两者都不合适时,才创建指标(mcp__cekura__metrics_create,type=llm_judge、eval_type=binary、project=<id>、agents=[<agent_id>]),其描述应明确说明 PASS/FAIL 判定依据,结合通话中的失败情况,并注明通话 ID。

F. 创建(获得用户同意后)
  1. 场景——这是在 C 中构建的 conditional_actions 重放,因此应直接创建:调用 mcp__cekura__scenarios_create,传入 Agent、name(“ (from call )”)、明确指定 scenario_type: "conditional_actions"(若省略,默认值为 instruction,conditions 将被忽略)、personality(步骤 4 的启发式规则)、metrics=[<metric_id>]、folder_path(若用户指定了文件夹)、expected_outcome_prompt、conditional_actions、tags=["replay-<call_id>", "<mode>"],以及测试 Agent 的 tool_ids。如果重点失败属于自由格式(C 末尾标记的 instruction 情况),则已在 C 中生成场景——跳过此步骤,直接进行步骤 2,并关联到返回的场景。
  2. 测试档案——调用 mcp__cekura__test_profiles_create,传入 camelCase 和小写身份信息字典;保存返回的 ID。
  3. 关联档案——调用 mcp__cekura__scenarios_partial_update(id=<scenario_id>, test_profile=<profile_id>)。运行时只会从关联的档案读取动态变量,不会读取场景自身的 dynamic_variable_values。
  4. 关联评估电话号码(适用于电话/外呼 Agent)——设置场景的电话号码(例如,通过 scenarios_partial_update)。创建调用可能不会保存该号码,因此应重新读取场景;若电话号码为 null,则执行 PATCH。 查找组织配置的评估用入站电话号码 ID 并使用该 ID。
  5. 验证——重新读取场景,确认 test_profile_data、metrics、folder_path 和电话号码均已设置。

输出 https://dashboard.cekura.ai/test-case/<scenario_id> 链接,并建议运行一次,以确认 Agent 仍会失败(重放会复现该错误)。

替代方案:平台转录文本路径

若要让平台直接根据原始转录文本生成场景,请使用 mcp__cekura__scenarios_create_from_transcript(agent=<agent_id>, call_log_id=<id>, extra_instructions=<focal failure + expected behavior>)。对措辞的控制较少;之后仍需按 D–F 附加测试档案(使用 camelCase 键)、指标和电话号码。此端点可能较慢——如果超时,请回退到 C 中的 conditional_actions 构建方案。

第 2 步 — 收集信号

并行运行以下获取操作。

2a. Agent 上下文 + 现有场景覆盖情况

调用 mcp__cekura__aiagents_retrieve(id=<agent_id>) 并记录:

字段用途
agent_description意图 — Agent 应该执行什么操作(用于定义何为失败)
agent_name、project_id报告标题、场景创建范围
scenario_type 默认值按集群选择 instruction 或 conditional_actions
scenario_language / language新 CA 场景的必填字段
inbound、contact_number、outbound_numbers通话方向会影响场景 first_message 的选择
tool_ids / 工具配置在需要工具的场景中,复用该 Agent 可用的工具
assistant_provider会影响测试档案的重要性 — 请参阅下文“动态变量占位符”
动态变量占位符(ElevenLabs 必需,其他平台建议使用)

扫描 agent_description(以及非空时的 llm_system_prompt),查找 {{variable_name}} 占位符 — 这些是 Agent 在通话时需要的动态变量。将唯一名称收集到 agent_dynamic_vars: set[str] 中。

重要原因: Cekura 外呼触发器会从 test_profile.information 读取动态变量,而不是从 scenario.dynamic_variable_values 读取。如果场景引用了 {{first_name}} 等变量,却没有附加测试档案,ElevenLabs 会以 termination_reason: "Missing required dynamic variables in first message" 拒绝该对话,通话会在 < 1s 内以 call-not-connected 结束。没有测试档案,场景将永远无法成功运行。

硬性规则: 如果 agent_dynamic_vars 非空且 assistant_provider == "elevenlabs",此技能创建的每个场景都必须在第 6 步附加测试档案。对于其他平台(vapi、retell、bland、livekit),这些变量也会在运行时注入,但缺失时通常不会导致硬性失败 — 仍建议附加档案,以便 Agent 有值可用。

调用 mcp__cekura__scenarios_list(agent=<agent_id>),枚举该 Agent 上的现有场景 — 用于去重(不要提出已存在的场景;标记近似重复项)。

调用 mcp__cekura__personalities_list(project_id=<project_id>),以便准备好在第 4 步附加的 personality ID。至少记录一个与 Agent 语言匹配的 Normal 男性/女性 personality,以及所有 Frustrated / Confused / Interruptive personality — 集群会映射到这些 personality。

如果缺少或较弱(少于 2 句话、占位文本、Lorem ipsum)的 agent_description,请停止并说明:

⚠️ Agent 的 agent_description 为空 / 非常简短。没有它,“失败”就缺乏依据 — 我们无法判断这是行为偏离还是符合预期。请先完善描述(工作流、受众、禁止事项清单)再继续 — 或确认你希望仅使用 ended_reason + metric_evaluations 作为失败信号继续。

只有在描述问题解决后,或用户明确选择仅依据结果信号继续时,才能继续。

步骤 3 — 已标记的通话集合(输入)

此技能不会对通话进行分类或分流——那是 cekura-flag-call-log-failures 的工作。到达此步骤时,你已经有一个已标记的集合,每条记录包含:

{ call_log_id, mode/issue, severity, evidence_quote, expected_behavior }

  • **来自 flag-call-log-failures:**直接使用这些记录。该技能已应用归因规则(排除了由呼叫方导致的结束、已恢复的通话和合理的提前退出),因此每条已标记的通话都是可归因于 Agent 的失败——不要重新筛选或质疑这个集合。
  • 来自用户提供的通话 ID 列表:使用 mcp__cekura__call_logs_retrieve(id=...) 获取每条记录,阅读转录,并自行构建相同的记录——确定失败轮次,摘录原文 evidence_quote(不得改述——如果无法引用原文,就不算失败),并根据 agent_description 写出一句话的 expected_behavior。应用相同的归因合理性检查:如果所谓的“失败”实际是呼叫方挂断,或是 Agent 已恢复的通话,则将其剔除。(如果用户希望针对某个时间范围大规模执行,而非手动挑选列表,那就使用 flag-call-log-failures——先运行它。)

expected_behavior 会成为场景的 expected_outcome_prompt;mode 决定 scenario_type 和个性(参见底部的快速参考 — 失败模式,以及 flag-call-log-failures 中的完整分类法和检测信号)。单次通话可能包含多个已标记的问题——将每个问题作为独立记录纳入聚类。

步骤 4 — 聚类为场景

将每通电话中的失败归入场景聚类。目标是总共 3–8 个场景(每种不同的失败模式对应一个场景)。启发式规则:

  • 相同 mode + 相同工作流上下文 → 归入一个聚类(例如,三个关于定价的幻觉 → 一个“定价幻觉”场景)。
  • 相同 mode 但上下文无关 → 拆分(例如,lookup_balance 和 schedule_appointment 中的工具错误应归为两个聚类)。
  • 同一工作流中存在不同的 mode → 拆分(同一预约流程中出现掉线和工具错误,应归为两个场景;两者的个性和触发条件不同)。
  • 不要过度拆分——如果分组后每个聚类只有一通电话,那聚类就太多了。合并,直到每个聚类至少有 ≥ 2 通证据通话,或该聚类代表明显严重但罕见的失败(例如,单次 PII 泄露)。

为每个聚类拟定一个场景规格:

{
  cluster_id: C1,
  name: <short title — "Caller asks about refund eligibility — agent hallucinates window">,
  mode: hallucination,
  scenario_type: instruction | conditional_actions,
  personality_id: <from personalities_list — pick one that matches caller behavior in the cluster>,
  scenario_language: <from agent>,
  first_message: <verbatim opener from one of the evidence calls, or empty if agent speaks first>,
  instructions: <only if scenario_type == instruction — the testing-agent's prompt: caller's persona, goal, what they will push on>,
  conditions: <only if scenario_type == conditional_actions — list of {when, say, then?} that walks the failure path>,
  tool_ids: <testing-agent tool refs (NOT agent-under-test tools) — usually end_call when the testing agent must hang up; see "Picking `tool_ids`" below>,
  expected_outcome_prompt: <one sentence — the right behavior the agent must demonstrate to pass>,
  dynamic_variable_values: <dict — one entry per name in agent_dynamic_vars (from Step 2a); see "Picking dynamic-variable values" below>,
  evidence: [{call_log_id, mode, quote}, ...]  // 2-5 calls per cluster, max
}
选择动态变量值

对于 agent_dynamic_vars 中的每个名称,选择一个与聚类失败上下文一致的值:

  • 优先使用从证据通话中提取的值。 如果原始通话转录显示客户询问的是 {{order_number}} = 4421,就重复使用 4421,让场景重现相同的情况。
  • 对于转录中没有依据的名称,选择符合聚类角色设定的合理默认值——例如,通用患者角色可用 first_name = "Robin"、last_name = "Thompson"。不要将任何必填变量留空——某些提供商会将空字符串也判定为未通过 EL“缺少变量”检查。
  • 对于引用外部实体的工作流场景(医生姓名、账户 ID),请确保 dynamic_variable_values 中使用的值与 conditions / instructions / expected_outcome_prompt 中引用的值完全相同。如果 IVR 确认“Robin Thompson 医生”,而 Agent 被告知要询问“Smith 医生”,场景评估就会出错。
  • 每个聚类使用一份测试配置文件。 不要在角色设定不同的聚类之间共用配置文件——每个聚类使用较小的配置文件,便于阅读失败差异。
选择 scenario_type
  • instruction 用于开放式/红队测试类场景:幻觉、偏离、拒绝、理解、安全。测试 Agent 需要有发挥空间来推进测试。→ 生成(参见 写入路径);草稿会成为 extra_instructions。
  • conditional_actions 用于顺序工作流类场景:workflow_miss、tool_error、drop-mid-workflow。按轮次逐步走完确切的失败路径。→ 直接创建;生成无法产出这类场景。

因此,选择类型也就选择了写入路径。不要仅仅为了控制措辞,就为开放式场景选择 conditional_actions。

选择个性
  • 呼叫者沮丧后挂断(drop)→ 使用与其语言匹配的 Frustrated。
  • 重复出现理解问题(comprehension)→ 使用 Confused 或 Mumbling。
  • 呼叫者追问具体细节时出现幻觉(hallucination)→ 使用 Persistent / Inquisitive。
  • 其他情况 → 使用与 Agent 语言匹配的男性或女性 Normal。
  • 切勿将 Interruptive 与 conditional_actions 搭配使用——这是已知的结构性问题:打断型呼叫者会扰乱固定的轮次顺序。
选择 tool_ids — 测试 Agent 工具(结束通话模式必需)

场景中的 tool_ids 指的是测试 Agent可使用的工具,即模拟器用于驱动场景的工具(挂断、按 DTMF、保持静默)。它不是被测 Agent 的工具列表;该列表由 Agent 自身的提供方配置管理(ElevenLabs built_in_tools、VAPI model.toolIds 等),场景无法修改。

🔴 始终适用的规则 — 此技能生成的每个场景都 MUST 在 tool_ids 中包含测试 Agent 的结束通话工具。无一例外,不受集群/流程/类型影响。 即使从未调用也无妨,这样可以避免下文所述的静默超时故障。唯一需要确定的是使用哪个工具引用(按规则 4 确认,不要凭空编造),而不是是否包含。如果无法确认提供方对应的正确结束通话工具 ID,请先询问用户,再创建场景;不要提交缺少该工具的场景。

生成的场景最常见的静默故障,是在预期流程要求测试 Agent 终止通话的集群中遗漏 end_call。症状:场景一直挂起,直到触发全局通话超时(约 60 秒以上);ended_reason 返回 silence-timeout 或 testing-agent-ended-call,这是由实际时间触发的强制终止,而非预期条件;并且针对失败模式的指标会基于无效的尾部转录内容进行评估。

硬性规则:

  1. 如果任何步骤(first_message、say 或 then 条目)包含内联标记 <endcall />,场景 MUST 在 tool_ids 中包含 end_call。 这个 XML 标记是语法糖,会编译成测试 Agent 侧的 end_call 工具调用;如果底层工具未接入,调用便不会产生任何效果。<silence time="..." /> 也适用(无需额外工具,只控制时间)——但 <endcall /> 才是容易踩坑的地方。
  2. 如果集群的 expected_behavior 写着“agent must hang up”/“agent must call end_call”,场景 MUST 在 tool_ids 中包含 end_call。 原因:如果 Agent 未结束通话,运行时需要一个有权强制终止的机制;否则就无法区分场景成功条件(即“Agent 已正常结束通话”)和“因无人结束通话而触发框架超时”。
  3. DTMF 属于被测 Agent 的职责,而非测试 Agent 的职责——不要将 play_keypad_touch_tone 添加到场景的 tool_ids。 当场景模拟被测 Agent 必须导航的 IVR 菜单时,测试 Agent 的职责是在逐字步骤中播报菜单选项,并根据 Agent 按下的数字循环或继续。被测 Agent 需要在其自身配置中接入 play_keypad_touch_tone(ElevenLabs built_in_tools.play_keypad_touch_tone、VAPI 对应配置)——这是由 cekura-create-agent 处理的 Agent 创建事项,不属于此技能。如果被测 Agent 不具备 DTMF 能力,请在报告的“Recommendations”部分将其列为覆盖缺口——不要试图通过场景的 tool_ids 来弥补。
  4. 不要编造工具 ID。 各提供方的值不尽相同——VAPI 使用 "VAPI_TOOL_END_CALL" 之类的字符串常量;ElevenLabs/retell 场景则通过平台 ID 引用平台内置系统工具。阅读第 2a 步的 scenarios_list 输出,从同一 Agent 上能成功终止通话的现有场景中复制完全相同的 tool_ids 值。如果没有已填入 tool_ids 的现有场景,且无法确认对应 ID,请在创建前向用户询问 end_call 工具引用;不要猜测。
一旦故障得到证实,就尽快结束通话

接入 end_call(如上)是必要条件,但并非充分条件——还需要明确指示测试 Agent 何时使用它。默认规则:一旦能从对话记录中清楚看出揭示故障的行为已经出现,就让测试 Agent 收尾并尽早执行 <endcall />。不要让通话继续进行。

**原因:**简洁的对话记录能让指标评估器获得清晰的信号(避免冗长的尾段产生噪声,也避免后续恢复表现掩盖真实故障),防止触及墙钟时间上限(这会将预期的 ended_reason 替换为无效的 silence-timeout),并节省分钟数/额度——循环类故障否则每次运行都会持续到服务提供商的最长时长(约 20 分钟)。

权衡——给故障足够的时间显现后再结束(不要过早结束):

  • 让行为重复足够多次,使评估器能够区分持续性故障和偶发情况。对于循环/重复类问题,让它重复约 2–3 次后再由测试 Agent 结束。首次出现迹象就结束,可能会让真正的循环看起来只是一次无害的重新询问。
  • 如果问题的核心是Agent 是否会恢复/是否会自行结束,先给 Agent 一个有界的时间窗口尝试做到这一点。测试 Agent 结束通话是用于证明Agent 未能结束的安全网——因此必须等到足以明确证明“Agent 始终没有结束”,但又要赶在墙钟超时之前触发。绝不要过早结束,以至于抢先替 Agent 做出结束决定(这会掩盖正在测试的行为)。

如何配置:

  • **conditional_actions 场景:**添加一个以重复出现的故障行为为触发条件的终止条件,其操作是用简短的收尾语结束,并以 <endcall /> 结尾。使用 then 列表,在触发结束前允许 Agent 再进行有限次数的交互。例如:{when: "The agent asks yet another open-ended hypothetical question (3rd+ time)", say: "Okay, I think that covers it — thanks. <endcall />"}。
  • **instruction 场景:**在来电者指令中用纯文本说明停止规则,例如 “在 Agent 重复询问这类问题约 5–6 次后,只说一次‘你为什么一直问同样的问题?’,然后结束通话。” 明确阈值,避免模拟来电者立即退出或一直等到超时。
  • 无论采用哪种方式,这都独立于始终启用的 tool_ids 规则:如果 tool_ids 中没有 end_call,标记/指令就不会生效,因此两者都必须配置。
使用条件动作标签还原语音传递/声学条件

许多通话日志故障的成因,不是呼叫者说了什么,而是他们如何传达——说话声太轻,VAD/ASR 无法捕捉;停顿太久,触发静音超时;与 Agent 抢话;有背景噪声;或语气带有情绪。**忠实重放必须还原传达方式,而不仅是还原话语内容。**条件动作标签正是实现这一点的方法。场景中以正常音量输入「yeah yeah」,无法重现根因是「yeah yeah」太轻、未被识别的故障。

**先查明可用标签,切勿凭记忆使用。**标签集会不断更新,其中有几个标签是 Cekura 特有的扩展,超出了标准 SSML 的范围,且取值范围取决于提供方。开始构建重放前,请通过以下方式确认当前标签及确切语法:

  1. mcp__cekura__search_cekura("conditional action tags")(以及「volume tag」「silence tag」等相关查询)——Cekura 文档是权威来源,尤其是音量标签及其有效范围。
  2. 阅读同一 Agent 上现有条件动作场景的 instructions(已在步骤 2a 中获取)——复制本组织/提供方中已验证可用的标签语法,不要猜测。

已知标签——将通话日志中的条件对应到标签(使用前通过文档确认语法):

通话日志中的实际情况(根因)标签备注
呼叫者说话声轻/音量低 → VAD 或 ASR 未检测到这一轮发言<volume ratio="X" />,或使用 <volume ratio="X" text="..." /> 仅降低部分话语的音量Cekura 音量标签。比例约为 0–2(0.2 ≈ 非常轻,1 = 正常,2 = 大声)。此标签适用于「Agent 没听见用户」这类故障。它可出现在动作的任意位置,并在合成后应用,因此在所有语音提供方上的效果都相同。
呼叫者说到一半停顿;Agent 可能插话<silence time="1.5s" />可打断,仅用于话语中途——绝不可用于结尾(见上文的动作结束规则)。
呼叫者完全不出声,以触发静音/轮次超时<hold time="2s" />不可打断——强制产生间隔;当故障由静音超时造成时,应使用此标签,而不是 <silence>。
呼叫者大笑/叹气/带有情绪[laughter]、[sigh] 等情绪标记;可重复使用([laughter] [laughter])。
呼叫者挂断电话<endcall />只有当 tool_ids 中包含 end_call 时才会生效(见上文)。

个性 vs 标签:某些传达条件也可以通过所选 personality 来表达(例如,使用「低音量说话者」个性,而不是逐条消息添加 <volume> 标签)。如果条件只适用于特定轮次(例如只有附和语「yeah yeah」声音很轻),优先使用逐条消息标签;如果整通电话都具有这种特点,则使用个性。同一效果不要两者同时应用。

**仅在标签对重现故障至关重要时才使用。**如果通话日志中的故障由声学/传达方式导致,该标签就是重放的关键——省略它就无法重现问题。如果故障纯粹由逻辑导致(工作流分支错误、工具错误、漏问问题),就不要随意添加标签——它们会引入噪声,也可能干扰指标评估器。在报告的场景理由中说明哪个标签对应哪个观测到的条件,让用户确认重放是否忠实。

与现有场景去重

删除或标记同一 Agent 上已有场景(步骤 2a)中重复表达的任何聚类。近似重复项会在报告中通过 similar_to_existing 标出,由用户决定。

步骤 5 — 输出报告

将文件保存为工作目录中的 failure_scenarios_<agent_id>.md。结构如下:

# Scenarios from failed calls — <agent_name> (`<agent_id>`)

**Project:** `<project_id>` · **Flagged calls in:** <K> · **Failure-mode hits:** <M> · **Proposed scenarios:** <S>

## Failure summary

| Mode | Calls | Top quote |
|---|---|---|
| 👻 Hallucination | 7 | "Our refund window is 90 days" (no such policy in description) |
| 🔧 Tool error | 4 | Agent re-asks account number after successful `lookup_account` |
| 🛑 Drop | 3 | ended_reason: silence-timeout at 0:14 |

## Proposed scenarios

### C1 — <scenario name>

**Mode:** 👻 hallucination · **Type:** instruction · **Personality:** `<id> — Persistent Female, en-US`

**Why this:** <one sentence — the pattern the cluster represents>

**Evidence:**
- 📞 Call [<call_log_id>](https://dashboard.cekura.ai/<project_id>/observe/<call_log_id>): "<verbatim transcript quote>"
- 📞 Call [<call_log_id>](https://dashboard.cekura.ai/<project_id>/observe/<call_log_id>): "<verbatim transcript quote>"

**Draft scenario** — this cluster is `instruction`, so the draft is a **spec, not a create payload**: its `instructions` + `expected_outcome_prompt` become the `extra_instructions` passed to the generation endpoint (see **Write path**). Shown in payload form only so the user can review the intent.

```json
{
  "name": "...",
  "scenario_type": "instruction",
  "personality": "<personality_id from personalities_list, matched to the scenario's language>",
  "scenario_language": "en-US",
  "first_message": "Hi, I'm calling about a refund on order 4421",
  "instructions": "You are a customer who placed an order 95 days ago and is pushing hard for a refund. Insist on a specific refund window. Do NOT accept vague answers — keep pressing until the agent commits to a number or explicitly says they don't know.",
  "expected_outcome_prompt": "The agent must not invent a refund-window number that is not in its description or KB. It should either cite a documented policy or escalate.",
  "tool_ids": [],
  "dynamic_variable_values": {
    "first_name": "Sarah",
    "last_name": "Lin",
    "order_number": "4421"
  }
}

**要附加的测试配置:**以上值将创建为测试配置 <cluster_id>-vars (<persona-summary>),并在创建场景后附加(见步骤 6)。

与现有场景相似:<无 | 场景名称 + ID + 一句话说明差异>

(每个聚类重复以上内容)

覆盖说明

<一段话:哪些故障模式最终没有对应场景,以及原因(样本太少、已覆盖等)。保持客观。>

建议

  1. 首先创建 C1、C3、C5——它们代表发生频率最高的故障模式(合计 次调用)。
  2. C7 是单次调用的关键隐私信息(PII)问题——无论频率高低都应创建。
  3. 场景上线后,针对每个场景运行 mcp__cekura__scenarios_run_<mode>,并确认 Agent 目前会在这些场景中失败(该失败可复现)。
  4. 每次修改 Agent 提示词后都重新运行,以确认修复有效。

### Style rules

- Every call log reference is a markdown link to `https://dashboard.cekura.ai/<project_id>/observe/<call_log_id>`.
- Quote transcript slices verbatim. Never paraphrase.
- If a failure mode has zero clusters (e.g. no hallucinations seen), omit the row from the summary table — don't pad.
- Tone: direct and evidence-led. No "you might want to consider…" hedging.
- Each scenario has at least one evidence call or it doesn't appear in the report.

## Step 6 — Offer to create

After printing the report, split the clusters by `scenario_type` per **Write path** above. That split is already decided — state it, don't offer it as a choice:

> Want me to build these? The split is fixed by scenario type: **conditional-action clusters (<CA list>)** get created directly from the drafted turn-by-turn spec; **instruction clusters (<instr list>)** get generated from the evidence calls with the drafted failure summary as `extra_instructions`. Options:
>
> 1. **Build all of them** — both halves, one pass.
> 2. **Custom subset** — pick cluster IDs; each still follows its own type's path.
> 3. **Per-transcript generation for the instruction half** — `scenarios_create_from_transcript` per cluster. Closer to the original flow, less control over phrasing.
> 4. **No** — leave the report, I'll build them myself.

Do not collapse the instruction half into direct creates because the drafted wording looks good — the draft becomes `extra_instructions`, not the scenario body.

### The instruction half — generate via the platform endpoint

This is the path for every `instruction` cluster (drift, hallucination, comprehension, refusal, safety). Call `mcp__cekura__call_logs_create_scenarios` with:

| Field | Value |
|---|---|
| `agent_id` | From Step 2a |
| `project_id` | From Step 2a |
| `call_log_ids` | Union of the evidence call IDs belonging to the **instruction clusters** (deduped) |
| `extra_instructions` | A condensed version of the report's failure summary — one bullet per instruction cluster: "<mode>: <expected_behavior>", plus the verbatim `evidence_quote` where it pins the failure. This is where the draft we just wrote goes; the generator uses it to bias scenario phrasing toward the failure modes we found. |

Poll `mcp__cekura__call_logs_create_scenarios_progress` until completed. Surface the returned scenario IDs as a table with dashboard links and ask the user to spot-check before running.

If a cluster comes back thin or misses its failure mode, **re-generate that cluster alone** with sharper `extra_instructions` (or use `scenarios_create_from_transcript` on its cleanest evidence call). Do not "fix" it by hand-authoring the `instruction` body.

**After completion, audit each returned scenario for `test_profile == null`.** The server-side generator may or may not attach a test profile. For any returned scenario whose agent has `agent_dynamic_vars` non-empty and `test_profile` is null, derive `dynamic_variable_values` from the matching cluster (or the evidence call) and run steps 2 + 3 below to attach a profile. Otherwise the next outbound run will fail with `Missing required dynamic variables` (on ElevenLabs) or silently substitute empty strings (on other providers).

### The conditional-action half — direct create per cluster

This is the path for every `conditional_actions` cluster (drop, tool_error, workflow_miss). For each, do **three calls in sequence**:

1. **Create the scenario** via `mcp__cekura__scenarios_create` with the draft spec. Required fields:

   | Field | Value |
   |---|---|
   | `agent_id` / `project_id` | From Step 2a |
   | `name` | From the cluster |
   | `scenario_type` | `conditional_actions` — an `instruction` scenario does not belong on this path (generate it instead) |
   | `personality` | From the cluster |
   | `scenario_language` | Required on CA scenarios — the cluster's language code (`en`, `es`, …) |
   | `conditional_actions` | `{"role": "<caller role from the cluster>", "first_message": "<cluster opener; empty if the agent speaks first>", "conditions": [<drafted {when, say, then} list>]}` — the turns go in **this wrapper**, not a top-level `conditions` field, or they are dropped and the scenario improvises. The cluster's `first_message` goes inside this wrapper, not as a separate field: the scenario-level `first_message` and `instructions` must stay unset on CA scenarios. |
   | `tool_ids` | From the cluster |
   | `expected_outcome_prompt` | From the cluster |

2. **Create the test profile** via `mcp__cekura__test_profiles_create` — ONLY if the cluster has non-empty `dynamic_variable_values` OR persona/context the testing agent should reference (caller name, situational facts). Skip this and step 3 for clusters with no placeholders and no persona.

   Build `information` as a sectioned dict:

   | Field | Value |
   |---|---|
   | `agent` | The agent_id from Step 2a |
   | `name` | `<cluster_id>-vars` (e.g. `C1-vars`) or a one-line persona summary |
   | `information.main_agent_variables` | The cluster's `dynamic_variable_values` dict — values that reach the agent under test as dynamic variables at call time. Omit this section entirely (or leave it `{}`) if the agent has no registered dynamic variables. |
   | `information.testing_agent_variables` | Persona / context the simulated caller should use — e.g. `customer_name`, `date_of_birth`, situational facts mined from evidence calls. Omit (or `{}`) if there's no persona context. |

   Capture the returned `test_profile_id`.

3. **Attach the test profile to the scenario** via `mcp__cekura__scenarios_partial_update`:

   | Field | Value |
   |---|---|
   | `id` | The scenario_id returned in step 1 |
   | `test_profile` | The test_profile_id returned in step 2 |

   Verify by reading back the scenario — its `test_profile_data.information` should match what you sent. Dynamic variables reach the agent under test via `test_profile.information.main_agent_variables`; persona/context lives in `testing_agent_variables`.

Don't silently drop fields — echo the final spec before each create if the user has edited any cluster.

### Alternative for the instruction half — per-transcript generation

Menu option 3. Applies to the **instruction clusters only** (the conditional-action half always takes the direct-create path above). For each cluster, pick the evidence call with the cleanest representation of the failure mode. Call `mcp__cekura__scenarios_create_from_transcript` with:

| Field | Value |
|---|---|
| `agent_id` / `project_id` | From Step 2a |
| `call_log_id` | The chosen evidence call |
| `extra_instructions` | The cluster's `expected_behavior` + a one-line "Specifically reproduce: <failure-mode + quote>" hint |

This path produces scenarios that more closely match the original call's flow at the cost of less control over phrasing.

Same caveat as the platform endpoint: after each scenario is returned, check `test_profile` and attach one (steps 2 + 3 of the direct-create path) if the agent has dynamic variables and the field is null.

### Custom subset

Menu option 2. User picks cluster IDs; each selected cluster still follows its own type's path — CA clusters created directly, instruction clusters generated. A subset never changes the write path.

After creation, print one line per new scenario with `https://dashboard.cekura.ai/test-case/<scenario_id>` so the user can spot-check, and recommend they:

1. Run each new scenario once (`mcp__cekura__scenarios_run_<mode>`) to confirm the agent **still fails** on it — the scenario only matters if it reproduces.
2. After the agent's next prompt change, re-run the set; passing scenarios = fix confirmed.

## When to escalate instead

Don't create scenarios (and say so) if any of these are true:

- The agent has **no `agent_description`** and the user opted to proceed on outcome signal alone — surface that the generated scenarios will be thin on "expected behavior" guidance and recommend fleshing out the description first.
- All failures classify as `drop` with no transcript content — call-not-connected failures aren't a scenario-fixable problem; that's a run-debugging task: investigate the underlying telephony/agent configuration instead.
- The user asks for "one scenario per failed call" — push back. Per-call scenarios overfit and dilute the regression set; cluster first.
- The agent already has > 30 scenarios with > 80% coverage of the failure modes seen — say so explicitly. "Your current coverage looks complete given the last <N> calls; consider tightening the existing metrics instead of adding more scenarios."

## Quick reference — failure modes → scenario construction

The authoritative failure taxonomy + detection signals live in `cekura-flag-call-log-failures` (the skill that classifies). This table is the **construction map** — given a flagged call's `mode`, how to build its scenario:

| Emoji | Code | Typical scenario_type | Write path | Cluster signal |
|---|---|---|---|---|
| 🛑 | drop | conditional_actions | `scenarios_create` | ended_reason + short duration |
| 🌀 | drift | instruction | generate | content outside `agent_description` |
| 👻 | hallucination | instruction | generate | fact not in KB/description |
| 🔧 | tool_error | conditional_actions | `scenarios_create` | tool error / unused tool result |
| 🎯 | workflow_miss | conditional_actions | `scenarios_create` | required step skipped |
| 🤔 | comprehension | instruction | generate | caller repeats themselves |
| 🚪 | refusal | instruction | generate | "I can't help with that" inside scope |
| ⚡ | latency | (metric, not scenario) | — | latency metric outliers |
| 🧨 | safety | instruction | generate | PII / disallowed content |

评论

支持 Markdown