语音代理开发:将提示视为状态机
本文介绍如何通过将提示视为有限状态机(FSM)解决实时语音API的提示遵循问题。
实时语音LLM对于自然的对话非常棒,但存在权衡:如果坚持严格的确定性(精确脚本),就会失去流畅性;如果放任不管,就可能产生不可预测或不合规的回复。
实用的中间路径是将你的对话提示和流程设计为显式的FSM风格规范:简短的NLG模板 + 类型实体 + 验证器 + 小的、显式的状态转换 + 边界情况处理程序。这将LLM变成语言层,而一个简单的策略(FSM)则作为可靠的控制平面。
本文面向产品经理、工程师和设计师,他们正在构建语音代理(电话机器人、通话中的助手、演示流程),希望以一种实用且非技术的方式让实时LLM语音代理遵循所需的脚本,同时不会破坏自然性。
快速、通俗易懂的背景知识:
- 实时语音LLM(流式音频输入→流式音频输出)让你可以与用户进行低延迟的双向口语对话。这使得交互感觉真实——而且错误会立即被听到。
- LLM是概率性的:它们生成合理的语言,但不能保证精确的句子。模型设置或环境的微小变化可能会改变输出。
- FSM很简单:可以想象成一个简短步骤的地图(状态)。每个状态说明了基于前一个回复(条件)代理应该说什么(动作)、它需要从用户那里收集什么,以及哪些用户回复会将其带入下一步。结合简短的模板和验证器,这是可读、可测试和可预测的。

