- Published on
AI 伴侣服务技术选型
- Authors
- Name
- Shelton Ma
1. 功能拆解
多模态的语音伴侣,主要涉及:
- 语音输入 -> 语音识别(ASR)
- 文本处理 -> AI 生成回复(LLM 推理)
- 语音输出 -> 语音合成(TTS)见: 多模态交互中的语音设计方案
- 实时性 -> WebSocket / Streaming 支持, 见: 使用 WebSocket 实现流式对话
- 对话管理 -> 记忆 & 上下文管理 见: 多轮对话管理中的滑动窗口机制
2. 后端设计
1. 语音输入 API
api
POST /api/speech-to-text Content-Type: audio/wav { file: <音频文件> }
技术实现
- 调用 OpenAI Whisper API
- 自建 Vosk / DeepSpeech / WeNet
2. AI 处理 API
api
POST /api/generate-text Content-Type: application/json { "session_id": "xxx", "user_id": "12345", "input_text": "你好呀!今天过得怎么样?", "context": ["之前的对话1", "之前的对话2"] }
技术实现
- 使用 OpenAI GPT-4o 或 Claude / Gemini
- 如果需要本地推理,可以部署 Llama3 / Mistral
- 会话管理 可用 MongoDB 存储对话上下文
3. 语音合成 API
api
POST /api/text-to-speech Content-Type: application/json { "text": "我今天也很开心!有什么想聊的吗?", "voice": "female_1" }
技术实现
- OpenAI TTS (高质量)
- ElevenLabs API
- Google TTS / AWS Polly
- 自建 VITS / FastSpeech2
4. 实时 WebSocket API
api
GET /api/chat-stream Sec-WebSocket-Key: xxx
WebSocket 事件:
- send_audio (用户上传语音)
- receive_text (后端返回识别文本)
- generate_response (大模型返回文本)
- synthesize_audio (合成语音流返回)
demo
import { Hono } from 'hono' import { streamText } from './llm' import { transcribeAudio, synthesizeSpeech } from './speech' const app = new Hono() app.get('/api/chat-stream', async (c) => { const ws = await c.upgrade() ws.onmessage = async (event) => { const { event: eventType, data } = JSON.parse(event.data) if (eventType === 'send_audio') { const text = await transcribeAudio(data) ws.send(JSON.stringify({ event: 'receive_text', data: text })) const responseText = await streamText(text) ws.send(JSON.stringify({ event: 'generate_response', data: responseText })) const audioData = await synthesizeSpeech(responseText) ws.send(JSON.stringify({ event: 'synthesize_audio', data: audioData })) } } }) export default app
3. 技术选型
组件 | 方案 |
---|---|
语音识别 | OpenAI Whisper / WeNet / DeepSpeech |
大模型推理 | OpenAI GPT-4o / Llama3 / Claude |
语音合成 | ElevenLabs / VITS / FastSpeech2 |
存储 | MongoDB / PostgreSQL |
通信 | REST API + WebSocket (Hono.js) |
实时流式传输 | WebRTC / Streaming API |
4. 存储方案确认
1. 默认存储文本(MongoDB 持久化/ES-30D)
- 客户回溯检索
- 生成摘要优化会话质量
- 记录对话日志,分析用户行为、识别 AI 生成的错误回复
2. 存储语音
- 设置7 天 / 30 天自动清理
- 节省存储空间, 且文本回溯体验较好
- 当用户回溯记录时: 直接展示文本, 若用户需要语音,可实时调用 TTS 生成语音
5. CI/CD
尽可能简洁、低成本、可快速迭代
基础设施包含: GitHub / Docker / MongoDB Atlas(SaaS)/ AWS S3 / AWS MSK / AWS Redis / AWS EC-2
1. GitHub Actions + Auto Scaling Group
创建ec2并配置弹性伸缩
配置 GitHub Secrets
在 GitHub 项目 Settings > Secrets and variables > Actions 中,添加Secrets
Secret 说明 AWS_ACCESS_KEY_ID AWS 访问密钥 AWS_SECRET_ACCESS_KEY AWS 密钥 AWS_REGION AWS 区域(如 us-east-1) ECR_REPOSITORY ECR 仓库名称 EC2_INSTANCE_ID EC2 实例 ID(单机测试时用) EC2_SSH_KEY 连接 EC2 的 SSH 私钥(仅用于远程执行 Docker 命令) GitHub Actions 工作流
// .github/workflows/deploy.yml name: Deploy to AWS EC2 on: push: branches: - main # 仅在推送到 main 分支时触发 jobs: deploy: runs-on: ubuntu-latest steps: - name: 🛠️ 拉取代码 uses: actions/checkout@v3 - name: 📦 登录 AWS ECR run: | aws configure set aws_access_key_id ${{ secrets.AWS_ACCESS_KEY_ID }} aws configure set aws_secret_access_key ${{ secrets.AWS_SECRET_ACCESS_KEY }} aws configure set region ${{ secrets.AWS_REGION }} aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.ECR_REPOSITORY }} - name: 🛠️ 构建 & 推送 Docker 镜像 run: | IMAGE_TAG=$(date +%s) docker build -t ${{ secrets.ECR_REPOSITORY }}:$IMAGE_TAG . docker tag ${{ secrets.ECR_REPOSITORY }}:$IMAGE_TAG ${{ secrets.ECR_REPOSITORY }}:latest docker push ${{ secrets.ECR_REPOSITORY }}:$IMAGE_TAG docker push ${{ secrets.ECR_REPOSITORY }}:latest echo "IMAGE_TAG=$IMAGE_TAG" >> $GITHUB_ENV - name: 🚀 连接 EC2 并更新容器 env: PRIVATE_KEY: ${{ secrets.EC2_SSH_KEY }} HOST: ${{ secrets.EC2_INSTANCE_IP }} USER: ubuntu run: | echo "$PRIVATE_KEY" > private_key.pem && chmod 600 private_key.pem ssh -o StrictHostKeyChecking=no -i private_key.pem $USER@$HOST << EOF aws ecr get-login-password --region ${{ secrets.AWS_REGION }} | docker login --username AWS --password-stdin ${{ secrets.ECR_REPOSITORY }} docker pull ${{ secrets.ECR_REPOSITORY }}:$IMAGE_TAG docker stop myapp || true docker rm myapp || true docker run -d --name myapp -p 80:3000 ${{ secrets.ECR_REPOSITORY }}:$IMAGE_TAG EOF