第 7 节 / 共 7 节

Coze 实战案例拓展

🎯 通过更多实战案例巩固 Coze 开发能力。
📖 笔记

Day07 Coze 完整笔记

Day07 Coze AI 面试助手项目完整笔记

一、今日目标

  1. 能说清楚 AI 面试助手从简历评估扩展到录音分析、面试题生成、智能体统一入口的整体思路。
  2. 能理解录音分析模块的主链路:语音输入、ASR 转文字、角色划分、问答提取、循环评估、报告生成。
  3. 能区分 ASR、TTS、说话人识别、语义分角色各自解决的问题。
  4. 能理解为什么面试回答评估要使用循环、RAG、题库入库和联网兜底。
  5. 能理解面试题生成模块如何基于简历技能、项目经历和真实题库生成题目。
  6. 能理解 Agent 编排的作用:把多个工作流统一成一个用户入口。
  7. 能理解批处理和 API 调用的价值:把单次手动操作扩展成批量处理能力。

二、今日课程主线

前一天的简历评估模块解决的是“看简历、找问题”。今天要继续把 AI 面试助手做完整,让系统不仅能看简历,还能分析面试录音、根据简历生成面试题,并通过一个统一的智能体入口把这些能力串起来。

今天可以用一句话概括:

把“单个工作流功能”升级成“完整 AI 面试助手产品”。

整体链路如下:

  1. 简历评估:上传简历,发现简历问题。
  2. 录音分析:上传面试录音,分析面试表现。
  3. 面试题生成:上传简历,生成针对性的模拟面试题。
  4. Agent 编排:用户不用记住三个工作流,只需要和一个智能体对话。
  5. 批处理:大量简历或录音不再一个个手动点,而是通过 API 和程序批量处理。

三、录音分析模块

1. 录音分析模块解决什么问题

面试结束后,很多人只知道“自己好像答得不好”,但不知道具体是哪一道题没答好,也不知道标准回答应该怎么组织。

录音分析模块要解决的问题是:

  1. 把面试录音转成文字。
  2. 分清哪些话是面试官说的,哪些话是应试者说的。
  3. 提取自我介绍和每一组问答。
  4. 对自我介绍进行评价。
  5. 对每一道技术问题的回答进行评价。
  6. 汇总成一份可读的面试复盘报告。

它的核心价值是:把一次模糊的面试经历变成可以复盘、可以修改、可以训练的数据。

2. 录音分析的完整流程

录音分析模块的流程可以拆成五步:

  1. 输入面试语音:用户上传 MP3 或 WAV 格式录音。
  2. 面试语音转文字:通过 ASR 插件把声音转成文本。
  3. 角色划分与概要提取:识别面试官、应试者、自我介绍和问答对。
  4. 面试表现评估:评估自我介绍和每一道面试回答。
  5. 报告内容生成:把整体描述、问题评价、改进建议整合成报告。

3. ASR 是什么

ASR 的英文全称是 Automatic Speech Recognition,中文叫自动语音识别。

它解决的问题是:把声音转成文字。

常见例子:

  1. 微信语音转文字。
  2. 会议实时字幕。
  3. 手机语音输入。
  4. 面试录音转写。

在本项目中,ASR 的作用是把面试录音转成可被大模型继续处理的文本。

4. TTS 是什么

TTS 的英文全称是 Text To Speech,中文叫文本转语音。

它解决的问题是:把文字读成声音。

本节课主要使用 ASR,不重点使用 TTS。TTS 可以理解为 ASR 的反方向,以后如果要做“AI 面试官开口提问”,就会用到 TTS。

5. ASR 和说话人识别的区别

ASR 负责判断“说了什么”。

说话人识别负责判断“是谁说的”。

这两个问题不是一回事:

能力 解决的问题 输出
ASR 这段声音说了什么内容 一段文字
说话人识别 这句话是谁说的 说话人 A、B、C
语义分角色 根据上下文判断谁是面试官、谁是应试者 面试官/应试者对话

