Documentation
¶
Overview ¶
Package decision 是 **System One 协议**(非自回归判定模型)的客户端。
它和本仓其它能力的区别 ¶
本仓其它部分都是**自回归生成模型**(chat):有 messages 概念、输出散文、接 eino 的 BaseChatModel。System One 协议这一族模型(Jev 托管、Laya 自托管、 Kev、Clef、CLM…)**不是 chat 模型**:
- 输入是「待判定状态 + 若干 typed questions」,没有 messages;
- 输出是**有限标签 + 概率分布 + 校准置信度**,不生成散文;
- 一条状态一次前向 pass 答完全部问题(choice/score/noul 三原语同回)。
所以它进不了本仓的降级链/预算/成本那套(那是 chat 模型的形状),单独立包。 它要回答的问题是「**判**」,不是「**写**」。
协议 ¶
请求 `POST {base_url}/v1/systemone`,鉴权 `Authorization: Bearer <key>`:
请求 {"state": <文本或结构化对象>,
"questions": {"<题名>": {"type": "choice|score|noul",
"instructions": "问法",
"criteria": {"choice": {"标签": "判据"},
"score": ["档位0", "档位1"]}}}}
响应 {"model": "...",
"answers": {"<题名>": {"type": "choice", "choice": "billing", "confidence": 0.79,
"probabilities": {"billing": 0.86}},
"<题名2>": {"type": "noul", "noul": 0.8125},
"<题名3>": {"type": "score", "score": 1.92, "legend": {"0": "低"},
"probabilities": {"0": 0.05, "1": 0.72}}},
"usage": {"input_tokens": 349, "output_tokens": 0}}
Jev(托管)与 Laya(自托管)**同协议同载荷**——Laya 官方明言 answer payload 与 Jev schema-identical ⇒ **换端点即换模型**,本包只实现一套协议。
置信度口径必须由本包单源 ¶
真机实测(自托管 Laya 0.4.13,2026-10-23)发现**两个后端的 confidence 给法不一致**:
Laya choice: confidence=0.9013(自有公式),另给 x_jev_confidence=0.9712(Jev 口径) Laya noul: confidence=0.8125(直接给 p) Jev noul: 按 Jev 口径给 |2p−1|
两端各自自洽、都不报错,但**同一门槛在两端含义会漂移**。故本包一律按 Jev 公开 公式**从概率分布本地算**(见 Answer.EffectiveConfidence),分布缺失时才回落端点 给的 confidence。本地算法与 Laya 的 x_jev_confidence 逐位一致(实测 0.9712 / 0.6769)⇒ 公式是被后端实现交叉验证过的,不是纸上推导。
题面规范:criteria 描述必须写「类别特征」,不能写「动作指令」 ¶
实测(2026-10-23,自托管 Laya 0.4.13,59 条中文用例):同一个二分类任务, **决定判定质量的是描述写法,不是任务、也不是候选顺序**:
动作指令式(「可以直接回答的概念/知识问句」/「需要派专家做诊断的诉求」)
⇒ 准确率 12.7%,且 63/63 全判同一个标签(**退化成常量输出**):
模型把标签读成了「要执行的动作」,于是恒选「直接回答」。
类别特征式(「询问概念、原理、定义、含义…」/「描述故障、异常、容量、性能…」)
⇒ 准确率 81.0%(门槛 0.6 档 92.7%),且候选顺序对调结果完全相同
(**不顺序敏感**)。
写规范:**候选的描述要说明「这类输入长什么样」,不要说明「对它做什么」**。 「要不要派专家」「要不要直接回答」是**动作**,写在描述里会让模型按动作匹配; 「询问概念原理」「描述故障容量」是**类别特征**,才是模型要的判据。
纪律 ¶
- **不吞错**:端点不可达/超时/非 2xx 一律返错;「回落还是中断」是调用方的 义务(判定面通常是加速件,回落由调用方决定)。
- **legend 以真机为准是对象**(官方文档样例写成数组,真机返回对象,数组形态 解析会直接失败)。
- **mock 模式只验接线不验质量**(见 MockEnv);真机验收用 decision_live build tag 的可复跑用例。
Index ¶
Constants ¶
const DefaultTimeout = 10 * time.Second
DefaultTimeout 缺省超时。判定模型单次调用是毫秒级(本地实测 165-211ms), 10s 给端点冷启动与网络抖动留余量。
const MockEnv = "BQ_DECISION_MOCK"
MockEnv 开关:置 "1" 时客户端**不发网络请求**,本地合成固定答案。
用途与边界:mock 验的是**接线**(判定面装上了吗、快路径真会接管吗、出参形态 对吗),**不是模型质量**——判定质量由真机用例(build tag decision_live)背书。 别拿 mock 的准确率说事。
const Path = "/v1/systemone"
Path 系统一协议端点路径(相对 BaseURL)。
Variables ¶
var ErrNotConfigured = errors.New("decision: 未配置(base_url 为空)")
ErrNotConfigured 判定面未配置(base_url 缺省等)。调用方据此跳过判定面, 不应当作端点故障告警。
Functions ¶
This section is empty.
Types ¶
type ActionInfo ¶
type ActionInfo struct {
ActProbability float64 `json:"act_probability"`
}
ActionInfo 动作概率(Laya 专有)。
type Answer ¶
type Answer struct {
Type QuestionType `json:"type"`
// Choice 选择题所选项(标签原文)。
Choice string `json:"choice,omitempty"`
// Noul 是否题的概率(true 的概率)。
Noul *float64 `json:"noul,omitempty"`
// Score 评分题的档位索引(0-indexed;5 档时 3.87 落在档位 3 与 4 之间)。
Score *float64 `json:"score,omitempty"`
// Confidence 端点自算的置信度。**后端口径不一**(见包注释)⇒ 判定面一律走
// EffectiveConfidence(本地按 Jev 公开公式算),此字段只作原值留档。
Confidence *float64 `json:"confidence,omitempty"`
// AnswerConfidence 所选答案的概率(Laya 专有字段;Jev 侧无此键)。
AnswerConfidence *float64 `json:"answer_confidence,omitempty"`
// XJevConfidence 端点算好的 Jev 口径置信度(Laya 专有):与本包本地算法同值,
// 留作对拍基准——本地算出的值应与它一致。
XJevConfidence *float64 `json:"x_jev_confidence,omitempty"`
// Action 动作概率(Laya 专有)。
Action *ActionInfo `json:"action,omitempty"`
// Probabilities 概率分布(choice:候选→概率;score:档位索引→概率)。
Probabilities map[string]float64 `json:"probabilities,omitempty"`
// Legend 评分题的档位表(索引→档位描述)。
//
// **形状以真机为准**:官方文档样例里 legend 写作数组,但真机返回的是对象
// ({"0":"低",…})——数组形态解析会直接失败(实测)。对象同时兼容两种语义。
Legend map[string]string `json:"legend,omitempty"`
}
Answer 单题答案。
func (Answer) EffectiveConfidence ¶
EffectiveConfidence 有效置信度——**判定面的单一口径**。
为什么不直接用端点给的 confidence:**两个后端给法不同**(真机实测 2026-10-23), 同一门槛在两端含义会漂移 ⇒ 口径必须在本包:从概率分布按 Jev 公开公式本地计算, 分布缺失时才回落端点值。
公式(Jev 公开口径,与 Laya 的 x_jev_confidence 逐位一致——实测交叉验证: choice 3 选 1、p_top=0.9808 ⇒ (3×0.9808−1)/2 = 0.9712;score 4 档 ⇒ 0.6769):
choice confidence = (N·p_top − 1) / (N − 1)
(N=候选数;均匀分布时 0、单点全占时 1;两候选时退化为 p_top−p_second)
score confidence = max(0, 1 − Σᵢ pᵢ·|i − m| / MAD)
(m=最可能档位,档位并列取更小者;MAD=(1/N)·Σᵢ|i−(N−1)/2|)
noul confidence = |2p − 1|(越接近 0.5 越不确信)
注意口径**随候选集规模变化**:N=20 时 0.5 门槛约等于「p_top > 52.5%」⇒ 门槛须随候选集标定,不能跨候选集移植。
type Client ¶
type Client struct {
// contains filtered or unexported fields
}
Client 判定面客户端。并发安全(http.Client 自身并发安全,本包无共享可变态)。
type Config ¶
type Config struct {
// BaseURL 端点根地址(不含 /v1/systemone)。空=未配置。
BaseURL string
// APIKey 密钥(托管 Jev 必填;自托管未开 key 时可空)。只在内存里,不落盘、不回显。
APIKey string
// Model 默认模型/检查点名(留空=服务端自选)。
Model string
// Timeout 单次请求超时(0=默认 DefaultTimeout)。判定模型本地是毫秒级,
// 超时给小了会误杀端点冷启动(首启加载检查点实测数十秒~数分钟)。
Timeout time.Duration
// HTTPClient 自定义客户端(测试注入 / 连接池复用)。留空=内部默认。
HTTPClient *http.Client
}
Config 客户端配置(调用方从自己的配置面映射而来;本包不读环境变量、不读文件)。
type Question ¶
type Question struct {
Type QuestionType `json:"type"`
Instructions string `json:"instructions,omitempty"`
Criteria any `json:"criteria,omitempty"`
// MinConfidence 本题独立门槛(0-1):低于门槛即弃答(协议支持逐题门槛)。
MinConfidence *float64 `json:"min_confidence,omitempty"`
}
Question 单题。Criteria 的形态随题型而变(**描述写法见包注释的「题面规范」**):
choice → map[string]string{候选名: 判定说明}(说明可省)
score → []string{档位描述}(有序,0-indexed)
noul → nil
type QuestionType ¶
type QuestionType string
QuestionType 题型(System One 三原语)。
const ( // TypeChoice 选择题:候选集在请求里给(开放词表),返回所选项 + 概率分布 + 置信度。 TypeChoice QuestionType = "choice" // TypeScore 评分题:有序档位在请求里给,返回档位索引(0-indexed)+ 概率分布 + legend。 TypeScore QuestionType = "score" // TypeNoul 是否题:无候选集,返回概率(true 的概率)。 TypeNoul QuestionType = "noul" )
func (QuestionType) Valid ¶
func (t QuestionType) Valid() bool
Valid 题型是否合法(组题时校验;非法题型服务端会拒,早失败更省一次往返)。
type Request ¶
type Request struct {
State any `json:"state"`
Questions map[string]Question `json:"questions"`
// Model 指定检查点/模型(留空=服务端按语言与任务自选)。
Model string `json:"model,omitempty"`
// MinConfidence 请求级门槛(0-1),逐题门槛之外的兜底。
MinConfidence *float64 `json:"min_confidence,omitempty"`
// Task 任务标签(协议可选字段,透传)。
Task string `json:"task,omitempty"`
// Lang 语言提示(协议可选字段,透传)。
Lang string `json:"lang,omitempty"`
// MaxLen 输入截断上限(协议可选字段,透传)。
MaxLen int `json:"max_len,omitempty"`
}
Request 一次判定请求(协议请求体)。
State 是待判定状态:string 或结构化对象(两种都被协议接受)。 Questions 是本次要问的所有题——**一次前向 pass 全答**,这是判定模型的主卖点。