2025 年下半年,我们团队为某股份制银行搭建了一套 实时反欺诈智能体系统,覆盖信用卡交易、网银登录、转账汇款三大场景。上线 6 个月数据:
| 指标 | 上线前 | 上线后 |
|---|---|---|
| 欺诈交易识别率(召回率) | 62% | 94% |
| 误报率 | 8.5% | 1.2% |
| 单笔决策耗时 | 1.2 秒 | 180 毫秒 |
| 月均挽回损失 | 约 800 万 | 约 3800 万 |
本文完整复盘从需求调研、架构设计、模型训练、生产部署到效果评估的全流程。
整体架构分四层:
// 用户近1小时交易金额(滑动窗口)
DataStream<Transaction> txStream = ...;
DataStream<UserFeatures> features = txStream
.keyBy(Transaction::getUserId)
.window(SlidingEventTimeWindows.of(
Time.hours(1), Time.minutes(5)))
.aggregate(new AmountAggregator());
// 特征写入 Redis(TTL 24h)
features.addSink(new RedisSink<>(...));
| 特征类别 | 具体特征 | 计算方式 |
|---|---|---|
| 行为特征 | 近1h/24h/7d 交易次数、金额 | 滑动窗口 |
| 设备特征 | 设备指纹、IP地理位置、是否新设备 | 规则 + 模型 |
| 关系特征 | 用户关联账户数、共享设备数 | 图查询 |
| 时序特征 | 交易时间分布异常度(深夜/凌晨占比) | 统计检验 |
| 历史特征 | 近30天被拒次数、申诉成功率 | 数仓查询 |
import xgboost as xgb
from sklearn.model_selection import train_test_split
# 加载标注数据(历史6个月已确认欺诈/正常交易)
X_train, X_test, y_train, y_test = train_test_split(
features, labels, test_size=0.2, stratify=labels
)
params = {
'objective': 'binary:logistic',
'eval_metric': 'auc',
'max_depth': 8,
'learning_rate': 0.05,
'subsample': 0.8,
'colsample_bytree': 0.8,
'scale_pos_weight': 50, # 欺诈样本稀疏
}
dtrain = xgb.DMatrix(X_train, label=y_train)
dtest = xgb.DMatrix(X_test, label=y_test)
model = xgb.train(
params, dtrain,
num_boost_round=500,
evals=[(dtest, 'test')],
early_stopping_rounds=30
)
# 评估指标:AUC 0.96,召回率 89%
print(model.best_score)
欺诈分子常采用"养卡-套现-跑路"链路,多账户共享设备/IP/收款方。用 GNN 挖掘这种关联:
import torch
import torch_geometric.nn as gnn
class FraudGNN(torch.nn.Module):
def __init__(self, in_dim, hidden_dim, out_dim):
super().__init__()
self.conv1 = gnn.GATConv(in_dim, hidden_dim, heads=4)
self.conv2 = gnn.GATConv(hidden_dim*4, out_dim)
def forward(self, x, edge_index):
# x: 节点特征 (用户交易行为)
# edge_index: 关联边 (共享设备/IP/收款方)
x = self.conv1(x, edge_index).relu()
x = self.conv2(x, edge_index)
return x
# 训练数据:200万节点(账户)、5000万边
# 模型 AUC 0.92,能识别出 78% 的已知欺诈团伙
对于 XGBoost 和 GNN 都给不出明确结论的"灰色交易"(0.3-0.6 区间),我们用 LLM Agent 做二次判断:
你是一位资深反欺诈分析师。请基于以下交易信息判断是否存在欺诈风险。
【交易基本信息】
- 交易时间:2026-06-02 03:42:15
- 交易金额:¥58,000
- 商户类型:数码产品
- 商户所在地:境外(具体国家待定)
【用户行为画像】
- 用户年龄:28岁
- 账户开通时长:3年
- 历史月均消费:¥2,500
- 历史最大单笔消费:¥15,000
- 设备:iPhone 15 Pro(常用设备)
- IP:广东省深圳市(常住地)
- 近7天交易次数:23次
【异常信号】
- 交易时间异常:是(凌晨3点)
- 金额异常:是(高于历史均值20倍)
- 设备指纹匹配:是
- IP地理位置:与历史一致
- GNN 关联评分:0.45(中等)
请输出:
1. 欺诈风险等级:低/中/高
2. 决策建议:通过/人工审核/拦截
3. 关键风险点(最多3点)
4. 客户后续可补充的核实问题
from langchain.agents import initialize_agent, Tool
from langchain_openai import ChatOpenAI
# 工具1:查询用户画像
def get_user_profile(user_id: str) -> str:
return redis_client.get(f"profile:{user_id}")
# 工具2:查询商户风险
def get_merchant_risk(merchant_id: str) -> str:
return requests.get(f"{RISK_API}/merchant/{merchant_id}").text
# 工具3:GNN 子图查询
def query_subgraph(user_id: str) -> str:
return neo4j_query(f"MATCH (u:User {{id: '{user_id}'}})-[*1..2]-(n) RETURN n")
# 工具4:相似案例检索
def search_similar_cases(features: dict) -> str:
return milvus_client.search(features, top_k=5)
tools = [
Tool("用户画像", get_user_profile),
Tool("商户风险", get_merchant_risk),
Tool("关联图谱", query_subgraph),
Tool("相似案例", search_similar_cases),
]
llm = ChatOpenAI(model="gpt-4o", temperature=0)
agent = initialize_agent(tools, llm, agent="zero-shot-react-description")
result = agent.run("请分析交易 TX202606020342 是否存在欺诈风险")
LLM Agent 在 0.3-0.6 灰色地带的判断 AUC 达到 0.87,超过了资深人工审核员的平均水平(0.81)。
# Kafka 消费
from kafka import KafkaConsumer
consumer = KafkaConsumer(
'transaction-events',
bootstrap_servers='kafka:9092',
group_id='risk-control-v2'
)
# Flink + Redis + XGBoost 模型服务 + LLM Agent
# 端到端 P99 延迟 < 200ms
for msg in consumer:
tx = parse(msg.value)
# 1. 实时特征查询
features = redis.get_features(tx.user_id)
# 2. XGBoost 快速打分
xgb_score = xgb_model.predict(features)
# 3. GNN 关联评分(异步)
gnn_score = gnn_service.score(tx.user_id)
# 4. 综合判断
final_score = 0.7 * xgb_score + 0.3 * gnn_score
# 5. 决策路由
if final_score > 0.95:
block_transaction(tx)
elif final_score > 0.6:
queue_for_human_review(tx)
elif final_score > 0.3:
# LLM Agent 二次判断
agent_decision = llm_agent.analyze(tx, features)
if agent_decision['risk'] == 'high':
block_transaction(tx)
else:
approve(tx)
| 指标 | 上线前(规则引擎) | 上线后(AI 系统) | 提升 |
|---|---|---|---|
| 欺诈交易召回率 | 62% | 94% | +32% |
| 误报率 | 8.5% | 1.2% | -86% |
| 单笔决策耗时 | 1.2s | 180ms | -85% |
| 人工审核工作量 | 120 人/天 | 25 人/天 | -79% |
| 月均挽回损失 | ¥800 万 | ¥3,800 万 | +375% |
案例 1:跨境信用卡套现团伙
系统识别出 23 张信用卡共享同一设备指纹和 IP 段,结合 GNN 关联分析,发现该团伙通过虚假跨境电商交易套现 870 万元,自动上报央行反诈中心。
案例 2:凌晨大额可疑转账
某客户凌晨 3 点在境外网站消费 58,000 元(超其历史均值 20 倍),LLM Agent 判定为高风险并拦截。客户后续确认是被盗刷,避免资金损失。
案例 3:伪装"熟人"的电信诈骗
系统识别出收款账户近 7 天新增大量小额转入(疑似洗钱),即使单笔金额不大(8000 元),仍触发 LLM Agent 审核,成功拦截 12 笔可疑转账。
从单一银行系统扩展到整个金融集团(银行+保险+证券):
# 集团级风控中台架构
class GroupRiskControl:
def __init__(self):
self.channel_router = ChannelRouter() # 渠道路由
self.unified_profile = UnifiedProfile() # 统一用户画像(集团级)
self.federated_learning = FederatedTrainer() # 联邦学习(合规)
async def evaluate(self, request: RiskRequest):
# 1. 跨渠道用户识别(同一身份证多账户)
user_id = await self.unified_profile.identify(
request.id_card, request.phone, request.device_fp
)
# 2. 集团级特征聚合
features = await self.unified_profile.aggregate(user_id, channels=[
'bank', 'insurance', 'securities'
])
# 3. 跨渠道欺诈模式识别
pattern_score = self.federated_model.predict_pattern(features)
# 4. 决策
if pattern_score > 0.8:
return RiskDecision.BLOCK_ALL_CHANNELS
elif pattern_score > 0.5:
return RiskDecision.MANUAL_REVIEW
# 5. 单渠道细化决策
return self.channel_router.route(request)
反欺诈图谱需要实时更新(新交易、新关联):
# Neo4j 实时更新(Cypher)
# 1. 新增交易关系
MERGE (from:Account {id: $from_id})
MERGE (to:Account {id: $to_id})
CREATE (from)-[r:TRANSFER {
amount: $amount,
timestamp: datetime(),
fraud_score: $score
}]->(to)
# 2. 共享设备检测
MATCH (a:Account)-[:USED_DEVICE]->(d:Device)
WITH d, collect(a) as accounts
WHERE size(accounts) >= 5
SET d:RISKY_DEVICE
WITH accounts, d
UNWIND accounts as acc
SET acc:RISKY_ACCOUNT
RETURN accounts, d
# 3. 子图风险传播
CALL gds.nodeSimilarity.stream(
'account-similarity',
{ topK: 10 }
)
YIELD node1, node2, similarity
WHERE similarity > 0.8
MATCH (a:Account {id: node1})
MATCH (b:Account {id: node2})
MERGE (a)-[r:SUSPICIOUS_LINK]->(b)
SET r.similarity = similarity
不同银行之间数据不互通,但可以联邦学习合作:
import secretflow as sf
# 多家银行联合训练反欺诈模型(数据不出行)
sf.init(['bank_a', 'bank_b', 'bank_c'])
# 各自的数据
bank_a_data = sf.PYU('bank_a')
bank_b_data = sf.PYU('bank_b')
bank_c_data = sf.PYU('bank_c')
# 联邦 XGBoost 训练
fed_xgb = sf.ml.boost.XGBoost(
num_boost_round=200,
max_depth=8,
learning_rate=0.05
)
# 各方数据不离开本地
model = fed_xgb.train(
data={
bank_a_data: train_data_a,
bank_b_data: train_data_b,
bank_c_data: train_data_c
},
label=label_col
)
# 效果:相比单方训练,AUC 提升 18%
# 隐私:原始数据从未离开各行
| 法规 | 要求 | 我们的实现 |
|---|---|---|
| 《个人信息保护法》 | 最小化原则 | 只采集风控必需字段 |
| 《数据安全法》 | 分级分类 | 交易数据 L4 加密存储 |
| 《网络安全法》 | 等保 2.0 三级 | 通过测评 |
| 《反洗钱法》 | 可疑交易报告 | 10 分钟内自动上报 |
| 《金融数据安全》 | JR/T 0171-2020 | 数据脱敏+加密传输 |
央行要求"模型决策可解释",我们用 SHAP + LLM 双层解释:
import shap
# 1. SHAP 特征重要性
explainer = shap.TreeExplainer(xgb_model)
shap_values = explainer.shap_values(features)
# 2. 构造解释文本
top_features = sorted(
zip(feature_names, shap_values[0]),
key=lambda x: abs(x[1]), reverse=True
)[:5]
# 3. LLM 生成业务级解释
from openai import OpenAI
client = OpenAI()
prompt = f"""基于以下反欺诈模型特征重要性分析,向客户经理解释:
{top_features}
要求:通俗易懂,重点突出,给出处理建议"""
explanation = client.chat.completions.create(
model="gpt-4o",
messages=[{"role": "user", "content": prompt}]
).choices[0].message.content
# 4. 输出给客户经理
print(explanation)
# 每日 18:00 自动生成 STR/CSR
# 1. 可疑交易报告(STR)
def generate_str(tx: Transaction, risk_score: float):
str_doc = {
"report_id": f"STR-{tx.id}",
"timestamp": datetime.now().isoformat(),
"transaction": {
"id": tx.id,
"amount": tx.amount,
"timestamp": tx.timestamp,
"from_account": tx.from_id,
"to_account": tx.to_id
},
"risk_assessment": {
"score": risk_score,
"model_version": "v2.3.1",
"features": tx.features,
"explanation": tx.explanation
},
"regulator_report": {
"pbc_endpoint": "https://str.pbc.gov.cn/api/v1/report",
"report_type": "STR",
"priority": "HIGH" if risk_score > 0.9 else "MEDIUM"
}
}
return str_doc
# 2. 自动上报
async def report_to_regulator(str_doc):
# 加密 + 签名
encrypted = sm4_encrypt(json.dumps(str_doc), regulator_pub_key)
signature = sm3_sign(encrypted, bank_priv_key)
# 通过专线通道上报
response = await regulator_api.post("/str/submit",
data=encrypted,
headers={"X-Signature": signature}
)
return response.status
需要完整的源码 + 部署文档 + 模型训练数据样例?
📱 加微信 toukenai 获取项目咨询