打造 L5 级人机协同与全双工语音的 AI 面试官
Published in:2025-12-01 |
Words: 1.6k | Reading time: 7min | reading:

🚀 [实战] 进化!从脚本到数字员工:打造 L5 级人机协同与全双工语音的 AI 面试官

摘要:今天是一个里程碑。我们将 JD_Agent 从一个简单的 RAG 问答工具,彻底重构为一个具备 L5 级别自主性 的多智能体系统。同时,我们攻克了 全双工语音交互DeepSeek 风格思考过程 的工程难题,让 AI 不仅能“思考”,还能像真人一样“倾听”和“表达”。


🌟 架构全景图 (The Big Picture)

在 v2.0 版本中,我们不再简单的线性调用 LLM,而是构建了一个复杂的多智能体协作网络

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
graph TD
User((🙍‍♂️ 用户)) <-->|Web Audio / SSE| Frontend[🖥️ Next.js 前端]
Frontend <-->|HTTP / Stream| Backend[⚙️ FastAPI 后端]

subgraph "🧠 智能体大脑 (LangGraph)"
Start(开始) --> Parser[🔍 职位解析员]
Parser --> Researcher[🕵️ 商业情报员]
Parser --> TechLead[💻 技术面试官]

Researcher --> Context{信息汇总}
TechLead --> Reviewer[⚖️ 质量检察员]

Reviewer -->|评分 < 85| TechLead
Reviewer -->|评分 >= 85| End(✅ 输出报告)

Reviewer -.->|拿捏不准| Human[🛑 人工介入]
Human --> TechLead
end

subgraph "🔊 语音交互层"
ASR[👂 Whisper ASR]
TTS[🗣️ macOS Native TTS]
end

Backend --> ASR
Backend --> TTS
Backend --> Start

核心突破一:L5 级多智能体协同 (LangGraph)

我们要解决的核心痛点是:AI 生成的内容往往不够深度,或者一本正经胡说八道。
解决方案是引入 Reviewer (质检员) 角色,形成质量闭环。

1. 定义“团队” (The Team)

我们不再是一个 Agent 打天下,而是通过 LangGraph 组建了一个虚拟团队:

  • Tech Lead: 负责根据 JD 出题。
  • Reviewer: 负责给题目打分。如果不合格,打回重写。

2. 代码实现 (Workflow)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
# src/app/graph/workflow.py

# 定义路由逻辑:决定是“通过”还是“重写”
def qa_router(state: AgentState):
# 1. 防止死循环
if state["iteration_count"] > 3:
return "approved"

# 2. 质量把关
if state["quality_score"] >= 85:
return "approved"
else:
return "rejected" # ⬅️ 这里的 rejected 会导致回滚

# 构建图
workflow = StateGraph(AgentState)
workflow.add_node("tech_lead", tech_lead_node)
workflow.add_node("reviewer", reviewer_node)

# 编排循环
workflow.add_edge("tech_lead", "reviewer")
workflow.add_conditional_edges(
"reviewer",
qa_router,
{
"approved": END,
"rejected": "tech_lead" # 🔄 关键:打回重写
}
)

核心突破二:DeepSeek 风格的“思考过程” UI

为了缓解 L5 架构推理时间长(可能达 1-2 分钟)带来的焦虑感,我们复刻了 DeepSeek R1 的交互体验:把 AI 的思考过程透明化

1. 双流协议设计 (Dual Stream Protocol)

后端不再只返回文本,而是返回 JSON 事件流

  • type: "thought" -> 思考步骤(如“正在检索财报…”)
  • type: "token" -> 最终回复内容
1
2
3
4
5
// 后端 SSE 推送的数据流示例
data: {"type": "thought", "content": "正在分析 JD 技术栈..."}
data: {"type": "thought", "content": "正在构思追问策略..."}
data: {"type": "token", "content": "您好,"}
data: {"type": "token", "content": "根据您的经历..."}

2. 前端可视化组件 (Thinking Block)

