文档导航

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) -> str

text 的主导文字系统:'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,它接受同一个预算并透传下来。

参数

bodystr
max_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 时忽略此项,正文整段透传。

其它任何关键字都会成为状态的一个字段,因此会被模型读到;这里拼错字不是报错,而是改了输入。

参数

subjectstr
bodystr
senderOptional[str]= None
cleanbool= True
max_charsint= 3000
extra

问题预设

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 分,不会压过排在它前面的标签。

参数

stateAny
criteriaAny
embed_fnCallable[[Sequence[str]], Any]
kint= DEFAULT_SHORTLIST_K
DEFAULT_SHORTLIST_K
instructionsOptional[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=)。

参数

agentAny
stateAny
questionsDict[str, Dict[str, Any]]
embed_fnCallable[[Sequence[str]], Any]
kint= DEFAULT_SHORTLIST_K
DEFAULT_SHORTLIST_K
predict_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 之后也一样。

参数

agentAny
max_lengthint= 512
batch_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.ndarray
kint

confidence_from_probs

confidence_from_probs(p: np.ndarray, k: int) -> float

归一化香农熵置信度:1 - H(p) / log(k)。

它刻画的是整个分布有多集中。有用,但没有校准:温度缩放拟合的不是它,报告的 ECE 量的也不是它。见 answer_confidence。

参数

pnp.ndarray
kint

ece_score

ece_score(conf: np.ndarray, correct: np.ndarray, bins: int = 15) -> float

按置信度分箱计算的期望校准误差(ECE)。

参数

confnp.ndarray
correctnp.ndarray
binsint= 15

fit_temperatures

fit_temperatures = fit_temperature_map

fit_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。

参数

pairsSequence
min_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 始终按完整输入计数,包括拟合本身只用了子集的情况。

参数

recordsIterable
compute_ecebool= False
seedint= 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.Tensor
targettorch.Tensor
qtypetorch.Tensor
masktorch.Tensor
w_sphfloat= 0.5
w_rpsfloat= 1.0
log_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.Tensor
batchDict
lamfloat= 1.0

QTYPES

QTYPES = {"choice": 0, "score": 1, "noul": 2}

QTYPE_NAMES

QTYPE_NAMES = {v: k for k, v in QTYPES.items()}