☰
基于Rust与AI双层架构的高性能风控系统设计与实践
2026/11/3 8:46:10 网站建设 项目流程

1. 为什么要在风控架构里做“双层”设计

安全风控这个领域,做了几年之后你会发现一个很尴尬的现实:单层防御永远在“误杀”和“漏杀”之间反复横跳。规则写严了,正常用户的请求被拦得莫名其妙,客诉电话打爆;规则写松了,黑产和恶意流量像逛自家后花园一样进进出出。我最早做风控的时候也是单层规则引擎打天下,后来被现实教育得很惨,才慢慢摸索出这套“底层硬拦截 + 上层语义研判”的双层架构。

这套架构的核心思路其实不复杂,用一句话概括就是:让该快的快、该准的准,两层各司其职,互不越界。底层用 Rust 做硬拦截,负责那些毫秒级必须出结果的确定性判断——IP 黑名单、请求频率、设备指纹异常、参数格式违规这类“一眼假”的东西。上层用 AI 做语义研判,负责那些需要理解上下文、判断意图的模糊场景——比如一段文本到底是正常咨询还是恶意诱导,一个行为序列到底是真人操作还是脚本模拟。

为什么非得拆成两层?因为这两类判断的性能特征和准确率曲线完全不同。硬拦截追求的是极低延迟和确定性,你不可能让每个请求都去跑一遍大模型推理,那延迟和成本都扛不住。而语义研判追求的是准确率和召回率,它天然需要更多的计算资源和上下文信息,不可能放在请求主链路上同步阻塞。所以双层架构本质上是一个性能与准确率的解耦设计,底层做粗筛快速放行或拦截,上层做精判处理那些“拿不准”的灰色地带。

这套方案适合谁参考?如果你正在做 API 网关风控、内容安全审核、反爬反刷、交易反欺诈这类场景,并且已经感受到单层规则的局限性,那这套双层思路可以直接借鉴。哪怕你不用 Rust 也不用 AI,这个“快慢分离、确定性判断与概率性判断分层”的架构思想本身就有价值。

2. 双层架构的整体设计与选型考量

2.1 两层各自的职责边界怎么划

划边界这件事,我踩过最大的坑就是“什么都想放底层”。一开始觉得底层快,就把大量规则往 Rust 层塞,结果底层代码越来越臃肿,每次调整规则都要重新编译部署,迭代速度极慢。后来我总结出一个划分原则:底层只放“确定性规则”,上层只放“概率性判断”。

确定性规则的特征是:输入确定则输出确定,不需要上下文,不需要历史状态,判断逻辑可以用布尔表达式写清楚。比如“这个 IP 在封禁列表里”“这个手机号一分钟内请求了 50 次”“这个请求的 User-Agent 是已知的爬虫特征”。这类判断放在 Rust 层,用内存里的哈希表和计数器就能搞定,单次判断耗时可以控制在微秒级。

概率性判断的特征是:同样的输入在不同上下文下结论可能不同,需要理解语义、分析行为模式、结合历史数据。比如“这段评论是不是在阴阳怪气”“这个用户的操作序列像不像机器批量行为”“这条商品描述有没有违规暗示”。这类判断交给 AI 层,用模型推理输出一个风险分数,再结合阈值做决策。

注意:边界划分不是一成不变的。我建议每季度回顾一次,看看底层有没有混入本该上层的逻辑,或者上层有没有在重复底层已经做过的判断。边界模糊是架构腐化的开始。

2.2 为什么底层选 Rust 而不是 Go 或 C++

选 Rust 做底层硬拦截,最直接的原因是性能与内存安全的平衡。风控底层是典型的高并发、低延迟场景,每秒可能要处理几十万次判断,每次判断都涉及内存读写。Go 的 GC 在这种场景下会产生不可控的延迟毛刺,虽然大部分时候很快,但偶尔的 STW 会让 P99 延迟很难看。C++ 性能没问题,但内存安全全靠人肉保证,风控规则引擎这种频繁修改、多人协作的代码,指针错误的风险太高。

