关于 cekura-generate-scenarios
cekura-generate-scenarios 是用于把生产故障转化为评估场景的技能,面向维护 Cekura Agent 测试的人。它依据真实通话日志生成场景,并要求用户确认后才写入。
主要功能
- 01从通话日志提取故障证据
- 02按场景类型选择生成路径
- 03生成回放或行为指令场景
- 04创建或复用评估指标
- 05用户确认后才创建场景
适用场景
- 01将已标记的生产故障转为回归场景
- 02针对单次通话创建评估场景
- 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”指标,再创建重复指标只会让项目变得杂乱,并偏离平台维护的版本。
-
复用 Agent/项目上的现有指标。
mcp__cekura__metrics_list(agent_id=..., project_id=...)。如果已有指标评估相同行为,则按 ID 关联,不要另建一个。 -
检查预定义目录。
mcp__cekura__predefined_metrics_list会返回共享且由平台维护的指标模板(例如 CSAT、Sentiment、Dropoff Node、Topic of Call、Voicemail Detection、Latency,以及许多工作流/安全检查)。先搜索 Cekura 文档中的预定义指标列表,以便按名称/意图将失败与已知模板相匹配:- 概念说明 + 完整目录:https://docs.cekura.ai/documentation/key-concepts/metrics/pre-defined-metrics
- 通过 CLI/SDK 浏览:https://docs.cekura.ai/cli-sdk/metrics#browse-predefined-metrics
- API 参考:https://docs.cekura.ai/api-reference/test_framework/list-predefined-metrics
如果预定义指标与集群的失败模式匹配,请使用
mcp__cekura__predefined_metrics_copy_create将其复制到项目/Agent 中(复制后可编辑并进一步完善),而不是编写新提示词。请注意,预定义指标/LLM 评判指标每次评估需花费 0.2 个积分。 -
仅在两者都不合适时才编写新指标。 使用
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 询问:
-
Cekura 上的 Agent ID(数字,例如
12345)。如果未知,请使用mcp__cekura__aiagents_list帮助查找。 -
(可选)项目 ID,如果用户管理多个项目。
-
标记的通话集合——这些场景应重现的通话。本技能不会自行挖掘或分类通话日志;它需要一组已标记的通话,通常为以下之一:
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. 创建(获得用户同意后)
- 场景——这是在 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,并关联到返回的场景。 - 测试档案——调用
mcp__cekura__test_profiles_create,传入 camelCase 和小写身份信息字典;保存返回的 ID。 - 关联档案——调用
mcp__cekura__scenarios_partial_update(id=<scenario_id>, test_profile=<profile_id>)。运行时只会从关联的档案读取动态变量,不会读取场景自身的dynamic_variable_values。 - 关联评估电话号码(适用于电话/外呼 Agent)——设置场景的电话号码(例如,通过
scenarios_partial_update)。创建调用可能不会保存该号码,因此应重新读取场景;若电话号码为 null,则执行 PATCH。 查找组织配置的评估用入站电话号码 ID 并使用该 ID。 - 验证——重新读取场景,确认
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,这是由实际时间触发的强制终止,而非预期条件;并且针对失败模式的指标会基于无效的尾部转录内容进行评估。
硬性规则:
- 如果任何步骤(
first_message、say或then条目)包含内联标记<endcall />,场景 MUST 在tool_ids中包含end_call。 这个 XML 标记是语法糖,会编译成测试 Agent 侧的end_call工具调用;如果底层工具未接入,调用便不会产生任何效果。<silence time="..." />也适用(无需额外工具,只控制时间)——但<endcall />才是容易踩坑的地方。 - 如果集群的
expected_behavior写着“agent must hang up”/“agent must call end_call”,场景 MUST 在tool_ids中包含end_call。 原因:如果 Agent 未结束通话,运行时需要一个有权强制终止的机制;否则就无法区分场景成功条件(即“Agent 已正常结束通话”)和“因无人结束通话而触发框架超时”。 - DTMF 属于被测 Agent 的职责,而非测试 Agent 的职责——不要将
play_keypad_touch_tone添加到场景的tool_ids。 当场景模拟被测 Agent 必须导航的 IVR 菜单时,测试 Agent 的职责是在逐字步骤中播报菜单选项,并根据 Agent 按下的数字循环或继续。被测 Agent 需要在其自身配置中接入play_keypad_touch_tone(ElevenLabsbuilt_in_tools.play_keypad_touch_tone、VAPI 对应配置)——这是由cekura-create-agent处理的 Agent 创建事项,不属于此技能。如果被测 Agent 不具备 DTMF 能力,请在报告的“Recommendations”部分将其列为覆盖缺口——不要试图通过场景的tool_ids来弥补。 - 不要编造工具 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 的范围,且取值范围取决于提供方。开始构建重放前,请通过以下方式确认当前标签及确切语法:
mcp__cekura__search_cekura("conditional action tags")(以及「volume tag」「silence tag」等相关查询)——Cekura 文档是权威来源,尤其是音量标签及其有效范围。- 阅读同一 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 + 一句话说明差异>
(每个聚类重复以上内容)
覆盖说明
<一段话:哪些故障模式最终没有对应场景,以及原因(样本太少、已覆盖等)。保持客观。>
建议
- 首先创建 C1、C3、C5——它们代表发生频率最高的故障模式(合计 次调用)。
- C7 是单次调用的关键隐私信息(PII)问题——无论频率高低都应创建。
- 场景上线后,针对每个场景运行
mcp__cekura__scenarios_run_<mode>,并确认 Agent 目前会在这些场景中失败(该失败可复现)。 - 每次修改 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 |