Built-in functions like ‘end_call’ and ‘transfer_call’ support an optional description. However, these do not seem to be passed to the LLM along with the rest of the user-defined “custom” functions - at least what an empirical test shows by adding some secret info to the function description and asking the agent about it - with custom functions it clearly knows the secret but with the built-in function it does not. Is the choice whether a custom function should be called offloaded to a separate LLM request by Retell? They don’t seem to be part of the main context the bot has. This is important about the info we can put in there. Also important if you build a test harness that tried to replicate the Retell LLM invocations as close as possible.
Hello @plamen Good catch — your empirical result matches what the code does.
- **Built-in tools**
end_call,transfer_call,agent_swap,send_sms,press_digit,extract_dynamic_variable,bridge_transfer,cancel_transfer,adjust_voice_speed): theirdescriptionfield is overridden by a fixed internal string when we build the function schema for the LLM, so anything you type there is not visible to the model — that’s why the “secret” trick worked for custom functions but not for these. If you need the LLM to know something about when to fireend_call/transfer_call, put it in the system prompt (or, in a Conversation Flow, on the state-transition edge’s description — edge descriptions are forwarded). - **Tools that DO honor your
description**:customfunctions,codetools,integration_app,mcp, and the Cal.com toolscheck_availability_cal,book_appointment_cal). - **No separate classifier**: the decision to invoke
end_call/transfer_call/ etc. is made in the same single tool-calling LLM request as everything else — those tools are appended to the sametoolsarray sent to OpenAI / Anthropic / Gemini. The only adjacent extra LLM call is an internal helper that resolves a natural-language transfer destination into a phone number after the model has already decided to transfer; it does not decide whether to transfer.
For a test harness that mirrors our invocation, you can treat tool selection as a single tool-augmented chat-completion / Responses-API call, with built-in tools using our fixed descriptions rather than yours.
Thank You
@Shah-Fazal thank you, this is exactly what I was looking for! Two quick follow-up questions:
- Is this behavior of ignoring the user-provided description a bug or expected? It might be worth documenting this if intended.
- The retell UI also allows us to change the name of the built-in function. If we do so, do we refer to the built-in functions by their new name or by their original name when we add instructions in the system prompt. I.e. is the name field also ignored and is the original name sent with the LLM request?
Thanks!
Hey @plamen Two good follow-ups — here’s what the code does:
-
Is the description override a bug or intended? The behavior is consistent and deliberate-looking (every built-in branch in the serializer writes a hardcoded string), so I wouldn’t call it a bug — but it isn’t documented today, which is the real gap. Worth raising on our side as an API-reference update so this isn’t a surprise. If you want to influence when the model fires
end_call/transfer_call, the supported levers are: the system prompt, conversation-flow state-transition edge descriptions (those are forwarded to the LLM), and renaming (see below). -
Renaming a built-in tool — which name does the LLM see? The renamed one. The schema we send to the LLM uses
tool.nameverbatim from your config, so if you renameend_calltohangup_now, the LLM seeshangup_nowin its tools array and will call it ashangup_now. You should reference the renamed name in your system prompt. On our side, when the model emits ahangup_nowtool call, we look it up by name in your configured tool list and then dispatch based on its built-in type, so renaming doesn’t break the underlying behavior.
So: name field = honored (rename freely, refer to it by the new name in prompts). description field = ignored for built-ins (put guidance in the prompt or on flow edges instead).
Thank You