AI_Psyc_Project 是一个私有 Fork 项目。上游提供了最初的项目基础,我参与了当前应用的整体构建设计,并继续完成本地模型、实时会话、认证、进度和报告等部分的整合。

如果只把它理解成一个带语音输入的聊天网页,会漏掉这个项目真正想做的事。产品体验上,我希望它接近 GPT Live 或豆包电话:用户不用对着一张问卷逐项选择,而是通过连续的语音和视频对话完成心理测评。系统既要听懂说了什么,也要结合画面、语气和上下文决定下一轮怎么问,最后把整段会话整理成结构化结果。

这里不展开业务数据、测评量表、私有提示词和模型配置,只记录我怎样把多模态模型做成一套可以运行的应用。

从聊天框改成一次完整的测评会话

普通聊天应用只需要维护消息列表。实时心理测评还要处理麦克风、摄像头、语音播放、测评轮次、进度和报告状态。用户可能打断模型,也可能在模型说话时继续输入;网络中断后,页面不能把已经完成的轮次当成没发生过。

我把一次测评看成一条连续的数据链路:

flowchart LR
    A["浏览器:语音、视频与文字"] <--> B["Next.js 会话层"]
    B <--> C["FastAPI 实时网关"]
    C --> D["STT / 视觉 / 语气分析"]
    D --> E["多模态模型与测评逻辑"]
    E --> F["TTS 语音合成"]
    F --> C
    E --> G["测评进度与报告"]
    G --> H["认证与结果存储"]

浏览器采集语音和视频,后端完成语音识别、视觉理解和语气分析,再把这些信息交给负责对话与测评的模型。模型回复一边以文本返回,一边交给 TTS 合成语音。测评达到条件后,报告生成和结果保存才会启动。

这和“调用一次大模型接口”差别很大。模型只是中间一环,前后的媒体处理、状态同步和错误恢复同样决定体验能不能成立。

多模态能力怎样组织

本地 Python 后端把能力拆成 VisionService、STTService、ToneService、BrainService 和 MouthService。它们分别负责视觉、语音识别、语气分析、模型推理和语音合成,FastAPI 再负责对外组织接口。

我没有让这些服务互相读取页面状态。每一轮先形成明确输入,再把识别文本、视觉信息和必要的会话上下文送入模型。模型输出也不会直接塞进播放器,而是拆成文本、音频、轮次状态和测评进度。这样替换某个 STT 或 TTS 时,不需要重写整段对话逻辑。

在线模型与本地模型也收口到前端的 service 层。组件只提交统一的会话输入,具体使用 Gemini、OpenAI、Qwen 还是本地服务,由适配器处理 URL、鉴权、请求结构和返回格式。本地后端还提供 OpenAI 兼容接口,让已有调用路径可以复用。

实时通话最难的是状态

WebSocket 会连续返回识别结果、模型文本、合成音频、进度和报告就绪等消息。文本输出结束不等于这一轮已经结束,因为音频可能还在生成或播放。用户按下停止,也不代表后端任务已经取消。

我把前端状态拆成几个明确阶段:等待输入、采集中、识别中、生成中、播放中、被中止和发生错误。消息带有类型和完成标记,前端按协议更新状态,不再看到一个字符串就直接拼进对话框。

这套状态还要处理几个容易漏掉的边界:

  • 用户说话时打断正在播放的回答;
  • 页面刷新或连接断开后,不重复提交上一轮音频;
  • 文本已完成但 TTS 尚未完成时,不提前进入下一轮;
  • 测评进度由真实轮次推进,而不是定时动画;
  • 报告只在会话状态落定后生成,并能区分真实结果与演示数据。

后来项目也尝试把部分调用改成轻量的 HTTP 和 OpenAI 兼容模式。HTTP 更容易部署,WebSocket 更适合持续媒体流。两条路径保留下来,是应用从原型走向部署时的一次取舍,而不是简单的接口重复。

我怎样考虑本地模型部署

本地模式不是把 API 地址改成 localhost。模型启动时间、显存占用、音频格式、并发和服务退出都要有人负责。我的处理方式是让 FastAPI 作为统一入口,在启动阶段初始化需要的模型服务,前端不直接感知每个模型进程。

部署时我重点处理了这些问题:

  • 模型服务与 Web 前端分开运行,避免 Next.js 进程承担推理任务;
  • 使用统一适配层切换在线和本地模型,不把提供方判断散落在组件里;
  • 对长连接、超时、取消和异常建立统一消息协议;
  • 把认证、会话和测评结果交给 PocketBase 管理;
  • 私有模型地址、密钥和提示词保留在服务端,不下发到浏览器;
  • 允许部分能力缺失时降级,而不是让整次测评直接崩溃。

这部分工作让我真正接触到大模型应用部署的完整链路:浏览器媒体采集、实时传输、模型编排、服务生命周期、认证、持久化和结果展示。模型效果很重要,但模型之外的工程代码更多。

认证、隐私和使用边界

认证链路横跨 PocketBase、Next.js 服务端操作、Cookie 和客户端状态。登录接口返回成功,不代表每个页面已经拿到同一会话。我逐层检查 Cookie 写入、服务端读取、客户端初始化和退出清理,避免报告与错误用户绑定。

心理测评还涉及语音、图像和个人结果,部署时不能只考虑“接口能不能通”。原始媒体是否保存、保存多久、谁能读取、日志里是否包含敏感内容,都需要明确。项目定位也是辅助测评和交互验证,不把模型输出写成医疗诊断,更不能替代专业人员。

这次项目给我的经验

AI_Psyc_Project 最后形成了一套完整的大模型应用骨架:Next.js 负责用户交互,FastAPI 组织多模态能力,REST 与 WebSocket 处理不同类型的任务,PocketBase 管理认证和数据,本地与在线模型通过适配层切换。

我在这个项目里投入最多的并不是页面样式,而是让语音、视频、模型回复、测评进度和报告属于同一次会话。实时多模态应用一旦没有统一状态,任何一个看似很小的延迟或重连都会把体验打断。把这些边界理顺以后,才谈得上继续换模型、压缩延迟和部署到更多环境。