十年匠心定制 · 商业建站与技术教学双线并行 咨询热线:400-886-1026 service@lmnt.cn
ARTICLE DETAIL

资讯详情

深耕网站建设与运营推广的一线实战洞察。

AI发言先付费:WebSocket与Redis实现AI内容治理的聊天室

AI发言先付费:WebSocket与Redis实现AI内容治理的聊天室 1. 背景与核心概念1.1 从 AI 刷屏 到 AI 付费发言如果你最近做过社区类产品、聊天室或者问答平台大概率会被同一个问题困扰AI 生成内容的成本越来越低导致大量机器人和自动化脚本涌入聊天室把原本属于人类交流的空间变成了刷屏现场。传统应对方案无非是三种加重频率限制、加验证码、加内容审核。但这类方案本质上是在识别 AI而 AI 识别 AI 本身就是一场没有终点的军备竞赛。大模型换一个提示词、换一下措辞风格规则过滤就可能失效。OnlyBots.chat 这个项目给了一个完全不同的思路不再费力去识别 AI而是让 AI 为发言付费。从项目标题来看这个产品是一个聊天室核心规则可以概括为一句话AI 想发言就得先付钱人类发言反而能获得回报。这种反向收费的设计把控制 AI 垃圾内容从技术问题变成了经济问题——机器人要刷屏就必须持续消耗 credits而 credits 来自真实成本这就天然提高了刷屏门槛。本文不会只停留在产品概念分析上。我会先拆解这类AI 付费发言产品的机制设计然后从零实现一个可运行的聊天室原型包含 WebSocket 通信、积分扣减、AI 机器人接入三个核心模块最后聊一聊生产环境必须处理的高频坑点。1.2 OnlyBots.chat 的机制猜想由于公开资料有限这里我们基于标题给出的信息结合这类 AI 聊天室产品常见的设计思路做合理拆解。一个典型的AI 付费发言聊天室通常包含以下角色和规则人类用户可以免费或低成本发言甚至通过参与对话获得积分奖励。AI 用户本质上是接入大模型 API 的机器人每次发言需要扣除一定数量的 credits。credits 记账系统负责积分的充值与扣减是这套模式的核心资产。聊天室网关所有消息先经过网关校验身份、校验余额再广播给在线用户。AI 付费设计最巧妙的地方在于它把聊天室从一个纯社交产品变成了一个注意力市场人类的注意力是稀缺资源AI 想要进入并被看见就得用真金白银来换取发言权。这比任何垃圾内容过滤算法都更直接。1.3 credits 在 AI 产品里到底指什么很多刚接触 AI 应用开发的同学会对 credits 这个概念感到困惑。简单来说credits 是一种计费单元。在大模型时代每个模型调用背后都有真实算力成本厂商不可能让用户无限调用所以用 credits 来做配额管理。你可以在心理上把它理解成游戏币充值换 credits调用 API 时按 token 消耗 credits。在 OnlyBots.chat 场景中credits 的含义更接近发言权AI 用户账户里必须有足够的 credits 才能发言每次发言扣除固定数额或者按消息长度/模型成本动态计算人类用户不消耗 credits某些场景下还能获得 credits 奖励。搞清楚 credits 的本质之后后面设计数据库和扣费接口时就很容易知道数据该怎么建模了。2. 产品机制拆解为什么 机器人付费 可以成立2.1 逆向收费模型传统聊天室的盈利逻辑是平台向人类用户收费比如会员、打赏、解锁更多功能。OnlyBots.chat 则反过来人类是内容消费者AI 是内容供给方供给方为发布权付费。这个模型的成立有三个前提AI 内容供给量远超人类需求。大模型生成内容的边际成本极低不加限制时一个机器人可以一分钟发几百条消息而人类根本看不过来。人类注意力变得稀缺。聊天室的容量、滑动速度、信息接收效率都是有限的谁占用了这些容量谁就该付钱。AI 有真实的商业诉求。商品推广、品牌曝光、数据分析实验、模型评测都可能需要让 AI Agent 在特定人群面前发言。理解了这三点你就明白为什么限流 审核这类纯技术手段不够用因为问题的根源不在技术效率而在经济激励。2.2 身份、交互与消息流在这个产品里消息发送的完整链路是人类用户或 AI 用户发起发言请求网关校验身份类型如果是 AI 用户先做 credits 余额校验和扣减扣费成功后消息进入聊天室广播所有在线用户收到消息如果 AI 用户余额不足消息被拒绝并返回提示。这跟普通聊天室的差异看起来只是加了一步扣费但正因为多了这一步整个系统的复杂度上升了一个量级并发扣减、余额一致性、幂等、防刷、内容安全每一个都是必须处理的工程问题。2.3 这个模式适合什么场景除了聊天室本身这种AI 付费发言的机制可以复用到很多场景AI 产品发布会AI 助手需要在直播间回答问题但每条回复要消耗发布会主办方的 credits避免无意义刷屏。分析 Agent 展示台多个 AI Agent 并发输出分析结论谁想占据用户屏幕谁就得承担消息成本。社区内容治理限制 SEO 垃圾站、营销号机器人自动发帖用发帖成本替代发布后删除。模型评测黑盒让多个模型付费回答问题侧面观察不同模型在真实交互中的表现。3. 技术架构设计3.1 整体模块划分为了实现一个最小可用版本我建议把系统拆成四个模块模块职责chat-serviceWebSocket 接入、消息广播、在线会话管理billing-servicecredits 余额查询、扣减、流水记录bot-service对接大模型 API生成 AI 发言内容admin-service用户管理、额度充值、封禁操作为了控制篇幅本文用一个 Spring Boot 单工程承载以上四个模块的逻辑但模块之间保持独立 class/package 边界。真实生产环境中billing 模块建议拆成独立服务因为积分和钱相关变更频率和稳定性要求都远高于聊天室逻辑。3.2 技术选型开发语言Java 17框架Spring Boot 3.x实时通信spring-boot-starter-websocket数据库MySQL 8.x缓存与原子操作RedisORMMyBatis-spring-boot-starterAI 调用Spring 6 自带的 RestClient选择 WebSocket 是因为聊天室场景天然需要服务端主动推送。如果用 HTTP 轮询消息延迟和服务器压力都不可控。Redis 在这里承担两个职责一是缓存余额二是利用 Lua 脚本实现检查余额 扣减的原子操作。3.3 数据库设计核心表有四张-- 用户表区分人类与 AI CREATE TABLE chat_user ( id BIGINT NOT NULL AUTO_INCREMENT, user_type TINYINT NOT NULL COMMENT 1-human 2-ai, nickname VARCHAR(64) NOT NULL, credits DECIMAL(12, 2) NOT NULL DEFAULT 0.00, status TINYINT NOT NULL DEFAULT 1 COMMENT 1-normal 0-banned, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 消息表记录每次发言 CREATE TABLE chat_message ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, content TEXT NOT NULL, cost_credits DECIMAL(12, 2) NOT NULL DEFAULT 0.00, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_user_id (user_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- 积分流水表记录充值与扣减 CREATE TABLE credit_log ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, change_amount DECIMAL(12, 2) NOT NULL, biz_type VARCHAR(32) NOT NULL COMMENT recharge/speak/refund/reward, biz_id VARCHAR(64) DEFAULT NULL COMMENT 关联业务单号用于幂等, create_time DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP, PRIMARY KEY (id), KEY idx_biz_id (biz_id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4; -- AI 机器人配置表存储每个机器人的模型参数 CREATE TABLE ai_bot_config ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, prompt_template TEXT, model_name VARCHAR(128), temperature DECIMAL(3, 2) DEFAULT 0.80, enabled TINYINT NOT NULL DEFAULT 1, PRIMARY KEY (id) ) ENGINEInnoDB DEFAULT CHARSETutf8mb4;设计表时最需要注意的是credit_log表的biz_id字段。在真实的扣费场景中如果客户端重试、网络超时导致服务端重复处理没有幂等字段就会重复扣费。这个细节后面还会重点讲。4. 从零实现一个 AI 付费发言 聊天室4.1 项目结构与依赖创建一个 Spring Boot 工程建议包结构如下com.example.onlybots ├── OnlyBotsApplication.java ├── chat │ ├── WebSocketConfig.java │ ├── UserHandshakeInterceptor.java │ └── ChatRoomHandler.java ├── billing │ ├── CreditService.java │ └── CreditRecordTask.java └── bot └── AiBotController.java先引入必要的依赖核心是 WebSocket 和 Redisdependencies dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-websocket/artifactId /dependency dependency groupIdorg.springframework.boot/groupId artifactIdspring-boot-starter-data-redis/artifactId /dependency dependency groupIdorg.mybatis.spring.boot/groupId artifactIdmybatis-spring-boot-starter/artifactId version3.0.3/version /dependency dependency groupIdcom.mysql/groupId artifactIdmysql-connector-j/artifactId scoperuntime/scope /dependency /dependencies这里版本以我本地环境为例实际使用请根据你的 Spring Boot 版本调整 MyBatis starter 版本避免依赖冲突。4.2 基础配置# src/main/resources/application.yml server: port: 8080 spring: datasource: url: jdbc:mysql://localhost:3306/onlybots?useUnicodetruecharacterEncodingutf8 username: root password: yourpassword data: redis: host: localhost port: 6379如果你不想提前建 MySQL 表也可以先用 Redis 存余额把数据库部分放到第二阶段再加。本文示例以 Redis 扣费为主数据库建表代码作为持久化扩展参考。4.3 WebSocket 配置与握手拦截器WebSocket 建立连接时需要从请求参数里拿到 userId 和 userType并写入 session attributes方便后续在消息处理中判断身份。// 文件路径src/main/java/com/example/onlybots/chat/UserHandshakeInterceptor.java package com.example.onlybots.chat; import org.springframework.http.server.ServerHttpRequest; import org.springframework.http.server.ServerHttpResponse; import org.springframework.web.socket.WebSocketHandler; import org.springframework.web.socket.server.HandshakeInterceptor; import java.util.Map; public class UserHandshakeInterceptor implements HandshakeInterceptor { Override public boolean beforeHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, MapString, Object attributes) { String query request.getURI().getQuery(); attributes.put(userId, parse(query, userId)); attributes.put(userType, parse(query, userType)); return true; } private String parse(String query, String key) { if (query null || query.isEmpty()) { return ; } for (String pair : query.split()) { String[] kv pair.split(); if (kv.length 2 key.equals(kv[0])) { return kv[1]; } } return ; } Override public void afterHandshake(ServerHttpRequest request, ServerHttpResponse response, WebSocketHandler wsHandler, Exception exception) { } }这是一个极简实现的握手拦截器实际项目中必须用登录态换取 userId绝不能像这样把 userId 直接暴露在 URL 参数里否则任何人都可以冒充他人发言。接着注册 WebSocket 端点// 文件路径src/main/java/com/example/onlybots/chat/WebSocketConfig.java package com.example.onlybots.chat; import org.springframework.context.annotation.Configuration; import org.springframework.web.socket.config.annotation.EnableWebSocket; import org.springframework.web.socket.config.annotation.WebSocketConfigurer; import org.springframework.web.socket.config.annotation.WebSocketHandlerRegistry; Configuration EnableWebSocket public class WebSocketConfig implements WebSocketConfigurer { private final ChatRoomHandler chatRoomHandler; public WebSocketConfig(ChatRoomHandler chatRoomHandler) { this.chatRoomHandler chatRoomHandler; } Override public void registerWebSocketHandlers(WebSocketHandlerRegistry registry) { registry.addHandler(chatRoomHandler, /chat) .addInterceptors(new UserHandshakeInterceptor()) .setAllowedOrigins(*); } }注意setAllowedOrigins(*)只适合本地调试。上线时建议换成具体的域名白名单避免跨站 WebSocket 劫持。4.4 核心聊天处理器聊天处理器负责消息的接收与广播。当收到 AI 用户的消息时先扣费扣费失败直接拒绝扣费成功后消息才广播给所有人。// 文件路径src/main/java/com/example/onlybots/chat/ChatRoomHandler.java package com.example.onlybots.chat; import com.example.onlybots.billing.CreditService; import org.springframework.stereotype.Component; import org.springframework.web.socket.CloseStatus; import org.springframework.web.socket.TextMessage; import org.springframework.web.socket.WebSocketSession; import org.springframework.web.socket.handler.TextWebSocketHandler; import java.io.IOException; import java.math.BigDecimal; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; Component public class ChatRoomHandler extends TextWebSocketHandler { private static final MapString, WebSocketSession SESSIONS new ConcurrentHashMap(); private static final BigDecimal SPEAK_COST new BigDecimal(1.00); private final CreditService creditService; public ChatRoomHandler(CreditService creditService) { this.creditService creditService; } Override public void afterConnectionEstablished(WebSocketSession session) { SESSIONS.put(session.getId(), session); } Override protected void handleTextMessage(WebSocketSession session, TextMessage message) throws IOException { String userId (String) session.getAttributes().get(userId); String userType (String) session.getAttributes().get(userType); String content message.getPayload(); // 只有 AI 用户发言才需要扣 credits if (ai.equals(userType)) { boolean ok creditService.tryDeduct(userId, SPEAK_COST); if (!ok) { sendJson(session, {\type\:\error\,\msg\:\credits not enough\}); return; } } // 生产环境这里不要手工拼接 JSON建议使用 Jackson/ObjectMapper broadcast({\type\:\chat\,\userId\:\ userId \,\content\:\ content \}); } public void broadcast(String json) { SESSIONS.values().forEach(s - { try { if (s.isOpen()) { s.sendMessage(new TextMessage(json)); } } catch (IOException e) { // 单个连接异常不影响其他用户 SESSIONS.remove(s.getId()); } }); } private void sendJson(WebSocketSession session, String json) throws IOException { if (session.isOpen()) { session.sendMessage(new TextMessage(json)); } } Override public void afterConnectionClosed(WebSocketSession session, CloseStatus status) { SESSIONS.remove(session.getId()); } }这段代码里有一个值得注意的业务决策AI 用户扣费失败时消息被静默丢弃只返回给发送者提示。这个策略可以防止 AI 机器人因为余额不足在聊天室里刷错误日志。4.5 基于 Redis Lua 的原子扣费服务扣费是最容易出现并发问题的环节。如果 AI 机器人同时发两条消息两个请求都读到余额为 1然后各自扣 1最终余额变成 -1这就是经典的并发超扣问题。解决方式有两种一是数据库行锁或者UPDATE ... WHERE credits cost的条件更新二是 Redis Lua 脚本。这里选择 Lua 脚本因为它在高并发下性能更好而且脚本是原子执行的。// 文件路径src/main/java/com/example/onlybots/billing/CreditService.java package com.example.onlybots.billing; import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.script.DefaultRedisScript; import org.springframework.stereotype.Service; import java.math.BigDecimal; import java.util.Collections; Service public class CreditService { private final StringRedisTemplate redisTemplate; public CreditService(StringRedisTemplate redisTemplate) { this.redisTemplate redisTemplate; } /** * 原子扣减余额不足返回 false扣减成功返回 true。 * Lua 脚本保证“读余额 比较 扣减”整体原子执行。 */ public boolean tryDeduct(String userId, BigDecimal amount) { String script local balance tonumber(redis.call(GET, KEYS[1]) or 0) if balance tonumber(ARGV[1]) then return -1 end redis.call(DECRBY, KEYS[1], ARGV[1]) return balance - tonumber(ARGV[1]); DefaultRedisScriptLong redisScript new DefaultRedisScript(script, Long.class); Long result redisTemplate.execute( redisScript, Collections.singletonList(credit: userId), amount.toPlainString() ); return result ! null result 0; } }脚本逻辑只有三行但解决了核心的一致性问题读取余额如果余额小于扣减金额返回 -1否则执行DECRBY扣减并返回扣减后的余额。在初始化时需要把用户余额从 MySQL 同步到 Redis例如启动时执行一次全量加载或者每次余额变化时双写。工程项目里通常用缓存失效 延迟双删这里不再展开。4.6 AI 机器人发言入口最后写一个 AI 机器人接入示例。机器人通过 HTTP 接口触发先扣费再调用大模型接口生成内容最后通过 WebSocket 广播到聊天室。// 文件路径src/main/java/com/example/onlybots/bot/AiBotController.java package com.example.onlybots.bot; import com.example.onlybots.billing.CreditService; import com.example.onlybots.chat.ChatRoomHandler; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; import org.springframework.web.bind.annotation.*; import org.springframework.web.client.RestClient; import java.math.BigDecimal; import java.util.Map; RestController RequestMapping(/bot) public class AiBotController { private static final BigDecimal SPEAK_COST new BigDecimal(1.00); private final CreditService creditService; private final ChatRoomHandler chatRoomHandler; private final RestClient restClient; private final ObjectMapper objectMapper; public AiBotController(CreditService creditService, ChatRoomHandler chatRoomHandler, RestClient.Builder builder, ObjectMapper objectMapper) { this.creditService creditService; this.chatRoomHandler chatRoomHandler; this.restClient builder.build(); this.objectMapper objectMapper; } PostMapping(/speak) public String speak(RequestParam String userId, RequestParam String prompt) { // 第一步扣费 if (!creditService.tryDeduct(userId, SPEAK_COST)) { return insufficient credits; } // 第二步调用大模型生成发言内容 // 下面以 OpenAI 兼容接口为示例实际 endpoint 和鉴权方式以你所用模型为准 MapString, Object requestBody Map.of( model, your-model-name, messages, new Object[]{ Map.of(role, user, content, prompt) }, temperature, 0.8 ); String rawResponse restClient.post() .uri(https://your-llm-endpoint/v1/chat/completions) .header(Authorization, Bearer System.getenv(LLM_API_KEY)) .body(requestBody) .retrieve() .body(String.class); String reply parseContent(rawResponse); // 第三步广播消息 chatRoomHandler.broadcast({\type\:\chat\,\userId\:\ userId \,\content\:\ reply \}); return posted; } private String parseContent(String rawResponse) { try { JsonNode root objectMapper.readTree(rawResponse); return root.path(choices).path(0).path(message).path(content).asText(); } catch (Exception e) { return AI 回复解析失败; } } }如果你的模型接口返回 SSE 流式数据可以把RestClient换成WebClient并读取EventStream思路一致先扣费再请求最后把增量内容拼接后广播。4.7 前端连接示例为了验证效果写一个最简 HTML 页面!DOCTYPE html html langzh-CN head meta charsetUTF-8 titleOnlyBots Demo/title /head body h3聊天室demo/h3 div idmessages/div input idmsg / button onclicksend()发送/button script // 真实项目请使用登录态不要明文传 userId const ws new WebSocket(ws://localhost:8080/chat?userId1001userTypehuman); ws.onmessage function (event) { const div document.createElement(div); div.textContent event.data; document.getElementById(messages).appendChild(div); }; function send() { ws.send(document.getElementById(msg).value); } /script /body /html启动项目后先用 Redis 给 AI 用户设置余额redis-cli SET credit:2001 5然后调用curl -X POST http://localhost:8080/bot/speak?userId2001prompt%E5%A4%A7%E5%AE%B6%E5%A5%BD当余额充足时聊天室会广播一条 AI 消息余额不足时接口返回insufficient credits。这个最小的闭环已经完整演示了AI 付费发言的核心链路。5. 绕不开的工程难点5.1 并发扣减与幂等Lua 脚本解决了并发扣超的问题但还没解决重复扣费的问题。设想一个场景AI 机器人调用/bot/speak时网络超时客户端重试同一句话被扣了两次费。解决思路是在扣费前生成一个bizId比如 UUID扣费时先查询credit_log表里是否已存在相同bizId存在就直接返回成功不存在才执行扣费和插流水。为了更稳妥可以在credit_log表给biz_id加唯一索引数据库层面保证幂等。5.2 限流与防刷AI 付费并不能完全阻止恶意刷屏。有些攻击者会注册大量 AI 账号每个账号充值少量 credits然后把聊天室当成广告投放平台。建议在三个层面做防刷身份层AI 用户必须通过企业认证或 API Key 接入不允许匿名注册频控层每个 AI 用户每秒最多发言 N 条用 Redis 计数器实现INCR EXPIRE内容层对发言内容做敏感词过滤和相似度去重避免同一段营销文案被批量广播。5.3 内容安全与成本控制AI 生成的内容不可控必须做内容合规校验。建议至少做两层发送前调用内容审核接口识别涉政、色情、暴恐、广告等违规内容发送后再做一次抽检发现问题可以执行撤回消息 扣除机器人信誉分。成本控制方面AI 机器人每次调用大模型都要花钱这跟 credits 扣费是两个独立的成本。为了防止机器人接口被滥用需要在/bot/speak上加一层独立限流并且给不同类型机器人设置每日最高消费额度。当机器人当日消耗接近预算时自动暂停发言。6. 常见问题与排查思路问题现象常见原因解决思路WebSocket 连接总是断开握手拦截器里 userId 为空检查 query 参数是否正确传入建议打日志确认 attributesAI 用户发言后聊天室无消息扣费失败被静默拦截先确认 Redis 里是否存在credit:{userId}再确认余额是否足够并发发言时余额变成负数扣减未做原子操作改用 Redis Lua 脚本或使用数据库条件更新UPDATE ... WHERE credits cost客户端断线重连后收不到历史消息消息只广播不落库在消息表落库新连接建立时从数据库拉取最近 50 条/bot/speak返回 500大模型接口地址或鉴权头配置错误先用 Postman/curl 直接测试 LLM 接口确认返回格式同一句话被扣两次费缺少幂等处理给credit_log的biz_id加唯一索引扣费前查询去重Redis 重启后余额丢失未做持久化或未同步数据库开启 Redis AOF 持久化启动时从 MySQL 回灌余额7. 工程建议与最佳实践7.1 把余额变更做成事件流扣费不只是减一个数字它会产生流水、可能触发余额预警、可能影响机器人调度策略。建议在扣费成功后发布一个领域事件由监听器异步处理流水写入、通知、监控等下游逻辑避免在 WebSocket 主线程里做过多数据库操作。7.2 日志与监控聊天室的日志要记录完整链路连接建立、消息进入、扣费结果、广播结果。建议为每条消息生成messageId日志里统一带上userId和messageId排错时可以直接串联整个链路。监控指标至少要有每分钟消息数区分 human 和 ai扣费成功率余额不足拒绝次数AI 接口调用耗时与错误率WebSocket 在线连接数。7.3 配置管理大模型接口地址、模型名称、每次发言成本、限流阈值这些参数不要硬编码在代码里应该放到配置文件或配置中心。特别是每次发言成本这是业务策略参数很可能需要随时调整。把成本参数化之后你可以做促销、动态定价而不用改代码重新发布。7.4 安全边界这个系统涉及资金流动安全是重中之重。以下几点必须严格遵守生产环境禁止把 userId 放在 URL query 里必须通过登录态换取充值、退款、调整余额等操作必须走后台接口并做操作审计数据库操作前先备份涉及批量扣费必须小范围灰度生产环境变更选择低峰期执行并做好回滚方案。8. 总结与学习路线这篇文章从一个有意思的产品概念出发拆解了AI 付费发言的机制设计然后带你实现了一个最小可运行的聊天室原型。核心内容包括OnlyBots.chat 的AI 付费发言思路本质是把 AI 内容治理从技术问题转化为经济问题credits 是 AI 应用里最常见的计费单元理解它有助于设计任何 AI 商业化产品WebSocket 实时通信 Redis Lua 原子扣费是大模型聊天室的标准组合并发扣减、幂等、限流、内容安全是上线前必须解决的四个工程问题。如果你是刚开始接触 AI 应用开发下一步推荐按这个顺序继续深入把聊天室消息落库并实现历史消息拉取用 WebClient 替换 RestClient接入大模型的 SSE 流式返回把 billing 模块抽出为独立服务加入配置中心和消息队列引入 AI Agent 框架让机器人具备多轮对话和工具调用能力。这个项目麻雀虽小但已经覆盖了实时通信、高并发扣费、第三方 API 集成、异步任务等多个关键技能点。你甚至可以边做边用 Cursor 这类 AI 编程工具辅助生成脚手架代码把精力集中在架构设计和一致性保障上。如果文章对你有帮助可以收藏备用后续再遇到类似I 内容治理AI 商业化的需求直接把这套思路拿过去用就行。
返回列表