引言:为什么我要把 AIFriends 的架构和流程单独写成一份复习文档
如果你最近在准备 AI 应用方向的面试,或者正在从零搭一个带社交属性的 AI 产品,那 AIFriends 这个项目应该是个不错的参考样本。它不算特别复杂,但麻雀虽小五脏俱全——涵盖了用户体系、AI Agent 交互、消息推送、积分经济系统、管理后台这些典型模块,而且业务闭环是完整的。
说实话,我最初接到这个项目的技术梳理任务时,第一反应是“这不就是个套壳聊天应用吗”。但真正把架构图和业务流程逐层拆开之后,我才发现里面的设计取舍比预想中多得多。比如 AI 人格的会话上下文怎么管理、异步任务队列怎么设计、积分扣费怎么避免并发超扣、管理后台的审核流怎么和用户侧状态机联动——这些都是实际业务里绕不开的细节,也是面试官最喜欢深挖的点。
这份文档的定位是“复习用的”,所以我会刻意把每个模块的架构决策、流程节点、数据流转逻辑都讲透,而不是只罗列一堆技术名词。不管你是想照着这个思路做自己的 AI 社交产品,还是单纯想理解 agent 类项目的通用架构模式,这篇内容应该都能帮你省下不少瞎琢磨的时间。
1. 项目整体设计与架构思路拆解
1.1 项目定位与核心需求解析
AIFriends 本质上是一个“AI 虚拟好友社交平台”。用户注册之后,可以创建或者选择不同的 AI 好友角色,和这些角色进行多轮对话。这些 AI 好友不是简单的问答机器人,而是有固定人设、记忆能力、情感反馈的 agent 形态产品。
从业务需求倒推技术需求,核心要解决四件事:
- 多角色 AI 人格的管理:每个 AI 好友拥有独立的 prompt 模板、知识库、语气风格,甚至记忆片段
- 对话过程的稳定性:多轮对话不能丢上下文,消息要能实时送达,网络抖动不能直接吞消息
- 商业化闭环:免费用户每天有对话次数限制,付费用户解锁无限畅聊,需要一套积分/会员系统
- 内容安全:AI 生成内容不能失控,需要前置审核和后置举报机制
这个定位意味着架构不能只考虑“能跑”,还要考虑“能管”。很多 AI 应用死在两个地方,一个是上下文管理乱导致体验崩塌,另一个是商业化路径不清晰导致产品没法续命。AIFriends 的设计从一开始就把这两条线拉了进来。
1.2 整体架构分层与模块划分
整个系统采用前后端分离 + 微服务化的架构风格,但并没有一上来就拆十几二十个服务——那对小团队和快速迭代来说反而是灾难。它按照业务域拆成了六个核心服务:
| 服务模块 | 职责范围 | 关键技术点 |
|---|---|---|
| 用户服务 | 注册、登录、个人资料、好友关系 | JWT 鉴权、Redis 会话缓存 |
| AI Agent 服务 | 角色人格管理、对话生成、上下文管理 | LLM 接入层、Prompt 模板引擎、向量记忆库 |
| 消息服务 | 实时消息收发、消息持久化、已读回执 | WebSocket 长连接、消息队列削峰 |
| 订单/积分服务 | 积分充值、对话扣费、会员套餐 | 事务性扣费、幂等性保障 |
| 审核服务 | 用户输入侧敏感词、AI 输出侧合规过滤 | 异步审核队列、人工审核工作台 |
| 管理后台服务 | 角色管理、用户管理、数据看板、审核处理 | RBAC 权限模型、操作日志审计 |
每个服务独立部署、独立数据库,用 HTTP/REST 作为服务间同步调用的主要协议,异步场景走消息队列。没有引入太重的 Service Mesh,也没有强行上分布式事务,而是尽量把跨服务的数据一致性控制在“最终一致”的范围内。
提示:这个架构的聪明之处在于边界划分基本沿着“业务对象”走,而不是沿着“技术层次”走——按用户、对话、消息、钱、内容安全来分,每一个服务都能独立演进,这是新手做架构时最容易忽略的一点。
1.3 为什么选这个架构方案而不是单体应用
有人可能会问:一个 AI 聊天产品,单体应用不香吗?前期开发效率更高,部署也更简单。这个问题的答案在于“业务预期的变化方向”。
AI 对话类产品有三个天然特点:第一,LLM 调用的延迟和成本是波动的,需要独立的服务来做限流、降级、重试策略;第二,对话上下文可能包含大量的个性化记忆数据,存储层需要能够灵活扩容;第三,消息实时通道和业务 API 的负载特征完全不同——WebSocket 连接是长驻型的,而业务 API 是突发型的,放在同一个进程里互相拖累就是必然的。
所以在 AIFriends 的架构里,消息服务被单独拆了出来,这是一个非常正确的决策。长连接服务不会被业务接口的突发流量打垮,业务服务也不会因为消息广播的逻辑 bug 而全部宕机。你要做 AI 应用,消息通道和业务逻辑分离这条底线最好从一开始就守住。
2. 核心业务流程拆解与设计逻辑
2.1 用户从注册到首次对话的完整链路
一个用户从进入产品到产生第一段对话,背后走的是这样一条链路:
- 用户通过手机号或第三方账号注册,用户服务创建账号并返回 JWT Token
- 前端携带 Token 调用 AI Agent 服务,拉取可选择的 AI 好友列表
- 用户选择某个 AI 好友,点击“开始聊天”
- 前端发起 WebSocket 连接,消息服务完成连接鉴权和会话绑定
- 用户发送第一条消息,消息服务先落库,再转发给 AI Agent 服务
- AI Agent 服务加载该好友的人格 prompt、历史记忆、当前会话上下文,调用 LLM 生成回复
- 回复内容先送审核服务做合规检查,通过后由消息服务推送给用户
这个流程看着不复杂,但有两个细节值得展开。
第一个细节是“先落库再转发”。很多初版实现图省事,直接走内存转发,消息一旦服务重启就丢。AIFriends 的做法是用户消息到达消息服务之后立刻写数据库,状态标记为“已发送”,等 AI 回复生成后再把这一轮会话的状态更新为“已完成”。这样即使中间任何一环挂了,消息也不会丢,用户刷新页面还能看到历史记录。
第二个细节是 AI 回复的审核时机。这里是在“生成后、推送前”审核,不是生成前拦截。原因很简单:生成前的 prompt 审核只能拦住输入侧的问题,但 LLM 的输出不可完全预测,所以输出侧必须有一道独立检查。虽然这样会增加用户等待时间,但安全合规这条线不能省。
2.2 AI 对话流程中的上下文管理与记忆机制
AI 好友和普通聊天机器人的最大区别在于“记忆”。AIFriends 的 agent 设计里,记忆分为三个层次:
- 短期会话记忆:存储在当前 session 内,记录最近的对话轮次,保存在 Redis,过期时间 30 分钟
- 长期用户记忆:记录用户的基本喜好、性格标签、说过的重要信息,写入向量数据库
- 角色设定记忆:AI 好友自身的人设背景、说话风格、知识边界,作为系统级 prompt
每次用户发消息时,AI Agent 服务会执行一个“记忆组装”流程:从 Redis 取出短期会话记录,从向量库检索与当前话题相关的长期记忆片段,然后把角色设定、短期上下文、相关记忆拼装成最终的 prompt 发送给 LLM。
这个设计解决了一个核心矛盾:LLM 的上下文窗口是有限的,你不能把用户全部历史对话都塞进去,必须做有选择性的提取。向量检索在这里起到的作用就是“只挑和当前话题有关的记忆”,既控制 token 数量,又让回复看起来是“记得你”的。
注意:记忆检索是 AI 社交产品体验的分水岭。很多团队前期为了省事,只往 prompt 里塞最近 N 轮对话,结果 AI 聊了三天就把用户的生日、喜欢的音乐类型全忘了,用户立刻就会觉得“这是个假 AI”。记忆机制不是锦上添花,是产品能不能留住用户的关键。
2.3 积分扣费与会员体系的流程设计
商业模式部分,AIFriends 走的是“免费次数 + 积分充值 + 会员订阅”三者结合的路子。积分扣费流程是这里面最容易出并发问题的地方。
每次用户发送一条消息,会先经过积分服务的预扣费操作。这个预扣费不是直接扣余额,而是先冻结对应积分,等 AI 回复成功推送给用户后再转正式扣费;如果 AI 生成失败,则解冻退回。这样做的好处是避免“用户发了消息但 AI 没回复,钱却已经扣了”的客诉。
在技术实现上,扣费用的是 Redis 的 Lua 脚本做原子操作,先检查余额再扣减,保证并发场景下不会超扣。数据库层面记录每一笔积分流水,方便后续对账和客服查询。
会员体系则简单一些:会员用户在有效期内不限制对话次数,但会有每日最大消息数的风控上限,防止接口被脚本刷爆。会员状态存在用户服务的缓存里,AI Agent 服务每次收到对话请求时会校验会员资格。
这个积分流程设计里最值得学习的一点是“预冻结”的思路。真实业务里凡是涉及“扣钱 + 异步结果”的场景,都应该考虑这个模式。直接先扣款再退款也不是不行,但用户体验和客服压力完全不是一个量级。
3. 关键模块的技术实现与实操要点
3.1 AI Agent 服务中 Prompt 模板的角色管理实现
AI 好友的人格差异完全靠 Prompt 工程来实现。AIFriends 里每个 AI 角色对应一个 Prompt 模板,模板不是死字符串,而是支持变量的结构化配置。
举一个实际的模板片段:
你是{character_name},年龄{age},性格{personality}。 你正在和用户进行一场{relationship_type}的对话。 【背景记忆】 {memory_snippets} 【近期对话】 {chat_history} 【用户画像】 {user_profile} 请用{style_guide}的风格回复,回复长度控制在{max_tokens}字以内。这里的变量分别从角色配置表、记忆检索结果、会话上下文、用户画像服务中动态填充。模板本身存放在数据库里而不是硬编码在代码里,这样运营人员可以在管理后台直接调整某个 AI 角色的人设,不需要重新发版。
技术实现上有一个容易踩坑的点:模板变量的注入顺序会影响 LLM 的输出质量。AIFriends 的实测经验是“角色设定放最前面,用户画像放最后”效果最好,因为 LLM 对 prompt 开头和结尾的信息注意力更强,把最核心的人设约束放在开头,把对当前回复影响最大的用户信息放在结尾,回复质量的稳定性会有明显提升。
3.2 消息服务的推送机制与接口设计
消息服务需要同时支持 WebSocket 长连接和 HTTP 回调两种消息下发方式。WebSocket 用于实时推送,HTTP 回调主要用于第三方渠道或者前端断线重连后的消息补偿拉取。
实际的接口设计大概是这样的:
// 发送消息请求 POST /api/v1/chat/message { "session_id": "uuid", "session_type": "ai_friend", "content": "今天心情不太好", "message_type": "text" } // 响应 { "message_id": "uuid", "status": "pending", "estimated_reply_time_ms": 3500 }这里用 message_id 做全链路的追踪标识。消息从客户端发出到 AI 回复返回,中间经过消息服务、Agent 服务、审核服务,所有的状态变更都通过这个 message_id 关联。前端可以轮询或者通过 WebSocket 推送收到状态更新,当状态变为“completed”时渲染 AI 回复。
关于 WebSocket 连接管理,AIFriends 用了 Redis Pub/Sub 做多实例消息广播。单台实例只维护自己节点上的客户端连接,但服务端要推送某条消息时,通过 Redis 频道广播,所有实例收到后只推送给本地持有对应 session 的连接。这个方案比自研一套消息路由协议简单得多,实测在几千并发连接下完全够用。
3.3 审核服务的异步处理链路
审核服务在 AIFriends 里是独立部署的,没有嵌在 Agent 服务的同步调用链里。设计成异步的原因很简单:LLM 回复已经要花 2-5 秒了,如果审核再同步加 500 毫秒,用户体感会明显变差。
异步审核的流程是:
- AI Agent 服务生成回复后,把回复内容投递到审核消息队列
- 审核服务消费队列,先做机器敏感词过滤,再调用内容安全 API 做语义级检测
- 如果自动审核通过,直接把消息推送给用户;如果疑似违规,进入人工审核队列
- 人工审核完成后,审核结果通过回调接口告知消息服务,决定放行还是拦截
这个链路里有个取舍问题:用户体验和合规风险怎么平衡。AIFriends 的做法是“低风险秒放行,高风险进人工”。机器审核认为没有问题的消息直接推送,概率极低的高危内容即使要等人工审核,也绝对不能放出来。
还有一个细节是审核服务的降级策略。如果内容安全 API 调用超时,不能无限阻塞消息推送,可以设置一个超时阈值(比如 800ms),超时后先标记为“待复核”放行消息,后续在后台异步补充审核。属于业务向安全做适当妥协的经典做法,不能因为审核系统的问题把整个对话功能拖死。
4. 实操过程与核心环节实现复盘
4.1 从零搭建环境与依赖服务
如果你要复现这套架构,本地开发环境建议这样搭建。
基础设施部分,Docker Compose 是起步的正确选择。AIFriends 依赖的中间件主要包括:PostgreSQL(业务数据)、Redis(缓存和 Pub/Sub)、RabbitMQ(异步任务队列)、Milvus 或 Chroma(向量记忆库)、MinIO(对象存储,用于存用户头像和对话附件)。
按依赖顺序启动容器:
# 1. 启动基础设施 docker compose up -d postgres redis rabbitmq minio # 2. 启动向量数据库(无 GPU 需求,CPU 模式即可) docker compose up -d chroma # 3. 初始化数据库表结构 cd services/user-service && alembic upgrade head cd services/message-service && alembic upgrade head # 4. 启动各个微服务 cd services/ai-agent && python app.py --port 8001 cd services/message && python app.py --port 8002 cd services/order && python app.py --port 8003一个容易踩坑的点是数据库初始化顺序。user-service 和 message-service 之间有外键关联吗?从架构上看没有——各服务库都是独立的,所以不存在严格的建表顺序依赖。但如果你的实现里跨库引用了,记得先起的服务要容忍关联表暂不存在的异常,或者用事件机制等依赖服务就绪。
4.2 核心服务间的接口约定与联调记录
服务间通信的接口约定是整个项目里最需要提前锁死的东西。我在实际联调中吃过亏,两个服务各自开发,到联调阶段发现字段命名不一致、状态码语义不一致,返工成本极高。
AIFriends 的约定大致如下:
- 服务间 API 统一走 /api/v1/ 前缀,内部调用带 internal-token 头,与用户侧 JWT 区分
- 用户 ID 和会话 ID 统一用雪花算法生成,不用数据库自增 ID,避免跨服务暴露业务量
- 状态码统一使用 0 表示成功,非 0 为业务错误码,HTTP 层面只区分 2xx 和 5xx
- 关键链路(发送消息、扣费)必须打印链路 TraceID,日志格式统一为 JSON
联调时最耗时间的是 WebSocket 消息时序问题。我在本地模拟了弱网环境做测试,发现偶发的消息延迟会打乱前端渲染顺序——用户发了消息 A 和 B,AI 先回复了 B 再回复 A,对话顺序就乱了。解决办法是在消息体里加一个 client_msg_seq 字段,前端本地维护递增序号,渲染时先按 seq 排序再展示。这个字段在初版设计里完全没考虑,属于联调时踩坑后补的。
4.3 LLM 接入层设计与 Key 池管理
AI Agent 服务的核心是 LLM 接入层,这块做得不好,再好的 prompt 也白搭。AIFriends 没有直接在各处硬编码 LLM API 调用,而是抽象出了一个统一的 LLM Gateway。
这个 Gateway 做了三件事:
- 多厂商模型路由:不同 AI 角色可以配置不同的模型(比如知识型角色用更强的模型,闲聊型角色用更快更便宜的模型),Gateway 根据角色配置做路由
- Key 池化管理:多个 API Key 轮询使用,某个 Key 触发限流时自动切换下一个,并标记该 Key 冷却
- 超时重试与降级:单次 LLM 调用最长等待 15 秒,超时后自动重试一次(切换 Key),仍失败则返回友好话术给用户,并把这条消息标记为“ai 不可用”
Key 池管理是很多团队会忽视的模块。LLM 供应商的限流策略和成本控制直接关系到项目能跑多久,Key 被限流了没有自动切换机制,整个对话功能就是瘫痪的。这个模块用最简单的轮询 + 冷却期就够了,不必引入太复杂的负载均衡策略。
4.4 部署架构与配置管理的实战建议
部署方面,AIFriends 用 Docker Compose 做单机编排,生产环境建议升级到 Kubernetes。但要注意的是,微服务架构里服务拆得越多,部署和排障成本越高。
我的建议是:如果你的团队只有两三个人,前期用 Docker Compose 部署在单台 4C16G 的服务器上,撑住几千 DAU 完全没问题。等用户量起来之后再迁 K8s,利用 namespace 和 deployment 的滚动更新能力来降低发版风险。
配置管理上,强烈建议用环境变量 + 配置中心的方式,不要硬编码在代码里或者写死在配置文件中。至少要把数据库连接串、Redis 地址、LLM API Key 这些敏感配置单独抽离,生产环境用 K8s Secret 或在配置中心加密存储。
5. 常见问题与排查技巧实录
5.1 消息丢失与重复推送问题
这类问题在我实际运行中遇到得最多,尤其是消息丢失。
现象:用户发了一条消息,客户端状态一直停在“发送中”,数据库里没有这条记录。
排查路径:
- 先看 Nginx 和网关的访问日志,确认请求是否到达消息服务
- 确认消息服务日志里有没有对应的消息事件
- 查数据库对应表,看是否有记录但状态异常
- 如果服务日志都没有,基本可以断定是前端没发出来或者网关丢包,优先查前端逻辑
处理方案:客户端新增失败重试机制,超时 5 秒自动重发,并带上 client_msg_seq 去重;服务端在消息表增加唯一索引(session_id + client_msg_seq),从根本上杜绝重复入库。
重复推送的问题更隐蔽。用户发送一条消息,AI 回复生成后 WebSocket 推送了一次,前端断线重连后 HTTP 补偿拉取又拿到了一次,导致对话界面出现两条相同的回复。解决办法是在前端维护已渲染消息的 ID 集合,渲染前先查重。
5.2 LLM 响应超时与假死问题
AI Agent 服务调用外部 LLM 时,最常见的故障就是响应超时。这里的超时场景和常规数据库超时不一样,外部 LLM 服务可能因为自身负载高而响应极慢,或者长时间没有返回任何流式数据。
排查建议:
- 给 LLM 调用设置多级超时:连接超时(3 秒)、首 token 等待超时(10 秒)、整体响应超时(30 秒)
- 对同时发起的并发请求数做信号量控制,超出排队等待,防止 LLM API 被瞬时打满
- 流式模式下,如果超过 30 秒没有任何 token 返回,直接中断该次生成,返回一个兜底文案给用户
还有一个容易被忽略的点:LLM 调用线程池的大小设置不合理,会导致服务整体线程阻塞。AIFriends 踩过这个坑,最初设置的线程池太小,某个时刻用户量上来,所有线程都阻塞在 LLM 调用上,健康检查接口都没有空闲线程响应了。解决方法是把线程池换成 IO 密集型的 ThreadPool,并且给健康检查接口单独留出线程配额。
5.3 积分扣费不一致的排查记录
积分扣费不一致主要体现在三种情况:
- 用户发送消息后,余额扣了但 AI 回复没有生成
- 用户并发发送多条消息,只成功扣了部分积分
- 对账发现积分流水和订单记录金额不匹配
第一种情况在预冻结模式下很少出现,但如果出现,大概率是冻结转扣费的流程没走完。排查时看订单表的状态,如果订单停在“frozen”状态超过 30 分钟,手动补一个任务把它转成“failed”并解冻。
第二种情况一般是 Redis Lua 脚本在并发下执行了正确性校验,但 application 层把返回结果做了错误的空值处理。这种问题需要看具体代码,但排查思路是:在测试环境用并发工具(比如 jmeter 或 go-wrk)模拟 50 并发发消息,看 Redis 的扣减记录和数据库的流水是否一致。
5.4 审核服务堆积导致消息延迟推送
当内容安全 API 调用波动时,审核队列会积压,导致 AI 回复迟迟推不到用户端。
处理优先级从高到低:
- 检查内容安全 API 的调用成功率,如果 API 本身降级了,自动切换备用供应商
- 调整审核服务的消费并发数,加大消费线程池,把积压消化掉
- 动态调整超时阈值,把自动放行的判断标准适当放宽(比如超时 1 秒就标记待复核放行)
- 人工审核队列单独限流,防止操作员处理不过来之后积压到爆
这个问题的核心是“审核不能阻塞主流程”。AI 对话产品对实时性的要求很高,一条消息 10 秒内不回复,用户基本就流失了。所以审核链路一定要和业务主链路解耦,宁可放进来再处理,也不能让用户一直等。
5.5 新增 AI 角色的配置冷启动
运营在管理后台新建一个 AI 角色,用户端立刻就能看到并开始聊天,但实际体验可能会很差——因为这个角色还没有积累任何记忆,也缺少对话样本。这就需要一个冷启动策略。
AIFriends 的做法是给新角色填充“种子对话数据”:在角色上线前,运营配置 20-30 组预设问答对,写入角色的记忆库作为初始记忆。这样用户第一次和新角色聊天时,角色就已经“知道”一些关于自己的背景信息,不会出现一问三不知的情况。
技术上实现很简单,就是往向量库写入一批初始文档,同时更新角色配置表里的 greeting_message 和 初始人设关键词。
这里有一个典型坑:新角色上线后,由于没有历史对话数据,向量检索可能返回空结果,Prompt 模板里的 memory_snippets 变量为空,导致 LLM 生成的回复极其干瘪。所以模板渲染时要兼容空变量的情况,为空时直接省略该段提示,而不是输出一行空字段。
5.6 问题排查速查表
| 症状 | 可能原因 | 快速排查方向 |
|---|---|---|
| 用户消息发送失败 | WebSocket 连接断开 | 检查实例连接数、Redis Pub/Sub 频道是否存在 |
| 消息收到但 AI 未回复 | Agent 服务调用 LLM 超时 | 查看 LLM Gateway 的日志和耗时指标 |
| AI 回复了但用户没收到 | 审核服务拦截或推送失败 | 查审核队列状态和消息服务推送日志 |
| 积分被扣但 AI 没回复 | 预冻结后未正常转扣费 | 查订单状态,手动补偿解冻 |
| 用户反馈 AI 记忆混乱 | 向量检索质量低或上下文拼接错误 | 查记忆检索排名情况和 Prompt 组装结果 |
| 管理后台角色修改未生效 | 角色配置缓存未刷新 | 检查 Redis 缓存 key 的过期策略和手动刷新接口 |
6. 踩坑记录与架构演进复盘
6.1 初版单体应用到微服务拆分的迁移逻辑
AIFriends 最初的核心对话功能其实是一个单体应用,代码量到两万行左右时就明显吃力了。最典型的问题是:消息推送的 WebSocket 连接和业务 API 共享进程,一旦某个业务接口出现慢查询,GC 停顿时间变长,WebSocket 心跳就会断,客户端表现为“掉线”。
迁移到微服务之后,最直接的收益不是性能提升了多少,而是故障隔离做得更好了。消息服务宕机,用户还能正常登录浏览;Agent 服务依赖的 LLM 供应商故障,其他服务完全不受影响。这种隔离能力对在线业务来说比单纯的性能优化重要得多。
另外,拆分之后每个服务的数据结构可以做针对性优化。用户服务用关系型存储用户画像,Agent 服务用向量库支撑记忆检索,订单服务用事务性数据库保证资金安全。数据模型跟着业务属性走,而不是被一个“大而全”的库绑架。
6.2 数据一致性与多服务事务的取舍
微服务架构里最烦人的问题是跨服务的数据一致性。AIFriends 没有引入分布式事务框架,而是用“本地事务 + 消息事件”的模式保证最终一致。
举例来说,用户充值的流程:
- 订单服务在本地库创建订单,状态为“待支付”
- 支付回调成功后,订单服务更新订单状态,并发送“支付成功”事件到消息队列
- 积分服务消费事件,给用户增加积分,并写入积分流水
如果步骤 2 之后、步骤 3 之前积分服务恰好宕机,用户支付了但积分没到账。解决方式是引入一个对账补偿任务——定时扫描“支付成功但积分未增加”的订单,重新发送事件。这种模式的代码量不大,但能覆盖绝大多数故障场景,比引入 Seata 这类分布式事务中间件的成本低得多。
6.3 我对 AI 社交类项目架构扩展性的心得
这类项目的扩展路线,大致可以分三个阶段。第一个阶段是把核心对话链路跑通,重点在于 prompt 质量和记忆机制;第二个阶段是完善商业化和安全体系,积分、会员、审核、管理后台一个都不能少;第三个阶段才是规模化,引入更多的 AI 角色类型、多人聊天室、UGC 社区等。
如果一开始就按第三个阶段的标准做架构设计,大概率会过度设计。AIFriends 的做法是我比较认同的:按业务自然增长逐步演进,但每个阶段都预留好扩展点——比如 Prompt 模板从一开始就可以配置化,向量记忆库一开始就独立存储,消息服务从一开始就支持多实例广播。
我个人在实际项目中的体会是,AI 产品的架构难点从来不在“怎么调用大模型”,而在于怎么把大模型的输出安全、稳定、商业化地融入到你现有的业务体系里。Prompt 写得好,可以让一个角色讨人喜欢;但架构设计得好,才能让一百万个角色同时活着,且不出乱子。