Rust 的所有权模型在编译期就消除了数据竞争和悬垂指针,同时没有 GC,延迟曲线非常平稳。我实测下来,同样的规则匹配逻辑,Rust 版本的 P99 延迟比 Go 版本低了将近一个数量级,而且内存占用更可控。另外 Rust 的Arc+RwLock做共享状态管理很顺手,底层需要维护的 IP 黑名单、频率计数器这些共享数据结构,用 Rust 写出来既安全又高效。

至于热更新,Rust 可以通过动态加载配置或者用arc-swap这类库实现无锁配置切换,不需要重启进程就能更新规则。这一点对风控系统很关键,因为规则是随时在变的。

2.3 AI 语义研判层的模型选型思路

AI 层选型要考虑三个维度:推理延迟、准确率、成本。我试过几种方案,最后落在一个“小模型粗判 + 大模型精判”的级联结构上。

第一级用蒸馏过的小模型(参数量在亿级以内),做快速语义分类,输出一个初步风险分。这一级延迟控制在 50ms 以内,能过滤掉大部分明显正常的请求。第二级用大模型做精细研判,只处理第一级输出风险分在灰色区间的请求,这一级延迟可以放宽到几百毫秒,因为量已经小了很多。

为什么不直接上大模型?因为成本扛不住。假设每天一亿次请求,每次大模型推理按最低成本算,一个月下来也是天文数字。级联结构能把大模型调用量压到总请求量的 5% 以下,成本直接降一个数量级。

模型部署上,我用的是 ONNX Runtime 做推理引擎,配合 GPU 做批处理。批处理很关键,单条推理和批量推理的吞吐量差距巨大。我一般会把 100ms 窗口内的请求攒成一批一起推理,这样 GPU 利用率能拉满,单条平均延迟反而更低。

2.4 两层之间怎么衔接

两层之间的衔接方式直接决定了架构的灵活性和性能。我见过有人把两层做成串行同步调用,底层判断完直接调 AI 层,结果底层被 AI 层的延迟拖死。正确的做法是异步解耦。

具体来说,底层硬拦截在请求主链路上同步执行,判断结果分三种:明确放行、明确拦截、不确定。明确放行的直接过,明确拦截的直接拒,不确定的请求打上标记后放行到业务逻辑,同时异步投递到 AI 研判队列。AI 层消费队列做研判,如果研判结果是高风险,再通过回调或者消息通知的方式触发后续处置(比如封禁账号、加入黑名单、告警)。

这种设计的好处是主链路延迟不受 AI 层影响,AI 层可以慢慢算。代价是存在一个“研判窗口期”,在这个窗口期内风险请求可能已经执行了。所以这套架构更适合“事后可追溯、可回滚”的场景,比如内容发布后审核、交易事后风控。如果是“事前必须拦截”的场景,那就得把 AI 研判也放到主链路上,但那样延迟就上去了,需要权衡。

3. 底层 Rust 硬拦截的核心实现细节

3.1 规则引擎的数据结构设计

底层规则引擎的核心是快速匹配。我把规则分成三类,每类用不同的数据结构:

第一类是精确匹配规则,比如 IP 黑名单、设备 ID 黑名单。这类用HashSet或者更省内存的布隆过滤器。布隆过滤器有假阳性但没假阴性,对于黑名单场景可以接受——偶尔误伤一个正常 IP 比漏掉一个恶意 IP 代价小。我用的是bloomfiltercrate,配合定期重建来清理过期数据。

第二类是范围匹配规则,比如 IP 段、时间窗口内的频率。IP 段用前缀树(Trie)存储,查询复杂度只跟 IP 位数有关,跟规则数量无关。频率计数用滑动窗口,我实现了一个基于时间轮的分片计数器,每个分片独立加锁,减少锁竞争。

第三类是模式匹配规则,比如 User-Agent 特征、URL 路径模式。这类用 Aho-Corasick 自动机做多模式匹配,一次扫描就能匹配所有模式,比逐个正则快得多。Rust 的aho-corasickcrate 性能很好,我实测单次匹配在纳秒级。

