GitHub 仓库: https://github.com/caozhaoqi/jd_agent
摘要:在实现了基于 JD 生成面试指南(v1.0)后,我们并未止步。本文记录了
jd_agent项目的重大升级:引入 模拟面试(Mock Interview) 模式,并集成了 ASR(语音识别) 与 TTS(语音合成) 技术。我们通过 FastAPI 的流式响应、Next.js 的音频队列管理以及 双模型配置策略,打造了一个能够“听懂你、并开口提问”的 AI 面试官。
1. 架构升级:从单向生成到双向交互
v1.0 版本主要解决的是“信息提取”问题(JD -> 报告)。
v2.0 版本则致力于解决“实战演练”问题(用户 <-> AI 面试官)。为此,我们在架构中引入了 音频层 (Audio Layer) 和 会话状态管理。
1.1 系统架构图 (v2.0)
1 | graph TD |
2. 核心功能实现细节
2.1 模拟面试与流式对话 (Streaming Chat)
为了模拟真实的面试场景,我们摒弃了“生成完毕再一次性返回”的模式,全面拥抱 SSE (Server-Sent Events)。
- 后端实现:使用
LangChain的.astream()方法配合 FastAPI 的StreamingResponse。 - Prompt 设计:根据 Session 标题动态切换 System Prompt。如果检测到是面试模式,AI 变身为“严厉的面试官”,每次只问一个问题,并根据候选人的回答进行追问。
1 | # app/api/endpoints.py |
2.2 语音交互闭环 (The Voice Loop)
这是本次升级的技术高地。我们实现了一个完整的语音交互循环:录音 -> ASR -> LLM 思考 -> TTS 朗读。
A. ASR (听)
前端使用 react-media-recorder 捕获音频 Blob,后端对接 OpenAI 兼容接口(如 SiliconFlow 的 SenseVoiceSmall,识别速度极快)。
B. TTS (说) - 关键优化:音频队列 (Audio Queue)
为了解决“AI 说话卡顿”或“多重语音重叠”的问题,我们在前端实现了一个音频播放队列。
痛点:AI 生成速度很快,如果每收到一段文字就请求播放,声音会重叠。如果等全部生成完再播放,用户等待时间太长。
方案:
- 分句检测:前端监听 SSE 流,利用标点符号(
。?!)切分句子。 - 入队:切分好的句子推入
audioQueue。 - 串行播放:
processAudioQueue递归函数确保上一句播完才请求下一句。
1 | // hooks/useAudioQueue.ts |
3. 基础设施与配置策略 (Infrastructure)
在引入语音功能时,我们遇到了一个典型的工程问题:DeepSeek 很强,但它不支持音频。
为了兼顾智能(文本)与功能(语音),我们设计了双模型配置策略。
3.1 分离配置 (.env & config.py)
我们将 LLM 的配置与 Audio 的配置完全解耦,实现了“大脑”用 DeepSeek,“嘴巴和耳朵”用 OpenAI/SiliconFlow。
1 | # app/core/config.py |
这种设计使得系统极具灵活性,可以在不修改代码的情况下,随意更换底层的语音服务商。
4. 前端工程化踩坑记录 (Troubleshooting)
在 Next.js + React 的开发过程中,我们解决了几个棘手的 Bug。
坑位 1:Worker is not defined
- 现象:引入
react-media-recorder后,页面刷新直接报错。 - 原因:Next.js 默认进行服务端渲染 (SSR),而录音库依赖浏览器特有的
WorkerAPI,Node.js 环境里没有。 - 解决:使用
next/dynamic进行动态导入,并禁用 SSR。1
const ChatInput = dynamic(() => import("@/components/ChatInput"), { ssr: false });
坑位 2:布局遮挡与滚动失效
- 现象:输入框使用
absolute定位,导致聊天记录过长时被输入框遮挡,无法看到最后一条消息。 - 解决:重构 CSS 布局,放弃绝对定位,改用 Flexbox。
- 容器:
flex flex-col h-screen - 消息区:
flex-1 overflow-y-auto - 输入区:
flex-shrink-0(固定在底部,挤压消息区高度)
- 容器:
坑位 3:Markdown 样式丢失
- 现象:AI 回复的 Markdown 没有格式(无加粗、无列表)。
- 解决:引入
@tailwindcss/typography插件,并正确包裹prose类名。1
2
3<div className="prose prose-sm ...">
<ReactMarkdown>{content}</ReactMarkdown>
</div>
5. 总结
通过本次 v2.0 的迭代,jd_agent 已经不仅仅是一个简单的文本分析工具,而是一个具备多模态交互能力的智能助手。
- 技术栈: FastAPI, LangChain, Next.js, Tailwind, Web Audio API.
- 核心突破: 解决了 LLM 生成与 TTS 播放的时序同步问题,实现了流畅的语音对话体验。
希望这篇实战记录能为正在构建 AI Agent 的开发者提供参考!