chat() 无法传递采样参数:temperature / seed / max_tokens 都够不着,下游无法固定解码 #4
Reference in New Issue
Block a user
Delete Branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
问题
GatewayClient.chat()没有任何途径可以设置采样参数。全库检索temperature零命中;ChatRequest.overlay(types.py:46)虽然会被 transport 并进请求体(
transports/openai_compat.py:243的payload.update(overlay)),但它只被结构化输出中间件填充(
middleware/structured.py:98),chat()的签名里没有它,调用方够不着。
结果是下游无法固定解码行为。
为什么这对 dissect 是阻塞性的
该项目是一组受控实验,它的公共设定里写明「解码固定为 temperature 0」,Phase-0 的
交付物也写明「temperature 0、关闭 thinking 档」。原因是实验要在每格配置上跑 5 个
seed 并报标准差,而这个标准差必须只反映被研究的那个变量,不能混进解码温度带来的
随机性。拿不到 temperature 控制,就等于所有实验条件都在一个未知且可能随供应商默认
值变化的温度下跑——这批数据的可复现性无从谈起。
同理还需要
seed:实验代码的随机性必须显式受 seed 控制并记录进 run 快照。两个层次的需求,建议都支持
调用级,用于逐次变化的参数(
seed每个 rollout 不同):给chat()增加一个带默认值的 keyword-only 参数,并入
ChatRequest.overlay。带默认值的 keyword-only 参数不破坏
chat()的既有调用方,与 docstring 里「签名冻结」的兼容承诺不冲突。合并顺序建议让调用方的 overlay 覆盖配置级的,
但让结构化中间件的注入优先级最高(它关系到响应能否被解析)。
配置级,用于全局固定的参数(
temperature=0对本项目恒定):SourceConfig增一个
extra_body字段,从.env以 JSON 串配置,装配期解析。好处是不必让每个调用点都记得传 temperature——漏传一次就是一格实验作废,而这种错误不会报错,只会
让数字悄悄变得不可比。
顺带一提
providers.py的 minimax profile 里thinking_off是空字典,也就是SourceConfig.enable_thinking=False对 MiniMax 源不产生任何效果。如果 MiniMax 有关闭推理的请求参数,补进 profile 会更合适;如果没有,建议在 docstring 里写明这一档
对该 provider 无效,免得调用方以为自己关掉了。这一条与上面的 overlay 需求相关但独立
——有了 extra_body 也能绕过去,只是语义不如 profile 清晰。
已在 v1.0.5 实现,两个层次都支持(
ce630a3合入 main,包已发到 Gitea PyPI)。实现
chat(messages, overlay={"seed": 42}){SCOPE}__{PROVIDER}__{N}__EXTRA_BODY={"temperature":0}优先级 结构化注入 > 调用级 overlay > 源级 extra_body,由现有洋葱层序天然给出,没有引入新机制。
overlay是带默认值的 keyword-only 参数,既有调用点零改动,与"签名冻结"不冲突——这一点按 issue 里的判断采纳了。issue 未提、但一并处理的四点
设计过程中发现四个不做就会静默出错的地方,正是 issue 所担心的那种失败形态(不报错,只是数字悄悄不可比):
seed天然全部 miss(正确语义,但缓存对这条路径不再省钱)。不传采样参数时 key 与旧版逐字相同,存量缓存不受影响。{model, messages, stream, stream_options},配了直接ValueError。它们被覆盖会让成本按错单价算、绕过流式看门狗、丢 usage 帧。不可 JSON 序列化的值(numpy 标量)同样在进洋葱前报错——否则会在缓存层降级保护之外抛裸TypeError,连一行遥测都留不下。sampling列(端口 20 → 21)。"实验可复现"的另一半是参数进快照,不记的话事后无法证明某批数据跑在什么温度下。列语义是「调用方意图 ⊎ 生效源 extra_body」,不含结构化注入的response_format。extra_body并 warning。那两条路径的 transport 根本不发它(embed payload 硬编码{model, input}、MonkeyOCR 只走 multipart),不剥的话遥测会记录一个从未发出的参数——那是数据造假,比参数失效更坏。另外
SourceConfig加 mapping 字段后不再 hashable,asdict()/deepcopy()亦不适用(加任何 mapping 字段的固有代价)。已核实库内无调用点会踩,并加了锁定测试防止有人当 bug "修"回去。关于 minimax 那条
属实。
openai与minimax的 thinking profile 两档皆空,enable_thinking=False对它们不产生任何效果。查代码时发现providers.py那条"OpenAI 兼容基线"的注释在词法上其实属于 minimax 条目,openai 条目反倒没有注释——两者都只解释了"为何为空",没点明后果。已按你的建议补 docstring 说明(覆盖两个 provider),不改行为:真需要控制时用extra_body绕过。文档
wiki 新增 指南-采样参数,并同步了
参考-公共API/参考-配置键/指南-响应缓存/指南-遥测与成本。顺带补了两处历史欠账:v1.0.1/v1.0.2 当初只 bump 了版本号、实质内容(装配守卫在所有构造路径生效、scope 规范化为小写会切 Redis key 命名空间)一行没进 wiki;另经独立审查修正了 6 处 wiki 与代码不符之处,其中一条是教程说改错 API_KEY 会得到
AllSourcesExhausted,实测是CircuitOpenError——照着写的下游会捕不到。