use aho_corasick::AhoCorasick; use std::collections::HashSet; struct RuleEngine { ip_blacklist: HashSet<u32>, ua_patterns: AhoCorasick, rate_counters: ShardedCounter, } impl RuleEngine { fn check(&self, req: &Request) -> Verdict { if self.ip_blacklist.contains(&req.ip_u32()) { return Verdict::Block("ip_blacklist"); } if self.ua_patterns.is_match(&req.user_agent) { return Verdict::Block("ua_pattern"); } if self.rate_counters.exceed(&req.ip_u32(), 50, 60) { return Verdict::Block("rate_limit"); } Verdict::Pass } }

3.2 共享状态的无锁化处理

底层风控要维护大量共享状态:黑名单、计数器、配置。这些状态如果都用Mutex保护,高并发下锁竞争会成为瓶颈。我的做法是能无锁就无锁,不能无锁就分片。

黑名单这种读多写少的数据,用arc-swap做原子替换。读的时候直接读当前Arc,写的时候构造新的Arc然后原子替换,读路径完全无锁。代价是写的时候要复制整个数据结构,但黑名单更新频率低,这个代价可以接受。

计数器这种读写都频繁的,用分片。我按 IP 哈希分成 256 个分片,每个分片一个独立的Mutex<HashMap>。这样锁竞争降低了 256 倍,实测吞吐量提升非常明显。分片数不是越多越好,太多会导致内存碎片和缓存不友好,256 是我实测下来比较平衡的值。

use arc_swap::ArcSwap; use std::sync::Arc; struct Blacklist { inner: ArcSwap<HashSet<u32>>, } impl Blacklist { fn contains(&self, ip: u32) -> bool { self.inner.load().contains(&ip) } fn update(&self, new_set: HashSet<u32>) { self.inner.store(Arc::new(new_set)); } }

3.3 规则热更新的实现方式

风控规则必须能热更新,不能每次改规则都重启服务。我实现了一套基于配置中心的热更新机制:规则以 JSON 格式存在配置中心,底层服务订阅配置变更事件,收到变更后异步重建规则数据结构,重建完成后原子替换。

这里有个坑:重建过程中旧规则还在服务,新规则还没生效,这个窗口期内用的是旧规则。如果新规则是加严的,那窗口期内会有漏杀;如果新规则是放宽的,那窗口期内会有误杀。我的处理方式是双缓冲:同时持有新旧两套规则,重建期间两套都查,取更严格的结果。重建完成后切换到新规则,旧规则延迟一段时间再释放。

struct DualBufferRuleEngine { current: ArcSwap<RuleSet>, pending: ArcSwap<Option<Arc<RuleSet>>>, } impl DualBufferRuleEngine { fn check(&self, req: &Request) -> Verdict { let current = self.current.load(); let result = current.check(req); if let Some(pending) = self.pending.load().as_ref() { let pending_result = pending.check(req); return result.stricter(pending_result); } result } }

3.4 性能压测与调优实录

底层写完之后一定要压测,不压测你永远不知道瓶颈在哪。我用的是wrk和自研的压测工具,模拟真实请求分布。第一次压测结果很难看,P99 延迟到了 20ms,远超预期的 1ms。

排查下来发现两个问题:一是布隆过滤器的哈希函数计算太慢,换成了更快的xxhash;二是频率计数器的分片锁在极端热点 IP 下还是有竞争,后来对热点 IP 做了本地缓存,短时间内重复请求直接读缓存。

调优之后 P99 降到了 800 微秒,单机 QPS 从 5 万提升到了 30 万。这里的关键经验是:压测要模拟真实分布,不能只用均匀分布。真实流量里热点 IP 和长尾 IP 的分布差异很大,均匀分布压测会掩盖热点竞争问题。

4. AI 语义研判层的落地实践

4.1 语义研判的任务定义与标注体系

