Helpers
Helpers
语言检测
laya.detect_language 就是 laya.lang.analyse。
名称、类型、默认值与代码保持英文;其余为译文(尚未翻译的条目暂显示英文原文)。
analyse
analyse(state: Union[str, bytes, Mapping, list, None]) -> Dict[str, object]对一个状态做完整检测的结果。
返回 script、script_profile、language(尽力而为,可能是 None)、is_english、non_latin_fraction 与 mixed_segment(让一个以英语为主的状态被判成非英语的那一行或那个字段,没有就是 None)。
真正被读取的是字符串值。一个状态里有多个字符串值时,只要有一个非英语就够了:把所有值拼进同一段窗口,会让一段很长的英语备注占满 4000 字符,或者把一条很短的德语消息压过去,于是那条消息被送去了英语 checkpoint(#384)。段落扫描仍然在 4000 字符处停下,正是这一点让超大字段不至于变贵;没扫到的值之后会单独读一遍。
参数
stateUnion[str, bytes, Mapping, list, None]
detect_script
detect_script(text: str) -> strtext 的主导文字系统:'latin'、'han'、'devanagari' …… 没有字母时是 'unknown'。
参数
textstr
is_english
is_english(state: Union[str, bytes, Mapping, list, None]) -> bool当英语 checkpoint 有望读懂这个状态时为 True。
参数
stateUnion[str, bytes, Mapping, list, None]
邮件
clean_email_body
clean_email_body(body: str, max_chars: int = 3000) -> str去掉邮件里被引用的往来历史、签名与免责声明,让输入保持聚焦。
max_chars 是结果被截到的长度,默认 3000 字符 —— 见 email_state,它接受同一个预算并透传下来。
参数
bodystrmax_charsint=3000
email_state
email_state(
subject: str,
body: str,
sender: Optional[str] = None,
clean: bool = True,
max_chars: int = 3000,
extra,
) -> Dict为邮件分类构造一个干净的状态字典。
max_chars 是 clean_email_body 截断正文的预算,消息较长时值得调大:默认值下正文在 3000 字符处就停了,于是写在最后几段里的诉求永远到不了模型 —— 走 predict_long 也一样,它把状态切成窗口扫描,本来就是为了能读到超过一个窗口的内容。clean=False 时忽略此项,正文整段透传。
其它任何关键字都会成为状态的一个字段,因此会被模型读到;这里拼错字不是报错,而是改了输入。
参数
subjectstrbodystrsenderOptional[str]=Nonecleanbool=Truemax_charsint=3000extra
问题预设
triage_questions
triage_questions() -> Dict客服工单分流用的预设问题。
email_questions
email_questions(categories: Optional[Dict[str, str]] = None) -> Dict来信分流与威胁过滤用的预设问题。
参数
categoriesOptional[Dict[str, str]]=None
guard_questions
guard_questions() -> Dict实时 LLM 输入防护栏用的预设问题。
moderation_questions
moderation_questions() -> Dict内容安全与审核用的预设问题。
router_questions
router_questions() -> Dict智能模型路由用的预设问题。
候选筛选
shortlist_choice
shortlist_choice(
state: Any,
criteria: Any,
embed_fn: Callable[[Sequence[str]], Any],
k: int = DEFAULT_SHORTLIST_K,
DEFAULT_SHORTLIST_K,
instructions: Optional[str] = None,
) -> List[Any]返回 state 的前 k 个 choice 标签。
embed_fn 把一个字符串列表映射成形状为 (len(texts), dim) 的数组。它只被调用一次:先是查询文本,然后按 criteria 顺序每个选项一个字符串。选项字符串与 choice 问题的 render_options 一致。
当 k 不小于标签数时,所有标签按原顺序返回,且不调用 embed_fn。
并列时保留靠前的标签。零向量得 0 分,不会压过排在它前面的标签。
参数
stateAnycriteriaAnyembed_fnCallable[[Sequence[str]], Any]kint=DEFAULT_SHORTLIST_KDEFAULT_SHORTLIST_KinstructionsOptional[str]=None
predict_shortlist
predict_shortlist(
agent: Any,
state: Any,
questions: Dict[str, Dict[str, Any]],
embed_fn: Callable[[Sequence[str]], Any],
k: int = DEFAULT_SHORTLIST_K,
DEFAULT_SHORTLIST_K,
predict_kwargs: Any,
) -> Dict[str, Any]先对每个 choice 问题做候选筛选,再调用一次 predict 或 system_one。
非 choice 的问题原样透传。标签数 <= k 的 choice 问题也原样透传,且不调用 embed_fn。调用方传入的 questions 字典不会被修改。
返回的字典是模型结果加一个 shortlist 条目。经过筛选的 choice 问题,其概率只在保留下来的标签上归一。shortlist[qid] 里是 labels(排序后的顺序)、scores(余弦值;没有丢弃任何标签时是 None)、k、n 与 passthrough。
额外的关键字参数会透传给 predict / system_one(例如 Router 上的 model=)。
参数
agentAnystateAnyquestionsDict[str, Dict[str, Any]]embed_fnCallable[[Sequence[str]], Any]kint=DEFAULT_SHORTLIST_KDEFAULT_SHORTLIST_Kpredict_kwargsAny
embed_fn_from_agent
embed_fn_from_agent(
agent: Any,
max_length: int = 512,
batch_size: int = 32,
) -> Callable[[Sequence[str]], np.ndarray]对 agent 上已经加载好的 checkpoint 编码器做平均池化。
返回的可调用对象用 agent.tok 与 agent.model.encoder 嵌入一个字符串列表。它不跑决策头,也不下载权重。传入一个专门的 bi-encoder 作为 embed_fn 通常能筛得更准;这个辅助函数是给手上只有 Laya checkpoint 的调用方用的。
平均时排除 padding 位置。编码器的 train/eval 标志保持调用方设置的样子(加载好的 Agent 本来就在 eval)。每次调用都用当前的 agent.device,回退到 CPU 之后也一样。
参数
agentAnymax_lengthint=512batch_sizeint=32
cached_embed_fn
cached_embed_fn(
embed_fn: Callable[[Sequence[str]], Any],
maxsize: int = 4096,
) -> Callable[[Sequence[str]], np.ndarray]按输入字符串缓存 embed_fn 的输出,用一个 LRU 上界限制。
predict_shortlist 每次调用都要嵌入查询文本以及每个选项的文本。当每个请求筛选的都是同一组选项 —— 比如固定的意图或标签列表,README 里的 BANKING77 例子就是这样 —— 选项那些行在调用之间并没有变化,却每次都被重新嵌入一遍。把嵌入器包一层:
embed_fn = cached_embed_fn(embed_fn_from_agent(agent))
第一次调用不变,之后每次重复调用都只剩下嵌入新的查询文本。
查表按字符串精确匹配。缓存里没有的文本会先去重,再放在一次 embed_fn 调用里嵌入,所以冷缓存与不包一层时花费的批量调用次数相同。行以 float32 存储;缓存最多保留 maxsize 个字符串,超出后淘汰最久未用的那一项,内存上界约为 maxsize * dim * 4 字节。embed_fn 抛错或返回的形状不对时,什么都不缓存。
这个包装器可以安全地在多个线程间共用:锁只覆盖缓存的读写,从不覆盖嵌入调用本身。返回的可调用对象带有 cache_info() —— 一个含 size、maxsize、hits 与 misses 的字典 —— 以及 cache_clear()。如果 embed_fn 背后的模型或权重换了,请清空缓存。
参数
embed_fnCallable[[Sequence[str]], Any]maxsizeint=4096
弃权
check_min_confidence
check_min_confidence(v: Any) -> float校验可选的弃权阈值 min_confidence(#361)。
必须是 [0.0, 1.0] 区间内的实数。布尔值会被拒绝(尽管 isinstance(True, int) 成立)。
参数
vAny
flag_low_confidence
flag_low_confidence(results: List[Dict[str, Any]], min_confidence: float) -> None可选的弃权标记(#361):把置信度低于 min_confidence 的答案标出来。
读取 answer_confidence(即 max(p),校准指标所描述、且不随选项个数漂移的那个量),answer_confidence 缺失时退回 confidence。原始答案与置信度保持不变,只是多了一个 low_confidence: True。
参数
resultsList[Dict[str, Any]]min_confidencefloat
校准与训练
answer_confidence
answer_confidence(p: np.ndarray, k: int) -> float落在所报告答案上的概率质量:max(p)。
温度缩放拟合的就是这个量,本仓库里每一个校准数字也都是在这个量上算的 —— 两个基准测试框架在调用 ece_score 之前都取 conf = max(probs)。README 的门控一节依赖的是随之而来的性质:在置信度为 c 时返回的答案里,大约有 c 的比例是对的。这个性质是有条件的,而默认情况下条件并不成立 —— 只有在针对这个 checkpoint、这个选项个数,把温度拟合出来并在留出数据上验证过之后才成立。已发布的 checkpoint 是过度自信的:choice:11+ 是一个约 10 倍的锐化器,会返回落在 1.0 上的点质量,所以对它施加阈值会让选中率低于模型准确率(issue #394)。
下面的 confidence_from_probs 报告的是另一个量、另一个尺度,没有这个保证,所以两者不能拿同一个阈值去比。
参数
pnp.ndarraykint
confidence_from_probs
confidence_from_probs(p: np.ndarray, k: int) -> float归一化香农熵置信度:1 - H(p) / log(k)。
它刻画的是整个分布有多集中。有用,但没有校准:温度缩放拟合的不是它,报告的 ECE 量的也不是它。见 answer_confidence。
参数
pnp.ndarraykint
ece_score
ece_score(conf: np.ndarray, correct: np.ndarray, bins: int = 15) -> float按置信度分箱计算的期望校准误差(ECE)。
参数
confnp.ndarraycorrectnp.ndarraybinsint=15
fit_temperatures
fit_temperatures = fit_temperature_mapfit_one_temperature
fit_one_temperature(pairs: Sequence, min_n: Optional[int] = None) -> float用 NLL + LBFGS 在 log T 上拟合单个标量 T。
结果是优化出的尺度经过 clamp_temperature 之后的值,因此落在 [TEMP_MIN, TEMP_MAX] 内(值不是数时取中性的 1.0)。给出的样本对少于 min_n 时返回 1.0。min_n 默认是 MIN_BUCKET_N(每个桶的下限)。类型级拟合传的是更低的 MIN_TYPE_N,因此一个填不满任何桶的数据集仍然能得到一个标量,而不是停在 1.0。
参数
pairsSequencemin_nOptional[int]=None
fit_temperature_map
fit_temperature_map(
records: Iterable,
compute_ece: bool = False,
seed: int = 0,
) -> Dict[str, Any]拟合类型级标量与逐桶温度。
MIN_BUCKET_N 是每个桶的下限:更小的桶不进入 temperature_by_options,由类型级标量覆盖它们。MIN_TYPE_N 是只作用于那个标量的、单独且更低的下限。
compute_ece=False(默认值,也是 Agent.fit_temperatures 存下来的那条路径)在全部记录上拟合,且不返回 report 键。这条路径上 seed 被忽略。
compute_ece=True 会按 temp_bucket 分层,从每个桶里留出 ECE_HOLDOUT_FRAC 的比例,用 seed 保证同一批记录总是以同样的方式切分。温度只在剩下的部分上拟合,ECE 也只在留出的记录上评分。report["n"] 是传入的记录数;report["n_eval"] 是 ECE 所依据的留出条数。某个桶如果留出之后会低于 MIN_BUCKET_N,就在它的全部记录上拟合、不进入评估集,并写进 report["buckets_excluded_from_eval"],而不是被丢掉。n_by_bucket 始终按完整输入计数,包括拟合本身只用了子集的情况。
参数
recordsIterablecompute_ecebool=Falseseedint=0
render_options
render_options(q: Dict) -> List[str]按标签下标顺序渲染选项文本。Noul 的语义顺序永远是 [false, true]。
参数
qDict
proper_reward
proper_reward(
q: torch.Tensor,
target: torch.Tensor,
qtype: torch.Tensor,
mask: torch.Tensor,
w_sph: float = 0.5,
w_rps: float = 1.0,
log_floor: float = -9.21,
) -> torch.Tensor严格 proper 评分规则的奖励:log score + spherical score + ranked probability score。
q: [..., N, K] 报告的分布 target: [N, K](one-hot 或软目标分布)
参数
qtorch.Tensortargettorch.Tensorqtypetorch.Tensormasktorch.Tensorw_sphfloat=0.5w_rpsfloat=1.0log_floorfloat=-9.21
td_lambda_targets
td_lambda_targets(p_true: torch.Tensor, batch: Dict, lam: float = 1.0) -> torch.Tensor多轮对话轨迹的 TD(lambda) 目标。
参数
p_truetorch.TensorbatchDictlamfloat=1.0
QTYPES
QTYPES = {"choice": 0, "score": 1, "noul": 2}QTYPE_NAMES
QTYPE_NAMES = {v: k for k, v in QTYPES.items()}