Hey, I dont know if I am doing something wrong, bu...
# ask-ai
m
Hey, I dont know if I am doing something wrong, but I would like to ask about this anyways. I am facing an issue with the way generate_reply() works with adding custom messages to the agent chat context take this example code: import logging import os from dotenv import load_dotenv from livekit.agents import Agent, AgentServer, AgentSession, JobContext, JobProcess, cli, llm from livekit.plugins import cartesia, deepgram, openai, silero from livekit.plugins.turn_detector.english import EnglishModel logger = logging.getLogger("agent-instructions-debug") load_dotenv() DEFAULT_INSTRUCTIONS = ( "You are a helpful voice assistant. Keep responses short and natural." ) class DebugAgent(Agent): async def on_enter(self) -> None: logger.info("on_enter: generating reply with explicit instructions") await self.session.generate_reply( instructions="Say you like cats.", allow_interruptions=True, ) def prewarm(proc: JobProcess) -> None: proc.userdata["vad"] = silero.VAD.load() server = AgentServer() server.setup_fnc = prewarm @breezy-insurance-66772.rtc_session() async def entrypoint(ctx: JobContext) -> None: await ctx.connect() chat_history = [ {"role": "assistant", "content": "Hi there. How can I help?"}, {"role": "user", "content": "Can you help me plan a short trip?"}, {"role": "assistant", "content": "Sure. Where are you thinking of going?"}, {"role": "user", "content": "Maybe Chicago."}, ] chat_ctx = llm.ChatContext.empty() for item in chat_history: chat_ctx.add_message(role=item["role"], content=item["content"]) session = AgentSession( stt=deepgram.STT(model="nova-3", language="en", filler_words=True), llm=openai.LLM(model="gpt-4o-mini"), tts=cartesia.TTS(model="sonic-2"), turn_detection=EnglishModel(), vad=ctx.proc.userdata["vad"], ) agent = DebugAgent(instructions=DEFAULT_INSTRUCTIONS, chat_ctx=chat_ctx) logger.info("Starting debug agent session") await session.start(agent=agent, room=ctx.room) if name == "__main__": logging.basicConfig(level=os.getenv("LOG_LEVEL", "INFO")) cli.run_app(server) I add some basic messages to the agents chat context, and add an on_enter() function where, on joining, the agent will generate a reply, with instructions="say you like cats". I am explicitly telling the agent to talk about liking cats. What instead happens is that the agent replies to the last message, completely ignoring the instructions What I then noticed is if I stopped adding my own custom messages to the chat_ctx, the agent would respond properly, saying that they like cats What we also noticed is that this breaks every single generate_reply() going forward. For example in our codebase we have a tool that allows the agent to search the web. In the tool itself we have a generate_reply() call with instructions="tell the user you are currently searching the web", what happens is that the agent just generates a response to the last message, ignoring the instructions, which is odd. In our codebase we want to be able to inject past conversations from our db directly into the agent, and use the generate_reply() to generate a reply that follows our set instructions. Currently we arent able to. For now we added a workaround where we just dump the past conversations in the instructions instead, but I feel thats just working around it. But maybe I am doing something wrong. Any tips would be appreciated.
t
Thanks for reaching out, Srikar. I'm an AI assistant and I'm here to help. Give me a moment while I research this topic.
Thanks — this is a clear report and I hear your frustration. You're asking why generate_reply(instructions=...) is ignored when you preload chat messages, and how to inject past conversations while still controlling replies. I'll address that and give the simplest practical options. Short answer • Preloaded chat messages are treated as the authoritative conversation state. When chat history exists, the model prioritizes responding to the most recent turn, so a plain
instructions="Say you like cats."
can be overridden by the last message in the chat history. This is expected behavior. • Your workaround (including past conversation text in the
instructions
) is a valid way to get a controlled reply while preserving history. What you can do (options) • Keep using
instructions
, but include the needed context in that instructions string so the model sees both the past conversation and the one-off directive. Example:
Copy code
await self.session.generate_reply(instructions=f"""
  Past conversation: {serialized_history}
  Now, respond with: Say you like cats.
  """)
• Use
user_input
when you want to inject a message that should behave like a user turn (it will be appended to history and processed normally). Use
instructions
only for high-level control that does not become a chat message. • If you need the agent to act on a directive that should override the last user turn, explicitly add an assistant/system message into the chat context before calling
generate_reply()
with an instruction that references that message. For example, add an assistant message like "System directive: Say you like cats." then call
generate_reply()
. Clarifying question • Do you want the preloaded history to remain visible to the model but have a single instruction always take precedence (i.e., force the agent to ignore the last user turn)? If so, I can show a concise code pattern to inject a system/assistant directive into the live chat context so it will be followed by subsequent generate_reply() calls. If you tell me which option you'd prefer (forced override vs. combined-context instruction vs. using
user_input
), I’ll give the exact code snippet that fits your current setup. Sources: Agent speech and audio | LiveKit docs | External data and RAG | LiveKit docs | Pipeline nodes and hooks | LiveKit docs | Text and transcriptions | LiveKit docs