docs(registry): supersede flat temperature/top_p overrides with mutually-exclusive sampling_override
temperature and top_p cannot be set together; the flat-fields design allowed both. Replaced by a discriminated union (default | temperature | top_p) where overriding one omits the other from the request entirely. Co-Authored-By: Claude Opus 4.7 <noreply@anthropic.com>
This commit is contained in:
@@ -1,7 +1,9 @@
|
||||
# 会话级生成参数覆盖(temperature / top_p)设计
|
||||
|
||||
日期:2026-07-28
|
||||
状态:已批准(方案 A)
|
||||
状态:已被取代 — temperature/top_p 平铺字段方案存在互斥缺陷(temperature
|
||||
与 top_p 不能同时覆盖),由 2026-07-30-sampling-override-design.md
|
||||
(判别联合方案)替代。本文件仅作历史留存。
|
||||
|
||||
## 背景与目标
|
||||
|
||||
|
||||
@@ -0,0 +1,142 @@
|
||||
# 会话级采样参数覆盖互斥(sampling_override)设计
|
||||
|
||||
日期:2026-07-30
|
||||
状态:已批准(方案 B,判别联合)
|
||||
取代:2026-07-28-thread-generation-param-overrides-design.md 中
|
||||
temperature/top_p 平铺字段方案(该方案已落地,本次为修订重写)
|
||||
|
||||
## 背景与目标
|
||||
|
||||
temperature 与 top_p 是互斥的采样参数:主流 provider(Anthropic、OpenAI
|
||||
等)要求一次请求只调整其一,同时发送两者会被拒绝或行为未定义。2026-07-28
|
||||
的设计允许两个滑块独立覆盖、同时冻结进快照,这是错误的。
|
||||
|
||||
本设计把会话级采样覆盖改为**判别联合**:用户三选一——**默认(不设置)/
|
||||
temperature / top_p**,结构上同一时刻至多覆盖一个;覆盖其一时,另一个
|
||||
参数连同注册表默认值一起从请求中省略。
|
||||
|
||||
非目标:不改注册表默认值编辑路径;registry 自身同时配置两个默认值的行为
|
||||
维持现状(属于注册表配置者的责任);不覆盖 auxiliary/vision 角色。
|
||||
|
||||
## 方案
|
||||
|
||||
### 1. 数据模型(全链路统一命名 `sampling_override`)
|
||||
|
||||
WebUI(`EvoScientist-WebUI/src/lib/modelRegistry.ts`):
|
||||
|
||||
```ts
|
||||
type SamplingOverride =
|
||||
| { kind: "temperature"; value: number } // 0–2
|
||||
| { kind: "top_p"; value: number }; // (0,1]
|
||||
|
||||
type ThreadModelSelection =
|
||||
| "inherit"
|
||||
| {
|
||||
primary: ModelRef;
|
||||
reasoning_effort?: ReasoningEffort | null;
|
||||
sampling_override?: SamplingOverride | null; // null = 注册表默认
|
||||
};
|
||||
```
|
||||
|
||||
后端(`EvoScientist/model_registry/snapshots.py`)`SnapshotCreateRequest`
|
||||
移除平铺 `temperature`/`top_p` 字段,替换为:
|
||||
|
||||
```python
|
||||
class SamplingOverride(BaseModel):
|
||||
kind: Literal["temperature", "top_p"]
|
||||
value: float # model validator 按 kind 校验:temperature ∈ [0,2];top_p ∈ (0,1]
|
||||
|
||||
sampling_override: SamplingOverride | None = None
|
||||
```
|
||||
|
||||
- 线程元数据 `model_selection` 携带新形状;`useChat` 的元数据读取镜像同步。
|
||||
- **旧的平铺 `temperature`/`top_p` 元数据键容忍并丢弃**(与 legacy
|
||||
`auxiliary` 一致),不做迁移;功能上线时间短,用户重设一次即可。
|
||||
- `inherit` 模式:与推理滑块一致,不允许携带覆盖(BFF parse 拒绝)。
|
||||
|
||||
### 2. resolver 语义(核心)
|
||||
|
||||
`ModelResolver.resolve()` 与 `resolve_parameters()` 的
|
||||
`temperature_override`/`top_p_override` 两个 kwargs 替换为单个
|
||||
`sampling_override: SamplingOverride | None`:
|
||||
|
||||
- `kind="temperature"` → temperature 取覆盖值、走 adapter `ParameterRule`
|
||||
契约校验;**top_p 直接 `OMIT`**——不继承 model 值也不继承 provider 默认,
|
||||
请求里整个不出现。
|
||||
- `kind="top_p"` → 对称处理,temperature `OMIT`。
|
||||
- `None` → 维持现状(注册表默认照常继承,可能两者都发)。
|
||||
|
||||
`compute_selection_hash(primary, reasoning_effort, sampling_override)`:
|
||||
override 序列化为 `{"kind": ..., "value": ...}` 或 `null`。旧 hash 自然失效
|
||||
生成新快照,无需迁移。
|
||||
|
||||
快照 payload 的 `request_options` 结构不变,只改值来源。
|
||||
|
||||
### 3. BFF 与 runs 路由
|
||||
|
||||
- `parseThreadModelSelection`(`src/lib/server/threadModelSelection.ts`)
|
||||
按 kind 校验 value 范围(temperature 0–2;top_p (0,1]);kind 非法、value
|
||||
越界/非有限数 → 400。模型级契约(如 Anthropic temperature ≤ 1)不在 BFF
|
||||
硬编码,留给快照创建时 adapter 校验。
|
||||
- runs 路由 POST `/api/runtime-snapshots` 的 body 把两个平铺字段换成
|
||||
`sampling_override`(inherit 或无覆盖时为 null)。
|
||||
- CAS PATCH 端点不变,`model_selection_revision` 照常递增。
|
||||
|
||||
### 4. 聊天栏 UI
|
||||
|
||||
模型名旁放分段选择器(segmented control;小屏 <sm 收成下拉),取代此前
|
||||
两个常显滑块:
|
||||
|
||||
```
|
||||
[✨ qwen3.7-plus · 阿里] [推理: 中▔] [采样: 默认 | T | P]
|
||||
```
|
||||
|
||||
- **默认**:`sampling_override = null`,请求完全按注册表配置;不显示数值
|
||||
滑块。
|
||||
- **T**:显示 temperature 滑块(量程 0–2 步进 0.05,锚定注册表默认值或
|
||||
当前覆盖值),拖动松手(onCommit)发 CAS PATCH
|
||||
`{kind: "temperature", value}`。
|
||||
- **P**:同理,量程 (0,1] 步进 0.05。
|
||||
- 切换档位 = 替换整个 override(切到"默认"提交 null);结构上只能有一个
|
||||
kind,不可能同时覆盖。
|
||||
- `inherit`(未选模型)时选择器与滑块一起禁用。
|
||||
- 失败 toast 并回滚显示;CAS 冲突沿用现有逻辑。
|
||||
- 原"点击数值重置为默认"的交互由"默认"档取代,移除。
|
||||
|
||||
### 5. 目录默认值
|
||||
|
||||
`SelectableModel.default_temperature/default_top_p`(已上线)保留不变,
|
||||
仍作两个滑块的锚点显示。
|
||||
|
||||
### 6. 错误处理
|
||||
|
||||
- adapter 契约拒绝 → runs 路由透传 4xx,前端 toast 提示合法范围,选择器
|
||||
回滚;不产生快照。
|
||||
- 非法载荷在 BFF `parseThreadModelSelection` 直接 400;后端 pydantic
|
||||
validator 防御直接调 API 的调用方。
|
||||
- 携带旧平铺字段的元数据:键被丢弃、归一化为 null(不报错)。
|
||||
|
||||
### 7. 测试
|
||||
|
||||
- 后端(`tests/test_snapshots.py`、resolver/adapter 层):
|
||||
- `SamplingOverride` validator:按 kind 拒绝越界(kind 非法、value 越界
|
||||
/NaN)。
|
||||
- 覆盖冻结进 `request_options`,且另一参数为 OMIT/不出现。
|
||||
- 契约拒绝(越出 adapter 规则)→ 快照创建 4xx。
|
||||
- hash:kind/value 改变 hash;null 与无覆盖一致。
|
||||
- openapi.json 由脚本重新生成(同步测试校验)。
|
||||
- WebUI:
|
||||
- `threadModelSelection.test.ts`:合法 kind、越界 value、非法 kind、
|
||||
legacy 平铺 `temperature`/`top_p` 键被丢弃归一化为 null。
|
||||
- runs 路由测试:快照请求体带 `sampling_override`(含 null 情形)。
|
||||
- `useChat` 元数据读取镜像用例。
|
||||
- 端到端:dev 环境聊天栏切到 T 档调值后发消息,对照 provider test 的
|
||||
`effective_request_options` 确认只带 temperature、不带 top_p。
|
||||
|
||||
### 8. 移除项(不留兼容 shim)
|
||||
|
||||
- WebUI:`ThreadModelSelection.temperature/top_p` 平铺字段、两个常显
|
||||
`GenerationParamSlider` 挂载、相关 parse/镜像逻辑。
|
||||
- 后端:`SnapshotCreateRequest.temperature/top_p`、`resolve`/
|
||||
`resolve_parameters` 的两个旧 kwargs、`compute_selection_hash` 旧签名。
|
||||
- 上述已落地代码与测试随本修订一并重写。
|
||||
Reference in New Issue
Block a user