steep-balloon-41261
03/06/2026, 2:30 PMtall-airline-16756
03/06/2026, 2:30 PMtall-airline-16756
03/06/2026, 2:31 PMsession.generate_reply() which performs audio/speech generation but does not return a value to the LLM (it returns None). Because generate_reply() doesn’t produce a string result back into the tool outputs, the agent has nothing from that tool to include in the final text reply.
Why this happens
• session.generate_reply() is intended to produce speech playback or trigger an agent-side reply during turn handling or lifecycle hooks; it’s not a mechanism for returning structured text back to the LLM inside a tool call. The framework collects explicit return values from tools to build the final response; side effects like audio playback are not captured as tool outputs.
What you can do (two straightforward options) 1) Return the KB answer as a string from the tool (recommended)
• Change access_knowledge_base to return the generated answer text instead of calling session.generate_reply() inside the tool.
• Let the framework pass both returned strings (from confirm_application_details and access_knowledge_base) to the LLM so it can generate a single cohesive response.
Example:
@function_tool()
async def access_knowledge_base(self, context: RunContext[SessionState], question: str) -> str:
# ... build knowledge_instruction or the answer text ...
return knowledge_answer_text # return the KB answer as a string
2) If you need both text for the LLM and audio playback
• Have access_knowledge_base return the answer text, and then trigger session.generate_reply() from a place that the framework expects side-effecting speech (for example, after the tool returns, in the agent response handler or a lifecycle hook). This ensures the LLM receives the text and the user still hears audio.
Next step I can help with
• If you’d like, paste the code you use to build knowledge_instruction or the exact text you want spoken and I’ll show the exact code change to return the KB answer and optionally trigger speech after the tool returns. Sources: Testing and evaluation | LiveKit Documentation | Tool definition and use | LiveKit Documentation | Agent speech and audio | LiveKit Documentation | Pipeline nodes and hooks | LiveKit Documentationbetter-sandwich-65834
03/06/2026, 2:35 PMtall-airline-16756
03/06/2026, 2:35 PMsession.generate_reply() produces audio/speech as a side effect but does not return text back to the LLM. Inside a tool, its side effect is executed but the LLM still only receives the tool’s explicit return value (or None) as the tool output.
• The framework builds the final agent reply from the explicit tool return values. So if your KB tool does not return a text value, the LLM has no KB-derived text to include in the final reply.
Possible patterns that preserve LLM decision-making without returning full KB content
• Return a concise, structured summary/result instead of the whole KB
◦ Have your KB tool run the internal retrieval + reasoning and return just the final answer text (or a short structured object like {"answer": "...", "source_ids": [...]}) rather than returning full KB context.
◦ This keeps the KB lookup and reasoning inside the tool but still provides the LLM with a text result to incorporate in the final reply.
• Return an instruction-style answer for the LLM to adopt
◦ Let the KB tool produce the canonical response text (the text you want the LLM to speak/use). Return that short canonical text. The LLM can then weave it into the turn or rephrase if needed.
◦ This prevents exposing raw KB documents but still gives the LLM the knowledge it needs.
• Use a two-step flow: tool returns a decision token + ephemeral session context
◦ Tool returns a small token/flag + metadata (e.g., {"kb_decision":"approved","summary":"X is the answer","confidence":0.92}).
◦ After tools finish, an agent-side lifecycle hook or the agent’s turn handler can assemble the spoken response using that summary and call session.generate_reply() outside the tool so speech and final text align. This separates speech side-effects from the KB lookup while keeping the LLM aware of the KB-derived decision.
• Keep reasoning inside the tool but return the answer only when the LLM needs to craft tone/format
◦ If you want the LLM to control tone/phrasing, return a concise canonical answer from the tool and let the LLM rephrase. If you want deterministic wording (for e.g., compliance), return the exact text and let the LLM just pass it through.
Questions to pick the best pattern for you
1. Do you need the LLM to rephrase the KB answer (flexible tone), or must the KB answer be returned verbatim (regulatory/compliance)?
2. Do you also need speech playback in the same turn? If yes, should the speech match the LLM’s final phrasing or the KB tool’s canonical wording?
3. Is metadata useful (e.g., sources, confidence) to be returned along with the concise answer so the LLM can decide whether to disclose sources?
If you tell me:
• whether you need rephrasing vs. verbatim text,
• and whether speech must be triggered inside the tool or can be triggered after the tool returns,
I’ll provide one concrete code pattern that implements it (example for returning a concise answer + triggering speech outside the tool, or example for returning a structured object and using a lifecycle hook to play audio).