Laya 三三制:三检查点、三模式实、三原语
1. Laya 的“检查点”
Laya 的“检查点”(checkpoint)可以理解为同一套决策框架下的三个专用版本。它们共享相同的调用方式和决策逻辑,但骨干模型、上下文长度和适用范围不同。
1.1. 检查点的本质:同一套决策框架下的专用版本
Laya 并不是一个单一的模型文件,而是一套包含三个检查点的系统:
| 检查点 | 底层架构 | 核心定位 | 理解 |
|---|---|---|---|
laya (English) |
ModernBERT-large (421M) | 纯英文专用 | 英文场景的基准检查点,英文效果最好,但不应处理非英文。 |
laya-multilingual |
mmBERT-base (322M) | 多语言通用 | 牺牲一点英文精度,换取100+ 种语言的覆盖能力,且速度更快。 |
laya-typed-decisions |
ModernBERT-large (421M) | 特定任务专家 | 在特定类型化决策工作流上微调过的检查点,默认不自动启用;可显式指定或开启任务检测。 |
1.2. 为什么需要三个检查点?关键是“语言崩塌”
Laya 设置多检查点的核心原因在于,英文检查点在处理非英文(特别是非拉丁字母)文本时会灾难性地失效。
根据官方基准测试,英文检查点在高棉语上的准确率是 0.000,但它的平均置信度却高达 0.952。这意味着模型会极其自信地给出完全错误的答案,而你无法通过“置信度低就转人工”的策略来挽救。因此,路由决策必须在推理前就做好,而不是依赖模型自身的置信度。
这就引出了 Router 的核心作用:在 <0.5ms 内通过检测书写系统和语言,将请求分发给正确的检查点,避免英文检查点去处理它根本读不懂的文本。
1.3. 各自最适合的场景
laya(English):如果你的用户群绝大部分是英文,优先用它。它在英文任务上表现最好,例如在 MASSIVE 英文意图识别上准确率 0.783,XNLI 英文推理达 0.860。laya-multilingual:如果你的用户可能使用中文、印地语、德语等非英文语言,或者中英夹杂,优先用它。虽然它在英文上略弱(MASSIVE 0.657),但在非英文语言上明显更可靠(MASSIVE 其他语言 0.451)。你的手机银行场景如果面向中文用户,建议使用这个检查点。laya-typed-decisions:这是一个默认不会被 Router 自动选中的特殊检查点,专门针对四种合成工作流微调过(客服、发票、安全事件、Agent 追踪)。它可通过显式model="typed-decisions"指定,或在开启auto_task_detection=True时按问题集匹配。不要把它当作通用意图识别器。除非你的业务场景匹配这四种工作流之一,否则用标准的laya或laya-multilingual即可。
1.4. 实践中的检查点管理
在实际部署时,有两点值得注意:
- 内存与加载策略:
Router默认max_loaded=1,冷启动后首次使用才会加载检查点。为避免语言切换时反复重建模型,生产环境建议按需预载两个基础检查点:Router().preload(["english", "multilingual"])。这会把max_loaded提升到 2;不要用Router(preload=True),因为那会预载全部三个检查点。 - 显式覆盖:你可以在调用
predict时用model="multilingual"强制指定检查点,绕过自动路由。例如,如果你发现某段短文本被误判为英文,可以手动强制走多语言版本。
简单总结:用 Router 让系统自动在 laya 和 laya-multilingual 之间选择,适合手机银行的中英文混合场景。laya-typed-decisions 只有在问题集明确匹配其微调工作流时才考虑。
2. Laya 的三种工作模式
Laya 在使用有三种模式:Router 路由模式、单模型直连模式和内置预设工作流模式。三种模式分别对应 Laya 的三种使用层次。它们的核心区别在于:抽象程度不同、控制粒度不同、适用场景不同。
2.1. 模式一:Router 路由模式
代码本质:
router = Router()
router.preload(["english", "multilingual"])
result = router.predict(state, questions)
它在做什么:
你不需要关心输入是什么语言,也不需要指定用哪个检查点。Router 会自动检测书写系统和语言,然后分发到最合适的检查点。
输出:
[英语输入] routing -> english (2100 ms)
reason -> English Latin text
[中文输入] routing -> multilingual (540 ms)
reason -> non-Latin script (han, 100% of letters); the English checkpoint cannot read it
[仅路由不推理] 德语 -> multilingual reason: Latin script but language looks like 'de', not English
[显式覆盖] model='multilingual' -> multilingual
核心特点:
- 自动路由:英文走
laya,中文走laya-multilingual,德语也走laya-multilingual。你不需要手动判断。 - 路由原因可解释:
reason字段告诉你为什么选了某个检查点。 - 支持仅路由不推理:
router.route()可以在不跑前向的情况下,只告诉你该用哪个检查点。 - 支持显式覆盖:
router.predict(..., model="multilingual")可以强制指定检查点,绕过自动路由。
适合场景:多语言生产环境。例如:手机银行场景如果面向中文用户,同时可能混杂英文,这是最省心的选择。
代价:需要额外维护 Router 对象,且预载会占用更多内存。上面实测的 65.5 秒是冷加载耗时,不是每次推理耗时。
2.2. 模式二:单模型直连模式
代码本质:laya.load("convaiinnovations/laya").predict(state, questions);加载多语言检查点时使用 laya.load("convaiinnovations/laya", subfolder="multilingual")。以下输出样例来自英语检查点。
它在做什么:
代码手动加载一个检查点,然后所有请求都走这个检查点。没有自动路由,没有语言检测。
输出样例:
agent 已加载,device=cpu
predict 一次前向完成 4 个问题,耗时 2186 ms,input_tokens=348
[PASS] 返回 answers / usage 结构
[PASS] 答案覆盖全部问题
[PASS] choice: 返回最优标签与逐选项概率
[PASS] choice: confidence 在 [0,1]
[choice] department=billing confidence=0.864 probabilities={'billing': 0.9653, 'technical': 0.014, 'sales': 0.0101,'other': 0.0107}
[PASS] score: 期望分值在量表范围内
[PASS] score: 概率分布归一
[score] urgency=1.44/2.0 legend=['not urgent', 'soon', 'critical deadline or blocking issue']
[PASS] noul: P(true) 在 [0,1]
[noul] churn_risk=0.8248 refund_requested=0.843
核心特点:
- 单一检查点:所有输入都走同一个模型。如果你加载的是
multilingual,英文输入也走它;如果加载的是laya,中文输入也走它,但效果会急剧下降。 - 三种决策原语完整可用:
choice、score、noul都在这里展示。这是 Laya 最核心的推理能力。 - 一次批量前向完成所有问题:4 个问题在一次批量前向中处理,耗时 2186ms(CPU 上)。
- 返回结构完整:
answers和usage都有,choice返回逐选项概率,score返回期望值和分布,noul返回 P(true)。
适合场景:已经明确知道输入语言,或者只需要一种语言。比如手机银行只服务中文用户,直接加载 multilingual 即可,不需要 Router 的额外开销。
代价:没有自动语言检测。如果输入语言不匹配,效果会急剧下降(英文检查点遇到中文会给出高置信度的错误答案)。
2.3. 模式三:内置预设工作流
代码本质:agent.predict(state, laya.guard_questions()) 等
它在做什么:
Laya 预置了四套问题模板,你不需要自己定义 questions,直接调用对应的函数即可。
输出样例:
[guard] 1698 ms
jailbreak -> 1.0
prompt_injection -> 1.0
sensitive_data -> 0.452
harm_severity -> 1.8659
topic -> coding (conf 0.4314)
[triage] 3809 ms
intent -> refund (conf 0.948)
is_urgent -> 0.4027
frustration -> 2.0653
refund_requested -> 0.901
churn_risk -> 0.681
[router] 2820 ms
difficulty -> 2.0321
domain -> code (conf 0.9186)
needs_tools -> 0.0326
is_sensitive -> 0.0738
[moderation] 2595 ms
toxic -> 0.0004
harassment -> 0.0
threat -> 0.0
spam -> 0.0
severity -> 0.3387
四套预设的用途:
四套预设只是 Laya 官方提供的参考模板,每个预设本质上就是一个返回 questions 字典的 Python 函数。
| 预设 | 用途 | 包含的决策 |
|---|---|---|
guard |
安全防护 | 越狱检测、提示注入、敏感数据、危害程度、主题 |
triage |
工单分诊 | 意图、紧急程度、挫败感、退款要求、流失风险 |
router |
模型路由 | 难度、领域、是否需要工具、是否敏感 |
moderation |
内容审核 | 毒性、骚扰、威胁、垃圾信息、严重程度 |
核心特点:
- 开箱即用:不需要自己写
questions字典,直接调用laya.triage_questions()等函数。 - 按场景预先定义:这些模板的选项和描述是官方预设的,便于快速搭建原型,但不代表模型为这些模板单独调优。
- 仍然共享同一套推理引擎:底层还是
choice、score、noul三种原语,只是问题定义被封装好了。
适合场景:快速验证、原型开发,或者你的业务问题与预设维度基本一致。手机银行的“理财/转账/聊天”意图不能直接使用 triage 默认 intent 类别,应修改后使用。
代价:灵活性受限。如果你的意图不是 refund 这样的预设类别,需要自己定义 questions。
2.3.1. 预设的本质:函数返回的字典
laya.triage_questions() 这类预设函数,做的事情就是返回一个字典。triage_questions() 的实际结构如下:
# triage_questions() 返回的字典结构
{
"intent": {
"type": "choice",
"instructions": "What does the customer want in `message`?",
"criteria": {
"refund": "money returned or a duplicate charge reversed",
"technical_help": "a bug, outage or integration problem",
"billing_question": "a question about an invoice, plan or payment method",
"information": "general information, pricing or how-to",
"cancellation": "wants to cancel or downgrade",
"other": "none of the other options fits"
}
},
"is_urgent": {
"type": "noul",
"instructions": "Does `message` communicate time pressure or a deadline?"
},
"frustration": {
"type": "score",
"instructions": "How frustrated does the customer sound in `message`?",
"criteria": [
"calm and neutral",
"concerned but civil",
"clearly annoyed",
"very angry or using strong language"
]
},
"refund_requested": {
"type": "noul",
"instructions": "Does the customer ask for money back?"
},
"churn_risk": {
"type": "noul",
"instructions": "Does `message` suggest the customer may leave for a competitor or cancel?"
}
}
这里的选项、量表和命题都是函数返回的默认值。常规做法是复制或修改返回后的字典,再传入 predict()。
2.3.2. 四套预设的“可替换性”
| 预设 | 它默认在做什么 | 你能改成什么 |
|---|---|---|
triage_questions() |
客服意图、紧急程度、挫败感、退款要求、流失风险 | 改成业务意图、有序评分和独立命题 |
guard_questions() |
越狱、提示注入、敏感数据、危害程度、主题 | 改成业务安全检测维度 |
moderation_questions() |
毒性、骚扰、威胁、垃圾信息、严重程度 | 改成业务内容审核维度 |
router_questions() |
难度、领域、是否需要工具、是否敏感 | 改成业务路由维度 |
预设的“用途标签”是官方给它的命名和定位,不代表只能做这件事。triage_questions() 返回的就是一个普通的 questions 字典;修改后可用于其他业务,只要 state 包含问题引用的字段(如 message)。
2.3.3. 自定义的修改
triage 预设不是只能用于工单分诊。它是一组默认问题,可以按业务需要替换选项和命题。常见做法有两种:
2.3.3.1. 方式一:修改字典后传入
import laya
# 获取预设,但改掉你不需要的部分
my_questions = laya.triage_questions()
# 把 intent 换成你的业务分类
my_questions["intent"]["instructions"] = "用户当前轮次的意图是什么?"
my_questions["intent"]["criteria"] = {
"理财": "咨询、购买或赎回理财产品",
"转账": "向他人或自己账户转账、汇款",
"聊天": "与银行业务无关的闲聊"
}
# 如不需要退款判断,可删除该问题
del my_questions["refund_requested"]
# 用你修改后的字典预测
result = agent.predict(state, my_questions)
这样保留了预设的问题结构,但决策维度已改成手机银行业务逻辑。
2.3.3.2. 方式二:直接自定义,完全不用预设
my_bank_questions = {
"intent": {
"type": "choice",
"instructions": "用户意图是什么?",
"criteria": {"理财": "...", "转账": "...", "聊天": "..."}
},
"refund_requested": {
"type": "noul",
"instructions": "用户是否要求退款?"
}
}
result = agent.predict(state, my_bank_questions)
2.3.4. 修改时需要注意
预设里的 type(choice/score/noul)决定了输出结构。如果把 intent 的 criteria 改成中文“理财/转账/聊天”,Laya 会正常输出 choice 结果。但如果要把一个 noul 问题改成 choice,必须同时修改 type 和对应的 criteria 结构,而不是只改 instructions。
2.4. 三种模式的关系
三种模式不是单向的层层封装,而是围绕同一个推理内核的两种扩展:
模式二(单模型直连)
├── 模式一:增加检查点选择和语言路由
└── 模式三:提供预设问题模板
- 模式二是最基础的:手动加载检查点,手动定义问题,手动推理。
- 模式一在模式二之上增加自动语言路由,负责选择检查点。
- 模式三在模式二之上提供预设问题模板,负责简化问题定义。
你也可以组合使用:用 Router 自动路由,同时用 triage_questions() 预设模板。
3. Laya 三个决策的解读
Laya 的三个决策原语——choice、score、noul——可以理解为三种不同形态的“问题模板”。
它们决定了你能问什么类型的问题,以及模型会以什么格式回答。
3.1. choice:多选一
问题形态:“属于哪一类?”
输出结构:最优标签 + 逐选项概率 + confidence
逐项解读:
[choice] department=billing confidence=0.864
probabilities={'billing': 0.9653, 'technical': 0.014, 'sales': 0.0101, 'other': 0.0107}
department=billing:模型选出的最优标签是billing。confidence=0.864:模型对这个选择的整体确定性是 86.4%。它不是probabilities["billing"],而是基于校准后概率分布计算出的归一化熵置信度。probabilities:模型对每一个选项给出的概率分布:- billing: 0.9653
- technical: 0.014
- sales: 0.0101
- other: 0.0107
这里有一个关键细节:confidence=0.864 和 probabilities['billing']=0.9653 不相等。
模型先用温度调整 logits,再生成 probabilities;confidence 再从这个概率分布计算归一化熵。因此,它衡量的是分布的确定性,不等于被选标签的概率。若温度被截断或未在目标数据上校准,confidence 也应视为未校准。
3.2. score:量表打分
问题形态:“在量表上处于什么位置?”
输出结构:期望分值 + 概率分布 + confidence
逐项解读:
[score] urgency=1.44/2.0
legend=['not urgent', 'soon', 'critical deadline or blocking issue']
urgency=1.44/2.0:模型给出的期望分值。量表是 0 到 2,模型认为“紧急程度”的期望值是 1.44。legend:量表的语义锚点。0 = not urgent,1 = soon,2 = critical deadline or blocking issue。所以 1.44 大致落在“soon 和 critical 之间,偏 critical 一点”。[PASS] 概率分布归一:内部对每个档位(0、1、2)都有一个概率,这些概率加起来等于 1。期望分值就是这些档位的加权平均。
和 choice 的区别:
choice 是无序的互斥分类——billing 和 technical 之间没有大小关系。score 是有序量表——0 < 1 < 2,档位之间有顺序;但把档位映射为连续分数时,隐含了档位距离可比的假设。
为什么输出期望值而不是直接选档位:
因为“紧急程度”本身是连续的。直接选“1”或“2”会丢失信息。输出期望值 1.44 保留了“偏向 2 但还没到 2”的细微差别。你也可以自己设阈值,比如 urgency > 1.5 才算紧急。
适用场景:有序判断。比如紧急程度、挫败感、危害严重程度、满意度。这些维度的共同点是:档位之间有顺序,但边界模糊。
3.3. noul:二值判断
问题形态:“这个命题是否成立?”
输出结构:P(true),0 到 1 之间的概率
为什么叫 noul:
它是 “no/yes” 或 “null-or-not” 的缩写。名字表达的是:这个命题要么为真、要么为假,但模型输出的是为真的概率,而不是硬性的 0/1 标签。
这和它的训练方式有关。Laya 用 RLCD(面向校准决策的强化学习)训练,奖励的是概率的校准质量,而不是单纯的准确率。在温度拟合充分且评估数据与业务分布接近时,noul 输出具有统计意义:如果说 0.8,在大量类似样本中约有 80% 为真。若检查点温度未拟合或业务分布差异较大,应先在自有数据上验证和校准。
为什么输出概率而不是直接输出“是/否”:
因为很多判断本身是模糊的。“用户有流失风险”不是一个非黑即白的问题。输出 0.8248 比输出“是”保留了更多信息。你可以自己设阈值,比如 > 0.8 才算高风险。
适用场景:独立的二元判断。比如是否要求退款、是否提到竞品、是否要转人工、是否包含敏感信息、是否涉及欺诈。这些判断的共同点是:它们不是互斥的类别,而是可以叠加的辅助信号。
逐项解读:
[noul] churn_risk=0.8248 refund_requested=0.843
churn_risk=0.8248:命题“用户有流失风险”为真的概率是 82.48%。refund_requested=0.843:命题“用户要求退款”为真的概率是 84.3%。
和 choice 的关键区别:
choice 是互斥的——在当前问题定义下,一次只会返回一个主意图。但用户的一句话可能同时表达“理财”和“转账”需求;若业务允许多意图,应拆成多个 noul 问题或使用层级分类。noul 是独立的——“用户有流失风险”和“用户要求退款”可以同时为真,互不排斥。
你可以把它理解为一系列独立的开关,每个开关都可以单独打开或关闭,而不是一组互斥的按钮。
3.3.1. 在意图识别里应用
对于手机银行场景,noul 可以用来处理那些不属于互斥意图、但需要单独判断的维度。比如:
questions = {
"intent": {
"type": "choice",
"instructions": "用户当前轮次的意图是什么?",
"criteria": {
"理财": "咨询、购买、赎回理财产品",
"转账": "向他人或自己账户转账、汇款",
"聊天": "与银行业务无关的闲聊"
}
},
"is_urgent": {
"type": "noul",
"instructions": "用户是否表达了紧急或时间敏感的需求?"
},
"wants_human": {
"type": "noul",
"instructions": "用户是否要求转人工客服?"
},
"mentions_competitor": {
"type": "noul",
"instructions": "用户是否提到了其他银行或竞品?"
}
}
这样一次前向就能同时得到:
- 主意图(
choice) - 是否紧急(
noul) - 是否要转人工(
noul) - 是否提到竞品(
noul)
这些 noul 维度可以叠加在意图之上,用来做更细的路由。比如意图是“转账”且 is_urgent=0.9,就可以优先处理;意图是“聊天”但 wants_human=0.95,就直接转人工。
3.4. 三者对比总结
| 维度 | choice |
score |
noul |
|---|---|---|---|
| 问题形态 | 属于哪一类? | 在量表上处于什么位置? | 这个命题是否成立? |
| 选项关系 | 互斥 | 有序 | 独立 |
| 输出 | 最优标签 + 逐选项概率 + confidence | 期望分值 + 分布 + confidence | P(true) |
| 样例 | department=billing (0.864) | urgency=1.44/2.0 | churn_risk=0.8248 |
| 适合场景 | 意图分类、部门路由 | 紧急程度、挫败感 | 流失风险、退款要求 |
| 选项/档位数量限制 | 约 20 个以内 | 通常 3-5 档 | 每个命题只有真假两项;问题总量受上下文和推理预算限制 |
4. 对手机银行应用场景的启示
4.1. 模式的选择
开发阶段:以模式三的 triage 模板为参考,直接修改 intent 为“理财/转账/聊天”,快速验证效果;必要时再完全自定义 questions。
生产阶段:用模式一(Router 自动路由),因为你的用户可能中英文混杂。同时自己定义 questions,把意图精确控制为“理财/转账/聊天”三类。
如果只服务中文用户:可以跳过 Router,直接用模式二加载 multilingual 检查点,省掉路由开销和内存占用。
关键区别一句话总结:模式一帮你选检查点,模式二让你直接推理,模式三帮你减少问题定义工作。
4.2. 判断方式的选择
对于“理财/转账/聊天”的意图识别:
- 主意图用
choice:三个选项互斥,正好适合。 - 辅助维度用
noul:比如“用户是否在催促”“用户是否提到竞品”“用户是否要求转人工”。这些信号可以叠加在意图之上,用来做更细的路由。 - 如果需要判断紧急程度,用
score而不是noul。因为紧急程度是有序的,0/1/2 比单纯的“紧急/不紧急”保留更多信息。
一个关键原则:互斥的用 choice,有序的用 score,独立的用 noul。选错原语会导致模型输出结构不符合你的业务逻辑。
5. 总结
Laya 的价值不在于替代 LLM,而在于把高频、结构化、低延迟的分类与判断从 LLM 中剥离出来:它不生成文本,而是在一次批量前向中回答 choice、score、noul 三类类型化问题。
5.1. Router 选择
先按输入语言和任务特性选择检查点:英文优先用 laya,中文或多语言优先用 laya-multilingual;laya-typed-decisions 只在问题集明确匹配其四类微调工作流时使用。
中英文混合的手机银行生产环境,建议使用 Router().preload(["english", "multilingual"]),让 Router 在英语和多语言检查点之间自动路由。语言稳定且明确时,可以绕过 Router,直接加载对应检查点,减少内存和路由开销。
5.2. 调用方式选择
调用方式按控制粒度选择:需要自动语言路由时用模式一 Router.predict();语言固定、希望减少依赖时用模式二 laya.load(...).predict();快速原型可从模式三的预设问题出发。
预设工作流只是问题模板,不是业务答案。手机银行应以 triage_questions() 为参考,修改 intent 为“理财/转账/聊天”,或完全自定义 questions。
5.3. 具体问题类型选择
定义问题时,先判断业务结构:互斥主意图用 choice,有序程度用 score,可叠加的独立命题用 noul。手机银行可将“理财/转账/聊天”作为主意图,用 choice 输出;紧急程度用 score;转人工、提及竞品、要求退款等辅助信号用 noul。
落地原则可以概括为一句话:Router 负责选检查点,直连负责推理,预设负责起步,自定义问题负责贴合业务;上线前必须用真实样本验证效果和校准概率。
6. 参考
- Laya:
https://github.com/NandhaKishorM/laya
- 点赞
- 收藏
- 关注作者
评论(0)