AI 层要解决的核心问题是:给定一段文本或一个行为序列,判断其风险等级。这本质上是一个分类任务,但风险等级不是简单的二分类,我一般分成四档:安全、低风险、中风险、高风险。

标注体系的设计很关键。我踩过的坑是标注标准太模糊,导致标注员之间一致性很差,模型学出来的东西也是模棱两可。后来我制定了一份详细的标注手册,每个风险等级都有明确的判定标准和边界案例。比如“低风险”定义为“有轻微违规倾向但未实际违规”,“中风险”定义为“明确违规但情节较轻”,“高风险”定义为“严重违规或恶意行为”。

标注一致性用 Cohen's Kappa 系数衡量,我要求标注员之间的一致性达到 0.8 以上才允许进入训练集。达不到就重新培训标注员,或者细化标注标准。这个投入很值得,因为标注质量直接决定模型上限。

4.2 小模型粗判的工程实现

小模型我选的是蒸馏后的 BERT 变体,参数量控制在 1 亿以内,用 ONNX Runtime 部署。输入是文本的 token 序列,输出是四分类的概率分布。

推理服务用 Rust 写了一个 gRPC 服务,接收批量请求,攒批后调用 ONNX Runtime。攒批策略是“最多等 50ms 或攒够 64 条”,这样既能保证延迟可控,又能提高 GPU 利用率。实测下来,单条推理延迟 30ms 左右,批量推理摊薄后单条延迟降到 8ms。

async fn batch_infer(requests: Vec<InferRequest>) -> Vec<InferResult> { let batch = requests.into_iter().take(64).collect(); let inputs = preprocess(batch); let outputs = session.run(inputs).await; postprocess(outputs) }

小模型的准确率不需要太高,它的定位是“粗筛”。我要求它在高风险类别上的召回率达到 95% 以上,准确率可以放宽到 70%。因为漏掉高风险请求的代价远大于误判,误判的请求会被大模型层纠正。

4.3 大模型精判的提示词设计

大模型层处理的是小模型判为“中风险”和“低风险”的请求,以及小模型判为“高风险”但置信度不高的请求。这一层用提示词工程引导大模型做精细研判。

提示词设计的核心是给模型明确的判断框架和输出格式。我一般会包含这几部分:角色设定(你是一个内容安全研判专家)、判断标准(列出各风险等级的定义和边界案例)、待研判内容、输出格式要求(JSON 格式,包含风险等级、置信度、判断理由)。

你是一个内容安全研判专家。请根据以下标准判断待研判内容的风险等级: - 安全:无任何违规倾向 - 低风险:有轻微违规倾向但未实际违规 - 中风险:明确违规但情节较轻 - 高风险:严重违规或恶意行为 待研判内容: {content} 请以 JSON 格式输出:{"level": "风险等级", "confidence": 0.0-1.0, "reason": "判断理由"}

提示词要反复迭代。我一般会准备一个测试集,每次修改提示词后跑一遍测试集,看准确率和召回率的变化。提示词里的边界案例很关键,模型对边界案例的理解直接决定了灰色地带的判断质量。

4.4 级联决策与阈值调优

小模型和大模型的输出需要融合成最终决策。我的做法是:小模型输出风险分后,如果高于高阈值(比如 0.9)直接判高风险,低于低阈值(比如 0.1)直接判安全,中间灰色地带交给大模型精判。大模型的输出再结合业务规则做最终决策。

阈值调优是个细致活。阈值定高了漏杀多,定低了误杀多。我用的是 ROC 曲线找最优阈值点,同时结合业务对误杀和漏杀的容忍度做调整。比如内容发布场景对误杀容忍度低,阈值就定高一点;交易风控场景对漏杀容忍度低,阈值就定低一点。

提示:阈值不是定一次就完事,要定期用新数据重新评估。我一般每月跑一次阈值评估,看看当前阈值下的误杀率和漏杀率是否还在可接受范围内。

5. 两层协同的完整请求链路

5.1 请求进入底层硬拦截的处理流程

