Skip to content

BAZZ.AGENT v1.7.2

Choose a tag to compare

@github-actions github-actions released this 21 Sep 03:10
· 4 commits to main since this release

🆕 v1.7.2 更新要点(链上筹码接线:链下代理指纹 × 链上持仓证据,交叉打假)

这一版把一个躺了两版、从没被 import 过的模块接进了生产链路。 src/onchain.py 早就写好了
(989 行 / 47 KB),但全仓没有任何一处 import 它 —— 一个没有调用方的模块,等于没写。本轮把
它接进妖币雷达,接的过程中用真实链路打出两个真 bug(都不是逻辑错,都是「静默」型:

① 先纠正一个前提:这个模块此前一行代码都没跑过

grep -rn "import onchain" src/ desktop_app.py 命中 0 次。它是「写完就搁那儿」的状态 ——
没测试、没接线、没发版。所以本轮的顺序是先跑真实链路再谈接线:不跑就不知道它是能用的还是坏的,
而「读代码看着没问题」正是这类模块最常见的假象。

② 真 bug 一:代理端口被写死成 7899,而这个端口是动态分配的

onchain.py 里把「内核混合端口」硬编码成 http://127.0.0.1:7899。但 proxy_kernel 的混合端口
是 _free_port(7899) 动态挑的 —— 7899 被占(实测经常被占)时会换一个端口。

情况 硬编码 7899 的后果
7899 恰好是本项目内核 侥幸能用
7899 被别的进程占 请求指向别人的端口
内核在别的端口 整层静默全空(不报错、只是「没有链上数据」)

修法:删掉硬编码,改成运行期向内核问端口 ——

def _kernel_mixed() -> str:
    try:
        import proxy_kernel
        p = int(proxy_kernel.mixed_port() or 0)
        return f"http://127.0.0.1:{p}" if p else ""
    except Exception:
        return ""

护栏钉的是「源码里不得出现字面量 127.0.0.1:7899」—— 注意是字面量,源码扫描走 tokenize
剥注释与 docstring
,否则这条断言会被它自己的修复说明绊倒(本项目已经栽过两次)。

③ 真 bug 二:GoPlus 限流不是 429,是 HTTP 200 里藏了个 code

这条是本轮最有价值的实测。GoPlus 被限流时返回的是:

HTTP 200  {"code": 4029, "message": "too many requests"}      ← 没有 result 键

而原来的解析逻辑是「取 result,没有就当作这个币没数据」→ 限流被读成了「没测到」。
两者在界面上长得一模一样,但含义完全相反:一个是数据源在拒我,一个是我真没查到。
于是「链上筹码」这一块会静默地时好时坏,而且永远不报错。

实测出的限流节奏(这一条直接决定了实现):

策略 结果
0.12s 连打 第 11 次即被限流
限流后等 30s 重试 仍在限流
限流后等 45s 重试 恢复
统一 5.5s 间隔 14/14 全成功(82 秒)

结论:限流按窗口内次数算,慢下来就能绕开,不需要靠退避硬扛。所以实现是三件事一起做 ——
_throttle_signal()(识别 code in ("4029","429") 或消息含 too many requests,
必须在解析 result 之前判,否则限流又变成「没数据」)+ _src_available() 短路
(退避窗口内直接不发请求)+ 逐源计数(goplus_throttled / rug_throttled 进 _stats,
「被限了几次」从此是可见的数字,不是猜的)。

④ 交叉判据:链下代理指纹 × 链上持仓证据,只给证据不改分级

新函数 cross_verdict(reading, struct_flags) 六态:

状态 触发条件 含义
confirm 链下命中结构性指纹 且 链上 top10 高 两侧互证
refute 链下命中 但 链上分散 链下代理可能是误报(最有价值的一档)
warn 链下未命中 但 链上高度集中 链上先出现异常
clean 两侧都干净 —
neutral top10 落在 25%~60% 不判(不编故事)
unknown 这个币没有读数 是这个币的属性

阈值 60 / 25 不是拍的,是拿真实 16 币的 top10_pct 分布定的(中位 45.5):
取 60 让报警保持稀有,取 25 让「干净」不被随便给。

⚠️ 结构性指纹只有 3 个能参与交叉:无现货 / 合约独大 / 换手畸高。另外两个
(空头付钱 / 拉升无爆仓)是持仓侧指纹 —— 拿它和「链上持仓集中度」比大小是
两个范畴的东西互比,属于凭空造因果。这条落成模块级常量并在测试里钉死两侧名单不得漂移。

⚠️ 链上证据不参与 grade 赋值。这是刻意的:新数据在没有样本之前不许改判据。
它只往 evidence 里追加一行可读证据(chain / chain_struct 两个字段供消费方取用)。

⑤ 「先剔再算」:烧地址会把结论整个反过来

top10_pct 原始口径把烧掉/销毁地址算进去,而这些地址的筹码永远不会卖。
实测 CAKE(BSC):

口径 top10 占比 判据结论
原始(含烧地址) 92.74% 极度集中 → confirm
剔除烧地址后 4.14% 极度分散 → refute

结论完全反过来。 所以取值路径一律「先剔再算」,被剔掉的个数作为审计字段带出来。

⑥ 三个数据源(零 Key、全免费)

源 覆盖 直连 说明
CoinGecko L1/L2 层,symbol → cg_id → 合约地址 ❌ 必须走代理 只做地址解析
GoPlus EVM + Solana 持仓 / 貔貅 / 税率 ✅ 5.5s 间隔
RugCheck Solana 前 20 持有者 + 风险项 ✅ —

⑦ 「整条链路不通」与「这个币没有链上数据」必须分开说

_oc_cell(reading, manip_flags, measured) 在 measured=False 时出 source_down,
与逐币的 unknown 是两个不同的 state 码。理由:前者是我们的问题(代理没通 / 源挂了),
后者是这个币的属性。把它们合并,就等于替数据源背锅 —— 用户看到「没有链上数据」会以为
是币的问题,实际是我们没打通。payload 另外下发 onchain_health() 快照(含
goplus_available / goplus_backoff_sec / rug_backoff_sec / route / stats),
前端体检条在未通/限流时红标,正常时显示「链上 N 币」。

⑧ 派发必须在逐币循环之前

链上读数是异步刷新 + 读缓存(绝不阻塞雷达主路径)。所以 _onchain_dispatch(pool_syms)
放在扫描主体开头、_onchain_read(pool_syms) 在循环前读一次(不是每行读一次)。
派发晚一步,这一轮末尾就永远填不上读数 —— 表现是「链上那一列一直是空的」,不报错。

⑨ 前端:state 码 → i18n 键走 switch,不走动态键

t(\markets.onchain.${state}`)这种动态键会**绕开「用到的键必须两本字典都有」的静态扫描** —— 而且i18n.tsx的实现是dict[key] ?? key(**没有 zh 兜底**),缺键就是界面把 key 当文案印出来。 所以改成显式 switch映射到 16 个具名键(zh + en 各一次,共 32 处)。另外链上 pill 渲染的是 **i18n 标签**而不是后端下发的中文label` —— 否则英文界面串中文。

⑩ 护栏与变异验证

  • 新增 tests/test_v172_onchain.py —— 181 条断言,A~K 共 11 组:符号归一化、单位归一化
    (GoPlus 小数 ×100,RugCheck 百分比原样,两个源单位相反)、烧地址剔除(CAKE 必须翻转结论)、
    限流识别+计数+退避+短路、动态端口、损坏缓存不连坐、六态交叉判据含边界、只有结构性指纹能参与、
    扫描接线 + source_down ≠ unknown、monster 轴「只给证据」、前端契约(switch 覆盖全部 state 码 +
    不得用动态键 + i18n 成对)、模块真的被 import + 探针脚本已清理。
  • 新增 tests/_mutate_v172.py —— 26 组变异,全部转红(26/26)。第一轮有 1 组「锚点命中 0 次」
    被跳过(锚点与源码换行不一致,等于这条护栏根本没被测到),改正锚点后复跑转红。
    这正是变异验证的用处:它测的不是代码,是护栏本身。
  • tests/_e2e_v172_onchain.py —— 真实链路:代理自动选择、后台刷新不阻塞、真实读数与分布、
    CAKE 烧地址翻转、GoPlus 限流记账、逐币 row_cell;--radar 选项跑真实 scanner.get_radar_v2
    并核对 payload onchain 与行级 onchain 格。
  • 修 2 个真 bug 的方式是「先跑真实链路」,不是读代码 —— 这两条(硬编码端口 / 限流读成没数据)
    跑离线测试永远发现不了,因为它们在测试里不会复现。

⑪ 清理

删掉 4 个临时探针脚本(_probe_onchain.py / _probe_diag.py / _probe_syms.py / _probe_wif.py)
与产物目录 _ws_onchain/ —— 它们的作用已经固化成 tests/_e2e_v172_onchain.py,探针留在仓里只会腐烂。