在本项目里,Coze 中的语音识别插件先完成 ASR,再用大模型根据语义进行角色划分。

6. 为什么这里用语义分角色

真实会议场景可能需要声纹识别、时间戳对齐和说话人聚类,但面试场景通常只有两个角色:面试官和应试者。

面试对话有明显语义特征:

  1. 面试官经常提出问题。
  2. 应试者经常回答问题。
  3. “请介绍一下你自己”通常来自面试官。
  4. “我毕业于……”通常来自应试者。

所以在这个固定场景下,使用大模型根据语义划分角色,成本更低,搭建更简单,也更适合 Coze 工作流演示。

7. 角色划分节点的作用

角色划分节点接收 ASR 转写后的文本,输出带角色标记的对话原文。

正确效果示例:

面试官:请你介绍一下自己的项目经历。
应试者:我主要做过一个基于 RAG 的知识库问答项目……
面试官:这个项目里向量检索是怎么做的?
应试者:我们先对文档进行切分,然后生成向量……

易错点:

  1. 不要把所有内容都当成应试者发言。
  2. 不要丢掉原文中的卡顿、停顿和明显异常。
  3. 如果不是面试录音,不应该强行分析。

8. 概要提取节点的作用

概要提取节点主要输出两个结果:

  1. self_introduce:自我介绍原文。
  2. summarize:面试整体概要。

注意:如果面试官中途问“介绍一下你的项目”,这不属于开场自我介绍,而属于问答环节。

9. 问答对提取节点的作用

问答对提取节点把整场面试拆成多个“问题 + 回答”。

这样做的原因是:后续评估不能只对整段录音笼统打分,而应该逐题评估。

例如:

问题:RAG 的基本流程是什么?
回答:先把文档切分,然后做向量化,再检索相关内容,最后交给大模型生成答案。

易错点:

  1. 不要把自我介绍当成普通问答。
  2. 追问类问题要补全上下文,例如“解码器呢”要改写成“Transformer 的解码器是什么”。
  3. 输出用字符串数组更稳定,不要一开始就追求复杂对象结构。

四、面试表现评估

1. 自我介绍评估

自我介绍评估关注的是开场表达是否完整、清楚、有重点。

常见评价维度:

  1. 是否包含姓名、学历、工作经历、项目经历。
  2. 是否突出自己做过什么,而不是只说空泛优点。
  3. 是否技能罗列过多。
  4. 项目介绍是否时间过长。
  5. 是否用事实支撑能力。

错误示例:

我学习能力强,沟通能力好,抗压能力强,熟悉 Python、MySQL、Redis、Linux、深度学习、NLP、RAG、Agent……

问题:软素质太多,技能堆砌太多,没有证明能力的事实。

正确方向:

我主要参与过一个企业知识库问答项目,负责文档清洗、向量检索和召回效果优化。项目中我把文档按照标题层级切分,并对召回结果做了重排,使答案引用更加稳定。

优点:有项目、有职责、有动作、有结果。

2. 面试回答评估为什么要用循环

一场面试会有很多道题,题目数量不固定。

如果不用循环,就很难稳定地逐题处理。

循环结构解决的问题是:

  1. 有几道题就处理几次。
  2. 每道题都走同样的评估标准。
  3. 每道题都可以独立检索知识库。
  4. 每道题都可以单独入库。
  5. 最后把所有评估结果汇总。

3. RAG 在面试回答评估中的作用

RAG 的作用是给模型提供参考答案。

如果只让大模型凭记忆判断学生回答对不对,容易出现不稳定。加入题库和面试宝典后,模型可以先查参考资料,再做判断。

本项目中的评估逻辑:

  1. 用问题去面试题知识库检索。
  2. 如果查到相关答案,就基于知识库判断。
  3. 如果查不到,使用互联网搜索兜底。
  4. 根据标准答案和用户回答进行比较。
  5. 输出 A/B/C/D 等级和改进建议。

4. 为什么要把面试题入库

面试题入库不是为了当前这一道题,而是为了以后。

今天从录音中提取到的问题,存进数据库后,后续生成面试题、分析其他录音时都可以复用。

