用候选集和解析校验约束模型输出
发音练习应用先用代码确定训练音素、查询合法单词候选,再让模型补齐空位,并校验结果的候选归属、唯一性与数量。文章还展示去重、低分录音过滤和排序规则,无需训练时直接跳过模型调用。
AI 深度解读
让模型推荐三个发音练习词,看起来只是一个简单提示词任务。Vocalace 的做法却把大部分决定交给代码:先选薄弱音,再查词典,最后只让模型补齐缺少的位置。模型返回的内容必须通过校验,才能存入系统。
这篇文章值得读的地方,是它把「模型可以决定什么」划得很具体。对需要把 AI 输出写入数据库、接入后续流程的程序员,这种边界设计比一句「请严格遵守格式」更有参考价值。
最差的单次得分,为什么不一定优先练
作者先用确定性规则选出需要练习的音素(phoneme),模型不参与排名。
每个单词只贡献一票。同一个词录了多次,也只保留一个薄弱音焦点,取表现更差的那次。低于 40 分的录音不会进入排名;过滤后如果所有焦点都是绿色,就没有需要练的音,也不会调用模型。
这套规则优先处理多个词共同暴露的问题。原文中,s 的分数更差,但 th 出现在两个词的薄弱音焦点里,所以最终选中 th。重复录制 sorry 不会增加票数。
排序代码直接规定了各项指标的优先级:
return b.count - a.count
|| b.redCount - a.redCount
|| a.minScore - b.minScore
|| left[0].localeCompare(right[0])
要看第一行:先比较涉及的单词数量。只有数量相同,才依次比较红色焦点数量、最低分,最后按音素 ID 排序。红色与黄色如何划分,原文没有给出阈值。
已有的 think 和 thing 都暴露了 th,因此先占两个练习位置,分数更低的 think 在前。目标是三个词,此时只差一个,模型也只负责这一个位置。
模型的任务规模,是前面的业务代码算出来的。
候选词表和解析器,怎样堵住自由发挥
缺少的词来自词典查询。查询要求 SAPI 发音标记完整匹配 (^| )th( |$),单词具有常见词性,排除当天已经见过的词,最多返回 20 行。
这份数组就是合法答案的全集。模型收到音素、缺少的数量和候选词,只能从中选择。提示词还要求目标音容易单独听清,并且不要解释。
但「音够不够清楚」没有进入程序校验。候选表里的词即使不够适合练习,只要通过检查,仍然会被保存。解析器真正保证的只有三件事:属于候选集合、不重复、数量准确。
下面的解析器负责决定回复能否进入存储:
export function parseWordsFromAskPick({ raw, allowed, need }: {
raw: string
allowed: string[]
need: number
}): string[] | null {
const byWord = new Map(allowed.map(word => [word.toLowerCase(), word]))
const picked: string[] = []
for (const item of raw.trim().split(/[||]/)) {
const word = byWord.get(item.trim().toLowerCase())
if (!word || picked.includes(word)) {
return null
}
picked.push(word)
}
return picked.length === need ? picked : null
}
重点是遇到非法词或重复词时,直接 return null。程序不会从错误回复里捞出几个合法词,拼成一个看似成功的结果。
测试使用 allowed = cat, hat, map:返回 dog 会因不在集合内被拒绝;需要两个词却只返回 cat,会因数量不足被拒绝;cat|cat 会因重复被拒绝。末尾多一个分隔符也不行,因为拆分后会出现空项。
真实例子中,need 是 1,合法集合来自最多 20 行的查询结果。Month 只有在候选表包含 month 时才有效,thick 同理。若模型返回 month because the th is clear,整段会被当成一个词项查找,结果仍是 null。
返回 null 后最多重试四次,之后任务失败。错误文本不会被裁剪成部分成功的答案。
我的判断
我赞同把排名、候选范围和存储条件交给代码。这些规则可以直接验证,也能写出明确的失败用例。只靠提示词约束输出,很难给下游程序同样清楚的保证。
不过,标题里的「不能编造」有一个边界:模型仍然可能生成词典外的词,只是系统不会接受。可靠的是接收流程。
模型是否值得保留,则还需要证据。作者让它挑选「目标音容易听清」的词,却没有给出与随机选词、词频排序或人工规则的对照数据,也没有报告无效回复率、重试成本和练习效果。这部分属于设计经验,不能据此认定模型选得更好。
按单词覆盖数量优先排名,也是一种教学取舍。它偏向多个词共有的薄弱音,未必适用于所有练习目标。原文用测试固定了行为,但没有证明这套顺序在教学效果上最优。
能拿来做什么
- 在 Node 服务中实现有限候选的 AI 选择任务时,可以先查询合法集合,再让模型选择,并在写入数据库前检查集合归属、重复项和准确数量。
- 在日常 AI 编程中,可以把「选已有标识符」与「生成新内容」分开处理。需要模型选择已有项时,让程序传入完整候选,并拒绝集合之外的结果。
- 给类似流程补测试时,优先覆盖非法项、重复项、数量不足、尾部分隔符和夹带解释,同时明确重试上限与最终失败行为。
- 如果引入模型只是为了改善选择质量,先与简单排序规则做对照,确认它确实改善业务结果,再承担调用和重试成本。