sparse-appointment-26219
08/11/2025, 10:23 AM75:85:hdsr_multiagent/agents/base_agent.py
async def transition_to(self, agent_cls: type, message: Optional[str] = None) -> Tuple[Agent, str]:
"""Transition to another agent with optional announcement."""
from hdsr_multiagent.models.data_models import HDSRData
hdsr_data: HDSRData = self.session.userdata
hdsr_data.previous_agent = self
new_agent = agent_cls()
if message:
new_agent._transition_message = message
<http://logger.info|logger.info>(f"Transitioning from {self.__class__.__name__} to {agent_cls.__name__}")
return new_agent, message or f"Transitioning to {agent_cls.__name__}"
### 2. Orchestrator → Question Agent Handoff
60:75:hdsr_multiagent/agents/orchestrator_agent.py
@function_tool
async def record_consent(
self,
consent_given: Annotated[bool, Field(description="Whether the patient has given consent to participate")],
context: RunContext_T
) -> tuple[Agent, str]:
"""Record patient consent and begin the assessment if consent is given"""
hdsr_data: HDSRData = context.session.userdata
if not consent_given:
await self.say(CONSENT_DECLINED_MESSAGE)
return None, "Assessment cancelled - no consent given"
# Consent given - proceed directly to assessment
hdsr_data.assessment_started = True
hdsr_data.current_question = HDSRQuestion.AGE
hdsr_data.transition_count += 1
return await self.transition_to(
HDSRQuestionAgent,
"Thank you for your consent! Let's begin the assessment."
)
### 3. Question Agent → Summary Agent Handoff
520:540:hdsr_multiagent/agents/question_agent.py
@function_tool
async def record_backwards_response_second(
self,
backwards_sequence: Annotated[str, Field(description="The numbers the patient said backwards for the second sequence (4, 1, 5)")],
context: RunContext_T
) -> tuple[Agent, str]:
"""Record response to second backwards digit span sequence and transition to summary"""
hdsr_data: HDSRData = context.session.userdata
# Store second sequence and combine both
hdsr_data.backwards_second = backwards_sequence
first_sequence = hdsr_data.backwards_first or "Not recorded"
combined_response = f"First sequence: {first_sequence}, Second sequence: {backwards_sequence}"
hdsr_data.record_response(HDSRQuestion.BACKWARDS, combined_response)
hdsr_data.assessment_completed = True
hdsr_data.transition_count += 1
return await self.transition_to(
HDSRSummaryAgent,
"Perfect! We've completed all the questions. Thank you for your participation."
)
## Tool Calls for Questionnaire State Handling
### 1. Age Question
200:215:hdsr_multiagent/agents/question_agent.py
@function_tool
async def record_age_response(
self,
age: Annotated[int, Field(description="Patient's stated age")],
context: RunContext_T,
) -> None:
"""Record response to age question (Question 1)"""
hdsr_data: HDSRData = context.session.userdata
if not hdsr_data.can_proceed_to_question(HDSRQuestion.AGE):
await self.say(QUESTION_ORDER_ERROR_MESSAGE)
return
hdsr_data.record_response(HDSRQuestion.AGE, str(age))
await self.say("Great! Let's move to the next question.")
hdsr_data.current_question = HDSRQuestion.DATE_TIME
# Automatically move to next question
await self._ask_next_question(hdsr_data, context)
### 2. DateTime Question
220:240:hdsr_multiagent/agents/question_agent.py
@function_tool
async def record_datetime_response(
self,
year: Annotated[str, Field(description="Year mentioned by patient")],
month: Annotated[str, Field(description="Month mentioned by patient")],
day: Annotated[str, Field(description="Day mentioned by patient")],
day_of_week: Annotated[str, Field(description="Day of week mentioned by patient")],
context: RunContext_T,
) -> None:
"""Record response to date/time question (Question 2)"""
hdsr_data: HDSRData = context.session.userdata
if not hdsr_data.can_proceed_to_question(HDSRQuestion.DATE_TIME):
await self.say("We need to complete the previous question first.")
return
response = f"Year: {year}, Month: {month}, Day: {day}, Day of week: {day_of_week}"
hdsr_data.record_response(HDSRQuestion.DATE_TIME, response)
await self.say("Thank you. Moving to the next question.")
hdsr_data.current_question = HDSRQuestion.THREE_WORDS_IMMEDIATE
# Automatically move to next question
await self._ask_next_question(hdsr_data, context)
### 3. Fluency Test (Special State Handling)
380:420:hdsr_multiagent/agents/question_agent.py
@function_tool
async def start_fluency_test(
self,
understood: Annotated[bool, Field(description="Whether the patient understands the fluency instructions")],
context: RunContext_T,
) -> None:
"""Start the fluency test after patient confirms understanding"""
hdsr_data: HDSRData = context.session.userdata
if not hdsr_data.can_proceed_to_question(HDSRQuestion.FLUENCY):
await self.session.say("We need to complete the previous question first.")
return
if understood:
await self.say("Great. After I say START, you'll have exactly one minute to name as many animals as you can. I will remain completely silent during your minute.")
await asyncio.sleep(1.0)
await self.say("3... 2... 1... START!")
# Disable audio output channel and enable strict silent mode
try:
self.session.output.set_audio_enabled(False)
except Exception:
pass
self._fluency_silent_mode = True
# Schedule the strict silent timer
self._schedule_fluency_timer()
### 4. State Validation
70:85:hdsr_multiagent/models/data_models.py
def can_proceed_to_question(self, question: HDSRQuestion) -> bool:
"""Check if we can proceed to a specific question"""
questions_order = QUESTION_SEQUENCE
current_index = questions_order.index(question)
# Can always start with first question
if current_index == 0:
return True
# Check if previous question is completed
previous_question = questions_order[current_index - 1]
return previous_question in self.completed_questions
def get_next_question(self) -> Optional[HDSRQuestion]:
"""Get the next question that needs to be completed"""
questions_order = QUESTION_SEQUENCE
for question in questions_order:
if question not in self.completed_questions:
return question
return None
### 5. Response Recording
50:60:hdsr_multiagent/models/data_models.py
def record_response(self, question: HDSRQuestion, response: str, notes: str = ""):
"""Record a response and update state"""
self.responses[question.value] = HDSRResponse(
question=question.value,
response=response,
notes=notes
)
if question not in self.completed_questions:
self.completed_questions.append(question)
<http://logger.info|logger.info>(f"Recorded response for {question.value}: {response}")
The core pattern is:
1. Tool calls validate state and record responses
2. State updates happen in HDSRData
3. Automatic progression to next question after each tool call
4. Agent transitions use transition_to()
method with optional messages
5. Context preservation between agents via `userdata`al state examination) screening?
It has very clear step-by-step test/list of questions that need to be answered. I have currently built it using three separate Agents (intro, questioner and summary) but I seem to be having some inconsistencies with the reliability and handoff. This is how I currently use the livekit framework:tall-airline-16756
08/11/2025, 10:23 AMtall-airline-16756
08/11/2025, 10:24 AMuserdata
to keep track of everything—user responses, where they are in the exam, and more. For example, you might do session.userdata = MMSEState(responses={})
to get started.
• Agent Handoff: When one section wraps up, smoothly hand off to the next agent using the transition_to
method. So after finishing one part, you’d return a new agent instance for the next section.
• Use Function Tools for Interaction: Inside each agent, set up function tools to handle user responses and decide what happens next. Something like @function_tool() async def handle_response(...)
works great here.
• Initialize the Workflow: Kick off the session with the first agent managing the initial section of the MMSE. For example, await session.start(agent=MMSEAgent(job_context=ctx), room=ctx.room)
.
This way, you keep everything neat and flowing smoothly—from clear handoffs between agents to tracking progress throughout the exam. If you want, I’m happy to dive deeper into any part, like specific sections or how to handle scoring. Just let me know! Sources: Tool definition and use | LiveKit Docs | Workflows | LiveKit Docs