直接说结论这个标题本身就是三分真七分噱头。Azure OpenAI 并不直接帮你把录音变成文字真正干“听写”活的是 Azure 语音服务OpenAI 模型只负责把那堆口语文本整理成像模像样的会议纪要。但反过来讲如果你已经有转写文本用 Spring AI 整理纪要确实只需要写三行代码——不对准确说是“三行核心调用”。这篇文章我就按自己的实操路径来拆先破解标题讲明白架构上谁干什么再带你走一遍从 Azure 资源开通、依赖引入、音频转写到用 Spring AI 生成结构化纪要的完整流程。适合三类人看正在做线上会议存档系统的后端工程师、想用 Java 技术栈快速接大模型能力的开发者以及那些被老板要求“把每周例会录音变成纪要”的非专业运维同学。1. 整体思路拆解这个标题到底在做什么1.1 标题背后的真实技术栈先说个容易踩的坑很多人看到“Spring AI Azure OpenAI 转录”以为 OpenAI 模型能直接吃音频。实际上 OpenAI 的 Chat 模型是纯文本模型喂不进去 wav 或 mp3。所以标题里的“转录”严格来说是两层意思音转文音频文件送到 Azure 语音服务的 Speech to Text 能力输出带时间戳的逐字稿。文转纪要逐字稿交给 Azure OpenAI通过 Spring AI 调用由大模型提取议题、结论、行动项。简单画一下数据流音频文件 → Azure 语音服务 → 转写文本 转写文本 → Spring AI ChatClient → Azure OpenAI GPT-4o 最终输出 → 结构化会议纪要Markdown / Word / 邮件Spring AI 在这条链路里的角色是“模型调用抽象层”。它不直接提供转录能力但把 Azure OpenAI 的各种参数封装成了统一的 ChatClient 接口让你不用手写 HTTP 请求、鉴权和 JSON 解析。1.2 为什么先转录再交给模型才靠谱直接让模型看文本而不是音频有三个现实原因这决定了方案的合理性第一上下文窗口。目前大模型的上下文窗口虽然越来越长比如 128K 甚至更高但一场两小时的会议转写出来轻松两三万字如果还要塞系统提示词、范文示例、历史背景很容易把窗口挤爆。先转写可以先分段落再分段处理。第二幻觉控制。音频里的口误、人名、数字错误模型根本无从判断因为你没给它原始音频。但转写文本至少保留了识别置信度和时间戳你在提示词里明确“只总结事实不补充未提及的内容”比直接让它听声音输出要可控得多。第三成本与延迟。语音处理是专门的引擎比大模型推理便宜几个数量级。把音频丢给 LLM 做端到端理解目前根本没有公开合理的价格体系也不现实。所以架构上不要追求“标题党式”的一行调用老老实实拆成两段每一段都用自己的专业服务这才是工程上不会翻车的做法。1.3 方案选型实时识别 vs 批量转录这里有一个很多教程不会细讲的选择题读音频文件到底用 SDK 的recognizeOnceAsync()还是批量转录 API我的结论是会议纪要场景优先批量转录 APIBatch Transcription或者 SDK 里长音频异步识别接口而不是短音频的一次性识别。原因很简单recognizeOnceAsync()适合 15 秒以内的短句它内部是一次请求等待完整结果后返回。会议录音动辄 30 分钟到两小时必须用连续识别Continuous Recognition事件回调机制或者直接提交给批量服务由服务端异步处理完再回调通知。批量转录还有一个好处它会自动做说话人分离如果音频是立体声或多麦克风并且返回每个片段的词汇级时间戳。这对后续生成结构化纪要有巨大帮助——你可以按时间段把文本切成块再喂给模型而不是一次性塞整篇。如果你只是做个人快速实验用本地 SDK 连续识别也能跑通但生产环境我强烈推荐批量转录。2. 环境准备与服务开通把 Azure 侧的事情搞定2.1 语音服务资源开通与密钥获取进入 Azure 门户搜索“Speech”创建一个语音服务资源。需要记下两个信息Key用于鉴权形如一串 32 位十六进制字符串Region / Location比如eastasia、japaneast它决定了终结点地址注意Key 和 Region 必须配套使用。很多新手把两个资源的 Key 混用结果一直报 401/403查了半天才发现资源区域不一致。如果你只是本地测试可以在代码里硬编码 Key但生产环境一定要放到环境变量或密钥保管库里别提交到 Git。2.2 Azure OpenAI 模型部署Azure OpenAI 不是直接开通就能用的你还得在 OpenAI 资源里“部署”模型。操作路径Azure AI Foundry或者旧的 Azure OpenAI Studio→ 模型部署 → 选择gpt-4o或gpt-35-turbo→ 填写部署名称。这里务必记住你的部署名称它通常和你调用的模型名不一样。比如你在创建部署时命名为meeting-gpt4那 API 请求里的 model 参数就要写meeting-gpt4而不是gpt-4o。这个细节是接 Azure OpenAI 最容易迷惑的点跟裸 OpenAI API 的习惯完全不同。如果你的预算是中小团队级别建议用gpt-4o-mini做摘要任务价格更低速度更快。我实测下来会议纪要这种结构化提取任务mini 模型和完整版 gpt-4o 的输出质量差距不大但成本能省一半以上。2.3 Spring Boot 项目骨架与依赖清单创建一个 Spring Boot 3.x 项目Java 17 以上。核心依赖就两个dependency groupIdorg.springframework.ai/groupId artifactIdspring-ai-azure-openai-spring-boot-starter/artifactId !-- 版本请根据你使用的 Spring AI 版本锁定比如 1.0.0-M6 -- /dependency dependency groupIdcom.microsoft.cognitiveservices.speech/groupId artifactIdclient-sdk/artifactId version1.42.0/version /dependency提示Spring AI 的版本迭代比较快不同版本的坐标可能不一样。老版本用spring-ai-azure-openai-spring-boot-starter新版本可能迁移到spring-ai-starter-model-azure-openai。建议以官方文档为准不要照抄我这里给的 artifactId。配置文件application.yml里需要设置spring: ai: azure: openai: api-key: ${AZURE_OPENAI_KEY} endpoint: ${AZURE_OPENAI_ENDPOINT} chat: options: model: ${AZURE_OPENAI_DEPLOYMENT_NAME} temperature: 0.2 azure: speech: key: ${AZURE_SPEECH_KEY} region: ${AZURE_SPEECH_REGION}温度参数我建议固定 0.2 左右。生成纪要是提取类任务不是创意写作温度高了容易跑偏出现模型自创细节。3. 核心代码实现从转写到纪要的手把手拆解3.1 真正的“三行”Spring AI 调用部分先把大家最想看的部分亮出来。假设你已经拿到了会议转写全文transcript用 Spring AI 生成纪要的核心代码确实是三行ChatClient chatClient ChatClient.builder(chatModel).build(); String minutes chatClient.prompt() .system(systemPrompt()) .user(userPromptForMinutes(transcript)) .call() .content();第一行创建 ChatClient第二行开始组装提示词第三行取回结果。真正执行模型调用的封装Spring AI 已经帮你处理掉了。但这三行背后至少还要补三样东西系统提示词、用户提示词、以及处理长文本的策略。真正的工程量在提示词设计上后面我会单独展开。3.2 音频转录的完整姿势批处理转写的实现如果你录的是标准会议音频建议直接调 Azure 的批量转录 REST API。Java 实现大致是这样的流程public String transcribeAudio(Path audioFile) throws Exception { // 1. 上传音频文件到 Azure Storage拿到可访问的 URL String fileUrl uploadToStorage(audioFile); // 2. 创建批量转录任务 String transcriptId createBatchTranscription(fileUrl, meeting-record); // 3. 轮询任务状态直到完成 TranscriptResult result waitForCompletion(transcriptId); // 4. 拉取转写结果 JSON return result.getRecognizedPhrases() .stream() .map(phrase - String.format([%s] %s, formatTimestamp(phrase.getTimestamp()), phrase.getText())) .collect(Collectors.joining(\n)); }这里我简化了上传和轮询的中间代码但结构就是这四步。SDK 的连续识别也可以实现同样的效果但需要自己管理音频流和事件监听代码量更大而且如果服务端重启任务状态不好恢复。批量转录天然适合“离线生成纪要”的场景。有个省事技巧如果你只是想快速验证效果可以先用 Azure 语音服务在线体验页面传一个短音频拿到一段转写文本直接跳过转录 API 去测 Spring AI 部分。把链路拆开验证定位问题会快很多。3.3 会议纪要模板的 Prompt 设计这是整篇文章里我认为最有价值的部分因为代码大家都差不多但提示词决定输出质量。系统提示词我建议这样写你是一名专业的会议记录员。你的任务是将用户提供的会议转写稿整理成结构化会议纪要。 要求 1. 只基于转写内容不补充任何转写稿中不存在的信息。 2. 区分结论、讨论要点、待办事项行动项必须包含负责人和截止时间如果转写中提及。 3. 保持中立客观不添加个人评价。 4. 输出格式为 Markdown包含以下区块 ## 会议基本信息 - 会议时间/参会人/主持人如转写中提及 ## 核心议题与结论 ## 讨论过程摘要 ## 待办事项 | 任务 | 负责人 | 截止时间 |用户提示词更简单就是“以下是会议转写稿请按系统要求整理纪要{transcript}”。这里有几个明显的坑我全部踩过如果你不给输出格式模型就会自由发挥每条纪要结构都不一样。同一个公司内部每次会议结构不同后面想自动化提取待办都无从下手。“不补充不存在的信息”这条约束至关重要。大模型看到转写里的空白会自动脑补尤其是人名和数字。加了这条之后至少幻觉率肉眼可见地下降。如果会议超过一小时建议把文本按时间戳切分成 5–10 分钟一段分段调模型拿“分段纪要”最后再汇总让模型生成一份总纪要。一次喂全文单次 Token 成本高还容易丢失中段细节。3.4 自动发送纪要的扩展点Spring AI 的生态里有个很实用的点你可以把生成结果直接接入已有的通知渠道。比如生成纪要后自动把 Markdown 转成 HTML再通过 JavaMailSender 发给参会人。核心思路就是打通三个 BeanService public class MeetingMinutesService { private final ChatClient chatClient; private final JavaMailSender mailSender; public void handleMeeting(MeetingRecord record) { String transcript transcribeAudio(record.getAudioPath()); String minutes generateMinutes(transcript); sendEmail(minutes, record.getParticipants()); } }生产环境里一般不会真的每次都发邮件更多是把纪要存进企业知识库、关联到项目管理系统。但这套代码骨架完全够你扩展。4. 实操过程与经验值跑通一版要留意哪些事4.1 我从零到一跑通的流程回顾我第一次搭这个系统时掐表算过从资源开通到拿到一份基本可用的纪要前后花了大概半天。流程是这样的先开通语音服务和 OpenAI 资源各 10 分钟。拿一段 3 分钟的会议录音去 Azure 语音服务在线面板手动上传测试确认转写质量能接受。用 SDK 代码做本地连续识别跑通“音频 → 控制台打印文本”。在 Spring Boot 里引入 Spring AI 依赖配置好两个 Key先写一个最简单的“你是什么模型”的自测接口确认调用链路通。把转写文本粘进测试用例调 Prompt 调格式。最后才接批量转录 API把整个流程串起来。我强烈建议你也按这个顺序来不要一上来就想一步到位。每步只验证一环出问题能立刻定位到是录音问题、转录问题还是模型问题。我见过同事直接冲批量转录结果 Azure 上文件格式不对反复报 400排查了一个下午最后才发现是自己上传文件的 Content-Type 没设置成audio/wav。4.2 踩坑记录格式、识别率、密钥先说音频格式。Azure 批量转录对格式有明确限制最常见的问题是** MP3 文件必须指定音频格式的编码参数否则识别服务可能不认**。我后来统一要求会议系统导出 WAV 或 M4A转成 16kHz 单声道再上传识别率立刻上了一个台阶。再说识别率。中文会议场景下专有名词、英文术语、部门缩写往往会被识别成同音字。解决办法是给语音服务配置自定义语音模型——把公司内部的项目代号、同事称谓预先训练进去。初期没有训练数据也没关系可以在 Prompt 里加一条“如果转写稿中存在明显不通顺的同音字尝试根据语境修正但修改处用括号标注”。实测下来模型自己纠正的效果比完全不纠正要好。最后说密钥。你可能会遇到明明代码没问题但一直提示 401 的情况。排查顺序确认 Key 是否完整复制Azure 门户里 Key 末尾经常被截断、确认 Region 是否匹配、确认是不是密钥轮换后旧 Key 还在缓存里。我自己就遇到过Azure 自动轮换密钥后应用还在用旧 Key日志里全是 401折腾很久才发现是配置中心没同步。4.3 哪些场景适合、哪些不适用这个方案很适合以下场景线上会议平台需要自动生成纪要企业内训、讲座沙龙的语音资料归档面试录音整理必须注意隐私合规播客/自媒体多平台内容分发前的文字稿初稿不适合的场景实时会议字幕。批量转录延迟高做实时字幕要用语音服务的实时识别 API技术选型完全不同多说话人混叠严重的线下录音。如果麦克风离说话人远背景噪声大转写质量会断崖式下降需要精确时间戳逐字对齐的司法取证类需求5. 常见问题与排查技巧实录5.1 调用失败类问题速查表现象可能原因排查手段401 UnauthorizedAPI Key 错误、Region 不匹配在 Azure 门户核对 Key 与终结点404 model not found部署名称填错检查是不是把模型名当成了部署名429 Too Many Requests触发限流或配额不足查看 Azure 配额临时降低并发或增加重试400 Bad Request音频格式不正确检查文件扩展名、编码、采样率是否与服务要求一致Timeout转录任务处理时间过长改用批量转录异步轮询不要用同步请求5.2 识别质量问题处理如果转写稿错字太多先别急着调 Prompt。按顺序检查三件事采样率16kHz 单声道是平衡体积和识别率的最佳选择。通道数如果会议录音是双声道很有可能一个声道是远端参会人、一个声道是本地发言人。这时候可以让语音服务启用通道分离转录再分别生成文本效果远超混合声道。自定义词汇表在语音服务里创建一个关键词列表把公司产品名、常见人名放进去会显著改善专名识别。5.3 上下文超限与稳定性处理当你发现自己买的模型上下文窗口不够装下一整场会议时不要硬用更大的模型成本不划算。我采用的方案是“分段摘要再汇总”把转写按 5 分钟间隔切成 N 段。每段调用一次模型只提取“议题、结论、待办”三个字段的片段。最后把 N 段摘要拼在一起再让模型做一次全局整合去重并排优先级。这样既绕过了上下文限制又让模型在每个片段上注意力更集中出错率反而低。唯一的代价是调用次数变多但配合gpt-4o-mini和适度缓存成本完全可控。另外Spring AI 的 ChatClient 内部支持简单重试但如果 Azure 那边因为负载返回 429建议用 Spring 的Retryable注解自己做指数退避重试间隔从 1 秒、2 秒、4 秒递增。不要用固定间隔否则一旦触发限流很容易连续失败。6. 最后再分享两个小技巧让纪要真正可用而不是“看过即焚”第一个技巧是给纪要加标签索引。让模型在输出末尾追加一个标签清单比如#产品讨论 #性能优化 #决策。你不一定会用标签做自动化但归档检索的时候这些关键词比全文搜索高效太多。第二个技巧是保留一句原话引用。Prompt 里加一条“每个行动项后附上转写稿中对应的原话或时间戳引用”。这样即使模型总结有偏差你也能快速翻到录音对应位置核实。对于需要追责任的会议场景这个功能极其好用——它直接把 AI 生成结果从“不确定”变成了“可追溯”。我做这套系统最大的体会是真正难的不是让模型把文本变成纪要而是让整个链路稳定、可控、能追踪。Azure 语音服务负责把声音变成文字Spring AI 负责把文字变成纪要这个分工看起来简单但每一步都有大量工程细节。如果你只是想要一个能跑的 Demo照着前面的代码十分钟就能戳出个东西来但如果你要做成一个团队每天依赖的工具请一定要把 Prompt、音频预处理、失败重试和引用溯源这几块补齐。到那时候你会发现“三行代码”只是一个开始真正的价值是你为这三行代码搭建的整套工程体系。