Published on

AI 伴侣服务技术选型

Authors
  • avatar
    Name
    Shelton Ma
    Twitter

1. 功能拆解

多模态的语音伴侣,主要涉及:

2. 后端设计

1. 语音输入 API

  1. api

    POST /api/speech-to-text
    Content-Type: audio/wav
    
    { file: <音频文件> }
    
  2. 技术实现

    1. 调用 OpenAI Whisper API
    2. 自建 Vosk / DeepSpeech / WeNet

2. AI 处理 API

  1. api

    POST /api/generate-text
    Content-Type: application/json
    
    {
      "session_id": "xxx",
      "user_id": "12345",
      "input_text": "你好呀!今天过得怎么样?",
      "context": ["之前的对话1", "之前的对话2"]
    }
    
  2. 技术实现

    • 使用 OpenAI GPT-4o 或 Claude / Gemini
    • 如果需要本地推理,可以部署 Llama3 / Mistral
    • 会话管理 可用 MongoDB 存储对话上下文

3. 语音合成 API

  1. api

    POST /api/text-to-speech
    Content-Type: application/json
    
    {
      "text": "我今天也很开心!有什么想聊的吗?",
      "voice": "female_1"
    }
    
  2. 技术实现

    • OpenAI TTS (高质量)
    • ElevenLabs API
    • Google TTS / AWS Polly
    • 自建 VITS / FastSpeech2

4. 实时 WebSocket API

  1. api

    GET /api/chat-stream
    Sec-WebSocket-Key: xxx
    
  2. WebSocket 事件:

    • send_audio (用户上传语音)
    • receive_text (后端返回识别文本)
    • generate_response (大模型返回文本)
    • synthesize_audio (合成语音流返回)
  3. 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)

  1. 客户回溯检索
  2. 生成摘要优化会话质量
  3. 记录对话日志,分析用户行为、识别 AI 生成的错误回复

2. 存储语音

  1. 设置7 天 / 30 天自动清理
  2. 节省存储空间, 且文本回溯体验较好
  3. 当用户回溯记录时: 直接展示文本, 若用户需要语音,可实时调用 TTS 生成语音

5. CI/CD

尽可能简洁、低成本、可快速迭代

基础设施包含: GitHub / Docker / MongoDB Atlas(SaaS)/ AWS S3 / AWS MSK / AWS Redis / AWS EC-2

1. GitHub Actions + Auto Scaling Group

  1. 创建ec2并配置弹性伸缩

  2. 配置 GitHub Secrets

    在 GitHub 项目 Settings > Secrets and variables > Actions 中,添加Secrets

    Secret说明
    AWS_ACCESS_KEY_IDAWS 访问密钥
    AWS_SECRET_ACCESS_KEYAWS 密钥
    AWS_REGIONAWS 区域(如 us-east-1)
    ECR_REPOSITORYECR 仓库名称
    EC2_INSTANCE_IDEC2 实例 ID(单机测试时用)
    EC2_SSH_KEY连接 EC2 的 SSH 私钥(仅用于远程执行 Docker 命令)
  3. 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