Skip to content

multi2api v1.9.11

Choose a tag to compare

@github-actions github-actions released this 20 Oct 03:37
· 120 commits to master since this release

会话粘性修复(issue #2)

会话粘性自 v1.8.0 起从未生效过。这个版本修好了。

缺陷

ExtractKey 只从请求体里找三个字段:

metadata.conversation_id
conversation_id
metadata.user_id

而 OpenAI 兼容客户端(DSH / Cline / Continue 等绝大多数)从不发送这些字段:

  • sessionId 只在 baseUrl 含 api.openai.com 时才写进 prompt_cache_key(本地网关不满足)
  • x-session-affinity / session_id 头只对 OpenRouter 启用,网关侧也不读

⇒ sessKey 恒为空 ⇒ Resolve 从未被调用 ⇒ 粘性层退化为纯随机轮换。

实测后果

  • /status 的 sticky_sessions 在 6927 个请求下恒为 0
  • 同会话相邻两轮的账号转移矩阵 ≈ 20/20/20/20/20(5 个号纯随机)
  • 上游 prompt cache 按账号隔离 ⇒ 每次换号 = 整段对话重新预填充
    • 长对话单笔换号损失实测 2.8 ~ 6.0 credit(正常请求 0.1~0.3)
  • 受控对照:不带会话标识 19.8% 缓存命中 vs 带 conversation_id 98.7%

修法:第 4 级兜底「对话前缀指纹」

三条显式键都落空时,用 model + messages 前两条(system 截 512 字符)的 FNV-1a 指纹作会话键。

为什么这是对的不变量:多轮对话的 messages 是前缀增长的 —— 第 1 轮 [system, user1],第 2 轮 [system, user1, asst1, user2],前面永远不变 ⇒ 只取前两条,指纹在整段对话里恒定。这正是"缓存亲和"需要的东西,且不需要客户端配合。

三个刻意的取舍:

决定 理由
只取前两条 message 全量哈希会随每轮增长而变,指纹每轮都不同 = 等于没粘性
system 截前 512 字符 有的客户端往 system 塞时间戳/请求号,全取会漂移
形态不认识时返回空 宁可退回轮换,也不要算出一个会漂移的键(后者会让表里堆满一次性条目,既占内存又让 GC 白跑)

7 条守卫测试

StableAcrossTurns               ★ 核心:多轮对话指纹必须不变(1/2/3 轮同键)
DiffersByFirstUser              不同首条 user 必须不同键
DiffersByModel                  不同模型(模型进了指纹前缀)
ExplicitKeyWins                 显式键优先,不被指纹覆盖
ContentBlocksFlattened          内容块数组折平后与纯字符串同键
UnknownShapeReturnsEmpty        形态不认识返回空
SystemTimestampTailDoesNotDrift system 前 512 相同、尾巴时间戳不同 ⇒ 同键

预期效果(issue 报告的实测数据)

指标 修复前 修复后
缓存 miss 率 13.3% 3.1%
成本 / 百万 prompt token 3.7 credit 1.2 credit(−68%)
sticky_sessions 恒 0 14~16
同会话连续请求同号比例 ~20% 100%

⚠ issue #2 里另外两个观察,本版未处理

① 粘性表纯内存,重启即丢

无 Redis(redis_mode: noop)时,重启后所有长会话被判 assign、重新分配 ⇒ 整段重算。实测两笔:msgs=1146 花 5.59 credit、msgs=753 花 1.76 credit,仅仅因为重启。

建议:无 Redis 时也提供本地文件兜底(data/sticky-bindings.json,原子写 + 启动恢复)。

② context_length_exceeded 仍会轮换全部账号

上游 400 fatal_client("prompt is too long: 100001 tokens > 100000 maximum")实测仍把账号池轮一遍,最后返回笼统的 503 budget_exhausted:global,客户端看不到上游原因。

建议:确定性错误立即失败并透传上游原文。

升级注意

前端 embed 在二进制里,必须重新 build:

git pull
go build -o bin\wb2api-server.exe ./cmd/server

重启后验证粘性生效:/status 的 sticky_sessions 应不再是 0;同一会话连发多轮,request-log 里的 uid 应稳定在同一个账号。