我有一个习惯——通勤路上用手机录音记想法。录了三个月,积压了 80 多条没整理。

3 月的某个周末我花半天写了一个工具:语音 → 转文字 → LLM 整理 → 念给我听。 现在每天下班路上录完,到家时手机上已经有一份整理好的笔记了。

这篇文章把这个工具的完整 Go 代码给你。复制下来就能跑。


整体流程

手机录音(.m4a / .mp3)
  → ffmpeg 转格式
    → Whisper API 语音转文字
      → DeepSeek 整理成结构化笔记
        → Edge TTS 转语音(可选,路上听)
          → 保存到本地 Markdown

三块核心功能:语音识别 + LLM 整理 + 语音合成。每一步都有 Go 代码。


第一步:语音转文字 —— 调 Whisper API

OpenAI 的 Whisper API 是中文语音识别准确率最高的服务之一。当然你也可以用本地 Whisper 模型,但 API 省显卡:

package main

import (
    "bytes"
    "encoding/json"
    "fmt"
    "io"
    "mime/multipart"
    "net/http"
    "os"
    "os/exec"
    "path/filepath"
    "strings"
)

func convertToMp3(inputPath string) (string, error) {
    outputPath := strings.TrimSuffix(inputPath, filepath.Ext(inputPath)) + ".mp3"
    cmd := exec.Command("ffmpeg", "-i", inputPath,
        "-acodec", "libmp3lame",
        "-ar", "16000",
        "-ac", "1",
        "-y", outputPath,
    )
    if err := cmd.Run(); err != nil {
        return "", fmt.Errorf("ffmpeg 转换失败: %w", err)
    }
    return outputPath, nil
}

func transcribeAudio(audioPath string) (string, error) {
    apiKey := os.Getenv("OPENAI_API_KEY")

    // 构建 multipart 请求
    var buf bytes.Buffer
    w := multipart.NewWriter(&buf)

    // 添加文件
    file, _ := os.Open(audioPath)
    defer file.Close()
    part, _ := w.CreateFormFile("file", filepath.Base(audioPath))
    io.Copy(part, file)

    // 添加参数
    w.WriteField("model", "whisper-1")
    w.WriteField("language", "zh") // 中文
    w.WriteField("response_format", "text")
    w.Close()

    req, _ := http.NewRequest("POST",
        "https://api.openai.com/v1/audio/transcriptions",
        &buf,
    )
    req.Header.Set("Authorization", "Bearer "+apiKey)
    req.Header.Set("Content-Type", w.FormDataContentType())

    resp, err := http.DefaultClient.Do(req)
    if err != nil {
        return "", err
    }
    defer resp.Body.Close()

    text, _ := io.ReadAll(resp.Body)
    return string(text), nil
}

实测效果——我录了一段 45 秒的中文口述:

输入(录音):

"今天想到一个点子,就是可以把那个daily report agent
扩展一下,让它不只是读Git提交,还能读Jira的工单状态
...不对不对,应该是先读Jira再读Git,因为有的时候
Git提交里没有写清楚这个需求对应的是哪个Task"

Whisper 输出:

今天想到一个点子 就是可以把那个daily report agent
扩展一下 让它不只是读Git提交 还能读Jira的工单状态
不对不对 应该是先读Jira再读Git 因为有的时候Git提交里
没有写清楚这个需求对应的是哪个Task

口语化的「不对不对」、停顿、犹豫词全保留了——这就是原始转录。还需要 LLM 整理。


第二步:LLM 整理成结构化笔记

原始转录太碎,不能直接用。丢给 DeepSeek 整理:

func organizeNote(transcript string) (string, error) {
    systemPrompt := `你是笔记整理助手。将口语化的语音转录整理成结构化笔记。

规则:
1. 去掉口语冗余词("那个""就是说""不对不对"等)
2. 修正因语音识别导致的错别字
3. 保持原意,不要添加转录中没有的内容
4. 用 Markdown 格式输出

格式:
## [一句话标题]

**核心想法:**
(1-2 句总结)

**详细内容:**
(整理后的完整内容)

**待办/下一步:**
(如果有明确的下一步,列出来;没有就写"暂无")

**标签:** #标签1 #标签2`

    return callLLM(systemPrompt, transcript)
}

实际跑出来的结果:

## daily-report-agent 集成 Jira 工单

**核心想法:**
daily-report-agent 应扩展数据源,先读 Jira 工单再读 Git 提交,
实现需求到代码的完整追溯。

**详细内容:**
现有 daily-report-agent 仅支持 Git 提交记录。建议扩展为:
1. 先查询 Jira 中当前用户的活跃工单
2. 根据工单关联的分支/PR 拉取对应的 Git 提交
3. 生成"需求 → 开发 → 提交"完整链路的工作汇报

