零帧起步,手搓一个AI面试agent

还在苦苦寻找面试题不知道去哪找吗,还在为面试准备发愁吗?确实本人经常也会有这样的烦恼,但是想着近两年的AI大模型这么火爆,为什么不干脆自己手搓一个AI面试机器人呢,一边能从大模型上面出题,还能基于这个自己也回答,完事了给自己评价一下岂不是美哉,顺便还能纠正和发现自己的不足之处,针对性的补充自己的短板,快速提升面试技巧,看看自己和机器人到底谁厉害。

所以花了半个月自己从0开始,基于python+langraph自己做了个面试的智能体。

简要设计以及技术栈

技术栈:

后端:

python

fastapi

langraph

前端:

vue3

elementui-plus

主要功能点:

*本地电脑MIC进行收音;

*ASR识别;

*langraph创建agent,以及graph流程编排;

*与LLM进行交互;

*机器人回答进行TTS播报;

*前端与后端通过SSE进行消息推送;

*前端文字与面试结果进行显示

运行效果

aiInterview2

核心代码演示

python 复制代码
#依旧是放一些核心的部分,其余部分都是正常的执行流程代码,就不展示了,
# 核心的地方还是 graph这里流程的定义
#定义工作流
worker_builder = StateGraph(GraphState)
worker_builder.add_node('interview_init_node', interview_init_node)
worker_builder.add_node('interview_question_node', interview_question_node)
worker_builder.add_node('interview_answer_node', interview_answer_node)
worker_builder.add_node('interview_evaluate_node', interview_evaluate_node)
# 定义工作流边
worker_builder.add_edge(START, 'interview_init_node')
worker_builder.add_edge('interview_init_node', 'interview_question_node')
worker_builder.add_edge('interview_question_node', 'interview_answer_node')
worker_builder.add_conditional_edges("interview_answer_node",
                                     multi_question_next_node,
                                     ["interview_question_node","interview_evaluate_node"])
worker_builder.add_edge('interview_evaluate_node', END)
# 会话缓存
# cp = InMemorySaver()
# worker = worker_builder.compile(checkpointer=cp)
worker = worker_builder.compile()

#录音函数
def record_audio():   
 """    
 异步录音函数,启动录音线程    
 """    
 logger.info("core: recording audio...")    
 dashscope.api_key = settings.DASHSCOPE_API_KEY    
 dashscope.base_websocket_api_url = settings.DASHSCOPE_ASR_BASE_URL    
 global should_stop, recording_thread, recognition_instance,thread_id        
 # 重置停止标志    
 should_stop = False    
 thread_id = generate_uuid()    
 logger.info(f"{TAG}: 新线程ID: {thread_id}")     
 # 只在没有录音线程或线程已结束时创建新线程    
 if not recording_thread or not recording_thread.is_alive():        
   # 在新线程中运行录音函数        
   recording_thread = threading.Thread(target=continuous_recording_with_silence_detection)        
   recording_thread.daemon = True  
   # 设置为守护线程        
   recording_thread.start()        
   logger.info(f"{TAG}: 录音线程已启动")        
 # 异步函数可以立即返回,不会阻塞    
 return "录音已开始"
 
 # 停止录音
 def stop_recording():    
 """    
 停止录音函数,设置全局停止标志    
 """    
 logger.info(f"{TAG}: stop recording...")    
 global should_stop, recording_thread    
 should_stop = True    
 if recording_thread and recording_thread.is_alive():       
   recording_thread.join(timeout=3.0)  # 最多等待5秒        
   if recording_thread.is_alive():            
     logger.info(f"{TAG}: 录音线程仍在运行,可能需要强制终止")       
   else:            
     logger.info(f"{TAG}: 录音线程已成功停止")    
     logger.info(f"{TAG}: 停止录音完成")
   