这就是项目中的数据积累:

  1. 用户使用越多。
  2. 系统收集到的问题越多。
  3. 题库越丰富。
  4. 后续生成和评估效果越好。

这个思路在真实 AI 产品中很常见,叫做数据飞轮。

五、面试题生成模块

1. 面试题生成模块解决什么问题

简历评估告诉你简历哪里有问题,录音分析告诉你上一次面试哪里答得不好。

面试题生成模块则解决“下一次应该练什么”的问题。

它的输入是简历文件,输出是一组模拟面试题。

2. 面试题生成流程

面试题生成模块的流程:

  1. 解析简历文件。
  2. 提取技能点和项目经历。
  3. 查询最近 100 道真实面试题。
  4. 基于技能点生成技术问题。
  5. 基于项目经历生成项目追问。
  6. 整合、去重、补充人事问题。
  7. 输出面试题集合。

3. 为什么要拆细技能点

简历里常见技能描述是一个长句:

熟悉深度学习原理,对 RNN、LSTM、Transformer 的原理有深入研究,熟练使用 PyTorch 搭建神经网络。

这句话包含多个技术点:

  1. 深度学习原理。
  2. RNN。
  3. LSTM。
  4. Transformer。
  5. PyTorch。
  6. 神经网络搭建。

如果不拆细,模型很难生成精准问题。拆细后,可以针对每个技术点单独生成问题。

4. 技能题和项目题的区别

技能题偏通用,可以结合面试宝典、真实题库和联网搜索。

项目题偏个性化,需要根据简历项目内容生成。

类型 来源 生成方式
技能题 技能点、题库、面试宝典 检索 + 生成
项目题 简历项目经历 基于项目内容直接追问
人事题 常见面试场景 作为补充题加入

5. 面试题整合节点的作用

面试题整合节点不是简单拼接,而是要做整理:

  1. 去掉重复题。
  2. 保留和岗位最相关的题。
  3. 去除口语化表达。
  4. 控制不同技术点的题目数量。
  5. 补充适当的人事问题。

六、智能体编排

1. 为什么需要智能体编排

前面我们已经有多个工作流:

  1. 简历评估工作流。
  2. 录音分析工作流。
  3. 面试题生成工作流。

如果让用户自己选择工作流,会增加使用成本。

智能体编排的作用是:让用户只面对一个入口,由 Agent 根据用户输入判断应该调用哪个工作流。

2. 单 Agent 和 Multi-Agent

本项目场景不算复杂,用户意图比较容易判断,所以可以使用单 Agent 的自助规划模式来管理多个工作流。

可以把三个工作流理解成三条流水线:

  1. 简历评估流水线。
  2. 录音分析流水线。
  3. 面试题生成流水线。

Agent 负责判断用户要走哪条流水线。

3. Agent 的核心提示词要解决什么

Agent 提示词不是单纯写人设,而是要写清楚:

  1. 角色是什么。
  2. 能做哪些事。
  3. 什么情况下调用哪个工作流。
  4. 处理不了的情况怎么回复。
  5. 是否对用户隐藏内部调用细节。

例如:

  1. 用户上传简历并说“帮我看看简历”,调用简历评估。
  2. 用户上传录音文件,调用录音分析。
  3. 用户上传简历并说“帮我生成面试题”,调用面试题生成。
  4. 用户闲聊或问无关问题,固定提示当前只支持项目相关能力。

七、批处理和 API 调用

1. 为什么需要批处理

在课堂演示中,我们可以手动上传一个文件,点击一次运行。

真实工作中,如果有 100 份简历、50 段录音,一个个手动上传就不现实了。

批处理解决的问题是:

  1. 自动遍历文件夹。
  2. 自动上传文件。
  3. 自动调用智能体。
  4. 自动保存结果。
  5. 自动统计成功和失败。

2. API 调用的意义

发布 API 后,Coze 不再只是网页上的工具,而可以被 Python 程序、后台服务、企业系统调用。

