Help us shape the future of Go! Take the 2026 Go Developer Survey

router

package
v0.12.0 Latest Latest
Warning

This package is not in the latest version of its module.

Go to latest
Published: Oct 30, 2026 License: MIT Imports: 7 Imported by: 1

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 分类)。

子串命中远远不够——「磁盘没有问题」包含"磁盘"、「不要执行」包含"执行", 直配会把查询/拒绝误判成命中。本文件在子串之上叠加三重守门:

  1. 否定守门:命中处紧前方窗口内出现否定短语即该处作废(中文否定习惯前置,只回看不前看);
  2. 词边界:拉丁词命中处紧邻字符不得是 ASCII 字母/数字——kill 不再误命中 skill/killed;
  3. 排除构式:命中位置落在排除短语(「怎么执行」「可执行文件」)内部即该处作废。

三重守门都只作废**该处**命中:同一输入里关键词多处出现时,任一处通过全部守门即命中 (「先看下执行结果,怎么执行再定」的两处都被构式排除才判 false)。

从 bianque 意图路由层提炼(v0.9.4),语言层通用、无领域词。

Package router 路由(Router)架构原语:LLM 意图分类 → 选路 → 分发执行。

与 skill 包"agent 自主 use_skill"(隐式路由,决策权在执行 agent)不同, router 是显式路由:先用一次廉价分类调用决定走哪条处理链,再交给对应 handler (handler 可以是 ReAct agent、Plan-and-Execute、单轮生成——任意编排)。 适用:入口流量可枚举为有限意图类别、各类别处理链差异大的场景。

Index

Constants

View Source
const (
	// SourceLLM 判定来源:LLM 分类面(**零值即 llm**,向后兼容既有消费方)。
	SourceLLM = "llm"
	// SourceDecision 判定来源:决策面(System One 等非自回归判定模型)。
	SourceDecision = "decision"
)

判定来源常量(Decision.Source)。

Variables

View Source
var OnNegationHit func()

OnNegationHit 否定守门命中钩子(可选;观测面注册,如 metrics 计数)。 在「该处命中被否定作废」时回调——热路径上只在作废瞬间触发,零命中零开销。 构建期注册(无内部同步)。

Functions

func KeywordHit

func KeywordHit(lowerInput, keyword string) bool

KeywordHit 词表命中判定:子串匹配 + 否定前置守门——「磁盘没有问题」「不要执行」 不再因包含关键词而误命中;同一输入里关键词多处出现时,任一处未被否定即命中。 输入应已小写(中文不受影响;拉丁关键词内部自动 ToLower 兜底)。

func KeywordHitBoundary

func KeywordHitBoundary(lowerInput, keyword string) bool

KeywordHitBoundary KeywordHit 的词边界变体:命中处的前一字符与后一字符都不得是 ASCII 字母/数字(CJK 与标点均视为边界)——为「kill」这类拉丁词设计,杜绝 「skill」「killed」误命中;中文关键词不受影响(相邻字符本就不是 ASCII 字母数字, 行为与 KeywordHit 完全一致,混排输入如「ssh执行」也不受牵连——只约束声明的词)。

func KeywordHitBoundaryExcept

func KeywordHitBoundaryExcept(lowerInput, keyword string, masks []string) bool

KeywordHitBoundaryExcept KeywordHitBoundary 的排除构式变体(词边界 + mask 构式 + 否定守门三重判定)。

func KeywordHitExcept

func KeywordHitExcept(lowerInput, keyword string, masks []string) bool

KeywordHitExcept KeywordHit 的排除构式变体:关键词某处命中若落在任一 mask 复合短语 (如「怎么执行」「可执行文件」)内部,该处作废、继续找下一处;全部作废即 false。 否定守门与 KeywordHit 一致。语义:处置词出现在问句/名词性构式里不触发执行链, 而同句真正的处置词(kill)不受「执行结果」这类名词性短语牵连。

func KeywordPostNegated

func KeywordPostNegated(lowerInput, keyword string) bool

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

type DecisionObserver func(d Decision, err error)

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 路由器(构建后只读,并发安全)。

func New

func New(cfg *Config) (*Router, error)

New 创建路由器。

func (*Router) Classify

func (r *Router) Classify(ctx context.Context, input string) (Decision, error)

Classify 意图分类(不执行)。 门槛自守:分类合法性(结果不在路由表,含 "none")与 MinConfidence 置信度下限 在本方法统一校验,未过门槛返回携带原因的错误(Decision 仍返回,供调用方可观测)—— 只取 Classify 的编排器与 Do 分发共用同一闸门,不会出现"配置了阈值但没人校验"的死配置。 配置了 Slots 时同一调用顺带提取槽位(提示词追加槽位说明;模型未给的槽位不出现在结果里, 未配置的槽位名一律丢弃——槽位提取失败不影响选路本身)。

func (*Router) Run

func (r *Router) Run(ctx context.Context, input string) (Decision, string, error)

Run 分类 + 分发执行:门槛未过(Classify 报错)时走 Fallback 处理链。 (v0.10.11 自 Do 改名——全仓主执行方法词表统一为 Run,与 acpx.Registry.Run、 agentrun 同词表。)

type SlotSpec

type SlotSpec struct {
	// Name 槽位名(回复 JSON slots 对象的 key;短横线/字母风格,如 "plan")。
	Name string
	// Description 取值语义(合法值与判据,值一律字符串,如 "true"/"false")。
	Description string
}

SlotSpec 意图槽位定义:分类调用在选路的同时顺带提取的附加意图维度 (如「是否要方案」)。与选路共用一次 LLM 调用(零额外延迟/费用); 槽位是选路之外的正交维度——「要方案」不改变走哪条链,只改变链内行为。

Jump to

Keyboard shortcuts

? : This menu
/ : Search site
f or F : Jump to
y or Y : Canonical URL