class ParaforMerRecordAsr:    
   # 实时语音识别回调    
   class Callback(RecognitionCallback):        
     global recording_thread_status, _record_state_change        
     def on_open(self) -> None:            
       recording_thread_status = True            
       logger.info('RecognitionCallback open.')        
     def on_close(self) -> None:            
       recording_thread_status = False            _
       record_state_change(False)            
       logger.info('RecognitionCallback close.')            
       # 发送录音的结果到llm进行处理            
       send_record_msg_to_worker(record_msg)                    
     def on_complete(self) -> None:            
       recording_thread_status = False            
       _record_state_change(False)            
       logger.info('RecognitionCallback completed.')        
     def on_error(self, message) -> None:            
       recording_thread_status = False            
       _record_state_change(False)            
       logger.info(f'RecognitionCallback task_id: , { message.request_id }')            
       logger.info(f'RecognitionCallback error: , { message.message }')            
       # 强制退出程序            
       sys.exit(1)        
   def on_event(self, result: RecognitionResult) -> None:            
     sentence = result.get_sentence()            
     if 'text' in sentence:                
       if RecognitionResult.is_sentence_end(sentence):                    
           # logger.info(                    
           #     'RecognitionCallback sentence end, request_id:%s, usage:%s'                    
           #     % (result.get_request_id(), result.get_usage(sentence)))                    
           res_text = sentence['text']                    
           logger.info(f'RecognitionCallback text: , { res_text }')                    
           global record_msg                    
           record_msg += res_text                    
           sync_send_msg_sse(MessageData(user_id="user_678", type="message_user", data=res_text))    
   
     @staticmethod    
     def signal_handler(sig, frame):        
       logger.info(f'{TAG}: Ctrl+C pressed, stop recognition ... sig is {sig}')        
       # 强制退出程序        
       sys.exit(0)

写在最后

代码并不是很复杂,关键看流程部分如何设计搭配,以及和大模型的交互,其实这只是agent的一部分应用,应用还有很多方面,比如可以构建本地的知识库应用,MCP服务应用,就看具体的运用领域以及深度吧,包括现在各种AI应用工具,开发工具都非常多,像Dify这个工具很多企业也在用,像是几年前很火的低代码开发一样,下篇玩玩这个吧。对比一下代码开发和工具开发区别。

如果感兴趣可以后台交流~

参考:https://mp.weixin.qq.com/s/W69GhZyiMohRhs4xpeNYfg

相关推荐
独码侠1 分钟前
第04篇·Codex Harness 审批×权限边界:4 档策略怎么选,密钥与仓库永远碰不到
ai·agent·codex
ndsc_d1 小时前
AI生成的UI如何继续修改?画布内生成与独立AI设计工具比较
人工智能·ui·ai·ui设计·ai设计·ai设计工具·ai生成ui
土星云SaturnCloud2 小时前
土星云边缘计算+AI视觉+视频存管:连锁汽修数字化升级实践
服务器·人工智能·ai·边缘计算
Elastic 中国社区官方博客2 小时前
Elastic 线下 Meetup 将于 2026 年 12 月 19 号下午在西安举行
大数据·数据库·elasticsearch·搜索引擎·ai·数据分析·全文检索
衣舞晨风2 小时前
一次升级引出两个BUG:API7 AI 网关流式 500 与低流量 CPU 打满排查
网关·ai·bug·异常排查
酒醉的胡铁3 小时前
相似度搜索的常见算法
算法·ai
SamDeepThinking4 小时前
synchronized 和 lock 有什么区别?
java·后端·面试
考研保研资料分享4 小时前
普通211核专业保研经验:清华预推免录取与北大、西交面试复盘
面试·职场和发展
人道领域4 小时前
【面试八股速通篇01】锁与并发工具
java·开发语言·面试·c#
玫瑰互动GEO4 小时前
百度AI问答81.7%信源百度系:技术品牌GEO优化 3步自查工程化解读
人工智能·百度·ai·dubbo·ai搜索·geo优化