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,
return_scores: bool = False,
) -> Any返回 state 的前 k 個 choice 標籤。
embed_fn 把一個字串列表對映成形狀為 (len(texts), dim) 的陣列。它只被呼叫一次:先是查詢文本,然後按 criteria 順序每個選項一個字串。選項字串與 choice 問題的 render_options 一致。
當 k 不小於標籤數時,所有標籤按原順序返回,且不呼叫 embed_fn。
並列時保留靠前的標籤。排序用的是有符號餘弦,而不是相似度下限:得分為 0 的標籤 —— 完全沒有訊號,或者一個非有限向量被當作 0 處理 —— 確實會壓過更靠前但得分為負的標籤,而 k 會先丟掉負分標籤。
return_scores=True 時返回的是 (labels, scores) 對,其中 scores 按排名順序儲存每個保留標籤的有符號餘弦 —— 就是 predict_shortlist 在其 shortlist 後設資料裡報告的那些值。當沒有丟棄任何標籤時,scores 是 None,與該後設資料中完全一致。
參數
stateAnycriteriaAnyembed_fnCallable[[Sequence[str]], Any]kint=DEFAULT_SHORTLIST_KDEFAULT_SHORTLIST_KinstructionsOptional[str]=Nonereturn_scoresbool=False
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、k、n 與 passthrough。labels 是篩選產生的排序順序;當 passthrough 置位、沒有做排序時,就是 criteria 本身的順序。scores 是按該順序每個保留標籤的有符號餘弦 —— 包含負值,絕不夾到 0 —— 或者當沒有丟棄任何標籤時為 None。
額外的關鍵字參數會透傳給 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)校驗可選的棄權閾值 min_confidence(#361、#394)。
要麼是 [0.0, 1.0] 區間內的實數(所有答案共用一個閾值;布林值會被拒絕,儘管 isinstance(True, int) 成立),要麼是按桶對映(見 :func:check_min_confidence_map),讓閾值可以隨選項個數而不同。返回的是校驗後的值 —— 標量情形是 float,對映情形是 dict[str, float] —— 下面兩個門控函式都接受這種形式。
參數
vAny
check_min_confidence_map
check_min_confidence_map(m: Dict[Any, Any]) -> Dict[str, float]校驗一張按桶的棄權閾值對映(#394)。
鍵是 common.temp_bucket 拼法下的選項數桶字串 —— "choice:2"、"choice:3-5"、"score:6-10"、"noul:2" 等等 —— 外加一個可選的 "default",用於對映沒有點名的任何桶。值是 [0.0, 1.0] 內的浮點數。一個置信度閾值無法跨選項個數遷移(#394);這讓呼叫方可以按每個桶的校準實際能達到的水平來門控它。用 :func:laya.calibrate.fit_abstention_thresholds 擬合一張。
參數
mDict[Any, Any]
resolve_min_confidence
resolve_min_confidence(
answer: Dict[str, Any],
thresholds: Dict[str, float],
default: float = 0.0,
) -> float在按桶對映下,這個答案的選項數桶所被門控的閾值。
對於對映沒有點名的桶,依次回退到對映的 "default" 條目,再回退到 default(0.0 —— 不門控任何東西),所以未配置的桶永遠不會出其不意地棄權。
參數
answerDict[str, Any]thresholdsDict[str, float]defaultfloat=0.0
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,而先前被標記過的答案如果重新達標,就把它去掉(例如結果字典被複用時,或者用不同閾值重新評估時)。
min_confidence 要麼是浮點數(所有答案共用一個閾值),要麼是按桶對映(#394);後者下,每個答案按它自己選項數所在桶的閾值,經由 :func:resolve_min_confidence 做門控。
參數
resultsList[Dict[str, Any]]min_confidencefloat
apply_confidence_gate
apply_confidence_gate(
results: List[Dict[str, Any]],
min_confidence: Optional[float] = None,
) -> None在真正施加過門控的那些答案上,報告置信度門控的狀態。
門控是一種策略,而一個連施加與否都無法觀察的策略根本算不上策略。設定 min_confidence 後,這會把 abstention(:data:GATE_STATES 之一)寫到每個答案上,同時寫上 abstention_threshold,於是呼叫方能回答三個原本回答不了的問題:
- 有多大比例的決策棄權了,而不是靠
low_confidence是否恰好被設定去反推; - 有多少答案門控無法決定,這是布林值根本無法表達的;
- 是什麼閾值產生了這些結果 ——
flag_low_confidence會把閾值消費掉,所以沒有它,一次帶逐類閾值的批次執行就無法重新拆分。
GATE_UNEVALUATED 是布林值無法表達的情形:門控跑了,而答案沒有帶可用的置信度,於是門控無法決定。把它報告成通過,和報告成一個標記是同一個謊言。
未設定 min_confidence 時,這什麼都不寫。 沒有 abstention,沒有 abstention_threshold,也沒有標記。這就是全部的約定:一次未門控的呼叫返回的載荷與之前完全相同,而呼叫方是靠欄位是否出現(而不是從欄位裡讀出的第四個值)判斷門控跑過沒有。要在每次呼叫時無條件地呼叫它,取代 if min_confidence is not None: 這種守衛:正是那種守衛留下了一條什麼都不報告的通路,而這正是本函式要區分出來的狀態。
標記本身仍歸 :func:flag_low_confidence 所有 —— 這裡是委託,而不是重新實現那條規則,所以布林值與所報告的狀態不會漂移開。
恰好為 0.0 的 min_confidence 確實被設定了,所以狀態會被報告,而 :func:flag_low_confidence 把 0.0 當作空操作,因為沒有東西能掉到它下面。於是每個帶可用置信度的答案都讀作 passed,而閾值回顯正是把它與在真實閾值下一次真正的通過區分開的東西。
參數
resultsList[Dict[str, Any]]min_confidenceOptional[float]=None
GATE_STATES
GATE_STATES = (GATE_PASSED, GATE_ABSTAINED, GATE_UNEVALUATED)校準與訓練
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
fit_abstention_thresholds
fit_abstention_thresholds(
records: Iterable,
temperature: Sequence[float],
temperature_by_options: Dict[str, float],
binning_map: Optional[Dict[str, Dict[str, Any]]] = None,
target_error: float = 0.10,
min_bucket_n: int = MIN_ABSTAIN_BUCKET_N,
MIN_ABSTAIN_BUCKET_N,
conservative: bool = True,
) -> Dict[str, float]擬合一個逐 temp_bucket 的棄權閾值,讓門控在每個桶裡都守住目標誤差。
單個 min_confidence 無法跨選項個數遷移(#394):2 選項與 12 選項答案的校準置信度處在不同的尺度上,所以同一刀切會隨問題不同而過度棄權或棄權不足。這個函式改為每個桶擬合一刀,鍵與 temperature_by_options 完全一致(common.temp_bucket,例如 "choice:3-5"),結果是一張 min_confidence 對映,:func:laya.confidence.check_min_confidence / :func:laya.confidence.apply_confidence_gate 直接接受它。
records 與 fit_temperature_map 消費的是同一批 (qtype, logits, target[, k]) 元組(由 records_from_labeled 構造)。置信度是校準後的 max(p) —— logits 先被擬合出的 temperature / temperature_by_options 縮放,所以閾值與執行時報告的數字處在同一尺度上。target_error 是已接受答案中可容忍的誤差;min_bucket_n 會略過太小的桶不擬合,conservative 則加上一樣本量的餘量。這些閾值是校準集上的經驗切點,不是形式化的覆蓋率保證 —— 生產門控要在留出資料上驗證(fit_temperature_map(..., compute_ece=True) 會給出一個留出切分)。
當將要提供這些閾值的 agent 裝了一張 binning_map 時,要把它傳進來 —— 無論這張對映是 Agent.fit_binning 裝的,還是由帶 binning_map 的校準載荷裝的 —— 因為執行時會在任何東西讀取 answer_confidence 之前,先經由那張對映重新校準它,所以沒有它擬合出的切點,落在的是一個門控根本看不到的尺度上。這樣閾值就落在分箱後的尺度上,兩者擬合的先後順序也不再重要。在 1,200 條合成的 12 選項記錄上、target_error=0.10 時的實測結果:沒有對映擬合出的切點,在未分箱置信度上以 50% 覆蓋率守住 9.8% 的誤差;而一旦把同一個數字與分箱後的置信度相比,它在 25.6% 的誤差下就放行了 94.5% 的答案。
參數
recordsIterabletemperatureSequence[float]temperature_by_optionsDict[str, float]binning_mapOptional[Dict[str, Dict[str, Any]]]=Nonetarget_errorfloat=0.10min_bucket_nint=MIN_ABSTAIN_BUCKET_NMIN_ABSTAIN_BUCKET_Nconservativebool=True
fit_binning_map
fit_binning_map(
records: Iterable,
temperature: Sequence[float],
temperature_by_options: Dict[str, float],
bins: int = 15,
min_bucket_n: int = MIN_BINNING_BUCKET_N,
MIN_BINNING_BUCKET_N,
) -> Dict[str, Dict[str, Any]]為 answer_confidence 擬合一張逐 temp_bucket 的直方圖分箱重校準對映。
溫度縮放對每個桶只施加一個標量;它修不好一條可靠性曲線不是簡單銳化/鈍化的桶(隨英文包釋出的 choice:11+ 就是這樣一個病態桶)。直方圖分箱是非參數替代方案:把一個桶裡校準後的置信度分到 [0, 1] 上 bins 個等寬箱裡,再把落進某個箱的每個置信度對映到那個箱的經驗準確率。它不需要單調性假設,也不需要額外的依賴(只用 NumPy;等滲迴歸會引入 scikit-learn)。
records 與 fit_temperature_map 消費的是同一批 (qtype, logits, target[, k]) 元組;置信度是校準後的 max(p)(logits 先被擬合出的 temperature / temperature_by_options 縮放),所以分箱對映是疊加在溫度對映之上,而不是取代它。返回 {bucket: {"bins": N, "values": [recalibrated confidence per bin]}};低於 min_bucket_n 的桶會被略去。用 :func:apply_binning_map 施加它。空箱(校準集從未產生過的置信度區間)對映到它自己的中點,也就是讓那個區域保持不變,所以沒見過的值永遠不會被重校準成一個憑空捏造的 0。
參數
recordsIterabletemperatureSequence[float]temperature_by_optionsDict[str, float]binsint=15min_bucket_nint=MIN_BINNING_BUCKET_NMIN_BINNING_BUCKET_N
apply_binning_map
apply_binning_map(
confidence: float,
bucket: str,
binning_map: Dict[str, Dict[str, Any]],
) -> float按選項數 bucket(common.temp_bucket)重校準一個 answer_confidence。
當對映裡沒有該桶的條目時,返回原樣的置信度,所以對映沒有為它擬合的桶會直接通過,而不是被強行賦一個錯誤的值。
參數
confidencefloatbucketstrbinning_mapDict[str, Dict[str, Any]]
fit_binning
fit_binning(
records,
min_bucket_n: int = MIN_BINNING_BUCKET_N,
MIN_BINNING_BUCKET_N,
) -> Dict[str, Any]在這個 agent 已擬合的溫度之上擬合一張直方圖分箱對映,並存下來。
records 與 fit_temperatures 消費的是同一批 (qtype, logits, target[, k]) 元組。這張對映的鍵與 temperature_by_options 完全一致,疊加在當前溫度之上,由 save_calibration 寫成 binning_map。
參數
recordsmin_bucket_nint=MIN_BINNING_BUCKET_NMIN_BINNING_BUCKET_N
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()}