问题的三句话总结:
- 有些步骤必须逐字执行,出于法律/合规原因或完成业务操作。
- LLM有时会偏离精确措辞或在中断后选择错误分支。
- 团队花费太多时间手动迭代提示以使其“稳定”——这很慢且脆弱。
1、提示即FSM
创建一个简短、人类可读的“对话规范”,包含:
a) 状态(简短的命名步骤)
每个状态指定:
- 代理说什么(简短模板,1-2句话)。
- 它需要捕捉什么(例如,会议时间、同意与否)。
- 哪些用户意图会带你进入哪个下一个状态(例如,用户给出时间 → 确认;用户说不感兴趣 → 结束)。
b) 实体(类型槽位)
你收集的信息片段(日期时间、语言、布尔值)。定义友好的口语格式和标准机器格式(例如,“星期四,6月5日下午3点IST” vs ISO日期时间)。
c) 验证器和规范化器
简单规则说明“这看起来像有效的会议时间”或“这太模糊了,再问一次”。
d) NLG模板
代理说话的简短、标记模板。将模板标记为严格用于合规关键文本,或在允许改述时标记为软。
e) 边界情况处理程序
快速行为用于语言切换、中断、模糊回答或退出。
f) 接受标准
成功对话的简单检查清单(例如,“会议时间已捕获并确认”)。
# example_fsm_spec.yaml
domain: "appointment_booking_voice_agent"
meta:
description: "适用于实时音频中简短预订对话的初学者友好型FSM规范。"
base_language: "en-US"
supported_languages: ["en-US", "hi-IN", "mr-IN", "te-IN", "kn-IN"]
voice_profiles: ["neutral_male", "neutral_female"]
entities:
- name: customer_name
type: string
required: false
spoken_format: "{{ customer_name }}"
- name: language_preference
type: string
required: true
value: "en-US"
allowed_values: ["en-US", "hi-IN", "mr-IN", "te-IN", "kn-IN"]
- name: meeting_time
type: datetime
required: false
canonical_format: "ISO-8601"
spoken_format: "Thursday, June 5 at 3 PM IST"
- name: consent_call
type: boolean
required: true
default: true
validators:
- name: time_parser
description: "检测明确的时间并标记模糊的时间。"
pattern: "(?i)(tomorrow|today|\\b\\d{1,2}(:\\d{2})?\\s?(am|pm)?\\b)"
rule: "如果明确时间 -> 规范化为ISO;如果模糊 -> 标记为'vague_time'。"
intents:
- id: greet
examples: ["Hi", "Hello", "Namaste"]
- id: ask_info
examples: ["What is this about?", "Explain", "Tell me more"]
- id: provide_time
examples: ["Tomorrow 3pm", "Thursday at 3 PM", "5 PM"]
- id: vague_time
examples: ["Tomorrow", "Sometime next week", "Later"]
- id: not_interested
examples: ["Not interested", "No thanks", "Stop"]
- id: language_switch_request
examples: ["Can we speak in Hindi?", "Telugu please", "Marathi bolu?"]
- id: interruption
examples: ["Wait", "Hold on", "Stop"]
nlg:
opening:
description: "允许说话 + 目的"
strictness: soft
base_template: "Hi{{#if customer_name}} {{ customer_name }}{{/if}}, quick call to see if now is a good time for a 2-minute info session. Is now okay?"
info_outline:
description: "简短价值 + 询问可用性"
strictness: soft
base_template: "We help streamline scheduling and support. Would a short follow-up help? What time works for you?"
time_request:
description: "当模糊时重新请求具体时间"
strictness: strict
base_template: "Could you tell me a specific date and time for a quick follow-up (for example: Thursday at 3 PM)?"
confirm_time:
description: "确认捕获的时间并结束"
strictness: strict
base_template: "Great — locking in {{ meeting_time }}. I’ll send a reminder. Thanks!"
language_ack:
description: "承认并切换语言"
strictness: soft
base_template: "Sure — I will speak in {{ language }} from now on."
policy:
initial: S0_opening
states:
S0_opening:
say: nlg.opening
capture: []
on:
ask_info: S1_info
not_interested:
say: nlg.not_interested_close
end: closed
language_switch_request:
say: nlg.language_ack
action: update_entity(language_preference)
then: S0_opening
S1_info:
say: nlg.info_outline
capture: []
on:
provide_time: S2_confirm
vague_time:
say: nlg.time_request
then: S1_info
not_interested:
say: nlg.not_interested_close
end: closed
S2_confirm:
say: nlg.confirm_time
capture: [meeting_time]
end: booked
edge_cases:
- id: follow_up_missing_time
trigger_intent: provide_time
behavior: "如果解析的时间是模糊的,再次请求nlg.time_request。"
- id: language_switch
trigger_intent: language_switch_request
behavior: "更新language_preference并重新用翻译后的最后代理提示说话;从相同状态继续。"
- id: user_interrupt
trigger_intent: interruption
behavior: "短暂承认(1个token)并重新评估意图;在澄清之前不要继续状态转换。"
scenarios:
- id: happy_path_booking
steps:
- { bot: nlg.opening, expect: ask_info }
- { bot: nlg.info_outline, expect: provide_time }
- { bot: nlg.confirm_time }
- id: language_switch_example
steps:
- { bot: nlg.opening, context: { language_preference: "en-US" } }
- { user: "Hindi mein baat kar sakte hain?" }
- { edge_case: language_switch -> update language_preference: "hi-IN" }
- { bot: nlg.language_ack (translated) + re-issue nlg.opening translated }
acceptance_criteria:
- "打开状态说明目的并请求权限。"
- "以标准形式捕获会议时间并在口语形式中确认。"
- "立即尊重不感兴趣并礼貌结束。"
- "如果用户给出模糊答案,则提示具体时间。"
- "在请求时立即更新language_preference并将其用于后续输出。"
2、结束语
这里描述的方法在你需要确定性时效果最好——例如,合规性、预订或对话中精确措辞和可预测流程重要的情况。
如果你的目标是让模型自由思考并更自然地回应,那么这种FSM风格的提示不是你想要的。在这种情况下,要利用LLM的概率特性,允许更高的变化性,并专注于优先考虑参与度而非严格遵守的对话设计。
原文链接:The Secret to Reliable Voice Agents: Treat Prompts Like State Machines
汇智网翻译整理,转载请标明出处