一个请求进来,首先经过底层硬拦截。底层做的是同步快速判断,耗时控制在 1ms 以内。判断逻辑按顺序执行:IP 黑名单检查、设备指纹检查、频率检查、参数格式检查、UA 模式检查。任何一项命中就直接拦截,返回 403 并记录拦截原因。

如果全部通过,底层会给请求打一个“底层通过”的标记,然后放行到下一层。这里有个细节:底层判断结果要记录到请求上下文里,方便后续 AI 层和业务层使用。我用的是请求头透传的方式,把底层判断结果编码后放在一个内部头里。

底层拦截的日志要详细记录,包括命中的规则 ID、请求特征、时间戳。这些日志是后续分析和规则优化的基础。我一般会把日志异步写到 Kafka,再由下游消费做实时分析和离线统计。

5.2 不确定请求的异步投递机制

底层判断为“不确定”的请求,需要投递到 AI 研判队列。投递机制我用的是本地内存队列 + 异步 worker 的模式。请求线程把研判任务放入无锁队列就返回,不阻塞主链路。后台 worker 线程从队列消费任务,批量投递到 AI 服务。

队列满了怎么办?这是个必须考虑的问题。我的策略是降级处理:队列满时,新任务直接标记为“待研判”并放行,同时记录降级日志。这样保证主链路不阻塞,代价是部分请求的研判被延迟。降级日志会触发告警,提醒运维扩容。

struct JudgmentQueue { queue: ArrayQueue<JudgmentTask>, max_size: usize, } impl JudgmentQueue { fn try_push(&self, task: JudgmentTask) -> bool { if self.queue.len() >= self.max_size { metrics::increment("judgment_queue_full"); return false; } self.queue.push(task).is_ok() } }

5.3 AI 研判结果的回写与处置

AI 研判完成后,结果需要回写到风控系统并触发相应处置。处置动作分几种:加入黑名单、封禁账号、发送告警、记录审计日志。处置动作的执行要幂等,因为同一请求可能被多次研判(比如重试场景)。

回写机制我用的是消息队列,AI 服务把研判结果发到 Kafka,风控系统的处置模块消费后执行处置。这样解耦了 AI 服务和处置逻辑,处置逻辑变更不影响 AI 服务。

处置动作的执行要有审计日志,记录谁在什么时候对什么对象执行了什么处置,依据是什么。这个审计日志在出问题时是排查的关键依据。我见过因为没有审计日志,误封了用户却找不到原因,最后只能全量解封的案例。

5.4 端到端延迟与吞吐量实测数据

整套链路跑下来,我实测的数据是这样的:底层硬拦截 P99 延迟 800 微秒,AI 小模型粗判 P99 延迟 35ms,AI 大模型精判 P99 延迟 280ms。端到端来看,明确放行的请求延迟就是底层延迟,约 1ms;需要 AI 研判的请求,如果只走小模型,端到端延迟约 40ms;如果走大模型,端到端延迟约 300ms。

吞吐量方面,单机底层能扛 30 万 QPS,AI 小模型服务单卡能扛 5000 QPS,大模型服务单卡能扛 200 QPS。实际部署时按业务量做水平扩展,底层无状态可以随便扩,AI 层受 GPU 资源限制需要提前规划容量。

这套数据是在特定硬件和请求分布下测的,你的实际数据会有差异。关键是理解各层的性能量级,做好容量规划。

6. 常见问题与排查技巧实录

6.1 底层误杀率突然升高的排查思路

底层误杀率突然升高,最常见的原因是规则更新引入了过严的规则。排查第一步是看误杀请求命中了哪条规则,如果集中在某条新规则上,那就是规则本身的问题。第二步是看误杀请求的特征分布,如果集中在某个 IP 段或某个 UA 上,可能是规则的模式匹配写得太宽泛。

我遇到过一次误杀率飙升,排查发现是新加的一条 UA 模式规则用了太短的关键词,把大量正常 UA 都匹配上了。解决办法是把关键词加长,并且加上边界条件。这个教训是:模式匹配规则一定要用足够长的特征串,短特征串的误伤率极高。

