Slot fields and status transitions
A conversation is a slot-filling exercise. The user walks in with an unstructured sentence. By the end of the turn you want filled fields: intent, specialty, chosen slot, confirmed record. The job of each node is to own a specific slot and fill it. Nobody else writes that slot.
# Slot ownership by node:
#
# intent_router -> state["intent"]
# collect_symptoms -> state["symptom"]
# match_specialty -> state["specialty"]
# find_doctor -> state["doctor"]
# propose_slots -> state["slot_options"], state["status"] = "ready_to_confirm"
# confirm_booking -> state["slot"], state["status"] = "confirmed"
#
# status is the routing pivot. The next turn reads it to decide
# whether to enter the router or jump straight to confirm_booking.One writer per slot. When debugging, you always know which node produced which value. Status is the exception: multiple nodes write it because it drives routing for the next turn.
async def run_turn(thread_id, user_message, patient_id=None):
state = get_thread_state(thread_id)
state["user_message"] = user_message
state["patient_id"] = patient_id
state["events"] = []
state["assistant_reply"] = ""
graph = get_graph()
# The status field from the previous turn decides entry point
if state.get("status") == "ready_to_confirm":
result = await confirm_booking(state)
else:
result = await graph.ainvoke(state)
_THREADS[thread_id] = dict(result)
...The status field written on turn N decides the entry point for turn N+1. ready_to_confirm means "a slot was proposed, interpret the next user message as a choice". Any other status means "run the full router again".
Matching exercise: Match each slot to the node that owns it
Loading practice…
Quiz: Quiz
Loading practice…