如果让我重新选一次实训题目,我大概率还是会选“诈骗克星”。这个项目名字听起来很“大”,但真正立项之后你会发现,它解决的不是一个技术问题,而是一连串交织在一起的人性问题、数据问题和工程取舍问题。从山东大学创新实训开始到现在,我们团队已经跑了快三周,踩过的坑比预想中多得多,但思路也越来越清晰。这篇博文是“诈骗克星”项目记录的第一篇,我会把选题时的纠结、需求边界的确定、技术选型的判断、样本库的搭建,以及第一个可运行Demo的完整过程写下来。如果你也在做安全方向、风控方向或者NLP方向的学生项目,这篇文章应该能帮你省下不少试错时间。
1. 为什么实训会选“反诈”这个方向,而不是再做一套管理系统
每年创新实训,最不缺的就是管理系统、电商网站、校园社交App。不是说这些题目不好,而是它们大多数停留在“功能堆叠”的层面:用户表、商品表、订单表、权限控制,换一个场景,代码结构几乎可以原样搬走。我反感这种低水平重复,所以选题阶段就刻意避开了“又一个CRUD”的方向,想找一个有真实建模难度、也有社会价值的题目。
“诈骗克星”最初只是我随手写下的一个提名,理由是这几年的诈骗案例和防范宣传越来越多,但落到个人终端上的主动识别工具其实很少。大多数人接到诈骗电话、收到钓鱼短信,靠的是“感觉不对劲”或者事后才醒悟,缺少一条明确的判断链路。如果把这件事做成一个工具,它应该能回答三个问题:这条消息可不可疑、可疑点在哪、用户下一步该怎么做。
再往深处想,这个题目其实天然包含了算法和工程两个层面的挑战。算法层面,诈骗话术的变体极多,同一套刷单骗局可能换十种说法,句式和关键词都在变,简单的敏感词拦截很难覆盖;工程层面,识别出来之后怎么提示用户、怎么记录证据、怎么避免“狼来了”效应,都直接影响产品的可用性。这正好满足我对实训项目“既要有学术味道,又要有落地可能性”的预期。
还有一个很现实的动机:在校园场景里,大学生本身就是一个容易被诈骗话术盯上的群体。冒充客服退款、刷单返利、注销校园贷,几乎每一个细分场景都能在我们的社交群里找到真实案例。把实训课题做在身边的高频风险场景上,意味着我们后面找样本、做访谈、做验证都会方便很多,不用凭空想象用户需求。
所以最终确定下来:做一个面向个人用户的诈骗信息识别与提示工具,名字就叫“诈骗克星”。第一期实训目标不是做一个功能完整的产品,而是先把“文本诈骗信息识别”这个核心环节跑通,验证技术可行性。这个定位在后面帮了大忙,它让我们不至于被“App怎么做”“服务器怎么部署”这些外围问题拖住,把精力集中到最核心的算法验证上。
需要模型API调用? 免费领10W Token,多模型网关一键接入 Claude、DeepSeek 等主流模型。
2. 目标锁定的策略:先判定“是不是诈骗”,再划分具体类型
立项之后的第一件事不是写代码,而是给“诈骗”这件事做一个可执行的定义。互联网上的风险信息形形色色,应用商店里也有很多“反诈App”把功能铺得很宽:来电拦截、短信拦截、App检测、风险网址库、举报入口……全都想做,结果每个模块都做得不深。我们只有三个月的实训周期,不可能面面俱到,所以必须找到第一步的最小闭环。
2.1 先想清楚服务谁、承载在哪个渠道上
“诈骗克星”服务谁?我们没想做全人群,而是聚焦两个群体:大学生和刚刚接触智能手机的中老年人。大学生的优势是愿意尝鲜、反馈快,中老年人则是诈骗受害的重灾区,两者叠加能覆盖足够多的典型场景。
渠道上,我们一开始列了短信、电话、社交软件、浏览器四个入口,后来砍到只剩短信和社交软件文本。为什么砍掉电话?因为电话音频识别涉及语音转写、实时流处理、手机权限等一系列复杂问题,作为实训第一阶段的验证目标太重了。浏览器网址检测也有专门的威胁情报服务在做,我们短期做不出差异化。留下来的两个渠道本质上都是“文本输入”,可以共用一套识别引擎,这是最小化成本之后的自然选择。
2.2 二分类优先,多分类滞后
技术路线上,我们踩了一个常见的坑:刚开始就想做一个多分类模型,把诈骗信息细分为刷单返利、冒充客服、冒充熟人、虚假投资、虚假贷款、杀猪盘等十几个类别。后来发现,训练数据里某些类别样本极少,而且各种诈骗话术存在高度重合——刷单诈骗会冒充客服,投资诈骗也会先做“关怀”铺垫。多分类必然导致模型在少数类别上表现稀烂。
后来我们调整策略,把问题拆成两层:
- 第一层:二分类,判断“这条文本是否涉诈”;
- 第二层:在判定涉诈之后,再基于规则和相似度做“诈骗类型提示”,注意这里说的是提示,不是严格分类。
这样做的直接好处是,识别引擎的核心指标只有两个——召回率和误报率,模型目标清晰,评估起来不模糊。类型判断沦为一个辅助功能,就算它偶尔不准,也不会影响用户对整体风险等级的信任。后面的事实证明,这个设计决策帮我们在Demo阶段省了大量调参的精力。
2.3 用户故事驱动的功能边界
需求不能停留在“我们要做一个反诈工具”这种口号上,我们写了四个具体用户故事来约束功能范围:
- 用户A收到“亲,你的快递丢失,加客服QQ办理双倍理赔”的短信,工具识别并给出“冒充电商客服”提示;
- 用户B在聊天群里看到“兼职刷单,一单一结,日入500”的消息,工具标红并弹出风险提示;
- 用户C收到冒充公检法的电话,因为工具尚未覆盖语音,则提供一个手动查询入口,引导用户粘贴通话转写的文字或关键词;
- 用户D是推广运营角色,希望后台能看到诈骗样本的分布和识别命中情况,方便运营人员做数据分析。
这四个故事能落地,我们的第一版产品就成立。至于“自动拦截诈骗电话”“实时检测App风险”等内容,全部放进远期计划,不碰。把边界划清楚之后,团队每个人都知道自己这周该干嘛,这比任何项目管理工具都管用。
3. 技术路线取舍:规则引擎与语义模型两只脚走路
技术选型是这阶段最耗时间的部分。我一开始也犯了“模型崇拜”的毛病——觉得反诈识别这种自然语言理解任务,必须上BERT,甚至上大语言模型微调,才能显得有技术含量。但做了几次小实验后,我承认自己想多了。在数据量只有几千条的情况下,复杂模型和轻量方法之间的差距并不大,而工程复杂度却差了一个量级。
3.1 规则引擎:解释性就是最大的优势
规则引擎是我们最先做的模块,也毫不犹豫地留下了。它的逻辑很简单:对文本做分词和关键词匹配,命中预设的诈骗特征词库就累加风险分。比如“刷单”“做任务”“连单”“垫付”这类词只要出现两个以上,风险等级直接上调。
为什么必须保留规则引擎?因为解释性。当模型判定一条消息是诈骗时,用户第一反应一定是“凭什么”。规则引擎可以明确告诉用户:这条消息中出现了哪几个涉诈关键词、匹配到了哪个典型诈骗场景,用户能一眼看懂判断依据。这是一个面向普通用户的反诈工具不能被牺牲掉的特性。
另外,规则引擎的部署成本极低,不需要GPU,不需要向量化服务,一个嵌入式数据库就能跑起来。对于V1版本足够用了。
3.2 语义模型:对付“不说人话的骗子”
规则引擎可以对付那些直接使用诈骗黑话的消息,但骗子也在进化。现在的诈骗话术经常绕开敏感词,比如不直接说“刷单”,而是说“帮商家提升流量,完成后返红包”,不直接说“转账”,而是说“去京东买一张充值卡,给我卡号和密码就行”。这类文本没有明显的触发词,规则匹配很容易漏掉。
这就必须上语义模型。我们的方案是两条路并行:
- 一是基于预训练句向量模型,把输入文本转成向量,和已有诈骗样本做相似度检索,相似度超过阈值就认定为风险;
- 二是训练一个轻量文本分类器,用TF-IDF或者FastText做特征,和规则引擎的结果做加权融合。
我们真正跑通的是第二条路,因为它是确定性输出,模型文件小,推理速度快,适合嵌入到移动端。第一条路作为兜底方案,用来发现那些分布外的新式骗局。句向量模型我们选了中文效果比较稳定的多语言SentenceTransformer,虽然体积稍微大一点,但胜在语义对齐能力强,后面可以做欺诈话术的“家族聚类”,识别同一个骗术的多种变体。
3.3 技术栈和部署形态
关于形态,我们最终做了一个带后端API的轻量服务,而不是纯本地SDK。原因是希望收集匿名反馈和误报数据,方便后续迭代模型。后端用Python FastAPI,数据库用SQLite起步,算法模块单独拆成一个Python包,方便以后单独部署到服务器或者打包成SDK。
前端暂时不投入太重,先做一个简单网页接口和一个微信小程序原型。V1阶段核心是“能跑通一条完整的识别链路”,前端越简单越好,能用就行。实训结束前,如果我们有余力,再把本地缓存和隐私保护逻辑做进移动端。
这套技术路线,我最大的心得是:不要一开始就迷信某个技术,而是让技术方案跟着用户故事走。用户要解释性,就保留规则引擎;用户要泛化能力,就引入语义模型;团队要快速迭代,就选最顺手的轻量框架。技术选型的评价标准只有一个——它是否让项目更快逼近核心目标。
4. 从零攒数据:诈骗场景树和样本库的具体设计
做任何算法模型,数据都是第一关。反诈项目尤其头疼,因为真正的诈骗样本是敏感数据,不可能公开下载,也没有现成的数据集等着你。合规的第一原则是:绝不使用真实用户的隐私对话记录来训练模型,我们要找的是诈骗话术的“模板”和“公开案例”,而不是某个受害者的聊天记录。
4.1 场景树的搭建:按“骗术结构”而不是“诈骗名义”分类
我们最初参考网络上的公开诈骗分类,按电信诈骗、网络刷单、虚假投资这些口径去整理,结果发现这些分类来自公安通报和媒体报道,粒度太粗,不适合建模。比如“冒充公检法”和“冒充老板”本质上是同一个结构——冒充权威身份制造恐慌,只是身份外壳不同。
后来我们干脆自建了一棵“诈骗场景树”,根节点按人群接触点分:短信触达、社交触达、电话触达、网址触达。每个分支下再按话术结构拆,比如短信触达下有“冒充客服理赔”“虚假中奖”“冒充银行升级”“ETC过期”等。这棵树不是为了分类好看,而是为了让标注人员知道“这条样本该归到哪个文件夹”,也让模型特征更有辨识度。
4.2 样本来源与清洗流程
样本来源主要有三个:
- 公安和媒体公开通报的诈骗案例,里面通常会附上诈骗话术的原文或转述,这是最权威的来源;
- 小区电梯、学校公告栏、银行门口摆放的反诈宣传册,上面写的警示案例也是很好的语料素材;
- 论坛和社交媒体上网友自发的“防骗曝光帖”,这些内容需要我们手动脱敏,去掉联系方式、账号等个人信息后保存。
整个清洗流程分为四步:去重、打码、纠错、分级。去重不是简单看文本一样不一样,而是做一次相似度粗排,把同一套话术的变体聚在一起;打码是把手机号、银行卡、微信号等实体替换成占位符;纠错主要是处理从图片转文字时的乱码;分级是从危险性角度给样本标注“高危险”“中危险”“低危险/疑似”,这个分级会直接影响训练时的样本权重。
4.3 样本库的结构化设计
样本库不是一堆txt文件堆在一起,我们用了SQLite + CSV双轨制。CSV方便做模型训练时的数据读取,SQLite用来记录样本的来源、标签、清洗状态和标注人。核心表结构大致如下:
| 字段 | 说明 |
|---|---|
| sample_id | 样本唯一ID |
| text | 清洗后的诈骗文本 |
| scenario | 所属诈骗场景(对应场景树叶子节点) |
| risk_level | 高/中/低风险标注 |
| source | 来源类型:public_report / forum / manual |
| labeler | 标注人昵称 |
| label_time | 标注时间 |
| is_valid | 是否通过质量校验 |
标注工作我们自己人扛,但标注质量参差不齐。所以我们加了一道“双人复核”机制:每一条样本至少两个人独立打标签,不一致的进入争议池,由管理员仲裁。虽然慢,但能保证前期的种子数据质量。快不了,这是枯燥但必须做的工作。
三周下来,我们攒了约2600条高质量文本样本,分布在七个主要场景里,距离目标5000条还差一点。数据量不算多,但在实训项目里够用了。更重要的收获是整个清洗和标注流程跑顺了,后面扩展样本只是时间问题。
5. 第一个可运行Demo:短信风险打分链路的实战落地
Demo是实训第三周周五跑通的。这一节我完整记录一下当时的技术实现,包括代码、参数和经验教训。这条链路的目标很直接:输入一段短信文本,输出一个0到100的风险分,以及命中的风险点列表。
5.1 规则引擎的实现
规则引擎我们用了最顺手的方式:Python字典 + 正则 + jieba分词,没有任何重型依赖。代码看起来很简单,但简单不等于简陋,它的优势是可调试、可解释。
python复制import re
FRAUD_RULES = {
"刷单返利": [
r"刷单", r"刷流水", r"做任务", r"日结", r"返佣",
r"垫付", r"连单", r"居家办公.*日赚", r"动动手指"
],
"冒充客服": [
r"退款", r"理赔", r"会员注销", r"白条", r"屏幕共享",
r"加.*客服.*qq", r"交易异常.*处理", r"临时额度"
],
"冒充公检法": [
r"安全账户", r"通缉令", r"涉嫌洗钱", r"协查通知",
r"资金清查", r"冻结.*银行卡"
],
"虚假投资": [
r"内幕消息", r"稳赚不赔", r"开户入金", r"虚拟币.",
r"荐股群", r"打新.*名额"
]
}
def rule_score(text: str) -> dict:
hits = []
score = 0
for scam_type, patterns in FRAUD_RULES.items():
for pattern in patterns:
if re.search(pattern, text):
hits.append({
"type": scam_type,
"matched": pattern,
})
score += 20
return {"score": min(score, 80), "hits": hits}
每条规则命中加20分,上限80,剩下的20分空间留给语义模型去补。为什么上限设80而不是100?这是刻意的:规则引擎永远不给出“绝对安全”或“绝对危险”的结论,留一部分不确定空间给其他模块兜底,避免规则误伤导致用户对工具失去信任。
5.2 语义分类器的训练
规则引擎跑通之后,我们开始训练语义模型。第一版用了TF-IDF + 逻辑回归,原因很简单:没有标注失误的悬念,训练速度快,而且能在不同特征维度上给出概率输出。模型结构如下:
python复制from sklearn.feature_extraction.text import TfidfVectorizer
from sklearn.linear_model import LogisticRegression
from sklearn.pipeline import Pipeline
clf_pipeline = Pipeline([
("tfidf", TfidfVectorizer(
max_features=5000,
ngram_range=(1, 2),
analyzer="char_wb",
min_df=2
)),
("clf", LogisticRegression(
C=1.2,
class_weight="balanced",
max_iter=500
))
])
clf_pipeline.fit(X_train, y_train)
这里有个容易被忽略的细节:analyzer我用了char_wb而不是默认的word。中文分词在短文本场景下容易产生边界错误,比如“退款”被分成“退”和“款”后完全失去语义,而字符级别的char_wb配合bigram可以捕捉到“退款”“理赔”“验证码”这类常见的二字词组合,在短文本分类任务上稳定性更好。
训练集是我们自己标好的2600条样本,再加上500条正常的营销短信、话费账单通知等作为负样本。负样本数量偏少,所以用了class_weight="balanced"让模型更关注少数类别。
5.3 融合决策逻辑
最后我们把规则引擎和语义模型的结果做一个轻量融合。融合逻辑不搞复杂的概率校准,就用一个线性加权:
python复制def merge_score(rule_result, cls_prob):
risk_score = rule_result["score"] * 0.6 + cls_prob * 0.4
if risk_score >= 70:
level = "高危"
elif risk_score >= 40:
level = "可疑"
else:
level = "低危"
return risk_score, level
规则引擎权重占60%,语义模型占40%。为什么规则权重高?因为在诈骗识别场景里,“精确命中已知模式”比“泛化猜测未知模式”更可靠,用户对语义模型的“我觉得像”并不买账,而规则命中的每一个关键词都能解释。随着后面负样本积累,我们会逐渐调高模型权重。
5.4 一次真实测试的效果
我拿了一条非常典型的刷单短信做测试,原文是:“亲,在吗?我们正在招聘兼职刷单手,一单可以赚15-80元,不用押金,不用垫付,加客服QQxxxx详聊。”规则引擎命中了“刷单”“垫付”“加客服QQ”三个规则,得60分;语义模型给出的风险概率是0.87,加权后最终风险分约70.8分,判为“高危”。
输出结果在Debug界面上显示了命中规则列表,包括“反诈提示:该文本出现刷单返利相关话术,请勿相信任何要求垫资的兼职信息”。用户看到这个提示,至少会犹豫一下,而“犹豫一下”可能就是避免被骗的关键一秒。
这一整套链路从无到有用了大概五天时间,比预期快,主要原因是我们把问题边界收得很窄——只做短信和社交软件文本的二分类,不做语音、不做实时拦截。Demo跑通的那一刻,团队最大的感受不是“我们多厉害”,而是“规则和模型的配合比预想中顺畅”。
6. 误报与漏报的博弈:第一批评估结果让我清醒
Demo能跑是一回事,跑得可靠是另一回事。我们拿着团队七个人近一个月收到的各类短信,凑了500条正常文本,加上之前留出的200条涉诈文本,做了一个小规模离线评测。评测结果让人清醒:整体准确率接近87%,看着不错,但拆开看问题很明显。
6.1 最难受的是正常短信被误报
有几个典型的误报案例。比如一条物业通知:“您好,您的水费账单已出,请及时登录物业管理APP查看,客服电话xxxx。”规则引擎把“客服电话”匹配成了“冒充客服”特征,直接加了20分。再比如:“您的快递已到东门驿站,取件码1234,超时将收取保管费。”这条短信居然被语义模型判成了48分的“可疑”,可能因为出现了大量数字和“取件”这类和客服场景相近的词汇。
误报的危害在反诈场景里非常致命。用户被误报“狼来了”三次之后,真正的高危诈骗提示也会被无视。我们给第一篇测试取了个外号,叫“召回率自信、精度滑坡”:涉诈样本召回率做到92%,但正常文本里误报率接近13%。在安全场景里,这个误报率太高了。
6.2 漏报的深层原因:场景外话术
漏报案例同样值得分析。团队小张贡献了一个真实案例:他妈妈收到一条“您的电子医保卡即将停用,请在24小时内打开xxxx网址更新”的短信。我们的规则库没有任何医保场景的规则,语义模型没见过类似样本,结果文本被判成“低危”。这条短信其实是典型的钓鱼链接诈骗。
漏报让我们意识到一件事:涉诈文本的分布是开放的,骗子的创意几乎没有上限,而我们的样本库和规则库是封闭的。我们不可能枚举所有诈骗场景,只能建立一套“新场景发现机制”——比如把语义相似度高的未知文本聚类在一起,定期由人工审核加入规则库,让系统具备自我进化能力。这个机制目前还在开发中,但方向是对的。
6.3 权衡策略:宁可少报,不可误报
综合讨论后,团队决定把V1阶段的产品策略定为“宁可少报,不可误报”。逻辑很简单:一个提示工具,用户价值来自于“让用户认真看待提示”。如果提示频繁失效,工具就失去了存在意义。提高误报门槛后,我们把风险等级阈值从40分上调到50分,同时优化了关键词匹配逻辑——像“客服电话”这种高泛化词,必须同时出现“退款/理赔/注销”等强信号词才能触发提醒,单靠它本身不再加分。
这个决策牺牲了一部分召回率,但换来了用户信任,对实训项目来说,它是一个更正确的取舍。
7. 阶段复盘和下一期计划
到这里,“诈骗克星”第一阶段的实训目标算是初步达成。我们确认了一件事:在有限数据条件下,用规则引擎加轻量语义分类器来识别诈骗文本,是一条完全走得通的技术路线。团队的工程协作、数据标注流程也基本跑顺了,这是比模型效果更重要的资产。
下一阶段我计划做三件事:
- 第一,把样本库扩展到5000条,尤其是补齐“虚假投资”“色情诱导”等目前样本不足的场景,让语义模型在更多类别上有泛化能力;
- 第二,完成新场景发现机制,用句向量对未命中的“低危但异常”文本做离线聚类,每周人工审核一次,把新骗术以最快速度补充进规则库;
- 第三,把API封装得更完善,配合前端同学做一个小程序页面,让工具能被真实用户用起来,收集更多的反馈数据。
此外,团队讨论后倾向于把“诈骗类型提示”做成插件化,将来接入语音转写模块时可以直接复用现有的文本识别引擎,不用重新设计架构。
这次实训还让我想明白一个道理:做工具型产品,最难的不是“把功能做出来”,而是“在有限条件下作出正确的取舍”。砍掉电话识别很痛,但换来了Demo的如期跑通;牺牲一部分召回率也很痛,但保住了用户信任的基本盘。项目的每一步推进,本质上都是一连串“愿意放弃什么”的决策。
“诈骗克星”的下一步进展,我会继续在这个系列里记录下来。如果你也在做反诈相关或者风控方向的项目,欢迎多交流,尤其是数据样本和误报处理这两个环节,我特别想听听大家的做法。
