Documentation
¶
Overview ¶
决策面(System One 等非自回归判定模型)接入:**判定**与**生成**分家。
为什么 router 需要这一面 ¶
router 现有的分类执行体是 LLM(自回归生成):有 messages 概念、输出散文、要一次 模型调用。但「把输入路由到**有限候选集**」这件事本身不需要生成——它是非自回归 **判定模型**(System One 协议:Jev 托管 / Laya 自托管 / Kev / Clef / CLM)的主场: 一次前向 pass 答完全部问题,输出**有限标签 + 概率分布 + 校准置信度**,毫秒级、 几乎免费。
于是 router 有了**三种执行体**:
词面(确定性,本包 KeywordHit)→ 决策面(判定,毫秒级)→ LLM 面(生成,秒级)
决策面夹在词面与 LLM 面之间:它比词面能理解语义,比 LLM 面快且便宜。
编排语义(fail-open) ¶
决策面**先于** LLM 面被问;达门槛即采纳(省一次 LLM 分类调用),未达门槛 / 端点故障 / 影子模式 ⇒ **一律回落 LLM 面**——判定面是加速件,**不得阻断主链**。
置信口径由实现方负责 ¶
决策面返回的置信度语义**取决于实现方**:不同后端给法不一致(实测:某后端的 choice 用自有公式、noul 直接给概率;另一后端按公开公式给)⇒ 同一门槛在两端 含义会漂移。实现方应当**按统一口径本地算**(从概率分布算,见对应客户端包的 EffectiveConfidence),本包只负责比门槛。
另:口径**随候选集规模变化**(候选越多、同一 p_top 折出的置信度越低)⇒ 门槛须随候选集标定,不能跨候选集移植。
中文词表匹配内核:意图路由的确定性层(词表兜底优先于 LLM 分类)。
子串命中远远不够——「磁盘没有问题」包含"磁盘"、「不要执行」包含"执行", 直配会把查询/拒绝误判成命中。本文件在子串之上叠加三重守门:
- 否定守门:命中处紧前方窗口内出现否定短语即该处作废(中文否定习惯前置,只回看不前看);
- 词边界:拉丁词命中处紧邻字符不得是 ASCII 字母/数字——kill 不再误命中 skill/killed;
- 排除构式:命中位置落在排除短语(「怎么执行」「可执行文件」)内部即该处作废。
三重守门都只作废**该处**命中:同一输入里关键词多处出现时,任一处通过全部守门即命中 (「先看下执行结果,怎么执行再定」的两处都被构式排除才判 false)。
从 bianque 意图路由层提炼(v0.9.4),语言层通用、无领域词。
Package router 路由(Router)架构原语:LLM 意图分类 → 选路 → 分发执行。
与 skill 包"agent 自主 use_skill"(隐式路由,决策权在执行 agent)不同, router 是显式路由:先用一次廉价分类调用决定走哪条处理链,再交给对应 handler (handler 可以是 ReAct agent、Plan-and-Execute、单轮生成——任意编排)。 适用:入口流量可枚举为有限意图类别、各类别处理链差异大的场景。
Index ¶
- Constants
- Variables
- func KeywordHit(lowerInput, keyword string) bool
- func KeywordHitBoundary(lowerInput, keyword string) bool
- func KeywordHitBoundaryExcept(lowerInput, keyword string, masks []string) bool
- func KeywordHitExcept(lowerInput, keyword string, masks []string) bool
- func KeywordPostNegated(lowerInput, keyword string) bool
- type Config
- type Decision
- type DecisionFace
- type DecisionObserver
- type Route
- type Router
- type SlotSpec
Constants ¶
const ( // SourceLLM 判定来源:LLM 分类面(**零值即 llm**,向后兼容既有消费方)。 SourceLLM = "llm" // SourceDecision 判定来源:决策面(System One 等非自回归判定模型)。 SourceDecision = "decision" )
判定来源常量(Decision.Source)。
Variables ¶
var OnNegationHit func()
OnNegationHit 否定守门命中钩子(可选;观测面注册,如 metrics 计数)。 在「该处命中被否定作废」时回调——热路径上只在作废瞬间触发,零命中零开销。 构建期注册(无内部同步)。
Functions ¶
func KeywordHit ¶
KeywordHit 词表命中判定:子串匹配 + 否定前置守门——「磁盘没有问题」「不要执行」 不再因包含关键词而误命中;同一输入里关键词多处出现时,任一处未被否定即命中。 输入应已小写(中文不受影响;拉丁关键词内部自动 ToLower 兜底)。
func KeywordHitBoundary ¶
KeywordHitBoundary KeywordHit 的词边界变体:命中处的前一字符与后一字符都不得是 ASCII 字母/数字(CJK 与标点均视为边界)——为「kill」这类拉丁词设计,杜绝 「skill」「killed」误命中;中文关键词不受影响(相邻字符本就不是 ASCII 字母数字, 行为与 KeywordHit 完全一致,混排输入如「ssh执行」也不受牵连——只约束声明的词)。
func KeywordHitBoundaryExcept ¶
KeywordHitBoundaryExcept KeywordHitBoundary 的排除构式变体(词边界 + mask 构式 + 否定守门三重判定)。
func KeywordHitExcept ¶
KeywordHitExcept KeywordHit 的排除构式变体:关键词某处命中若落在任一 mask 复合短语 (如「怎么执行」「可执行文件」)内部,该处作废、继续找下一处;全部作废即 false。 否定守门与 KeywordHit 一致。语义:处置词出现在问句/名词性构式里不触发执行链, 而同句真正的处置词(kill)不受「执行结果」这类名词性短语牵连。
func KeywordPostNegated ¶
KeywordPostNegated 后置否定守门:关键词**任一处**命中后紧随否定单字(不/没/无/非) 即视为否定陈述——「负载不高」「磁盘不满」里命中词是被否定的谓词对象;多处出现时 逐一扫描(与其余守门「任一处」语义对齐:「磁盘没问题,再看下磁盘」不会因首处未被 否定而漏判第二处的否定形态)。紧随复合否定(「磁盘没有问题」)同命中。与否定前置 守门(negatedAt)对偶:那边中文否定习惯前置、按窗口回看,这边紧贴即判、无需窗口。 判定从严:宁可作废误归一,不放过反向表述。返回 true 表示该关键词应作废。 输入应已小写(关键词内部 ToLower 兜底)。
Types ¶
type Config ¶
type Config struct {
// Model 分类模型(一次 GenerateJSON 调用;用快模型即可)。
Model model.BaseChatModel
// ModelName 分类调用记账标识。
ModelName string
// Routes 路由表(≥1 条;Name 唯一)。
Routes []Route
// MinConfidence 置信度下限(0~1;低于则走 Fallback。0 = 不设门槛)。
MinConfidence float64
// Fallback 兜底处理链(nil = 低置信/无法分类时报错)。
Fallback func(ctx context.Context, input string, reason string) (string, error)
// Slots 意图槽位(nil = 纯选路:提示词与解析保持原样,模型多给的 slots 一律丢弃)。
Slots []SlotSpec
// DecisionFace 决策面(System One 等非自回归判定模型;nil = 不启用)。
//
// 启用后**先问决策面**:达门槛即采纳(省一次 LLM 分类调用);未达门槛 / 端点
// 故障 ⇒ 回落 LLM 面(fail-open:判定面是加速件,不得阻断主链)。
// 不启用时行为与从前**逐字节一致**。
DecisionFace DecisionFace
// DecisionMinConfidence 决策面置信门槛(0 = 不设;0-1)。未达门槛 ⇒ 回落 LLM 面。
//
// 注意口径**随候选集规模变化**(判定模型的置信度是分布集中度:候选越多、同一
// p_top 折出的置信度越低)⇒ 门槛须随候选集标定,不能跨候选集移植。
DecisionMinConfidence float64
// DecisionShadow 影子模式:调用决策面但**只记不采**(结果进 DecisionObserver,
// 行为面零变更)——上线前量一致率与延迟的标准姿势。
DecisionShadow bool
// DecisionObserver 决策面结果观察钩子(nil = 不观察)。每次决策面调用后回调一次
// (采纳时 err=nil;未过门槛/故障时 err 非 nil;影子模式时 err=影子标记)。
DecisionObserver DecisionObserver
}
Config 路由器配置。
type Decision ¶
type Decision struct {
Route string `json:"route"`
Confidence float64 `json:"confidence"`
Reason string `json:"reason"`
// Slots 槽位提取结果(仅含已配置槽位;未配置/模型未给 = nil)。
Slots map[string]string `json:"slots,omitempty"`
// Source 判定来源:llm(**零值即 llm**,向后兼容既有消费方)| decision
// (决策面,System One 等非自回归判定模型)。
Source string `json:"source,omitempty"`
}
Decision 分类决策(可观测)。
type DecisionFace ¶ added in v0.10.6
type DecisionFace interface {
// Classify 用给定候选集与槽位规格做一次判定。
//
// state 是待判定的原始输入;routes/slots 是本包的候选集与槽位规格
// (实现方据此组题)。返回的 Decision 只需填 Route/Confidence/Slots。
Classify(ctx context.Context, state string, routes []Route, slots []SlotSpec) (Decision, error)
}
DecisionFace 决策面的窄接口。
实现方(客户端包)负责:把候选集映射成判定题的候选、把槽位规格映射成题目、 以及**按自己的口径算校准置信度**。本包不关心它怎么实现,只关心编排语义 (门槛/回落/影子/来源标注)。
实现约定:
- 返回的 Route 必须**恰好等于**候选集里的某个名字(本包会校验,不在表内即拒);
- 返回的 Confidence 应是**校准置信度**(0-1,越大越确信);
- 返回的 Slots 只保留**本包配置过的槽位名**(其余一律丢弃,防幻觉槽位名透传)。
type DecisionObserver ¶ added in v0.10.6
DecisionObserver 决策面结果观察钩子(可选;观测面注册,如指标计数 / 一致率统计)。
调用时机:决策面被调用后**每次**回调一次——
- 采纳时:err = nil;
- 未过门槛 / 端点故障:err 非 nil(原因在 err 里);
- 影子模式:err = errDecisionShadow 的同义错误(**只记不采**)。
钩子在**分类热路径**上被调用,实现方须自行保证线程安全;不得在钩子里做重活。
type Route ¶
type Route struct {
// Name 路由名(分类目标词,须唯一;小写短横线风格,如 "bug-fix")。
Name string
// Description 意图描述(分类依据——写清"什么样的问题走这条")。
Description string
// Handle 处理链(任意编排:单轮生成 / ReAct / P&E / 纯函数)。
Handle func(ctx context.Context, input string) (string, error)
}
Route 一条路由:意图类别 + 处理链。
type Router ¶
type Router struct {
// contains filtered or unexported fields
}
Router 路由器(构建后只读,并发安全)。