零帧起步,手搓一个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 天前
Open WebUI安装指南。搭建自己的自托管 AI 平台
人工智能·windows·ai
鸿乃江边鸟1 天前
Nanobot 从onboard启动命令来看个人助理Agent的实现
人工智能·ai
本旺1 天前
【Openclaw 】完美解决 Codex 认证失败
ai·codex·openclaw·小龙虾·gpt5.4
风止何安啊1 天前
为什么要有 TypeScript?让 JS 告别 “薛定谔的 Bug”
前端·javascript·面试
张張4081 天前
(域格)环境搭建和编译
c语言·开发语言·python·ai
乐鑫科技 Espressif1 天前
使用 MCP 服务器,把乐鑫文档接入 AI 工作流
人工智能·ai·esp32·乐鑫科技
语戚1 天前
Stable Diffusion 入门:架构、空间与生成流程概览
人工智能·ai·stable diffusion·aigc·模型
俊哥V1 天前
每日 AI 研究简报 · 2026-04-08
人工智能·ai
rrrjqy1 天前
什么是RAG?
ai
Flittly1 天前
【SpringAIAlibaba新手村系列】(15)MCP Client 调用本地服务
java·笔记·spring·ai·springboot