评论区被广告机器人刷屏、用户互怼骂战、半夜三更大量灌水留言——这些问题做社区和内容产品的人几乎都遇到过。人工审核要么慢,要么漏,招两个审核专员三班倒也扛不住活动期间的评论洪峰。Spring Boot项目里接一套AI智能评论审核,再配套自动回复机制,是现阶段性价比很高的解法。这篇文章基于我在某电商社区项目里的实操经验,把整体设计、实现思路和踩坑点都过一遍,重点说清楚怎么用Spring Boot接AI服务、怎么做规则和模型的双层审核、怎么让自动回复不显得呆板,以及一整套异步和高可用的工程化处理。
1. 为什么要做AI评论审核与自动回复
先聊需求背景。真实生产环境里,评论内容几类问题最突出:一是营销广告类,商家和个人号在评论区刷微信号、外链、优惠券口令;二是攻击辱骂类,用户之间吵起来,一整页全是人身攻击;三是恶意灌水类,短时间批量发无意义字符或大量重复内容;四是擦边内容,不直接违规,但明显在引导用户去站外交易。单纯靠敏感词黑名单能挡住一部分,但广告文案每天都在变花样,“微❤”代替“微信”、“v❤”用谐音绕过去,规则写得再细也追不上。
自动回复的需求是另一维度的。用户留言问“怎么退款”“物流到哪了”“这个活动还能参加吗”,七十二小时内没人回,用户就流失了。我们做审核系统的同时,把“审核通过”和“自动回复”联动起来——合规评论不直接对用户展示,先走一遍意图识别,命中高频问题就直接回复答案;没命中就先展示,再由人工跟进。这套联动架构比单纯做个“敏感词拦截器”信息密度高得多。
实际开发中我采用了一个基本原则:不指望单一方案解决所有问题。规则引擎做第一道闸门,AI语义模型做第二道深度审核,自动回复作为审核通过后的增值环节。多层嵌套,每层做好自己那一档事,整体可靠性才有保证。
2. 方案选型与整体架构设计
方案设计阶段,最关键的选择有三个:模型接口用什么、规则和AI怎么分工、审核与回复的触发流程长什么样。这些选型直接决定系统上限和后续维护成本。
2.1 规则优先还是模型优先
早期我试过纯模型方案——所有评论直接调大模型API打分。效果整体不错,但有两个问题:第一,费用太高,每天几万条评论全量调API,按条计费,一个月账单相当难看;第二,响应延迟不稳定,大模型接口偶尔两秒、偶尔五秒,用户在等回复时体验很差。后面改为“规则前筛、模型精审”的混合架构。
具体分工是:评论先过本地规则层,包括敏感词库、URL链接检测、连续重复字符合并检测、发送频率限制。规则层直接打回确定违规的垃圾评论,这类评论的特征极其明显,模型判断它们反而浪费额度。规则层判定“疑似”或“无法确定”的评论,才进入AI模型层做语义分析。这样模型调用量总体降到原来的两三成,费用和延迟都大幅下降。
另外还有一个容易被忽视的点:规则和模型的判断结果要做置信度分层。我设计了三个出口状态——直接拦截、疑似犯规、正常通过。直接拦截的是规则层百分百命中;疑似犯规的展示给管理员人工复审;正常通过的进入自动回复和前台展示。这种“三态”设计让人机协作非常灵活,既不会全自动误伤正常用户,也不会把模型搞不定的模糊内容硬推到线上。
2.2 自动回复的技术路线
自动回复我同样做了两级设计。
第一级是意图模板匹配。用户评论往往很短,比如“怎么退款”“退款流程是什么”“怎么申请退款”,这三种表达其实指向同一个意图。我用关键词组合+正则模板的方式,把高频意图预置好,每个意图关联回复模板。命中模板的直接回复,零模型调用成本,实时性也最好。
第二级是基于大模型的智能生成。模板没命中的长尾问题,交给大模型基于商品/文章的资料生成个性化回复。这一步要注意的是不能把原文直接丢给大模型让它“自由发挥”然后就把回复发给用户。我自家踩过坑,模型回复虽然语句通顺但可能给出完全不准确的规则解读,比如把退款期限说错。最终我限制模型只能基于给定的知识内容组织回复语言,不允许瞎编事实。知识内容由业务方提供,维护在配置中心里。
2.3 技术栈选型与依赖清单
整体基于Spring Boot 2.x搭建,核心依赖如下表:
| 组件 | 用途 | 说明 |
|---|---|---|
| Spring Boot Web | 提供REST接口 | 评论提交入口、审核结果查询 |
| Redis | 缓存 + 限流 | 缓存审核结果、防止重复消费 |
| 异步线程池 | 审核计算隔离 | 模型调用和回复生成不阻塞主流程 |
| OkHttp或WebClient | 调用AI API | 统一HTTP客户端连接池 |
| XXL-Job或Spring Schedule | 定时扫描 | 处理审核超时任务和模型状态探测 |
| MySQL/HBase | 评论存储 | 落库并记录审核状态流转 |
异步处理是这套系统的灵魂。用户提交评论后,接口立刻返回“评论已收到,审核中”,真正审核在后台线程池里跑。前端根据审核状态轮询或推送结果。这个设计避免了同步调AI带来的超时和主线程阻塞问题,也是高并发场景下的保命招。
3. 评论实体与审核状态机设计
状态机是这套系统最容易做乱的地方。我这里花了比较多的时间在设计评审上,最终用了一套七状态模型,看起来复杂,但每个状态对应一个明确出口,逻辑非常清晰。
3.1 评论状态定义
核心状态如下:
- PENDING:待审核,评论刚落库
- REJECTED:规则层直接拦截,不进模型
- SUSPENDED:疑似违规,进入人工复审队列
- APPROVED:模型判定通过
- AUTO_REPLIED:已自动回复
- MANUAL_REPLIED:人工回复
- INVALID:申诉后被标记为失效
为什么需要这么细分?因为运营要数据分析——某个活动来了五千条评论,多少被机器拦截、多少需要人工看、自动回复覆盖了多少。只有状态流完整记录,这些数字才分得出来。之前我看过不少项目只用一个is_deleted字段打天下,审到后面全是糊涂账。
3.2 实体类与状态流转代码实现
直接看代码,评论实体我这样设计:
@Entity @Table(name = "t_comment") public class Comment { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; @Column(nullable = false) private Long userId; @Column(nullable = false, length = 3000) private String content; @Column(nullable = false) private Long targetId; // 关联的商品/文章ID @Column(nullable = false) private Integer targetType; // 1-商品 2-文章 3-动态 @Column(nullable = false) private Integer status; // 对应上述状态枚举的code @Column private String rejectReason; // 拦截原因,如“命中广告规则” @Column private Integer riskScore; // 模型返回的违规分数 0-100 @Column private Integer replyStatus; // 0-未回复 1-已自动回复 2-已人工回复 @Column private String replyContent; // 最终回复内容 private LocalDateTime createdAt; private LocalDateTime auditedAt; private LocalDateTime repliedAt; }状态机流转的核心逻辑我写在一个独立的Service方法里,方便复用:
@Service public class CommentReviewStateMachine { public Comment transition(Comment comment, CommentEvent event) { CommentStatus current = CommentStatus.of(comment.getStatus()); switch (current) { case PENDING: if (event == CommentEvent.RULE_REJECT) { comment.setStatus(CommentStatus.REJECTED.getCode()); comment.setRejectReason(event.getMessage()); } else if (event == CommentEvent.MODEL_PASS) { comment.setStatus(CommentStatus.APPROVED.getCode()); comment.setRiskScore(0); } else if (event == CommentEvent.MODEL_SUSPEND) { comment.setStatus(CommentStatus.SUSPENDED.getCode()); comment.setRiskScore(60); } break; case SUSPENDED: if (event == CommentEvent.STAFF_APPROVE) { comment.setStatus(CommentStatus.APPROVED.getCode()); } else if (event == CommentEvent.STAFF_REJECT) { comment.setStatus(CommentStatus.REJECTED.getCode()); } break; // 其他状态流转省略 } return comment; } }这样设计的好处是,无论审核请求来自规则层、模型层还是人工后台,都走同一个状态机入口,不会出现某个分支漏更新状态的问题。
3.3 审核结果的持久化与冗余字段
经验之谈:审核结果一定要尽量多的冗余,不能只存一个状态。我每个评论都额外存了riskScore和rejectReason,方便你后面做数据分析和模型效果评估。比如你调了一版提示词,想知道误判率是升了还是降了,没有这些历史字段根本查不了。
另外,针对同一用户多次提交相似评论的场景,我用Redis建了一个指纹缓存。评论内容做归一化(去空格、转小写、繁体转简体)后MD5取指纹,24小时内有相同指纹的评论直接沿用上次审核结果。这个逻辑虽然简单,但能把模型调用量再压下去很大一截,尤其是套路化的广告评论,往往几分钟内就会以同样的文案刷一大批。
4. 双层审核核心实现
规则和模型的组合,是整个系统的核心。我单独讲讲每一层怎么写。
4.1 规则层:敏感词、频率与垃圾文本识别
规则层我重点做了三件事。
第一件是敏感词库的多级管理。词库分两个等级:一级词命中直接拦截,这类词基本没有争议;二级词命中标记为疑似,交给模型判断。词库支持热更新,运营后台改配置后无需重启服务。用数据库表+本地缓存的方式,而不是写死在配置文件里。
敏感词匹配我用的是DFA算法。这个算法本质上就是构建一个状态转移树,把需要匹配的词一次性读入,扫描评论时每个字符最多跟着状态树走一步,复杂度O(n)。在几万词库规模下,一秒钟处理几千条评论没有问题。这里贴一下匹配核心代码:
public class DfaSensitiveMatcher { private final Map<Character, Map> rootNode = new HashMap<>(); public synchronized void addWord(String word) { Map current = rootNode; for (char c : word.toCharArray()) { Map child = (Map) current.computeIfAbsent(c, k -> new HashMap()); current = child; } current.put("end", true); // 标记词尾 } public List<String> match(String text) { List<String> hits = new ArrayList<>(); for (int i = 0; i < text.length(); i++) { Map current = rootNode; int j = i; StringBuilder sb = new StringBuilder(); while (j < text.length() && current.get(text.charAt(j)) != null) { current = (Map) current.get(text.charAt(j)); sb.append(text.charAt(j)); j++; if (Boolean.TRUE.equals(current.get("end"))) { hits.add(sb.toString()); break; // 匹配到最短敏感词即返回 } } } return hits; } }上面这段代码有个注意点:敏感词匹配默认按“最短匹配”来,比如词库里同时有“赌博”和“赌博上线”,评论出现“赌博上线”时只会先命中“赌博”。后面我处理最长匹配还是最短匹配花了不少心思,最后结论是:业务上不需要覆盖所有组合,命中一个就足以触发人工复核,不必追求完美。
第二件事是URL和联系方式检测。我维护了一个超链接识别正则,http、子域名、短链口令都在覆盖范围内。广告评论区的一大特征就是大量外链跳转。评论里出现链接但目标域名不在白名单上时,直接标记为疑似广告。
第三件事是频率控制和重复度检测。拿到用户ID后,查询最近五分钟内该用户的评论条数,超过阈值就拦截并提示“评论太频繁”;用SimHash算法算评论相似度,和最近一小时相同用户发过的评论比对,重复度过高直接合并,判定为灌水。
4.2 模型层:提示词设计与JSON结构化输出
规则层过了之后,剩下的内容进模型。这里的核心不只是“调接口”,而是怎么设计提示词,让大模型输出稳定、可解析的判断结果。
我的提示词模板大概长这样:
你是社区内容安全审核员。请判断以下用户评论是否违规。 违规类型包括:恶意营销广告、人身攻击、色情低俗、违禁品交易、外部导流。 用户评论内容: [评论内容] 请严格按以下JSON格式返回,不要输出任何解释: { "riskType": "none | ad | attack | porn | illegal | guide", "riskScore": 0-100, "reason": "一句话说明判断依据" }这段提示词的几个细节值得注意:第一,明确枚举违规类型,让模型从有限的类别里做选择,而不是开放回答;第二,强调“不要输出任何解释”,模型才乖乖只给JSON,否则会附赠一堆废话,解析的时候很容易错位;第三,riskScore设置成0-100连续值,方便后续调阈值,比如score > 80直接拦截,score在50到80之间进人工,score低于50自动通过。
调用代码我用的是WebClient异步方式。这里贴一个简化版:
@Component public class AiReviewApiClient { private final WebClient webClient; public AiReviewApiClient(WebClient.Builder builder) { this.webClient = builder.baseUrl("https://your-ai-endpoint.example") .build(); } public Mono<ReviewResult> review(String content, Long commentId) { Map<String, Object> body = new HashMap<>(); body.put("prompt", buildPrompt(content)); body.put("temperature", 0.1); body.put("max_tokens", 200); return webClient.post() .uri("/v1/chat/completions") .bodyValue(body) .retrieve() .bodyToMono(String.class) .map(this::parseReviewResult) .timeout(Duration.ofSeconds(10)) .onErrorReturn(buildFallbackResult()); // 超时或异常返回“疑似人工审核” } }这里特别说一下温度参数temperature。我实测下来,审核任务要求的是稳定和可预期,温度必须设得非常低,0.1甚至0。温度越高模型输出的随机性越强,同样一段话这次打分80下次打分40,这种波动在生产环境里是灾难。自动回复场景可以稍微调高一点温度让措辞更自然,但审核场景一定用最低。
4.3 模型输出解析与兜底容错
模型输出解析是容易被忽略的坑。你让模型返回JSON,它偶尔就是会给你JSON前后加个```markdown标记或者解释性文字。我写了个容错解析方法:先用正则把最外层的花括号包裹内容提取出来,再走JSON解析;解析失败直接当“疑似违规”处理,进人工复审,绝不主动放行。这个“解析失败默认从严”的原则一定要守死。
另外就是超时。生产环境大模型接口的P99延迟经常在5秒以上,网络抖动时还可能冲到十几秒。我给WebClient设置了10秒超时,超时后返回一个最低置信度的“需人工复审”结果,而不是直接放行。宁可让运营多看一条,也不能把违规内容放上线。
执行顺序上是串行的:规则层跑完,是正常评论才发AI请求;规则层已经拦截的直接结束。这样一层闸门一层闸门往下收,收缩逻辑非常清晰。
4.4 自动回复的意图识别与模板设计
审核完的合规评论,会同时进入自动回复流程。自动回复我区分了两种情况。
一种是纯模板回复。比如“怎么退款”这套意图,模板关联的回复是“您好,退款请进入订单中心点击申请退款,审核通过后1-3个工作日原路退回哦”,这类回复固定、安全、无需动态生成,命中模板后立即返回。
意图识别不需要上多复杂模型,我用的是词典权重匹配:每个意图设定一组关键词,默认权重1,某些独特词权重2,命中权重加总后超过阈值就判定为该意图。比如“退款”2分,“退钱”2分,“怎么申请”1分,阈值3分,那么“怎么申请退款”就是4分,命中意图。
意图判定代码我用一个简单的规则引擎实现:
@Service public class IntentMatcher { private final Map<IntentEnum, Integer> intentKeywordWeights = new HashMap<>(); public IntentEnum match(String content) { int maxScore = 0; IntentEnum matched = IntentEnum.UNKNOWN; for (Map.Entry<IntentEnum, Integer> entry : intentKeywordWeights.entrySet()) { int score = 0; for (String keyword : entry.getKey().getKeywords()) { if (content.contains(keyword)) { score += keywordWeights.getOrDefault(keyword, 1); } } if (score > maxScore) { maxScore = score; matched = entry.getKey(); } } return maxScore >= 3 ? matched : IntentEnum.UNKNOWN; } }另一种是模型生成回复。命中不了任何模板但评论确实在提问时,才动用大模型。这时候我会把商品FAQ、平台规则、订单相关信息拼到提示词里,让模型“根据给定资料组织回复”,并明确禁止编造。收到模型回复后还要过一遍规则层——如果生成的回复本身命中敏感词,宁愿不回复,也不能把问题内容发出去。自动回复的合规校验这条线很容易漏,一定要补上。
5. 异步化、缓存与降级兜底
工程化处理决定系统能不能真正上线扛住流量。评论审核如果全部同步处理,大促期间接口会直接被打挂。这里分享一下我实际使用的异步化与降级方案。
5.1 异步处理链路的落地与线程池隔离
我用的是Spring @Async + 自定义线程池。评论提交接口只做两件事:写库、发事件。真正审核和回复逻辑放在异步方法里完成。
@Component public class CommentSubmitService { @Autowired private CommentMapper commentMapper; @Autowired private CommentReviewOrchestrator orchestrator; public Long submit(CommentSubmitDTO dto) { Comment comment = new Comment(); comment.setUserId(dto.getUserId()); comment.setContent(dto.getContent()); comment.setTargetId(dto.getTargetId()); comment.setStatus(CommentStatus.PENDING.getCode()); commentMapper.insert(comment); // 关键:异步提交审核任务,接口立即返回 orchestrator.startReview(comment.getId()); return comment.getId(); } }异步执行的方法长这样:
@Component public class CommentReviewOrchestrator { @Autowired private CommentMapper commentMapper; @Async("commentReviewExecutor") public void startReview(Long commentId) { Comment comment = commentMapper.selectById(commentId); // 第一步:规则层审核 RuleResult ruleResult = ruleEngine.check(comment); if (ruleResult.isBlocked()) { stateMachine.transition(comment, CommentEvent.RULE_REJECT); commentMapper.updateStatus(comment); return; } // 第二步:模型层审核 ReviewResult modelResult = aiReviewService.review(comment); if (modelResult.shouldBlock()) { stateMachine.transition(comment, CommentEvent.MODEL_REJECT); } else if (modelResult.shouldSuspend()) { stateMachine.transition(comment, CommentEvent.MODEL_SUSPEND); } else { stateMachine.transition(comment, CommentEvent.MODEL_PASS); // 第三步:审核通过后尝试自动回复 autoReplyService.tryReply(comment); } commentMapper.updateExtraInfo(comment); } }线程池我专门独立定义,不跟其他业务共用默认的Spring线程池。原因很简单:AI调用是慢IO,一个请求会阻塞线程好几秒,如果用公共池,整个应用的业务线程都会被拖垮:
@Configuration public class AsyncConfig { @Bean("commentReviewExecutor") public Executor commentReviewExecutor() { ThreadPoolTaskExecutor executor = new ThreadPoolTaskExecutor(); executor.setCorePoolSize(8); executor.setMaxPoolSize(16); executor.setQueueCapacity(1000); executor.setThreadNamePrefix("comment-review-"); executor.setRejectedExecutionHandler(new CallerRunsPolicy()); return executor; } }线程池参数这里有一个真实的调优经验。核心线程8、最大16、队列1000看起来普普通通,但实际生产里关键看“背书速率”和“消费速率”的匹配。AI接口平均延迟2秒的情况下,单线程每秒最多处理0.5条评论;核心线程8意味着每秒理论吞吐4条。如果突发流量把任务全塞进去,队列堆到1000,消费完这些任务要200多秒。所以我额外加了一个负载监控:队列深度超过80%就触发最外层限流,新评论直接提示“系统繁忙稍后再试”。宁可短期拒绝少量用户,也不能让审核延迟不可控。
另外用了CallerRunsPolicy作为拒绝策略,意思是线程池满的时候由调用线程自己执行任务。这个策略看着简单,实际上有个隐藏副作用——提交入口线程会被阻塞。但如果用DiscardPolicy扔任务又会丢评论,所以我最终保留CallerRunsPolicy,让提交线程帮忙消化任务,同时下游接口自然背压,整个链路不会雪崩。
5.2 Redis缓存方案:评论指纹与结果复用
Redis在系统里的用途有两块。第一块是审核结果缓存,第二块是限流计数。
审核结果缓存的key设计为review:content:{md5(content)},value存放审核结果JSON,包括状态和风险分数,过期时间设24小时。同一段广告文案重复出现在评论区时,第二次命中缓存直接沿用上次拦截结果,不用再调模型。
这里要注意:缓存值不能直接存“通过/拦截”这种最终判定,要连触发条件一起存。为什么?因为规则库可能热更新,如果昨天判定合法的内容今天新加了一条敏感词,应该重新审核。我设计时缓存里特意带上了词库版本号,发现词库版本变了,缓存自动失效重新审核。
限流用的Redis是经典的滑动窗口。评论提交前先按userId计数,窗口长度5分钟,每个用户最多发10条评论。超出直接打回并提示。这个逻辑拦住了灌水机器人,它们通常单账号高频发评论,人的正常评论频率远低于这个量级。
5.3 模型服务不可用的降级策略
任何第三方依赖都有挂的可能。大模型API更是如此,动不动就限流、网络抖动、返回500。我做了三级降级策略:
第一级,模型调用失败时自动切换“保守模式”:该评论直接标记为“疑似”进人工队列,不让它上线到前台。这不是最优解,但安全。第二级,如果短时间模型失败率达到阈值,由配置中心下发开关,把模型层整体关闭,评论审核只走规则层,所有“未拦截”评论都进人工。第三级,定时任务每30秒探测模型健康状态,恢复后自动切回正常模式,整个过程不用人工介入。
降级链路通过配置中心动态开关控制。我用的配置项大概如下:
review: ai: enabled: true conservative-mode: false运营同学在后台点一下,系统立马切模式。这块给到业务方很强的可操作性,他们在活动前也可以主动把审核调严格一点。
日志也是降级排查的重要凭据。审核链路上每个环节都打了日志:规则层命中哪个规则、模型调用耗时、解析结果是否成功、降级开关状态。排查线上问题时,能通过一条评论ID把整个链路串起来看。
6. 常见问题与排查技巧实录
最后这部分我把项目上线以来踩过的一些典型坑整理一下,都是文档里不太会写但在生产环境一定会碰到的实际问题。
6.1 误杀正常评论怎么处理
AI审核最大的槽点是误杀。用户发一句“这个产品真的好用得不得了”,如果词库里碰巧有个敏感词与之有重叠,就可能被拦截。我解决误杀问题从三个角度入手。
第一,词库分级。明确容易误伤的日常用语不进一级词库,只进二级词库,二级命中后交给模型判断,模型判断正常就放行。第二,加用户白名单。历史发言记录良好的老用户,规则层命中疑似但不严重时直接跳过模型,人工轻量复核即可。第三,用户申诉闭环。每一条被拦截的评论都提供“我要申诉”入口,申诉后评论重新进入审核队列,由人工终审。申诉渠道很关键,它不仅是挽回误杀的正常用户,更是给审核模型提供反馈数据的通道——每周拉取申诉数据,看哪些词是误伤重灾区,直接调整词库。
这里还有个小技巧:后台审核界面要把“规则命中词”和“模型判断依据”并排展示,管理员一看就知道这条评论为什么被拦,不需要点开好几层页面。这个细节能把人工再审的效率提升一半以上。
6.2 评论漏审和审核延迟排查
现象是有些明显违规评论漏到了前台。排查思路不是先怀疑AI模型,而是检查异步链路有没有丢任务。
我最常遇到的原因是线程池队列溢出或被拒绝策略丢弃。用CallerRunsPolicy后不会丢,但会出现提交变慢。另外一个非常隐蔽的坑是事务边界问题:保存评论和发异步任务之间如果包在同一个事务里,异步线程还没跑事务就提交了,导致异步线程读取评论内容时查不到记录。解决方法是让“写评论”和“触发审核”完全分开,写库用独立事务,写完立刻提交,任务触发放在事务提交后的监听器里,或者直接让异步线程延迟几百毫秒去查数据。
审核延迟通常发生在模型接口抖动的时候。排查这类问题,我会先把模型调用的P99延迟监控拉出来,设置一个告警阈值。如果连续三分钟P99超过5秒,就说明模型侧出问题了,触发降级。
6.3 自动回复的质量问题
自动回复的质量问题主要有两类。一类是模板回复太机械,用户一眼看出是机器人,影响体验。我的优化思路是维护多个同义模板,随机抽取并插入用户昵称,比如“@小明,关于退款问题……”比冷冰冰的固定文案亲切很多。另一类是模型生成的回复信息有误,关键原因就是提示词没做好约束。
这里把生成的回复也过一遍敏感词规则是我强烈建议的一项。模型有时会在回复里带上站外联系方式,这种内容如果没过滤就发出去,反而成了违规内容发布渠道。自动回复模块我最后接了一层和评论审核一样的规则检查,生成的回复违规就直接降级为不回复,也不该把违规内容送到用户面前。
6.4 成本控制与模型选型
成本这块,日常优化空间比想象中大。我算过一次账:评论量日均10万条的情况下,规则层能拦住六成,实际到模型的只有四万条左右;再加上指纹缓存命中,真正发到模型的评论大约两万多条。如果单价按千token计算,每天的成本完全可控,远低于请两个专职审核的工资。
选型建议:如果预算紧张,优先用性价比高的通用对话模型,而不是能力最强的大模型。审核任务本身对生成能力要求不高,它需要的是“遵照指令输出结构化结果”的能力,大多数中等规模模型都能做得不错。回复生成场景则可以用更强一些的模型,因为措辞自然度直接决定用户体验。
做个小结的话,这套系统的核心思路可以概括为:规则拦截降本、AI精审兜底、自动回复增值、异步处理保性能、降级开关保稳定。我实际项目从第一个版本上线到稳定运行,前前后后迭代了大半年,最大的体会是:评论区治理不是一次性的功能开发,而是一个需要持续根据线上数据调整策略的运营工程。模型的提示词、规则的阈值、状态的流转,每个环节都值得花时间打磨。如果你正准备在Spring Boot项目里落地类似的功能,建议先画清评审流程图,把各层职责定清楚,再动手写代码,框架搭对了后面填细节就顺了。