:模擬面試 Agent——五階段狀態(tài)機(jī))
本篇拆解模擬面試 AgentInterview。它的核心是一個五階段狀態(tài)機(jī)熱身 → 技術(shù)基礎(chǔ) → 項目深挖 → 反問收尾 → 結(jié)束。每次學(xué)員回答都會完整跑一遍 LangGraph 圖圖內(nèi)的check_stage節(jié)點用輪數(shù) 回答質(zhì)量 內(nèi)容條件三重信號驅(qū)動階段流轉(zhuǎn)回答質(zhì)量四檔標(biāo)簽excellent/adequate/weak/no_answer驅(qū)動追問或換題決策最后生成五維度面試報告。這是狀態(tài)機(jī)模式在 Agent 里最完整的應(yīng)用。一、五階段狀態(tài)機(jī)classInterviewStage(str,Enum):面試的五個階段狀態(tài)機(jī)的狀態(tài)取值。WARMUPwarmup# 熱身邀請自我介紹TECH_BASEtech_base# 技術(shù)基礎(chǔ)題庫問答PROJECTproject# 項目深挖針對簡歷項目追問CLOSINGclosing# 反問收尾讓學(xué)員提問FINISHEDfinished# 終態(tài)觸發(fā)報告生成階段流轉(zhuǎn)順序固定WARMUP → TECH_BASE → PROJECT → CLOSING → FINISHED但每一步推進(jìn)都有條件不是簡單的計數(shù)器階段轉(zhuǎn)移推進(jìn)條件WARMUP → TECH_BASE學(xué)員完成自我介紹≥1輪TECH_BASE → PROJECT問過 ≥8 道題或題庫問完P(guān)ROJECT → CLOSING所有簡歷項目都深挖過任意 → FINISHED強(qiáng)制總輪數(shù) ≥ 上限-2 或?qū)W員說結(jié)束面試CLOSING → FINISHED反問環(huán)節(jié)滿 2 輪二、圖拓?fù)湟淮螌υ捙芤槐閳Dfinished其他STARTload_context加載簡歷/題庫/歷史check_stage階段判斷generate_report生成五維報告evaluate_answer評估回答質(zhì)量save_report保存報告save_memory記憶保存generate_response生成下一輪面試題ENDdef_route_after_check_stage(state:InterviewState)-str:ifstate.get(current_stage)InterviewStage.FINISHED.value:returngenerate_report# 結(jié)束 → 報告路徑returnevaluate_answer# 其他 → 正常對話路徑關(guān)鍵理解模擬面試不是一個大圖持續(xù)運(yùn)行而是每一輪對話都完整執(zhí)行一次圖。上一輪的 State 通過 MemorySaver 按thread_id持久化下一輪ainvoke時自動加載——這就是每次跑一遍圖還能記得上下文的原因。三、check_stage純邏輯的階段推進(jìn)不調(diào) LLMcheck_stage_node是狀態(tài)機(jī)的心臟注釋明確寫著純邏輯不調(diào) LLM——階段判斷這種事用代碼寫死比讓 LLM 決定更可靠、更省錢asyncdefcheck_stage_node(state:InterviewState)-dict:current_stagestate.get(current_stage,InterviewStage.WARMUP.value)stage_turnsstate.get(stage_turn_count,0)total_turnsstate.get(total_turn_count,0)max_turnsstate.get(max_turns,DEFAULT_MAX_TURNS)# 從后往前找學(xué)員最后一條消息檢測結(jié)束面試關(guān)鍵詞latest_student_msgformsginreversed(state.get(messages,[])):ifisinstance(msg,HumanMessage):latest_student_msg_msg_text(msg)break# 強(qiáng)制結(jié)束判斷輪數(shù)快到上限或?qū)W員主動說要結(jié)束force_end_keywords[結(jié)束面試,不想繼續(xù),面試結(jié)束,結(jié)束吧,到此為止]is_forced_end(total_turnsmax_turns-2orany(kwinlatest_student_msgforkwinforce_end_keywords))ifis_forced_end:return{current_stage:InterviewStage.FINISHED.value}# CLOSING → FINISHED反問環(huán)節(jié)滿 2 輪就結(jié)束ifcurrent_stageInterviewStage.CLOSING.value:ifstage_turnsSTAGE_MIN_TURNS[closing]:return{current_stage:InterviewStage.FINISHED.value}return{}# 常規(guī)推進(jìn)到上限強(qiáng)制推到下限看內(nèi)容條件stage_minSTAGE_MIN_TURNS.get(current_stage,2)stage_maxSTAGE_MAX_TURNS.get(current_stage,10)should_advanceFalseifstage_turnsstage_max:should_advanceTrue# 到上限 → 強(qiáng)制推進(jìn)elifstage_turnsstage_min:should_advance_check_advance_condition(# 到下限 → 看內(nèi)容current_stage,state,latest_student_msg)ifshould_advance:next_stage_next_stage(current_stage)return{current_stage:next_stage,stage_turn_count:0,# 新階段輪數(shù)從 0 開始followup_count:0,# 追問計數(shù)清零current_question:None}# 清當(dāng)前題新階段重新選題return{}# 不推進(jìn)保持當(dāng)前階段最小/最大輪數(shù) 內(nèi)容條件的雙閾值設(shè)計非常實用不到最小輪數(shù)不問內(nèi)容條件防止問兩題就跑超過最大輪數(shù)強(qiáng)制推進(jìn)防止一直卡在某個階段中間才看內(nèi)容條件技術(shù)題問夠 8 道、項目全深挖完。內(nèi)容條件_check_advance_condition也是純邏輯def_check_advance_condition(stage,state,latest_msg)-bool:ifstageInterviewStage.WARMUP.value:returnTrue# 學(xué)員發(fā)任意消息即視為完成自我介紹ifstageInterviewStage.TECH_BASE.value:bankstate.get(question_bank,[])asked_countsum(1forqinbankifq.get(asked,False))returnasked_countmin(8,len(bank))# 至少問過8道ifstageInterviewStage.PROJECT.value:projectsstate.get(resume_projects,[])ifnotprojects:returnTrue# 無簡歷項目直接推returnlen(state.get(projects_asked,[]))len(projects)returnFalse注意 TECH_BASE 用min(8, len(bank))——題庫不足 8 道時問完即走不會死等。四、evaluate_answer回答質(zhì)量四檔標(biāo)簽classAnswerQuality(str,Enum):EXCELLENTexcellent# 優(yōu)秀有技術(shù)細(xì)節(jié)/量化/原理ADEQUATEadequate# 基本及格方向?qū)Φ鄙疃萕EAKweak# 較弱方向偏或太表面NO_ANSWERno_answer# 未作答明說不知道或為空evaluate_answer_node評估學(xué)員本次回答寫入last_answer_quality供generate_response_node決定追問還是換題excellent認(rèn)可 → 進(jìn)入下一題adequate輕追問一次引導(dǎo)補(bǔ)充深度weak換個角度再問一次或直接講解no_answer給出參考答案進(jìn)入下一題。每個問題的追問次數(shù)有上限followup_count≤ 2防止在單題上無限糾纏。這個質(zhì)量標(biāo)簽驅(qū)動交互策略的模式把面試官的經(jīng)驗變成了可執(zhí)行的規(guī)則。五、generate_response按階段生成不同響應(yīng)generate_response_node按當(dāng)前階段路由到四個響應(yīng)函數(shù)asyncdefgenerate_response_node(state:InterviewState)-dict:stagestate.get(current_stage)ifstageInterviewStage.WARMUP.value:content,updatesawait_respond_warmup(state)elifstageInterviewStage.TECH_BASE.value:content,updatesawait_respond_tech_base(state)elifstageInterviewStage.PROJECT.value:content,updatesawait_respond_project(state)else:# CLOSINGcontent,updatesawait_respond_closing(state)return{messages:[AIMessage(contentcontent)],**updates}5.1 技術(shù)基礎(chǔ)題庫 LLM 生成結(jié)合_respond_tech_base的策略優(yōu)先用題庫從question_bankseed_interview_questions.py預(yù)置的面試題庫里挑一道未問過的題標(biāo)記askedTrue題庫不夠用 LLM 按target_position現(xiàn)場出題結(jié)合追問如果上輪回答質(zhì)量不佳且追問次數(shù)未超限先追問當(dāng)前題。5.2 項目深挖從簡歷來_respond_project讀resume_projects簡歷審查 Agent 的結(jié)果注入的從沒深挖過的項目里挑一個針對其技術(shù)棧和亮點生成追問并記入projects_asked防止重復(fù)深挖。這就是簡歷 → 面試的聯(lián)動面試題不是憑空生成而是從學(xué)員自己的簡歷里長出來的。5.3 記憶注入_build_system_with_summary把歷史摘要existing_summary拼進(jìn) System Prompt讓面試官記得學(xué)員之前說過什么——長面試不會前后矛盾。六、報告生成五維度評估面試結(jié)束進(jìn)入 FINISHED后generate_report_node用結(jié)構(gòu)化輸出生成InterviewReportclassInterviewReport(BaseModel):dimensions:list[DimensionEval]# 五個維度的評估結(jié)果overall_score:int# 綜合得分 0-100五維度加權(quán)平均strengths:list[str]# 2-3條核心優(yōu)勢improvements:list[str]# 2-3條重點提升方向overall_comment:str# 綜合評語recommended_topics:list[str]# 建議重點復(fù)習(xí)的3-5個知識點next_step_advice:str# 下一步備考建議classDimensionEval(BaseModel):dimension:strscore:int# 得分 0-100comment:strhighlights:list[str]weaknesses:list[str]classReportWrapper(BaseModel):report:InterviewReport# ★ with_structured_output 需要頂層對象注意ReportWrapper這層包裝with_structured_output要求頂層是對象而非裸列表/裸模型所以包了一層。這是 resume 的IssueList同款經(jīng)驗。報告生成失敗時走降級fallback_usedTrue返回基礎(chǔ)反饋“面試評估服務(wù)暫時不可用已記錄本次面試對話請稍后查看報告?!逼?、記憶保存與上下文加載load_context_node加載簡歷審查結(jié)果resume_review_id→ 項目/技能、題庫、歷史摘要save_memory_node超過閾值觸發(fā)摘要壓縮把長面試壓成學(xué)員畫像摘要寫庫。整個面試的狀態(tài)階段、輪數(shù)、已問題目、追問計數(shù)、質(zhì)量標(biāo)簽全部存在 State 里由 MemorySaver 按 thread_id 跨請求持久化——狀態(tài)機(jī) checkpointer 的組合讓長對話天然成立。八、本篇小結(jié)知識點實現(xiàn)位置核心技巧狀態(tài)機(jī)定義state.pyInterviewStage枚舉 固定流轉(zhuǎn)順序階段推進(jìn)check_stage_node純邏輯不調(diào) LLM 最小/最大輪數(shù)雙閾值內(nèi)容條件_check_advance_condition題庫問夠 / 項目全深挖才推進(jìn)質(zhì)量標(biāo)簽evaluate_answer_node四檔標(biāo)簽驅(qū)動追問/換題決策分階段響應(yīng)generate_response_node按階段路由到 4 個響應(yīng)函數(shù)簡歷聯(lián)動_respond_project從簡歷項目生成追問防重復(fù)深挖報告生成generate_report_node結(jié)構(gòu)化輸出 頂層包裝Interview Agent 的核心思想把面試官的經(jīng)驗翻譯成狀態(tài)機(jī) 規(guī)則 少量 LLM——階段推進(jìn)、輪數(shù)控制、追問次數(shù)、項目去重這些確定性邏輯全部用代碼寫死LLM 只負(fù)責(zé)出題和評估這種需要語義理解的部分。能用規(guī)則就用規(guī)則LLM 只做規(guī)則做不了的事——這是整個項目貫穿始終的工程哲學(xué)。下篇預(yù)告系列最后一篇《EduAgent 項目全解析七系統(tǒng)集成——Orchestrator 編排與統(tǒng)一 SSE 入口》看四個 Agent 如何被組裝成一個完整系統(tǒng)Orchestrator 的三種執(zhí)行模式單 Agent / Pipeline / 澄清、懶加載機(jī)制、簡歷審查模擬面試求職全鏈路 Pipeline、統(tǒng)一入口的五類零 Token 規(guī)則攔截 LLM 意圖路由 SSE 流式分發(fā)。項目源碼僅供參考?xì)g迎評論區(qū)交流討論。