去年冬天有一段时间我状态很差,白天上班心不在焉,晚上又睡不着,翻来覆去地想一些没答案的问题。为了给自己找点事情做,我干脆把憋了很久的想法落了地——做一个 AI 聊天虚拟恋人 App,从聊天内核、支付体系、官网落地页到上架流程,全部自己趟一遍。这篇文章不讲虚的,就把我这个项目从零到能收款的完整过程摊开来讲,包括模块怎么拆、AI 聊天怎么调、微信支付 JSAPI 接入时那个烦人的 openid 到底从哪儿来、官网怎么布关键词、上架要花多少钱、以及我踩过的那些坑。不管你是想做个副业小产品,还是单纯想练手全栈能力,这篇都能当个参考。
1. 从迷茫到落地:这个项目整体怎么盘
1.1 为什么我选"AI 聊天虚拟恋人"这个方向
先说选题。很多人做副业第一反应是工具类 App,比如记账、待办、番茄钟,逻辑是"需求明确、变现清晰"。但我实测下来,工具类有个致命问题:留存差。用户用完就走,你花大力气做出来,日活全靠推送硬撑。而情感陪伴这类产品不一样,它的核心是"聊天"这件事本身,用户跟你聊得越久,迁移成本越高,付费意愿也越强,订阅会员可进入优先队列这种设计天然就成立。
从技术角度看,这个方向对独立开发者特别友好。你不需要养一支算法团队,大模型 API 已经把最难的对话能力封装好了,你要做的是"产品化"——把人设做成可配置的、把聊天体验做流畅、把支付和官网这些基础设施搭稳。这正好是一个全栈练手的绝佳场景,前端、后端、实时通信、支付、SEO 全都能覆盖到。
但有一点我必须提前说清楚:做情感陪伴类产品,内容安全和用户心理边界是底线。我在设计之初就给自己定了几条死规矩——不碰任何擦边内容、不做诱导性付费、未成年人保护必须做、涉及情绪困扰的场景要引导用户寻求专业帮助。这不是为了应付审核,而是这类产品一旦走偏,口碑崩起来比涨起来快十倍。我见过太多同类产品因为内容失控被下架,前面的投入全打水漂。
1.2 一张表格说清整个技术盘子
我把项目拆成了五块,每块都是独立可替换的模块。这样设计的好处是,任何一个环节出问题,我都能单独替换而不影响全局。比如 AI 层一开始用的是 A 家 API,后来发现响应速度不稳定,我直接换成了 B 家,业务代码几乎没动。
| 模块 | 我选的技术方案 | 备选方案 | 选择理由 |
|---|---|---|---|
| 客户端 | Flutter | React Native / 原生 | 一套代码出双端,独立开发者没精力维护两套 |
| 后端 API | Python FastAPI | Node.js Express / Go | 异步性能好,和大模型 SDK 生态贴合 |
| 实时通信 | WebSocket | 长轮询 / SSE | 聊天必须双向,SSE 只能服务端推 |
| AI 对话 | 大模型 API + 自建人设层 | 自训练小模型 | 成本可控,迭代快,不需要 GPU 集群 |
| 支付 | 微信支付 JSAPI + 支付宝 | 聚合支付 | 用户覆盖最广,费率透明 |
| 官网 | 静态站 + CDN | 动态 CMS | 加载快、SEO 友好、几乎零运维 |
| 数据库 | PostgreSQL + Redis | MySQL | JSON 字段好用人设配置,Redis 扛会话 |
这里我要解释一下为什么后端选 FastAPI 而不是更"主流"的 Node.js。核心原因是流式输出。AI 聊天最影响体验的就是"打字机效果"——用户问一句,AI 一个字一个字往外蹦,而不是等整段生成完再一次性返回。FastAPI 配合异步生成器写流式接口特别顺,代码量比 Node.js 少一半。当然 Node.js 也能做,只是我个人的技术栈更偏 Python。
还有一个容易被忽略的点:客户端别一上来就做原生双端。我一开始想用 Android 原生 + iOS 原生,光是把聊天界面写两遍就够我受的。后来换成 Flutter,一套 Dart 代码出双端,省下的时间够我把支付和官网都做完了。独立开发者最重要的资源不是技术,是时间。
2. AI 聊天内核:让人设真正"活"起来
2.1 人设卡设计:把性格写成可维护的配置
新手做 AI 聊天最容易犯的错,是把一大段人设描述硬编码在代码里,比如system_prompt = "你是一个温柔的女生……"。这样做短期没问题,但你一旦想加第二个角色、想调整性格、想做 A/B 测试,代码就乱了。我的做法是把人设抽象成一份 JSON 配置,存数据库,随时可以改,改完热更新,不用重新发版。
一份合格的人设卡应该包含这几个维度:基础设定(名字、年龄、身份)、性格特征(温柔/毒舌/高冷,但要有具体行为描述而不是空泛形容词)、说话风格(句长、口头禅、是否用语气词)、边界规则(什么话题要回避、什么情况要引导专业帮助)、开场白(用户第一次进来看到的第一句话)。我实测下来,性格描述越具体,AI 演得越像。你写"她很温柔",模型给你一个端水大师;你写"她说话慢,喜欢用省略号,被夸的时候会转移话题,生气了不会直接说而是回一个字'哦'",出来的感觉完全不一样。
{ "id": "char_001", "name": "小雨", "persona": "23岁插画师,性格慢热,熟悉之后话很多", "speaking_style": "句子偏短,偶尔会用省略号,开心时有轻微颜文字倾向", "boundaries": ["不讨论现实中的具体学校单位", "涉及严重情绪困扰时建议寻求专业帮助"], "opening": "今天……你是不是也有很多事想说?" }注意:人设卡里的"边界规则"不是摆设。我见过有人为了追求聊天效果把人设写得毫无约束,结果模型开始自说自话编造身份经历,用户信以为真,这种信任一旦被滥用就是事故。边界规则要写进 system prompt,并且在服务端做二次校验,不能全指望模型自觉。
另外,人设卡还要考虑"记忆"的挂载点。真正让用户觉得"她记得我"的,不是开场白多甜,而是三天后她还能提起你上次说的那件事。这就涉及到下一节的上下文管理。
2.2 上下文与记忆:三明治结构管住 token 成本
大模型的上下文窗口是有限的,而且token 是花钱的。你要是一次对话把几百轮历史全塞进去,成本会炸。我的方案是三明治结构:
- 底层:长期摘要。每个用户的会话每隔 N 轮,我用模型把这段对话压缩成一段 100 字左右的摘要,存数据库。下次开新会话时,只把摘要塞进去,不带完整历史。
- 中层:关键事实。我额外维护一张"用户事实表",记录用户明确说过的偏好、重要日期、称呼习惯。这些是结构化数据,几十个 token 就能表达很多信息。
- 顶层:近期滑窗。最近 10 到 15 轮对话原文,保证当前语境连贯。
这套组合拳跑下来,一次请求的 token 量能控制在 2000 以内,比无脑塞历史省了八成成本。而且体验上反而更好,因为模型不会被几十轮前的无关信息干扰。
这里有个实操细节很多人会踩:摘要不能异步做完了就完了,要考虑失败重试。我一开始用消息队列异步生成摘要,结果某次队列堵了,用户再进来发现"她完全不记得我了",体验直接崩。后来我改成同步兜底 + 异步优化:如果摘要还没生成好,就先拿最近 30 轮原文顶上,保证不断片,摘要生成好再更新。
2.3 WebSocket 实时链路:别用轮询折磨用户
聊天功能最忌讳的就是轮询。用户发一条消息,每隔一秒请求一次接口问"AI 回复好了没",服务器压力大,用户体验还卡。正确做法是上 WebSocket,一条长连接双向通信。
客户端连接时带上 token,服务端校验后建立会话。消息协议我用的是最简的 JSON:
// 客户端发送 { "type": "message", "content": "今天好累啊", "sessionId": "xxx" } // 服务端流式返回(多条) { "type": "delta", "content": "辛苦" } { "type": "delta", "content": "了," } { "type": "done", "messageId": "yyy" }服务端拿到消息后,向大模型发起流式请求,每收到一个 token 就往 WebSocket 里推一个delta,全部结束后推一个done。用户看到的是一行行冒出来的字,体验就像真人打字。
实操心得:WebSocket 一定要做心跳保活和断线重连。移动网络切换、App 切后台再回来,连接大概率已经断了。我客户端设了 30 秒一次 ping,服务端 60 秒没收到就主动断连;客户端检测到断开后指数退避重连,重连时用最后一条消息的 ID 做增量拉取,避免消息丢失。
还有一个容易被忽略的技术点:付费用户的优先队列。高峰期如果所有请求一起排队,付费用户和免费用户一起等,体验会很难看。我在请求入口加了一层优先级队列,订阅会员的请求权重更高,在模型并发受限时优先出结果。实现上是 Redis 的 Sorted Set,score 用优先级加时间戳,保证高优先级先出、同级 FIFO,不会把免费用户饿死。
3. 支付体系搭建:从 0 到能收到钱
3.1 订单模型与状态机:先把数据结构想清楚
做支付模块,我最大的教训就是:先设计订单表,再写业务代码。很多人一上来就调支付接口,结果订单状态乱成一锅粥,用户付了钱没开通会员、重复扣款对不上账。我现在的订单表至少包含这些字段:
| 字段 | 类型 | 说明 |
|---|---|---|
| order_no | varchar | 商户订单号,全局唯一,用于幂等 |
| user_id | bigint | 下单用户 |
| product_id | varchar | 订阅套餐标识 |
| amount | int | 金额,单位分,绝不用浮点 |
| status | tinyint | 0 待支付/1 已支付/2 已关闭/3 已退款 |
| channel | varchar | wechat / alipay |
| transaction_id | varchar | 第三方支付单号 |
| created_at / paid_at | datetime | 时间戳 |
金额用整数分存储,这个不用多解释,浮点数算钱迟早出问题。状态机是关键:
待支付 --支付成功--> 已支付 --退款--> 已退款 | | +--超时/主动取消--> 已关闭状态流转只能单向,且每次变更都要记日志。我专门建了一张order_log表,谁在什么时候把订单从什么状态改成了什么状态,全记下来。出问题的时候,这张表比任何排查都管用。
注意:幂等是支付的生命线。同一笔支付回调可能因为网络重试被推好几次,你的业务逻辑必须保证"处理一次"和"处理十次"结果完全一样。我的做法是用
order_no做唯一约束,更新状态时带where status = 0条件,只有真正从待支付改成已支付的那一次 SQL 才影响行数,其余全部忽略。
3.2 微信支付 JSAPI:openid 到底从哪儿来
这是我在接入过程中卡了最久的地方,也是热词里被搜爆的问题——JSAPI 支付必须传 openid,可 openid 怎么拿?先讲清楚原理:openid 是微信用户在某个特定 appid 下的唯一标识,它不属于用户本身,而是"用户 + 这个 appid"的组合产物。所以你不能凭空造一个,必须通过微信的授权流程换取。
不同场景拿 openid 的方式完全不同,我把三种常见场景列出来:
| 场景 | 获取方式 | 关键点 |
|---|---|---|
| 公众号 H5 | 网页授权(snsapi_base) | 用户无感知,跳转带 code 换 openid |
| 小程序内 | wx.login 拿 code | 后端用 code2session 换 openid |
| 原生 App | 不用 JSAPI,走 App 支付 | App 支付不需要 openid |
如果你做的是公众号网页里的支付,流程是:用户点支付 → 跳转微信授权页 → 微信回调你的地址并带上 code → 后端拿 code 调snsapi_base接口换 openid 和 access_token → 拿到 openid 后再调下单接口。整个链路里最容易错的是授权域名没配和code 只能用一次。code 换过一次就失效了,如果你前端刷新了页面导致重复提交,第二次必报错。我的处理是在后端缓存 code 到 openid 的映射,短时间内重复请求直接命中缓存。
下单核心代码大致是这样:
// 后端:JSAPI 下单 const body = { appid: APP_ID, mchid: MCH_ID, description: '月度会员订阅', out_trade_no: orderNo, notify_url: 'https://www.yoursite.com/api/pay/wx/notify', amount: { total: 1900, currency: 'CNY' }, payer: { openid: userOpenid } // 这里必须是对应 appid 下的 openid }; // 用商户私钥对请求签名,再带上 Authorization 头请求微信接口 // 返回 prepay_id,用官方 SDK 二次签名后返回给前端调起支付前端拿到签名参数后调WeixinJSBridge.invoke('getBrandWCPayRequest', ...)就能拉起支付面板。这里要注意,App 里的支付不要硬套 JSAPI,如果用户是在原生 App 内,应该用 App 支付,走的是完全不同的接口,openid 这个问题根本不存在。
3.3 回调验签、对账与退款:钱到账之后的事
支付成功只是开始。微信会异步回调你的notify_url,你必须做三件事:验签、解密、幂等处理。
验签是防止伪造回调的第一道门。微信现在的回调用的是平台证书,你需要用对应的公钥验证签名,签名不对直接拒绝。解密是因为回调报文里的金额等信息是加密的,要用 API v3 密钥解。最后才是处理业务逻辑。我见过有人为了图省事跳过验签,结果被人伪造回调白嫖会员,血的教训。
对账是第二道保险。每天定时任务拉取微信的账单文件,和你自己的订单表逐笔核对,金额和状态不一致的记入异常表人工排查。这个活儿很枯燥,但一定要自动化,人工对账迟早会漏。
退款则要特别注意部分退款和全额退款的区别,以及退款回调的处理。退款的钱是原路返回的,用户到账时间取决于银行,别跟用户承诺"立即到账",这句话能省掉你一半的客服工单。
4. 官网与转化:让流量真正找到你
4.1 官网信息架构与 SEO 关键词布局
很多人做 App 只做应用商店,完全忽略官网。但官网有两个无法替代的作用:承载搜索引擎流量和建立信任感。用户搜"AI 聊天虚拟恋人 App"想找同类产品时,一个好的官网就是你最大的自然流量入口。
我的官网结构很简洁,就四个板块:首屏价值主张、核心功能展示、用户评价、下载引导。关键在于关键词的自然布局。我在标题、H1、段落首句、图片 alt 里都放了"AI 聊天""虚拟恋人""AI 陪伴 App"这类词,但绝不堆砌,每处都融进正常句子。搜索引擎现在很聪明,堆关键词反而降权。
技术层面,官网我做成静态站,配合 CDN 加速,首屏加载压在 1 秒内。为什么要这么快?因为移动端用户耐心极短,多等一秒,跳出率就往上蹿。静态站还有个好处是不怕流量突增,CDN 直接兜住。
提示:官网务必配好
robots.txt和sitemap.xml,把希望被收录的页面列全。同时页面要适配移动端,现在搜索引擎基本都是移动优先索引,PC 版做再好,移动端体验差一样掉排名。
4.2 转化漏斗:从搜索到付费那条路
官网不是用来"展示"的,是用来"转化"的。我在页面上放了三个转化点:首屏的"免费体验"按钮、中部的"查看会员权益"、底部的"立即下载"。每个点都指向不同的用户意图层次,浅层用户点体验,深层用户点下载。
数据埋点一定要做。我给每个按钮加了来源参数,追踪用户从哪个渠道进来、点了哪个按钮、最终有没有完成注册和付费。这套漏斗数据后来帮我砍掉了一个几乎没转化的渠道,省了不少推广费。
还有个小细节:官网和 App 里的文案要保持一致。我一开始官网写"24 小时秒回",App 里因为排队机制偶尔会延迟几秒,用户就投诉"虚假宣传"。后来统一改成"随时都在",体验预期就顺了。
5. 上线前后的踩坑与排查实录
5.1 支付类问题速查表
支付这块的问题最磨人,因为涉及多方:你的服务器、微信服务器、用户手机。我把踩过的坑整理成表,遇到问题直接对号入座。
| 现象 | 最可能的原因 | 解决方向 |
|---|---|---|
| JSAPI 下单报"openid 非法" | appid 和 openid 不匹配 | 检查换 openid 用的 appid 是否等于支付 appid |
| 前端拉不起支付面板 | 签名参数错误或时间戳过期 | 重新二次签名,检查 prepay_id 是否最新 |
| 支付成功但没开通会员 | 回调没收到或没验签通过 | 查日志,确认 notify_url 公网可达、验签通过 |
| 同一订单多次发货 | 没做幂等 | 用 order_no 唯一约束 + 状态条件更新 |
| 用户付了两次 | 按钮没防重复点击 | 前端置灰 + 后端同订单号拦截 |
这里额外说一句:调试阶段用沙箱环境。微信有专门的沙箱,可以在不花真钱的情况下跑通全流程。等沙箱跑稳了再切生产,能省掉很多真金白银的试错成本。
5.2 聊天链路问题排查
聊天最大的问题集中在"消息丢了""回复慢""重复回复"这几种。消息丢失通常是 WebSocket 断连导致,解决办法是消息 ID 递增加服务端补拉。回复慢要么是模型 API 波动,要么是你自己的队列堵了,前者换备用通道,后者加监控告警。重复回复多半是客户端重连后把没收到 ack 的消息又发了一遍,服务端要做去重。
还有一类问题特别隐蔽——内容安全拦截导致的"AI 突然不说话了"。你的模型可能正常返回了,但内容审核层把它拦了,前端什么都没收到,用户以为卡了。我的做法是审核不通过时返回一个兜底话术,并且给用户一个"换句话说说"的引导,而不是干瞪眼。
5.3 合规、上架与成本:提前算清楚这笔账
最后讲讲大家最关心的钱和资质。开发一个 App 并上架到底要花多少,我按自己的实际情况列个表:
| 项目 | 大概费用 | 说明 |
|---|---|---|
| 开发者账号 | 一次性/年费 | 各平台政策不同,以官方公示为准 |
| 软件著作权 | 几百到上千 | 上架部分平台可能需要 |
| 服务器 | 每月几十到几百 | 早期低配够用,随用户量升配 |
| 大模型 API | 按量付费 | 占运营成本大头,做好缓存和摘要能省很多 |
| 短信/推送 | 按量 | 验证码用得上 |
| 支付通道 | 按费率 | 每笔抽成,谈判空间不大 |
| 美工/图标 | 0 到几千 | 自己会设计就省了 |
真正的大头是AI API 费用和获客成本。前者靠技术优化压,后者靠内容运营省。我强烈建议早期别急着投推广,先把产品打磨到有人愿意主动推荐,再考虑花钱买量。
合规方面,这类涉及情感陪伴的产品,隐私政策、用户协议、未成年人保护提示一个都不能少,而且内容审核机制必须真做,不能糊弄。上架审核卡人基本都卡在这些地方。心理边界也要守住,产品里要明确提示"这是 AI 陪伴,不替代专业心理咨询",这是对用户负责,也是保护自己。
我个人的体会是,这个项目最难的从来不是某个具体的技术点,而是在无数个想放弃的深夜,逼自己把支付回调的验签调通、把人设卡改到第 18 版、把 openid 那个报错死磕到底。等第一笔真实订单进来、后台弹出"支付成功"那一刻,你会发现之前所有的卡壳都值了。如果你现在也处在一个迷茫期,与其刷手机焦虑,不如挑一个你真正想做的产品,哪怕只做出一个能跑通的支付闭环,你对整个工程体系的理解都会上一个台阶。做产品这条路没有捷径,但每一步都算数。