acoustic-pharmacist-34178
03/17/2026, 7:46 PMclass Assistant(Agent):
def __init__(self, config: AssistantConfig):
chat_ctx = ChatContext()
chat_ctx.add_message(
role="system",
content=SYSTEM_INSTRUCTIONS,
)
super().__init__(
instructions=USER_INSTRUCTIONS,
tools=config.tools,
allow_interruptions=True,
chat_ctx=chat_ctx
)tall-airline-16756
03/17/2026, 7:46 PMtall-airline-16756
03/17/2026, 7:47 PMinstructions and a chat_ctx system message conflict and which takes priority in your example. Short answer: they both end up as system messages, so they stack rather than one overriding the other.
What happens in your code
• You create a ChatContext and add a system message with SYSTEM_INSTRUCTIONS.
• You then call the Agent constructor with instructions=USER_INSTRUCTIONS and pass your chat_ctx.
• The constructor will add a system message from instructions in addition to the system message already in chat_ctx.
Result
• Your prompt will contain two system messages:
a. The system message you added to chat_ctx (SYSTEM_INSTRUCTIONS)
b. The system message created from instructions (USER_INSTRUCTIONS)
• They do not replace one another — they both appear in the context. That can lead to ambiguous or conflicting guidance for the model.
Recommended approach
• If you build a custom chat_ctx that already includes the system message(s), omit the instructions parameter and put all needed system-level guidance into chat_ctx.
• If you prefer the convenience of instructions, pass it and let the Agent construct the system message, and avoid adding a separate system message to chat_ctx.
• If you need to combine instructions carefully, keep them in a single system message (in chat_ctx) so the ordering and wording are explicit.
If you'd like, I can:
• Show a small code example that uses only chat_ctx, or
• Show a version that uses only instructions, or
• Help you merge SYSTEM_INSTRUCTIONS and USER_INSTRUCTIONS into one clear system message.
Which of those would be most helpful?
Sources: Agents and handoffs | LiveKit Documentation, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 7:49 PMtall-airline-16756
03/17/2026, 7:49 PMinstructions and any system message you add to chat_ctx become system messages in the prompt. They do not override each other — they both appear and therefore can conflict or confuse the model if not coordinated.
• To avoid ambiguity, treat all policy/guardrail content as a single, explicit system message rather than splitting it between instructions and another system message.
Recommended approaches
• Preferred (clear and explicit)
◦ Put all policy and guardrail content into one system message inside chat_ctx. Do not pass instructions to the Agent constructor.
◦ This gives you full control of wording and ordering so the model will see one authoritative set of system-level rules.
Example pattern:
1. Create ChatContext.
2. Add a single system message containing both high-level policy and any operational guardrails.
3. Pass that chat_ctx to the Agent and omit instructions.
• Alternative (convenience)
◦ Use instructions alone (no system messages added to chat_ctx). This is simpler if you only have one source of system-level instructions and don’t need extra messages in the context.
◦ Avoid adding another system message to chat_ctx in this case.
• Combining carefully (when you must)
◦ If you must combine both, merge them into one system message before adding to chat_ctx so the effective instruction text is deterministic.
◦ If you cannot merge, ensure the system messages are complementary and that the policy/guardrail message appears first and clearly states that it takes precedence.
Practical tips for policy/guardrails
• Make the system message explicit about priority: e.g., start with “System policy — must follow:” and list rules.
• Keep guardrails short, numbered, and unambiguous (e.g., “Do not disclose PII,” “Always refuse X,” “When X occurs, respond with Y”).
• If you need user-facing behavior that changes per conversation, keep that as user messages in chat_ctx or as tool configuration rather than adding more system-level texts.
If you want, I can:
• Show a code example that merges your SYSTEM_INSTRUCTIONS and user-provided policy text into a single system message in chat_ctx, or
• Review your existing SYSTEM_INSTRUCTIONS and USER_INSTRUCTIONS and suggest how to consolidate them into one clear guardrail message.
Which would you prefer?
, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 7:51 PMtall-airline-16756
03/17/2026, 7:51 PMChatContext.
• Put user-provided instructions as a user message (or agent instructions only if you prefer convenience, but not both).
• This keeps the system-level rules authoritative and the user instructions visible and separate.
Example (best practice)
# Pseudocode / illustrative example using LiveKit-like Agent and ChatContext APIs
SYSTEM_POLICY = """
System policy (must follow):
1. Never disclose personal or sensitive data.
2. If asked to perform an action that violates policy, refuse and provide a safe alternative.
3. Always follow legal and safety constraints.
"""
# Instructions the user of the assistant provides to shape helpful behavior
USER_INSTRUCTIONS = """
You are a helpful assistant for customer support. Be concise, friendly, and provide step-by-step troubleshooting.
When unsure, ask clarifying questions before making assumptions.
"""
from livekit.agent import Agent, ChatContext, AssistantConfig # adjust imports to your SDK
# 1) Build a ChatContext with one explicit system message containing policy/guardrails
chat_ctx = ChatContext()
chat_ctx.add_message(role="system", content=SYSTEM_POLICY)
# 2) Add the user-provided instructions as a user message (keeps them separate and editable per conversation)
chat_ctx.add_message(role="user", content=USER_INSTRUCTIONS)
# 3) Construct your Agent without passing the `instructions` parameter (avoid duplicate system messages)
config = AssistantConfig(tools=[...], allow_interruptions=True) # fill with your config
assistant = Agent(config=config, chat_ctx=chat_ctx)
# 4) Start or run a conversation — subsequent user prompts can be added dynamically
assistant.chat_ctx.add_message(role="user", content="Hi—help me triage a failed room connection.")
response = assistant.run() # or whichever method triggers the agent to produce output
print(response)
Why this pattern works
• The system policy is a single, explicit source of guardrails and is always present at the highest priority level.
• User-provided instructions are kept as user messages so they can be changed per session and do not create competing system-level directives.
• You avoid having two system messages (one from instructions and one in chat_ctx) which can produce ambiguous model behavior.
Variants
• Convenience-only: If you prefer convenience and have no custom system policy, pass instructions=USER_INSTRUCTIONS into the Agent constructor and omit any system messages in chat_ctx.
• Merged-single-system: If you want everything at system level (not recommended if user instructions must be adjustable per-session), combine policy and user-instructions into a single system message in chat_ctx and omit instructions.
If you want, I can:
• Convert the example to the exact function/class names in your SDK version, or
• Show an example where user instructions are passed via instructions while ensuring the system policy remains authoritative (by merging policy into that single instructions value). Which would you prefer?, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 7:52 PMtall-airline-16756
03/17/2026, 7:53 PMinstructions is a convenience: it is turned into a system message and added to the ChatContext under the hood. It does not create a separate hidden instruction channel with special runtime behavior beyond becoming a system message.
Details and useful nuances
• When you pass instructions to the Agent constructor, the Agent creates a system-role message from that string and adds it to the ChatContext.
• If you also pass a pre-built chat_ctx that already contains system messages, the instructions-created system message will be added in addition to those messages (i.e., they stack).
• ChatContext.copy() will include instructions unless explicitly excluded (so copies propagate that system message unless you opt out).
• Because instructions ends up as a normal system message, it follows the same ordering and precedence rules as any other system message in the context. There is no extra runtime priority applied to instructions beyond being a system-role message.
Practical implication
• Treat instructions the same as any other system message when reasoning about ordering and potential conflicts. If you need authoritative, unambiguous guardrails, put them in a single explicit system message in chat_ctx (and omit instructions) or merge your texts before adding them.
If you want, I can show:
• A tiny code snippet that demonstrates the stacking behavior, or
• How to explicitly exclude instructions when copying a ChatContext so you don’t duplicate system messages.
Which would be most helpful?
, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 7:54 PMtall-airline-16756
03/17/2026, 7:54 PMinstructions becomes a chat_ctx message and explain what to look for in the source.
Short answer
• The instructions string is converted into a system-role chat message and added to the ChatContext during Agent initialization. You should look in the Agent constructor (or the function that builds/initializes self.chat_ctx) for the logic that creates a `ChatMessage`/system message from the instructions parameter.
Where to look in the repo
• Open the Agent implementation file (search for the Agent class or Agent.__init__).
• Search inside that file for occurrences of instructions, chat_ctx, ChatContext, or code that constructs a message with role="system" (e.g., ChatMessage(role="system", content=...)).
• Also check any helper or factory functions that build or normalize the chat context (names like build_chat_context, initialize_chat_ctx, or copy() methods on ChatContext).
What you’ll typically find
• A branch that checks if instructions is provided.
• Creation of a message object with role set to system and content set to the instructions string.
• Appending or inserting that message into self.chat_ctx (or passing it to ChatContext constructor).
• A ChatContext.copy() implementation that copies messages and typically includes system messages unless exclude_instructions=True is passed.
If you want, I can:
• Point to the exact file and line if you tell me which repository URL and branch/tag (for example, main or a specific SDK version) you’re using.
• Or, if you prefer, I can show a short illustrative snippet of the exact pattern to expect (how the constructor turns instructions into a system message). Which would you like? Sources: Function tool definition | LiveKit Documentation | Agents and handoffs | LiveKit Documentation | Context Variables | LiveKit Documentation | Build Your First AI Voice Agent in Python: Complete Tutorial | LiveKit, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 7:55 PMtall-airline-16756
03/17/2026, 7:55 PMmain, v1.2.3), or
• Whether you installed from PyPI (and if so, run and share the output of:
python -c "import livekit; print(livekit.__file__, getattr(livekit, '__version__', 'no-version'))"
)
• Or if you have a cloned source tree, the local path to the file that contains Agent (or paste your Agent.__init__ snippet).
Once you tell me which package/repo and version (or provide the small diagnostic output), I’ll point to the exact file and show the lines where instructions is converted into a system-role ChatContext message.
, with interactive elementsacoustic-pharmacist-34178
03/17/2026, 8:03 PMtall-airline-16756
03/17/2026, 8:03 PMAgent.__init__ or equivalent) — search for the parameter name instructions.
• Code that creates a chat message with role "system" (look for ChatMessage(... role="system" ...) or similar).
• Logic that appends or inserts that message into the agent's chat_ctx (or calls a helper like build_chat_context or initialize_chat_ctx).
• Any ChatContext.copy() implementation that mentions including or excluding instructions (e.g., flags like exclude_instructions=True).
Why this is the right place
• The Agent class is where initialization parameters are normalized into the conversation state. The SDK adds instructions as a system-role message inside the agent’s ChatContext during initialization, so the creation and insertion happen in the Agent implementation.
If you want, I can:
• Show an illustrative snippet that demonstrates the exact pattern to expect (how instructions is wrapped into a system ChatMessage and added to chat_ctx), or
• If you tell me the branch or tag you’re using (for example main or a release tag), I can point to the exact lines in that file. Which would you prefer? Sources: Agents and handoffs | LiveKit Documentation | Function tool definition | LiveKit Documentation | Build a salary negotiation coach with a LemonSlice avatar | LiveKit, with interactive elements