还有一种情况是共享状态出了问题,比如黑名单被错误地全量替换了。这种要看黑名单的更新日志,确认更新来源和内容。我一般会给黑名单更新加审批流程,避免误操作。

6.2 AI 研判延迟毛刺的定位方法

AI 研判延迟毛刺通常来自几个方面:GPU 资源竞争、批处理攒批超时、模型推理时的内存分配。定位方法是看延迟分布,如果毛刺是周期性的,可能是批处理攒批导致的;如果是随机的,可能是 GPU 竞争或内存分配。

批处理攒批导致的毛刺,可以通过调整攒批窗口来缓解。攒批窗口太长,延迟高;太短,吞吐低。我一般从 50ms 开始调,根据实际延迟和吞吐做权衡。

GPU 竞争导致的毛刺,要看 GPU 利用率和显存占用。如果 GPU 利用率已经打满,那就得加卡或者优化模型。显存占用高的话,可能是批大小设太大了,调小批大小能缓解。

内存分配导致的毛刺,在 Rust 里相对少见,但如果用了动态大小的输入,频繁的堆分配还是会有影响。解决办法是预分配缓冲区,复用内存。

6.3 两层判断结果不一致的处理策略

两层判断结果不一致是常态,底层说放行,AI 说高风险,这种怎么处理?我的策略是以 AI 层为准,但记录不一致日志。因为 AI 层的信息更丰富,判断更精细。不一致日志用来分析两层的判断差异,如果某类请求经常出现不一致,说明底层的规则需要调整,或者 AI 层的判断标准需要校准。

反过来,底层拦截了但 AI 判为安全的情况,我一般维持底层拦截。因为底层拦截是确定性的,AI 判断是概率性的,确定性判断的优先级更高。但如果这类不一致频繁出现,说明底层规则可能过严,需要 review。

不一致日志我建议保留至少 30 天,定期做分析。我一般每周看一次不一致率,如果超过 5% 就要深入排查原因。

6.4 高频问题速查表

问题现象可能原因排查方法解决措施
底层误杀率升高规则过严或模式匹配太宽查看命中规则和请求特征分布调整规则,加长特征串
AI 延迟毛刺批处理攒批或 GPU 竞争看延迟分布和 GPU 利用率调整攒批窗口或加卡
两层判断不一致规则与模型标准不匹配分析不一致日志校准规则或模型阈值
队列积压AI 服务处理能力不足看队列长度和消费速率扩容 AI 服务或降级
黑名单误封更新操作失误查黑名单更新日志加审批流程,回滚
模型准确率下降数据分布漂移对比新旧数据分布重新训练模型

6.5 几个我踩过的坑和独家经验

第一个坑是底层规则和 AI 模型用了不同的特征定义。比如底层判断“高频请求”用的是每分钟 50 次,AI 模型训练时用的却是每分钟 30 次,导致两层判断标准不一致。后来我统一了特征定义,所有层用同一套特征口径。

第二个坑是AI 模型的版本管理混乱。模型更新后没有记录版本,出问题回滚时找不到旧版本。后来我加了模型版本管理,每次更新都记录版本号、训练数据、评估指标,回滚时直接切版本。

第三个坑是底层规则的热更新没有做灰度。一次全量更新了一条规则,结果误杀率飙升。后来改成灰度发布,先放 1% 流量观察,没问题再逐步放大。

第四个坑是日志量太大导致存储成本失控。底层每次判断都打日志,一天几十亿条。后来改成只记录拦截和不确定的请求,放行的请求采样记录,日志量降了 90%。

提示:风控系统的日志要分级,拦截日志全量记录,放行日志采样记录,不确定日志全量记录。这样既保证排查有据可查,又控制存储成本。

7. 架构扩展与后续优化方向

7.1 从单机到分布式的演进路径

单机版跑通之后,下一步就是分布式。底层硬拦截是无状态的,水平扩展很简单,加机器就行。但共享状态(黑名单、计数器)需要从单机内存迁移到分布式存储,比如 Redis 或者自研的分布式缓存。