这一步意味着:

  1. 从“手动工具”变成“可集成服务”。
  2. 从“单次处理”变成“批量处理”。
  3. 从“课堂项目”接近“真实业务系统”。

3. 批处理的基本思路

批处理不需要一开始写得很复杂,基本流程是:

  1. 准备一个文件夹,里面放简历或录音。
  2. 遍历文件夹中的文件。
  3. 判断文件格式是否符合要求。
  4. 上传文件到 Coze。
  5. 构造用户消息。
  6. 调用 Agent。
  7. 接收输出结果。
  8. 保存为 Markdown 文件。

八、今日易错点

易错点 错误做法 正确做法
音频格式 上传 m4a 后直接分析 使用 MP3 或 WAV
ASR 超时 使用默认超时时间 长录音把超时调到 600 秒
ASR 和说话人识别 以为 ASR 会自动分清谁说话 ASR 只转文字,角色划分要单独处理
角色划分 用弱模型或提示词太短 用更强模型,并写清楚面试官/应试者格式
问答提取 不补全追问上下文 把“解码器呢”改写成完整问题
循环处理 整段面试一次性评价 对问答数组逐题循环评估
知识库匹配 匹配度设置太低 提高匹配度,避免错误参考
面试题生成 直接按整段技能生成题 先拆细技能点,再生成问题
Agent 编排 只写人设,不写调用规则 明确什么输入调用什么工作流
批处理 只考虑成功情况 要考虑文件不存在、格式错误、调用失败

九、今日总结

今天的重点不是单独学某一个节点,而是理解完整 AI 项目如何从多个工作流组合成一个产品。

录音分析体现的是“语音到结构化评估”的能力。

面试题生成体现的是“根据用户材料生成个性化训练内容”的能力。

Agent 编排体现的是“把多个功能统一成一个入口”的产品能力。

批处理和 API 调用体现的是“把网页工具接入真实业务系统”的工程能力。

学完今天,应该能说清楚:

  1. 录音分析为什么要先 ASR,再角色划分,再逐题评估。
  2. 面试题生成为什么要拆技能点和项目经历。
  3. Agent 为什么要负责选择工作流。
  4. API 和批处理为什么让项目更接近真实工作。
✏️ 作业

Day07 作业

Day07 Coze AI 面试助手项目作业

一、选择题

  1. ASR 在录音分析模块中的主要作用是:
    A. 把文字转成声音
    B. 把声音转成文字
    C. 判断用户身份
    D. 生成面试题

  2. TTS 主要解决的问题是:
    A. 把文字读成声音
    B. 把声音转成文字
    C. 对问题进行检索
    D. 对简历进行分块

  3. 在面试录音分析中,ASR 完成后还需要角色划分,是因为:
    A. ASR 一定会输出错误文字
    B. ASR 通常只解决“说了什么”,不一定解决“谁说的”
    C. 角色划分可以替代语音识别
    D. 角色划分只用于生成图片

  4. 问答对提取节点的主要目的是什么?
    A. 把整场面试拆成可逐题评估的数据
    B. 把录音转换为 MP3
    C. 删除所有回答
    D. 生成数据库表结构

  5. 面试回答评估中使用循环结构,是因为:
    A. 每场面试的问题数量不固定
    B. 循环可以让 ASR 更准确
    C. 循环可以替代知识库
    D. 循环只用于播放音频

  6. RAG 在面试回答评估中的作用是:
    A. 让模型有参考资料再判断回答质量
    B. 把文字转换为声音
    C. 自动压缩音频文件
    D. 删除数据库中的题目

  7. 面试题入库的主要价值是:
    A. 让系统以后可以复用真实面试题
    B. 减少用户上传文件数量
    C. 让 ASR 不再需要插件
    D. 让 Agent 不需要提示词

  8. 面试题生成模块中,为什么要拆细技能点?
    A. 为了让生成的问题更精准
    B. 为了减少简历内容
    C. 为了让文件更小
    D. 为了避免使用大模型

  9. Agent 编排的核心作用是:
    A. 根据用户意图调用合适的工作流
    B. 替代所有数据库
    C. 让用户手动选择每个节点
    D. 只负责保存图片

  10. 批处理模块最适合解决哪个问题?
    A. 手动处理大量简历或录音效率低
    B. 单个音频无法播放
    C. 图片不能显示
    D. 自我介绍内容太短

