fix(channel/flowgate): 审查移出准入路径、节流覆盖失败路径、BOM 解析韧性
- Admit 不再同步调用审查模型:触发时只把审查**派发**到后台 goroutine,准入只读 当前已有结论。三重上界:同一适配器至多一次在飞(reviewInFlight)、同一会话每窗口 至多一次(lastReview)、整个适配器每窗口至多 defaultReviewPerWindow=6 次(reviewHits, 写成可测断言)。 - st.lastReview 改为在**派发时**置位 ⇒ 成功与失败两条路径同样被节流。修复前它只在 err == nil 分支置位,而实测 68% 的审查因对端回带 BOM 解析失败 ⇒ 缓冲满后每条入站帧 都付一次 ~6s 的模型调用(qq-ws/kook-ws/weixin-poll 三路单读 goroutine 全被钉住)。 - 新增 adapter.ParseReviewIndices:容忍 UTF-8 BOM、首尾空白/换行、Markdown 代码围栏、 数组外解说文字;越界/重复编号照旧丢弃。channelbridge.parseKeepIndices 改为委派它 (仅函数体 + 注释,未动限流/丢弃/审查触发语义)。 - 语义差异(已写进 flowgate_review.go 文件头):rate_limited 的门口丢与 FIFO 上限 一字不改;审查修剪降级为后台积压去噪,只修剪它当时看到的那批历史条目 —— review_pruned 不再从门口丢消息(实测 41–74 条/日),完整性↑、噪声↑,是否开口仍由 engage_judge/冷避/owner 闸判(三处判据未动)。 - cmd/cyrene:逐账号启动日志如实报出生效的入口流控(塔裁决 A:零行为改动、不翻默认), 让「没配流控键的账号闸门仍开着」一眼可见。 - 测试:4s 慢审查替身证明入站帧不被阻塞(变异回同步 ⇒ 红)、节流在失败路径生效 (摘掉派发时置位 ⇒ 红)、每适配器窗口预算上界(摘掉预算 ⇒ 红)、同适配器单飞 (摘掉 in-flight ⇒ 红)、在飞期间到达的消息不被连带修剪、带 BOM 字节的夹具 (夹具去掉 BOM ⇒ 红)。闸门用例 -race 通过。
This commit is contained in:
1 parent
4ed5925296
commit
76d972f0f2
8 files changed
+1043
-150
No files matched your search
@@ -2,10 +2,8 @@ package channelbridge
|
||||
|
||||
import (
|
||||
"context"
|
||||
"encoding/json"
|
||||
"fmt"
|
||||
"log/slog"
|
||||
"sort"
|
||||
"strings"
|
||||
|
||||
"git.yeij.top/AskaEth/Cyrene_v2/internal/channel/adapter"
|
||||
@@ -201,22 +199,14 @@ func truncateText(s string, n int) string {
|
||||
return string(runes[:n]) + "…"
|
||||
}
|
||||
|
||||
// parseKeepIndices 解码审查模型返回的 JSON 索引数组,并按升序
|
||||
// 只保留范围内、去重后的值。越界或重复的
|
||||
// 索引被丢弃,因此模型幻觉出的数字绝不会淘汰真实消息。
|
||||
// parseKeepIndices 解码审查模型返回的编号数组,并按升序只保留范围内、去重后的值。
|
||||
// 越界或重复的索引被丢弃,因此模型幻觉出的数字绝不会淘汰真实消息。
|
||||
//
|
||||
// 解析本身在 adapter.ParseReviewIndices(解析属于闸门的契约,和 Reviewer 类型同一个包),
|
||||
// 它容忍对端回带的东西:**UTF-8 BOM**、首尾空白/换行、Markdown 代码围栏、数组外的解说
|
||||
// 文字。修复前这里是裸 json.Unmarshal,实测 9 天 1,909 次审查里 1,301 次解析失败
|
||||
// (其中 1,288 次错误串逐字是 `invalid character 'ï' looking for beginning of value`),
|
||||
// 每次失败都白烧一次模型调用 —— 见 DevDocs/audit/群聊排队与回旧话-2026-10-24.md。
|
||||
func parseKeepIndices(s string, bound int) ([]int, error) {
|
||||
var idx []int
|
||||
if err := json.Unmarshal([]byte(s), &idx); err != nil {
|
||||
return nil, err
|
||||
}
|
||||
seen := make(map[int]bool, len(idx))
|
||||
out := make([]int, 0, len(idx))
|
||||
for _, i := range idx {
|
||||
if i >= 0 && i < bound && !seen[i] {
|
||||
seen[i] = true
|
||||
out = append(out, i)
|
||||
}
|
||||
}
|
||||
sort.Ints(out)
|
||||
return out, nil
|
||||
return adapter.ParseReviewIndices(s, bound)
|
||||
}
|
||||
Reference in new issue
Block a user