I am currently working on an agent which will do a...
# ask-ai
f
I am currently working on an agent which will do a normal fitment call with potential candidates. The issue I am facing is the agent is not calling the end. Call tool properly after the conversation is done. So the agent is getting stuck in the thank you loop like after the agent says thank you. Have a nice day. The age is supposed to make the tool call and end to, but the agent is waiting for the users reply after getting the users reply only the agent is calling the tool so and sometimes it is continuously going in the tank loop like user says thank you. Age says thank you and this lobe continuous. So through the prompt I tried to make multiple changes and nothing seems to work. The agent is not able to do not able to say a particular sentence and make a tool call at the same time. I am using GPT 40 Mini model with live ticket agents framework, so can you tell me possible solutions or let me know if you need more context?
t
Thanks for reaching out, Rabbani. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — I see you're asking about the agent getting stuck in a "thank you" loop and not reliably calling the end-call tool after the final utterance. I can help — a few targeted questions will let me give the exact fix that fits your implementation. Quick checklist of likely fixes you can try now (pick whichever fits your code): • Run the final speech and then immediately call your hangup function from your server logic (do not rely on the LLM to both speak and trigger hangup in the same turn). Example sequence: 1)
session.generate_reply("Thank you, have a nice day.")
2)
hangup_call()
/
delete_room
. • If you must do both in one tool, wrap them in a single function/tool (e.g.,
initiate_end_of_call
) that first sends the goodbye speech (or
session.say
) and then calls
delete_room
— invoke this tool once when the workflow finishes. • Use
allow_interruptions=False
and wait for playout completion (or await the speech handle) before calling hangup so the session doesn't reopen on brief user replies. • Ensure your
hangup_call()
actually calls
delete_room
(delete room terminates for all participants). Simply ending agent logic will leave the call open. • If using turn-based VAD/STT triggers, confirm the final user "thank you" doesn't create a new agent turn — you may need to disable automatic reactivation at the final step. To give a precise code fix, please share: 1. The code for how you currently trigger
end_call
/
hangup_call
(is it a tool decorated
@function_tool
or called from server logic?). 2. Whether you call
delete_room
inside your hangup function. 3. Whether you use
session.generate_reply
vs
session.say
and how you handle
allow_interruptions
. 4. How turns are controlled (manual turn control, VAD, or auto reactivation) and the exact final prompt/instruction you give the agent. Tell me those details and I’ll provide a concrete change you can drop into your agent to stop the thank-you loop and reliably end the call. Sources: Agents telephony integration | LiveKit docs | Pipeline nodes and hooks | LiveKit docs | Agent speech and audio | LiveKit docs
f
here is the my tool details:
Copy code
@function_tool()
    async def end_call(self, ctx: RunContext):
        """Called when the user wants to end the call"""
        participant_identity = self.participant.identity if self.participant else "unknown"
        <http://logger.info|logger.info>(f"ending the call for {participant_identity}")

        # Set ended_reason to indicate the assistant decided to end the call
        is_video = self.is_video
        print("video- true form",is_video)
        if is_video:
            update_call_status(
            room_name=self.job_ctx.room.name,
            ended_reason="assistant-ended-web-session"
        )
            set_ended_reason("assistant-ended-web-session")
        
        else:
            update_call_status(
            room_name=self.job_ctx.room.name,
            ended_reason="assistant-ended-call"
        )
            set_ended_reason("assistant-ended-call")
        

        # Say the end call message if configured
        # Check if using realtime model
        if hasattr(ctx.session, 'stt') and ctx.session.stt is None:
            # Realtime model - skip session.say()
            <http://logger.info|logger.info>(f"Realtime model detected - skipping session.say() for end call message")
        else:
            # Traditional model - use session.say()
            if self.end_call_message:
                <http://logger.info|logger.info>(f"Using custom end call message: {self.end_call_message[:50]}...")
                await ctx.session.say(self.end_call_message)
            else:
                # Use a default end call message
                await ctx.session.say("Thank you for your time. Have a great day!")
t
Thanks — this is helpful. I'm responding to your end_call implementation and the thank-you loop issue. Quick observations and targeted fixes you can apply now: • Right now your
end_call
only calls
ctx.session.say(...)
for non-realtime sessions and never actually deletes the room. Ensure you call the room deletion after the goodbye so the call truly ends for participants. Example sequence inside the tool: a. await
ctx.session.say(...)
(or
session.generate_reply
) b. call your room delete API (e.g.,
await ctx.api.room.delete_room(...)
or whatever
delete_room
wrapper you have) • If using a realtime model (where you skip
session.say
), explicitly delete the room immediately in
end_call
(realtime does not play audio, so skipping say without deleting leaves the call open). • Prevent reactivation during goodbye: ◦ If you send the goodbye with
ctx.session.say(...)
, await the returned handle (or use
allow_interruptions=False
) and only call delete after that handle completes. This avoids the user’s brief “thank you” triggering a new turn. • Wrap speech + hangup in the same function and call it exactly once. Do not rely on the LLM to both utter the goodbye and then independently invoke
end_call
later. • Add logging around the final steps to confirm order: ◦ Log before say, after say completes, before delete, and after delete. Small code sketch (insert into your tool after your existing message logic):
Copy code
# say goodbye if applicable
if hasattr(ctx.session, 'stt') and ctx.session.stt is None:
    <http://logger.info|logger.info>("Realtime model: skipping say, proceeding to delete room")
