康心伴Logo
康心伴WellAlly
Health

构建语音日记应用:Whisper API与Node.js集成指南 | WellAlly康心伴

5 分钟阅读

构建语音日记应用:Whisper API与Node.js集成指南

概述

语音日记(Voice Journal)是通过语音输入记录日常想法和感受的健康应用。相比文字日记,语音更自然、更能捕捉情感。

使用OpenAI的Whisper API,我们可以实现高精度的语音转文字功能。


系统架构

正在渲染图表...
flowchart LR
    A[React Native<br/>移动端] -->|录音| B[Node.js<br/>后端]
    A -->|API调用| B
    B --> C[Whisper API]
    B --> D[(PostgreSQL<br/>文本存储)]

    style A fill:#e3f2fd,stroke:#1976d2,stroke-width:2px
    style B fill:#f3e5f5,stroke:#7b1fa2,stroke-width:2px
    style C fill:#fff3e0,stroke:#f57c00,stroke-width:2px
    style D fill:#e8f5e9,stroke:#388e3c,stroke-width:2px

数据流程说明:

  1. 录音阶段:React Native 移动端录制音频
  2. API调用:将音频上传到 Node.js 后端
  3. 语音转文字:后端调用 Whisper API 进行转录
  4. 数据存储:将转录文本存储到 PostgreSQL 数据库

前端录音实现

React Native录音组件

code
// VoiceRecorder.tsx
import React, { useState, useEffect } from 'react';
import { Audio } from 'expo-av';
import * as FileSystem from 'expo-file-system';

export function VoiceRecorder() {
  const [recording, setRecording] = useState(false);
  const [audioUri, setAudioUri] = useState<string | null>(null);

  const [recordingInstance, setRecordingInstance] = useState<Audio.Recording>();

  async function startRecording() {
    try {
      console.log('Requesting permissions..');
      await Audio.requestPermissionsAsync();

      // 设置录音配置
      const recording = new Audio.Recording();
      setRecordingInstance(recording);

      await recording.prepareToRecordAsync({
        android: {
          extension: '.m4a',
          outputFormat: Audio.AndroidAudioOutputFormat.MPEG_4,
          audioEncoder: Audio.AndroidAudioEncoder.AAC,
        },
        ios: {
          extension: '.m4a',
          outputFormat: Audio.IOSAudioFormat.MPEG_4AAC,
          audioQuality: Audio.IOSAudioQuality.HIGH,
        },
        web: {
          mimeType: 'audio/webm',
        },
      });

      await recording.startAsync();
      setRecording(true);
    } catch (err) {
      console.error('Failed to start recording', err);
    }
  }

  async function stopRecording() {
    if (!recordingInstance) return;

    console.log('Stopping recording..');
    setRecording(false);

    await recordingInstance.stopAndUnloadAsync();
    const uri = recordingInstance.getURI();
    setAudioUri(uri);

    // 上传到服务器
    await uploadAudio(uri);
  }

  async function uploadAudio(uri: string) {
    const formData = new FormData();
    formData.append('audio', {
      uri,
      name: 'voice.m4a',
      type: 'audio/m4a',
    } as any);

    try {
      const response = await fetch('http://localhost:3000/api/transcribe', {
        method: 'POST',
        body: formData,
      });

      const result = await response.json();
      console.log('Transcription:', result.text);
    } catch (error) {
      console.error('Upload failed:', error);
    }
  }

  return (
    <View>
      <Button
        title={recording ? '停止录音' : '开始录音'}
        onPress={recording ? stopRecording : startRecording}
      />
      {audioUri && <Text>录音已保存</Text>}
    </View>
  );
}
Code collapsed

后端API实现

Express服务器 + Whisper集成

code
// server.js
const express = require('express');
const multer = require('multer');
const FormData = require('form-data');
const fs = require('fs');
const { Configuration, OpenAIApi } = require('openai');

const app = express();
const upload = multer({ dest: 'uploads/' });

// OpenAI配置
const configuration = new Configuration({
  apiKey: process.env.OPENAI_API_KEY,
});
const openai = new OpenAIApi(configuration);

