返回结构契约 · 由发布门禁强制执行
我们不会改这些字段。
而且这句话有一把锁。
做 Agent 集成的人最怕的从来不是功能少,是某天我们改了返回结构,害他半夜起来修 bug。「我们不会乱改」这句话谁都会说 —— 所以我们把它变成一道发布门禁:已发布的标识符只要有一个消失或改名,我们自己的构建就过不去。
31
22
90 天
同一个问题,问两次答案一样
同一时刻、同一份名单下,同一个地址查两次,判决、原因码和证据链接完全一致。判决来自名单和确定性规则,不来自模型打分,所以不会因为「再问一次」而改变。
答案只会因为三件事而变化:名单本身更新了、链上情况变了(交易数、地址年龄、关联的风险地址),或者判定规则调整了。每条返回都带 evaluated_at(这次判定算出来的时刻)和 evidence_as_of(各份名单覆盖到哪天)—— 事后复盘时,凭这两个字段就能说清「当时为什么这么判」。
可以放心写进代码的
这四个是语言无关、结构稳定的。新值可能随时增加 —— 遇到没见过的值请当作「没处理」,不要当作错误。
signal_keysverdictreason_coderetry千万不要写进代码的
signals · note · advice
这些是给人读的散文。会翻译、会改措辞、会因为一次文案返修就全变。按它做逻辑,下一次改文案你就崩。
risk 分数的具体数值
0–100 这个刻度不变,但同一个地址得几分会随数据变好而变。用它排序可以,用它做阈值判决要自己承担漂移。
数组里的顺序
signals、evidence 的顺序不作承诺。
要改的话,我们必须怎么做
- 新标识符先与旧的并存,两个一起返回。
- 旧的在 /api/contract 里标 deprecated,并写明移除日期,至少 90 天之后。
- 到期才移除。
在第 2 步走完之前,门禁不会放行删除 —— 这不是流程规定,是构建脚本里的一行判断。
当前已登记的 signal_keys
按前缀分组。前缀本身也是稳定的:list.* 是名单命中,taint.* 是资金沾染,age.* 是地址年龄/活跃度,src.* 是数据源降级,scope.* 是覆盖边界,endpoint.* 是端点层。
age.activeage.active.tronage.brandnewage.brandnew.tronage.new7dage.new7d.tronage.receiveonlyendpoint.deep_subdomainendpoint.delistedendpoint.free_hostingendpoint.ip_literalendpoint.non_publicendpoint.parent_listedendpoint.phishing_listedendpoint.punycodefreeze.unknownfrozen.issuerfrozen.usdtintel.profilelabel.tronscanlist.delistedlist.oceanaltrelated.onehop.outscope.btcscope.solsrc.degradedsrc.onchainsrc.tron.degradedsrc.tronscantaint.onehoptaint.usdt当前已登记的 reason_codes
behavioral_anomalyclearelevated_risk_payeeidempotency_key_invalididempotency_key_reusedidempotency_request_in_progressintent_information_requiredintent_injection_markerinvalid_inputkey_ip_deniedkey_scope_deniedkya_proof_missingkya_unattributedkyc_requiredmandate_intent_mismatchmandate_revokedover_daily_limitover_per_tx_limitpayee_not_allowlistedreplay_detectedsanctioned_or_high_risk_payeescreening_unavailable机读版本
GET https://oceanalt.com/api/contract
同一份内容,含语义说明与弃用清单。你的 CI 可以直接拉它做回归比对。

