This lesson covers the different values a function can return and when to use each one. Understanding return values gives you precise control over what the agent says and when.
The two valid return types
Functions called by the LLM can only return a string or a dictionary. Any other type — integer, list, boolean — will cause an error. The LLM will retry up to three times before giving up and producing an error response.Returning a string
The simplest and most common pattern. Return a string and the LLM reads it, considers it alongside the conversation history, and formulates its own response.function role. The LLM then produces a natural response based on it.
Trade-off: The LLM has flexibility to express the result conversationally — but you do not control the exact phrasing.
Returning a dictionary
Dictionaries unlock more specific control. The following keys are supported individually or in combination:content
Equivalent to returning a string. The value is shown to the LLM to inform its next response.
utterance
A hard-coded response that bypasses the LLM entirely. The text is spoken directly to the user without any further LLM request.
- Return string (LLM generates response)
- Return utterance (bypasses LLM)
content + utterance together
When both keys are returned, the utterance is played immediately to the user, and the content is stored in conversation history to inform the LLM’s response on the next turn.
- play a hard-coded holding phrase while the agent processes something
- give the LLM context for how to handle the follow-up
end_turn: False
By default, returning an utterance ends the turn. Setting end_turn to False plays the utterance but then immediately triggers another LLM request in the same turn.
hangup: True
Ends the call after the function executes.
utterance when hanging up — otherwise the call ends silently, which feels like a dropped call to the user.
Returning an empty dictionary
An empty dictionary{} means the function returns no output. The LLM calls the function, receives nothing, and has no new information to work with. It will typically produce a filler response (“One moment, please”) and then hallucinate the rest of the conversation.
Avoid this in production. Always return something meaningful.
Passing the utterance as a function argument
When using Raven (PolyAI’s in-house LLM), the model returns either a function call or text — not both in the same response. This means you cannot rely on the LLM to generate a goodbye message at the same time as calling a hangup function. A useful pattern for this situation: pass the utterance as a parameter of the function.utterance — The goodbye message to say to the user before ending the call
The LLM generates the utterance value and passes it as an argument, so the response is contextually appropriate. The function then plays it as a hard-coded utterance.
This gives you the best of both worlds: LLM-generated phrasing with deterministic execution.
Quick reference
| Return value | LLM reads it? | User hears it directly? | LLM requests |
|---|---|---|---|
"string" | Yes | No — LLM generates response | 2 |
{"content": "..."} | Yes | No — LLM generates response | 2 |
{"utterance": "..."} | No | Yes | 1 |
{"content": "...", "utterance": "..."} | Yes (on next turn) | Yes (immediately) | 1 + next turn |
{"utterance": "...", "end_turn": False} | No | Yes, then LLM continues | 1 + immediate follow-up |
{"utterance": "...", "hangup": True} | No | Yes, then call ends | 1 |
{} | No | No | — (LLM gets no feedback) |