// 音频转录端点
app.post('/api/transcribe', upload.single('audio'), async (req, res) => {
  try {
    const audioFile = req.file;

    // 读取音频文件
    const audioData = fs.createReadStream(audioFile.path);

    // 调用Whisper API
    const response = await openai.createTranscription({
      file: audioData,
      model: 'whisper-1',
      language: 'zh', // 中文
      response_format: 'text',
    });

    const transcribedText = response.data.text;

    // 保存到数据库
    await saveJournalEntry({
      text: transcribedText,
      audioPath: audioFile.filename,
      timestamp: new Date(),
    });

    res.json({
      success: true,
      text: transcribedText,
    });

    // 删除临时文件
    fs.unlinkSync(audioFile.path);

  } catch (error) {
    console.error('Transcription error:', error);
    res.status(500).json({ error: 'Transcription failed' });
  }
});

// 获取日记列表
app.get('/api/journals', async (req, res) => {
  try {
    const entries = await JournalEntry.findAll({
      order: [['timestamp', 'DESC']],
      limit: 20,
    });

    res.json(entries);
  } catch (error) {
    res.status(500).json({ error: 'Failed to fetch journals' });
  }
});

app.listen(3000, () => {
  console.log('Server running on port 3000');
});
Code collapsed

数据库模型

Sequelize模型定义

code
// models/JournalEntry.js
const { DataTypes } = require('sequelize');

module.exports = (sequelize) => {
  const JournalEntry = sequelize.define('JournalEntry', {
    id: {
      type: DataTypes.INTEGER,
      primaryKey: true,
      autoIncrement: true,
    },
    text: {
      type: DataTypes.TEXT,
      allowNull: false,
    },
    audioPath: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    timestamp: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: DataTypes.NOW,
    },
    mood: {
      type: DataTypes.STRING,
      allowNull: true,
    },
    tags: {
      type: DataTypes.JSON,
      allowNull: true,
    },
    createdAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: DataTypes.NOW,
    },
    updatedAt: {
      type: DataTypes.DATE,
      allowNull: false,
      defaultValue: DataTypes.NOW,
    },
  });

  return JournalEntry;
};
Code collapsed

关键功能

1. 情感分析

code
// 分析日记的情感倾向
async function analyzeSentiment(text) {
  // 可以使用情感分析API或本地模型
  const response = await openai.createCompletion({
    model: 'gpt-4',
    messages: [
      {
        role: 'system',
        content: '分析以下日记的情感倾向(积极/中性/消极)并提取关键词:',
      },
      {
        role: 'user',
        content: text,
      },
    ],
    });

  return response.choices[0].message.content;
}
Code collapsed

2. 摘要生成

code
// 生成日记摘要
async function generateSummary(entries) {
  const allText = entries.map(e => e.text).join('\n');

  const response = await openai.createCompletion({
    model: 'gpt-4',
    messages: [
      {
        role: 'system',
        content: '为以下日记条目生成简短摘要(100字以内):',
      },
      {
        role: 'user',
        content: allText,
      },
    ],
  });

  return response.choices[0].message.content;
}
Code collapsed

关键要点

  1. Whisper API支持多种语言:包括中文
  2. 音频文件需合理压缩:平衡质量和传输速度
  3. 后端处理转录文本:情感分析、摘要生成等
  4. 安全存储音频和文本:隐私保护
  5. 提供文字编辑功能:修正识别错误

常见问题

Whisper API价格如何?

  • 按分钟计费:$0.006/分钟
  • 100小时约$36:适合个人项目

如何提高识别准确率?

  1. 使用高质量麦克风
  2. 环境安静时录音
  3. 说话清晰、靠近麦克风
  4. 后处理文本修正

支持哪些音频格式?

MP3、M4A、WAV、OGG等主流格式


参考资料

  • OpenAI Whisper API文档
  • React Native Audio API
  • Express文件上传处理

发布日期:2026年3月8日 最后更新:2026年3月8日

免责声明: 本内容仅供教育参考,不能替代专业医疗建议。请咨询医生获取个性化诊断和治疗方案。

#

文章标签

语音识别
Node.js
Whisper API
日记应用
AI集成

觉得这篇文章有帮助?

立即体验康心伴,开始您的健康管理之旅