前端实现了一个可折叠的面板,实时渲染 thought 流。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
// src/components/ThinkingBlock.tsx
export default function ThinkingBlock({ thoughts, isFinished }) {
return (
<div className="bg-gray-50 border rounded-xl p-4">
<div className="flex items-center gap-2 text-gray-500">
{isFinished ? <BrainCircuit /> : <Loader2 className="animate-spin"/>}
<span>{isFinished ? "深度思考完成" : "DeepSeek 正在思考..."}</span>
</div>

{/* 动态渲染思考步骤 */}
<ul className="mt-2 space-y-1 border-l-2 border-gray-200 pl-4">
{thoughts.map(step => (
<li className="text-xs text-gray-600 animate-in fade-in">{step}</li>
))}
</ul>
</div>
)
}

核心突破三:全双工语音交互 (ASR + TTS)

我们实现了 录音 -> ASR -> LLM -> TTS 的完整闭环,让 AI 变身为真正的面试官。

1. 语音合成 (TTS) 的避坑之路

这是今天遇到的最大工程挑战。

  • Edge-TTS: 在国内网络环境下频繁报 503 Service Unavailable,不稳定。
  • Pyttsx3: 生成的 AIFF 格式在 Chrome 浏览器中无法播放 (NotSupportedError)。
  • macOS 原生 say 命令: 最终方案!

利用 Mac 系统底层的 say 命令生成 .m4a 文件,既利用了 Siri 的高质量语音,又做到了零网络延迟、零成本

后端实现 (FastAPI):

1
2
3
4
5
6
7
8
9
10
11
12
13
14
import subprocess

@router.post("/audio/tts")
async def text_to_speech(text: str):
"""
Mac 专属黑科技:调用系统底层 TTS
"""
tmp_path = f"/tmp/{uuid.uuid4()}.m4a"

# 调用系统命令,直接生成 m4a (浏览器兼容性极好)
subprocess.run(["say", "-o", tmp_path, text])

with open(tmp_path, "rb") as f:
return Response(content=f.read(), media_type="audio/mp4")

2. 前端音频队列 (Audio Queue)

为了防止流式生成时语音重叠(AI 还在打字,上一句还没读完,下一句又来了),我们在前端实现了一个分句缓冲队列

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
sequenceDiagram
participant SSE as 后端流
participant Parser as 前端解析器
participant Queue as 音频队列
participant Player as 播放器

SSE->>Parser: "你好,"
SSE->>Parser: "我是"
SSE->>Parser: "面试官。" (检测到标点)
Parser->>Queue: 入队: "你好,我是面试官。"

loop 队列监听
Queue->>Player: 取出第一句
Player-->>Player: 播放中...
Player->>Queue: 播放结束 (onEnded)
end

🛠️ 踩坑实录 (Troubleshooting)

🐛 Bug 1: React 严格模式下的“复读机”

  • 现象:AI 回复出现 AABB 重复(如“好好…的的…”)。
  • 原因Next.js 开发环境下 React.StrictMode 会执行两次 setState。而我们的代码中直接修改了对象属性 (msg.content += chunk)。
  • 修复:严格遵循 Immutability 原则。
1
2
3
4
5
6
7
// ✅ 正确写法
setMessages(prev => {
const newMsgs = [...prev];
// 创建新对象,而不是修改引用
newMsgs[lastIndex] = { ...lastMsg, content: lastMsg.content + chunk };
return newMsgs;
});

🐛 Bug 2: Worker is not defined

  • 现象:引入录音库 react-media-recorder 后页面崩溃。
  • 原因:Next.js 服务端渲染 (SSR) 无法访问浏览器特有的 Worker API。
  • 修复:使用 dynamic import 隔离加载。
1
const ChatInput = dynamic(() => import("@/components/ChatInput"), { ssr: false });

🔮 总结

现在的 JD_Agent 已经不仅仅是一个代码 demo,它具备了:

  1. 大脑:懂反思、会纠错的多智能体团队。
  2. 嘴巴:零延迟的本地 TTS。
  3. 颜值:媲美 DeepSeek 的现代化 UI。

Next Step: 我们将尝试引入 WebRTC,将现在的“对讲机模式”升级为真正的“打断式实时通话”。

Keep Building, Keep Evolving. 🚀

Prev:
基于 FastAPI + LangChain 的高并发 Agent 架构解析
Next:
实时语音模拟面试