迁移过程中最大的挑战是一致性。单机内存里的黑名单更新是原子的,分布式环境下多个节点之间的黑名单同步有延迟。我的做法是用配置中心做统一推送,各节点订阅变更事件,收到后异步更新本地缓存。更新期间用双缓冲保证一致性。

计数器从单机分片迁移到 Redis 后,延迟会上升。我的优化是用本地缓存 + 定期同步的方式,热点 IP 的计数在本地累加,定期同步到 Redis。这样既保证了性能,又保证了全局一致性。

7.2 模型迭代与数据闭环的建立

AI 层的效果依赖持续迭代。我建立了一个数据闭环:线上研判结果 → 人工抽检 → 标注 → 加入训练集 → 重新训练 → 评估 → 上线。这个闭环每周跑一次,保证模型能跟上数据分布的变化。

人工抽检很关键,我一般按风险等级分层抽样,高风险全量抽检,中风险抽 10%,低风险抽 1%。抽检结果用来评估模型的准确率和召回率,同时发现新的风险模式。

新风险模式发现后,要快速补充标注数据并重新训练。我一般会维护一个“紧急样本池”,发现新风险模式后立即标注一批样本,用增量训练的方式快速更新模型。

7.3 成本控制与资源调度策略

AI 层的成本主要是 GPU 资源。控制成本的核心是提高 GPU 利用率。我做了几件事:一是批处理,把零散请求攒批推理;二是模型量化,用 INT8 量化把模型体积和计算量降下来;三是动态扩缩容,根据流量峰谷调整 GPU 实例数。

动态扩缩容我用的是基于队列长度的策略:队列长度超过阈值就扩容,低于阈值就缩容。扩容要快,缩容要慢,避免频繁抖动。我一般设置扩容阈值低一点,缩容阈值高一点,中间留一个缓冲区间。

底层硬拦截的成本主要是 CPU 和内存。优化方向是减少不必要的内存分配和锁竞争。我用jemalloc替换了默认分配器,内存分配性能提升明显。锁竞争方面,能无锁就无锁,不能无锁就分片,分片数根据实际竞争情况调整。

7.4 安全合规与审计要求

风控系统本身也要合规。我做了几件事:一是数据脱敏,研判用的文本数据在存储和传输时脱敏处理;二是访问控制,风控系统的操作权限严格管控,所有操作留审计日志;三是数据保留期限,研判数据保留 90 天后自动清理,审计日志保留一年。

审计日志要包含操作人、操作时间、操作对象、操作内容、操作依据。我一般用结构化的方式存储审计日志,方便查询和分析。审计日志的存储要独立于业务数据,避免被误删。

合规要求每个行业不一样,我建议在架构设计初期就把合规要求考虑进去,不要等上线了再补。补合规的代价远大于一开始就设计好。

8. 一些个人体会

这套双层架构我从第一版到现在,前后迭代了两年多。最大的体会是:架构设计没有银弹,只有权衡。底层硬拦截快但不够聪明,AI 研判聪明但慢且贵,两层结合是在快和聪明之间找平衡。这个平衡点随着业务发展会变化,所以架构要留出调整空间。

另一个体会是数据比算法重要。AI 层的效果,七分靠数据,三分靠模型。标注数据的质量、覆盖度、时效性,直接决定模型上限。我在数据标注上的投入,回报远大于在模型结构上的折腾。

最后说一个实操建议:先跑通最小闭环,再逐步优化。不要一上来就追求完美架构,先把底层硬拦截和 AI 研判的基本链路跑通,哪怕 AI 层先用简单的规则代替,也能验证架构的可行性。跑通之后再逐步替换和优化各层实现,这样风险可控,迭代也快。

这套架构后续还可以往几个方向扩展:一是引入在线学习,让模型能实时更新;二是做多模态研判,支持图片和视频内容;三是做跨场景的风控中台,把能力复用到更多业务线。这些方向我还在探索中,有新的进展再分享。

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询