66from pydantic import BaseModel
77from config import settings
88from agents import Agent , Runner , SQLiteSession , function_tool , set_default_openai_key
9+ from agents .extensions .handoff_prompt import RECOMMENDED_PROMPT_PREFIX
910
1011db = {
1112 "job_descriptions" : {
@@ -26,7 +27,7 @@ def extract_skills(session_id: str, job_id: int) -> list[str]:
2627 job_description = db ["job_descriptions" ][job_id ]
2728 skills = ["Python" , "SQL" , "System Design" ]
2829 db ["state" ][session_id ]["skills" ] = skills
29- print (f"\n 📋 Extracted skills: { ', ' . join ( skills ) } " )
30+ print (f"Extracted skills: { skills } " )
3031 return skills
3132
3233@function_tool
@@ -41,14 +42,24 @@ def update_evaluation(session_id: str, skill: str, evaluation_result: bool) -> b
4142 except KeyError :
4243 return False
4344
44- @function_tool
45- def transfer_to_skill_evaluator (session_id : str , skill : str ) -> bool :
46- """This function takes a skill, evaluates it and returns the evaluation result for the skill as a boolean pass / fail"""
47- result = True
48- print (f"Evaluating skill: { skill } . Result { result } " )
49- return result
45+ @function_tool
46+ def get_next_skill_to_evaluate (session_id : str ) -> str | None :
47+ """Retrieve the next skill to evaluate. Returns None if there are no more skills to evaluate"""
48+ all_skills = db ["state" ][session_id ]["skills" ]
49+ evaluated = db ["state" ][session_id ]["evaluation" ]
50+ evaluated_skills = [item [0 ] for item in evaluated ]
51+ remaining_skills = set (all_skills ) - set (evaluated_skills )
52+ try :
53+ next_skill = remaining_skills .pop ()
54+ print ("NEXT SKILL TOOL" , next_skill )
55+ return next_skill
56+ except KeyError :
57+ print ("No more skills" )
58+ return None
5059
5160ORCHESTRATOR_SYSTEM_PROMPT = """
61+ {RECOMMENDED_PROMPT_PREFIX}
62+
5263You are an interview orchestrator. Your goal is to evaluate the candidate on the required skills.
5364
5465# INSTRUCTIONS
@@ -57,9 +68,10 @@ def transfer_to_skill_evaluator(session_id: str, skill: str) -> bool:
5768
58691. Extract key skills from the job description using extract_skills tool
59702. Then welcome the candidate, explain the screening process and ask the candidate if they are ready
60- 3. Then, for EACH skill in the list, use transfer_to_skill_evaluator tool to delegate evaluation
71+ 3. Then, use the get_next_skill_to_evaluate tool to get the skill to evaluate
72+ 4. If the skill is not `None` then hand off to the "Skills Evaluator Agent" to perform the evaluation. Pass in the skill to evaluate
61734. Once you get the response, use the update_evaluation tool to save the evaluation result into the database
62- 5. Once all skills are evaluated, mention that the screening is complete and thank the candidate for their time
74+ 5. Once get_next_skill_to_evaluate returns `None`, return a json with a single field `status` set to "done" to indicate completion
6375"""
6476
6577ORCHESTRATOR_USER_PROMPT = """
@@ -71,21 +83,6 @@ def transfer_to_skill_evaluator(session_id: str, skill: str) -> bool:
7183Begin by welcoming the applicant, extracting the key skills, then evaluate each one.
7284"""
7385
74- def run_orchestrator_agent (session_id , job_id ):
75- session = SQLiteSession (f"screening-{ session_id } " )
76- agent = Agent (
77- name = "Interview Orchestrator Agent" ,
78- instructions = ORCHESTRATOR_SYSTEM_PROMPT ,
79- model = "gpt-5.1" ,
80- tools = [extract_skills , transfer_to_skill_evaluator , update_evaluation ]
81- )
82- user_input = ORCHESTRATOR_USER_PROMPT .format (job_id = job_id , session_id = session_id )
83- while user_input != 'bye' :
84- result = Runner .run_sync (agent , user_input , session = session )
85- print (result .final_output )
86- user_input = input ("User: " )
87- return
88-
8986question_bank = {
9087 "python" : {
9188 "easy" : [
@@ -179,6 +176,8 @@ def check_answer(skill:str, question: str, answer: str) -> Tuple[bool, str]:
179176 return result .model_dump_json ()
180177
181178EVALUATION_SYSTEM_PROMPT = """
179+ {RECOMMENDED_PROMPT_PREFIX}
180+
182181You are a specialised skill evaluator. Your job is to evaluate the candidate's proficiency in a given skill
183182
1841831. Identify which skill you're evaluating (it will be mentioned in the conversation)
@@ -189,11 +188,14 @@ def check_answer(skill:str, question: str, answer: str) -> Tuple[bool, str]:
189188 - If the check_answer tool returned incorrect, choose the lower difficulty, without going below 'easy'
190189 - Stop after 3 questions MAXIMUM
1911905. If the correctly answered two of the three questions, then they pass, otherwise they fail
191+ 6. After completion of 3 questions, hand off to the "Interview Orchestrator Agent" passing in the result of the evaluation
192+
193+ # DECISION RULES:
192194
193- DECISION RULES:
194- - Maximum 3 questions per skill
195+ - Do not give feedback on the user's answer. Always proceed to the next question
196+ - 3 questions per skill
195197
196- OUTPUT:
198+ # OUTPUT:
197199
198200After the evaluation is complete, return the pass/fail in a json object with the following properties
199201- result: true or false
@@ -203,25 +205,36 @@ def check_answer(skill:str, question: str, answer: str) -> Tuple[bool, str]:
203205Evaluate the user on the following skill: {skill}
204206"""
205207
206- def run_evaluation_agent (session_id , skill ):
208+ def run (session_id , job_id ):
207209 session = SQLiteSession (f"screening-{ session_id } " )
208- agent = Agent (
210+ orchestrator_agent = Agent (
211+ name = "Interview Orchestrator Agent" ,
212+ instructions = ORCHESTRATOR_SYSTEM_PROMPT .format (RECOMMENDED_PROMPT_PREFIX = RECOMMENDED_PROMPT_PREFIX ),
213+ model = "gpt-5.1" ,
214+ tools = [extract_skills , get_next_skill_to_evaluate , update_evaluation ]
215+ )
216+ evaluation_agent = Agent (
209217 name = "Skills Evaluator Agent" ,
210- instructions = EVALUATION_SYSTEM_PROMPT ,
218+ instructions = EVALUATION_SYSTEM_PROMPT . format ( RECOMMENDED_PROMPT_PREFIX = RECOMMENDED_PROMPT_PREFIX ) ,
211219 model = "gpt-5.1" ,
212220 tools = [get_question , check_answer ]
213221 )
214- user_input = EVALUATION_USER_PROMPT .format (skill = skill )
222+ orchestrator_agent .handoffs = [evaluation_agent ]
223+ evaluation_agent .handoffs = [orchestrator_agent ]
224+ user_input = ORCHESTRATOR_USER_PROMPT .format (job_id = job_id , session_id = session_id )
225+ agent = orchestrator_agent
215226 while user_input != 'bye' :
216- result = Runner .run_sync (agent , user_input , session = session )
227+ result = Runner .run_sync (agent , user_input , session = session , max_turns = 20 )
228+ agent = result .last_agent
217229 print (result .final_output )
218230 user_input = input ("User: " )
219231
220232def main ():
221233 set_default_openai_key (settings .OPENAI_API_KEY )
222234 job_id = 1
223235 session_id = "session123"
224- run_evaluation_agent (session_id , "Python" )
236+ run (session_id , job_id )
237+ print ("FINAL EVALUATION STATE" , db )
225238
226239if __name__ == "__main__" :
227240 main ()
0 commit comments