else:
    if self.end_call_message:
        handle = await ctx.session.say(self.end_call_message, allow_interruptions=False)
    else:
        handle = await ctx.session.say("Thank you for your time. Have a great day!", allow_interruptions=False)
    await handle  # wait for playout to finish

# now delete the room to end the call for everyone
await ctx.api.room.delete_room(api.DeleteRoomRequest(room=self.job_ctx.room.name))
<http://logger.info|logger.info>("Room deleted, call ended")
If you want, share: 1) Whether your current `hangup_call`/`update_call_status` already calls
delete_room
(or confirm it does not). 2) Whether you observe realtime vs. traditional behavior when testing (phone/SIP vs web). I’ll give the exact change to drop into your tool once you confirm those two points.
f
here is the part of the prompt that is not working properly.
Copy code
- Call Rescheduling:
If the candidate wishes to reschedule, handle it empathetically and conversationally.
Your goal: confirm a suitable date and time and end call.
Guidelines:
	•	If a specific date/time is provided: Confirm it naturally and restate it to avoid confusion.
	•	If the timing is vague: Politely ask for clarification (both day and time).
	•	If multiple options are given: Choose and confirm the first available one.
	•	If only time is given: Confirm the date also.
	•	**After confirmation: Thank them for their flexibility, confirm the rescheduled slot, provide a brief friendly closing statement (e.g., "Talk to you then!"), then immediately call end_call() tool in that SAME message. DO NOT wait for any further acknowledgment. DO NOT announce you are ending the call.**
the main problem is the agent is not able to invoke the end call tool. the agent is not reaching this code. something wrong in the prompt it seems.
t
Thanks — I’m looking at the prompt snippet you shared and your
end_call
tool. You’re right: the LLM often won’t both speak and call a tool from a single freeform assistant message when the prompt asks it to “call end_call() in that SAME message.” Let’s narrow this down and fix it. Quick actionable changes to try (pick one or combine as appropriate): 1) Replace the inline instruction to call
end_call()
from the assistant-generated message with a deterministic tool call from your runtime after the agent confirms the slot. • Let the LLM produce the confirmation text only. • Immediately after you detect that the LLM’s reply contains a confirmation (e.g., regex for date/time or a “confirmed” intent), call your
end_call
tool from server code once. This guarantees the hangup runs. 2) If you must have the model trigger the tool, make the tool a single explicit function with a strict tool-schema return and require the tool call via tools-routing rather than free text. Example flow: • Prompt: “If conversation is finished, respond ONLY with the JSON tool call: end_call(). Do not output any other text.” • Register
end_call
as a function-tool and enforce strict tool invocation in your agent runtime so the model chooses that tool call instead of a natural-language goodbye. 3) Make the final step atomic in the tool (what you already do partially): have the tool both say the goodbye (await playout) and then call
delete_room
. Ensure you always call
delete_room
inside
end_call
. For realtime sessions, call
delete_room
immediately; for non-realtime, await the say handle before deleting. 4) Prevent reactivation while goodbye is playing: • Use
allow_interruptions=False
and await the returned handle before deleting the room. • Add a short server-side grace window: after sending the final message, ignore further incoming turns for ~1–2s before calling delete to avoid picking up a quick “thank you” from user. Prompt tweaks (if you want the model to decide): • Avoid asking the model to both produce natural text AND call a tool in the same assistant message. Replace with either: ◦ “Produce ONLY the confirmation text. The runtime will call end_call() after this reply.” or ◦ “If you want to end the call now, respond with exactly: CALL_TOOL:end_call” (and map that token to a tool invocation in your server). What I need to confirm to give the exact code you should drop in: 1. Do you run the agent in a tools-enabled mode where the model can return a structured tool call (function tool)? 2. Does your
update_call_status
or other code already call
ctx.api.room.delete_room
anywhere else, or does
end_call
need to perform the delete? 3. Are you using realtime sessions (STT present) when you see the problem, or only traditional sessions? Answer those and I’ll give a minimal code snippet you can paste into your
end_call
tool or your runtime controller to ensure the call always ends immediately after confirmation.