二、填空题

  1. ASR 的中文含义是________,它的作用是把________转成________。

  2. TTS 的作用是把________转成________。

  3. 在录音分析模块中,ASR 之后需要通过大模型进行________,把对话整理成面试官和应试者两类发言。

  4. 概要提取节点通常会输出两个重要结果:和。

  5. 问答对提取节点要把整场面试拆成多个________,方便后面逐题评估。

  6. 面试回答评估中使用循环,是因为每场面试中的________数量不固定。

  7. RAG 的作用是先从知识库中________,再辅助大模型判断回答质量。

  8. 面试题生成模块主要从简历中提取________和________两类信息。

  9. Agent 编排需要写清楚用户在什么情况下调用________。

  10. 批处理的基本思路是遍历文件夹、上传文件、调用智能体、接收结果并________。

三、简答题

  1. 请说明 ASR 和 TTS 的区别。

  2. 为什么录音分析模块不能只做“语音转文字”?

  3. 为什么本项目中可以先使用语义分角色,而不是完整的声纹识别方案?

  4. 问答对提取时,为什么要把“解码器呢”这类追问补全成完整问题?

  5. 为什么面试回答评估要使用循环结构?

  6. RAG 在判断面试回答质量时有什么价值?

  7. 为什么要把录音中提取出来的真实面试题写入数据库?

  8. 面试题生成模块中,技能题和项目题有什么区别?

  9. Agent 编排和单独运行工作流相比,有什么优势?

  10. 为什么批处理和 API 调用更接近真实工作?

四、实操题

1. 录音分析流程梳理

根据今天所学,把录音分析模块的流程补全为 6 个步骤,并说明每一步的输入和输出。

2. 角色划分提示词优化

设计一段角色划分提示词,要求满足:

  1. 能区分面试官和应试者。
  2. 能保留原始内容。
  3. 如果不是面试录音,能停止分析。
  4. 输出格式清晰。

3. 问答对提取规则设计

设计问答对提取节点的规则,要求:

  1. 不提取自我介绍。
  2. 每个问题和回答配对。
  3. 保留回答中的卡顿或停顿。
  4. 补全追问上下文。

4. 面试题生成流程设计

假设用户上传了一份简历,请设计面试题生成模块的流程,要求包含技能题、项目题和题目整合。

5. Agent 编排规则设计

为 AI 面试助手设计 4 条调用规则:

  1. 上传简历并要求看简历。
  2. 上传录音。
  3. 上传简历并要求生成面试题。
  4. 用户闲聊或问无关问题。

6. 批处理代码阅读与补全

下面是一段极简批处理思路代码,请阅读并补全注释中的关键逻辑。

"""
需求:
批量处理一个文件夹中的简历文件,并把每份简历交给 Coze 智能体分析。

步骤:
1. 遍历指定文件夹。
2. 找到符合格式要求的简历文件。
3. 上传文件到 Coze。
4. 调用智能体获取分析结果。
5. 把结果保存为 Markdown 文件。
"""

from pathlib import Path


def is_resume_file(file_path: Path) -> bool:
    """判断当前文件是否是需要处理的简历文件。"""
    # TODO:允许处理 pdf、doc、docx 三类文件
    pass


def save_result(result_dir: Path, file_path: Path, result_text: str) -> None:
    """把智能体返回的结果保存成 Markdown 文件。"""
    # TODO:根据原文件名生成 md 文件名,并写入分析结果
    pass

7. 综合案例:AI 面试训练闭环

请设计一个完整的 AI 面试训练闭环,要求包含:

  1. 简历评估。
  2. 面试题生成。
  3. 模拟面试录音。
  4. 录音分析。
  5. 下一轮改进建议。

五、答案区