**待办/下一步:**
- 调研 Jira REST API(/rest/api/2/search)
- 确认 CNB 的 Jira 集成方式
- 估算开发工时

**标签:** #daily-report-agent #Jira #工作流优化

口语碎碎念变成了结构化笔记。这才是我记完能用的东西。


第三步:TTS —— 让 AI 念给你听

有时候不想看屏幕,想在路上听。Edge TTS 是微软的免费服务,中文语音质量非常好:

import (
    "io"
    "net/http"
    "os"
    "strings"
)

// Edge TTS 免费,不需要 API Key
func textToSpeech(text, outputPath string) error {
    // Edge TTS 的 SSML 格式请求
    ssml := fmt.Sprintf(`<speak version="1.0" xmlns="http://www.w3.org/2001/10/synthesis"
    xmlns:mstts="https://www.w3.org/2001/mstts" xml:lang="zh-CN">
    <voice name="zh-CN-XiaoxiaoNeural">
        <prosody rate="0.9" pitch="0%%">
            %s
        </prosody>
    </voice>
</speak>`, text)

    // 调 Edge TTS 的 WebSocket 接口比较复杂,这里展示 HTTP 接口简化版
    // 实际推荐使用 github.com/xxx/edge-tts-go 封装好的库
    req, _ := http.NewRequest("POST",
        "https://speech.platform.bing.com/consumer/speech/synthesize/readaloud/voices/list",
        nil,
    )
    // ... Edge TTS 的具体调用(完整代码见 GitHub 仓库)
    _ = req
    return nil
}

Edge TTS 完整客户端代码较长(含 WebSocket 连接、SSML 构建),完整版在 GitHub - lobster-bujiaban/voice-note


组装:一个 main 函数串起全流程

func main() {
    if len(os.Args) < 2 {
        fmt.Println("用法: voice-note <音频文件>")
        fmt.Println("支持格式: m4a, mp3, wav, ogg")
        os.Exit(1)
    }

    audioFile := os.Args[1]
    fmt.Println("🎤 处理音频:", audioFile)

    // 1. 格式转换
    fmt.Print("  → 转换格式...")
    mp3File, err := convertToMp3(audioFile)
    if err != nil {
        fmt.Printf("失败: %v\n", err)
        os.Exit(1)
    }
    fmt.Println(" 完成")

    // 2. 语音转文字
    fmt.Print("  → 语音转文字...")
    transcript, err := transcribeAudio(mp3File)
    if err != nil {
        fmt.Printf("失败: %v\n", err)
        os.Exit(1)
    }
    fmt.Printf(" 完成 (%d 字)\n", len([]rune(transcript)))

    // 3. LLM 整理
    fmt.Print("  → LLM 整理...")
    note, err := organizeNote(transcript)
    if err != nil {
        fmt.Printf("失败: %v\n", err)
        os.Exit(1)
    }
    fmt.Println(" 完成")

    // 4. 保存 Markdown
    filename := fmt.Sprintf("note-%s.md", time.Now().Format("2006-01-02-150405"))
    os.WriteFile(filename, []byte(note), 0644)
    fmt.Println("  → 已保存:", filename)

    // 5. 生成语音(可选)
    audioOutput := strings.TrimSuffix(filename, ".md") + ".mp3"
    fmt.Print("  → 生成语音...")
    if err := textToSpeech(note, audioOutput); err != nil {
        fmt.Printf("跳过: %v\n", err)
    } else {
        fmt.Println(" 完成:", audioOutput)
    }

    fmt.Println("\n✅ 完成!你的笔记:")
    fmt.Println(note)
}

成本

步骤 服务 成本
语音转文字 Whisper API $0.006/分钟 ≈ ¥0.04
整理 DeepSeek V4 Flash 约 500 token ≈ ¥0.0005
TTS Edge TTS 免费
合计 约 ¥0.04 / 条

录一条 1 分钟的语音,整理成笔记,念给你听——总成本 4 分钱。


还能怎么扩展

这个基础骨架搭好后,往哪个方向扩展都可以:

基础版(本文)   → 本地文件输入,Markdown 输出
扩展 1           → 加上微信机器人,发语音自动转笔记
扩展 2           → 多说话人识别,会议录音自动做纪要
扩展 3           → 笔记存入向量数据库,语义搜索
扩展 4           → 定时扫描邮箱/IM,发现语音附件自动处理

其实这个工具的骨架跟 daily-report-agent 是一样的——数据源 + AI 处理 + 结果输出。只是数据源从 Git API 换成了音频文件。

下一篇:让 AI 看视频。

Logo

欢迎加入 MCP 技术社区!与志同道合者携手前行,一同解锁 MCP 技术的无限可能!

更多推荐