选择题答案

  1. B
  2. A
  3. B
  4. A
  5. A
  6. A
  7. A
  8. A
  9. A
  10. A

填空题答案

  1. 自动语音识别;声音;文字
  2. 文字;声音
  3. 角色划分
  4. 自我介绍原文;面试整体概要
  5. 问答对
  6. 问题
  7. 检索参考资料或标准答案
  8. 技能点;项目经历
  9. 对应工作流
  10. 保存到文件

简答题参考答案

  1. ASR 是把声音转成文字,TTS 是把文字转成声音。录音分析主要使用 ASR,因为系统需要先把面试录音变成可处理的文本。

  2. 只做语音转文字只能得到一段原文,不能知道谁在提问、谁在回答,也不能判断回答好不好。录音分析还需要角色划分、问答提取、逐题评估和报告生成。

  3. 面试场景通常只有面试官和应试者两个角色,对话语义比较明显。使用大模型根据语义划分角色,成本更低,流程更简单,适合当前项目阶段。

  4. 追问如果不补全上下文,后续检索和入库会失去语义。例如“解码器呢”单独看不清楚问题,补全成“Transformer 的解码器是什么”后才能准确检索和评估。

  5. 每场面试的问题数量不固定,循环可以让系统对每一道题执行同样的处理流程:拆分问题和回答、检索知识库、入库、评估回答。

  6. RAG 可以给大模型提供参考答案,减少模型凭空判断的风险。模型可以先参考题库或知识库,再判断用户回答是否准确、完整、有逻辑。

  7. 真实面试题入库后可以被后续复用。题库积累越多,之后的面试题生成和回答评估越有依据,这体现了项目中的数据积累思路。

  8. 技能题通常来自简历技能点,可以结合题库和知识库生成;项目题来自具体项目经历,更个性化,需要围绕项目背景、技术选型、职责、难点和指标追问。

  9. Agent 编排让用户只面对一个统一入口,不需要知道背后有几个工作流。Agent 根据用户上传的文件和表达判断该调用简历评估、录音分析还是面试题生成。

  10. 真实工作中常常要处理大量文件,不能靠人工一个个上传。API 和批处理可以自动遍历、上传、调用、保存结果,让 Coze 能力接入业务系统。

实操题参考答案

1. 录音分析流程梳理

步骤 输入 输出 说明
上传录音 MP3/WAV 音频 音频对象或链接 接收原始材料
ASR 转写 音频链接 转写文本 把声音转成文字
角色划分 转写文本 面试官/应试者对话 分清谁问谁答
概要与问答提取 对话文本 自我介绍、概要、问答数组 把文本结构化
面试表现评估 自我介绍、问答数组、知识库 评估结果 对自我介绍和每题回答进行判断
报告生成 各评估结果 复盘报告 汇总输出给用户

2. 角色划分提示词参考

你是一个面试录音转写内容整理助手。
请根据输入的语音转文字内容,判断每句话更可能来自面试官还是应试者。

要求:
1. 按“面试官:内容”和“应试者:内容”的格式输出。
2. 尽量保留原始内容,不要随意省略。
3. 如果出现明显卡顿、停顿或异常表达,可以用【停顿】等方式标注。
4. 如果输入内容不是面试对话,请直接输出空字符串,不要强行分析。

输入内容:
{{input}}

3. 问答对提取规则参考

你是一个面试问答提取助手。
请从面试官和应试者的对话中提取问题和对应回答,并以字符串数组返回。

要求:
1. 不提取开场自我介绍。
2. 每个数组元素只包含一个问题和对应回答。
3. 回答中的【停顿】、【卡顿】等信息要保留。
4. 如果问题是追问,需要结合上文补全问题含义。
5. 输出格式为:
   问题:xxx
   回答:xxx

面试对话:
{{qa}}

4. 面试题生成流程参考

  1. 用户上传简历。
  2. 文件解析节点把简历转成文本。
  3. 内容分块节点提取技能点数组和项目经历数组。
  4. 数据库查询节点获取最近 100 道真实面试题。
  5. 技能点循环:每个技能点检索题库并生成技术问题。
  6. 项目经历循环:每个项目生成项目追问。
  7. 面试题整合节点去重、筛选、书面化,并补充人事问题。
  8. 结束节点输出最终面试题集合。

5. Agent 编排规则参考

  1. 如果用户上传简历,并表达“帮我看看简历”“优化简历”等意图,调用简历评估工作流。
  2. 如果用户上传音频文件,无论用户文字怎么描述,优先调用录音分析工作流。
  3. 如果用户上传简历,并表达“生成面试题”“模拟面试题”等意图,调用面试题生成工作流。
  4. 如果用户闲聊或提出无关请求,不调用工作流,提示当前只支持简历评估、录音分析和面试题生成。

6. 批处理代码参考答案

"""
需求:
批量处理一个文件夹中的简历文件,并把每份简历交给 Coze 智能体分析。

步骤:
1. 遍历指定文件夹。
2. 找到符合格式要求的简历文件。
3. 上传文件到 Coze。
4. 调用智能体获取分析结果。
5. 把结果保存为 Markdown 文件。

说明:
这段代码只保留“文件筛选”和“结果保存”两个基础动作。
真正上传 Coze、调用智能体的部分,后续可以再接入 SDK。
"""

from pathlib import Path


def is_resume_file(file_path: Path) -> bool:
    """判断当前文件是否是需要处理的简历文件。

    参数:
    file_path:当前遍历到的文件路径。

    返回:
    True:说明这个文件是简历文件,可以继续处理。
    False:说明这个文件不是目标格式,应该跳过。
    """
    # suffix 表示文件后缀名,例如:.pdf、.docx。
    # lower() 的作用是统一转成小写,避免 .PDF 和 .pdf 被当成不同格式。
    suffix = file_path.suffix.lower()

    # allowed_suffixes 保存允许处理的简历格式。
    # 这里先支持 pdf、doc、docx 三类常见简历文件。
    allowed_suffixes = {".pdf", ".doc", ".docx"}

    # 同时判断两件事:
    # 1. 当前路径必须是文件,不能是文件夹。
    # 2. 文件后缀必须在允许列表中。
    return file_path.is_file() and suffix in allowed_suffixes


def save_result(result_dir: Path, file_path: Path, result_text: str) -> None:
    """把智能体返回的结果保存成 Markdown 文件。

    参数:
    result_dir:保存结果的文件夹。
    file_path:原始简历文件路径。
    result_text:智能体返回的分析结果。

    效果:
    假设原文件是 zhangsan.pdf,
    最终会在 result_dir 中生成 zhangsan.md。
    """
    # mkdir 用来创建结果文件夹。
    # exist_ok=True 表示如果文件夹已经存在,也不会报错。
    result_dir.mkdir(parents=True, exist_ok=True)

    # stem 表示不带后缀的文件名。
    # 例如 zhangsan.pdf 的 stem 是 zhangsan。
    result_file_name = file_path.stem + ".md"

    # 拼接最终的结果文件路径。
    result_file = result_dir / result_file_name

    # 把分析结果写入 Markdown 文件。
    # encoding="utf-8" 可以避免中文乱码。
    result_file.write_text(result_text, encoding="utf-8")

7. 综合案例参考答案

一个完整的 AI 面试训练闭环可以这样设计:

  1. 用户先上传简历,系统调用简历评估工作流,输出简历问题和修改建议。
  2. 用户根据建议修改简历,再上传简历生成模拟面试题。
  3. 用户根据生成的题目进行模拟面试,并录制音频。
  4. 用户上传面试录音,系统调用录音分析工作流。
  5. 系统分析自我介绍、每道题回答质量、表达问题和知识薄弱点。
  6. 系统输出下一轮改进建议,例如需要复习 RAG、优化项目介绍、控制自我介绍时长。
  7. 用户根据建议继续修改简历、练习题目、再次模拟面试。

这个闭环的价值是:简历、题目、录音、复盘互相连接,训练不再靠感觉,而是靠数据和反馈持续改进。