Context Engineering

Configure system messages, instructions, and context for agents.

Context engineering is the process of designing and controlling the information (context) that is sent to language models to guide their behavior and outputs. In practice, building context comes down to one question: "Which information is most likely to achieve the desired outcome?"

The context of an Agno agent consists of the following:

  • System message: The system message is the main context that is sent to the agent, including all additional context
  • User message: The user message is the message that is sent to the agent.
  • Chat history: The chat history is the history of the conversation between the agent and the user.
  • Additional input: Any few-shot examples or other additional input that is added to the context.

System message context

The following are some key parameters that are used to create the system message:

  1. Description: A description that guides the overall behaviour of the agent.
  2. Instructions: A list of precise, task-specific instructions on how to achieve its goal.
  3. Expected Output: A description of the expected output from the Agent.

The system message is built from the agent's description, instructions, and other settings.

Install dependencies and set your key before running the examples:

pip install agno openai sqlalchemy
export OPENAI_API_KEY="your-api-key"

Examples are independent unless they explicitly reuse an earlier object. Optional toolkit examples need their own packages and credentials, as linked beside them.

from agno.agent import Agent
from agno.models.openai import OpenAIResponses

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    description="You are a famous short story writer asked to write for a magazine",
    instructions=["Always write 2 sentence stories."],
    markdown=True,
    debug_mode=True,  # Set to True to view the detailed logs and see the compiled system message
)
agent.print_response("Tell me a horror story.", stream=True)

Will produce the following system message:

You are a famous short story writer asked to write for a magazine
Always write 2 sentence stories.

<additional_information>
- Use markdown to format your answers.
</additional_information>

By default, instructions are not wrapped in <instructions> tags. If you prefer to wrap instructions in XML tags (for example, when using models that benefit from XML structure), set use_instruction_tags=True:

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    description="You are a famous short story writer",
    instructions=["Always write 2 sentence stories."],
    use_instruction_tags=True,  # Instructions will be wrapped in <instructions> tags
)

System message Parameters

The Agent creates a default system message that can be customized using the following agent parameters:

ParameterTypeDefaultDescription
descriptionstrNoneA description of the Agent that is added to the start of the system message.
instructionsList[str]NoneList of instructions added to the system prompt. Default instructions are also created depending on values for markdown, expected_output etc.
use_instruction_tagsboolFalseIf True, wrap the instructions in <instructions> tags.
additional_contextstrNoneAdditional context added to the end of the system message.
expected_outputstrNoneProvide the expected output from the Agent. This is added to the end of the system message.
markdownboolFalseAdd an instruction to format the output using markdown.
add_datetime_to_contextboolFalseIf True, add the current datetime to the prompt to give the agent a sense of time. This allows for relative times like "tomorrow" to be used in the prompt
add_name_to_contextboolFalseIf True, add the name of the agent to the context.
add_location_to_contextboolFalseIf True, add the location of the agent to the context. This allows for location-aware responses and local context.
add_session_summary_to_contextboolNoneIf True, add the session summary to the context. Resolves to True when session summaries are enabled. See sessions for more information.
add_memories_to_contextboolNoneIf True, add the user memories to the context. Resolves to True when memory is enabled. See memory for more information.
add_session_state_to_contextboolFalseIf True, add the session state to the context. See state for more information.
enable_agentic_knowledge_filtersboolFalseIf True, let the agent choose the knowledge filters. See knowledge for more information.
system_messagestrNoneOverride the default system message.
build_contextboolTrueOptionally disable the building of the context.

See the full Agent reference for more information.

How the system message is built

Let's take the following example agent:


from agno.agent import Agent

agent = Agent(
    name="Helpful Assistant",
    role="Assistant",
    description="You are a helpful assistant",
    instructions=["Help the user with their question"],
    additional_context="""
    Here is an example of how to answer the user's question: 
        Request: What is the capital of France?
        Response: The capital of France is Paris.
    """,
    expected_output="You should format your response with `Response: <response>`",
    markdown=True,
    add_datetime_to_context=True,
    add_location_to_context=True,
    add_name_to_context=True,
    add_session_summary_to_context=True,
    add_memories_to_context=True,
    add_session_state_to_context=True,
)

Below is the system message that will be built:

You are a helpful assistant

<your_role>
Assistant
</your_role>

Help the user with their question

<additional_information>
- Use markdown to format your answers.
- The current time is 2025-09-30 12:00:00.
- Your approximate location is: New York, NY, USA.
- Your name is: Helpful Assistant.
</additional_information>

<expected_output>
You should format your response with `Response: <response>`
</expected_output>

Here is an example of how to answer the user's question: 
    Request: What is the capital of France?
    Response: The capital of France is Paris.

You have access to user info and preferences from previous interactions that you can use to personalize your response:

<memories_from_previous_interactions>
- User really likes Digimon and Japan.
- User really likes Japan.
- User likes coffee.
</memories_from_previous_interactions>

Note: this information is from previous interactions and may be updated in this conversation. You should always prefer information from this conversation over the past memories.

Here is a brief summary of your previous interactions:

<summary_of_previous_interactions>
The user asked about information about Digimon and Japan.
</summary_of_previous_interactions>

Note: this information is from previous interactions and may be outdated. You should ALWAYS prefer information from this conversation over the past summary.

<session_state> ... </session_state>

This example is exhaustive and illustrates what is possible with the system message. In practice, you would only use some of these settings.

Additional Context

You can add additional context to the end of the system message using the additional_context parameter.

Here, additional_context supplies a fictional support policy as application context.

from agno.agent import Agent
from agno.models.openai import OpenAIResponses

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    additional_context="Example support policy: Standard support hours are 09:00–17:00 UTC, Monday through Friday.",
)
agent.print_response("When is standard support available?")

Tool Instructions

If you are using a Toolkit on your agent, you can add tool instructions to the system message using the instructions parameter:

For this optional example, install slack-sdk and set SLACK_TOKEN for an authorized Slack app. Follow the Slack toolkit setup for required permissions.

from agno.agent import Agent
from agno.tools.slack import SlackTools

slack_tools = SlackTools(
    instructions="Use `send_message` to send a message to the user. If the user specifies a thread, use `send_message_thread` to send a message to the thread.",
    add_instructions=True,
)
agent = Agent(
    tools=[slack_tools],
)

These instructions are injected into the system message after the <additional_information> tags.

Agentic Memories

If you have enable_agentic_memory set to True on your agent, the agent gets the ability to create/update user memories using tools.

This adds the following to the system message:

<updating_user_memories>
- You have access to the `update_user_memory` tool that you can use to add new memories, update existing memories, delete memories, or clear all memories.
- If the user's message includes information that should be captured as a memory, use the `update_user_memory` tool to update your memory database.
- Memories should include details that could personalize ongoing interactions with the user.
- Use this tool to add new memories or update existing memories that you identify in the conversation.
- Use this tool if the user asks to update their memory, delete a memory, or clear all memories.
- If you use the `update_user_memory` tool, remember to pass on the response to the user.
</updating_user_memories>

This is the generated prompt text. Actual agentic actions depend on MemoryManager permissions: creating and updating are enabled by default; deletion and clearing require their respective flags.

Agentic Knowledge Filters

If you have knowledge enabled on your agent, you can let the agent choose the knowledge filters using the enable_agentic_knowledge_filters parameter.

This will add the following to the system message:

<knowledge_base>
You have a knowledge base you can search using the search_knowledge_base tool. Search before answering questions—don't assume you know the answer. For ambiguous questions, search first rather than asking for clarification.
The knowledge base contains documents with these metadata filters: filter1, filter2, filter3.
Always use filters when the user query indicates specific metadata.

Examples:
1. If the user asks about a specific person like "Jordan Mitchell", you MUST use the search_knowledge_base tool with the filters parameter set to {'<valid key like user_id>': '<valid value based on the user query>'}.
2. If the user asks about a specific document type like "contracts", you MUST use the search_knowledge_base tool with the filters parameter set to {'document_type': 'contract'}.
3. If the user asks about a specific location like "documents from New York", you MUST use the search_knowledge_base tool with the filters parameter set to {'<valid key like location>': 'New York'}.

General Guidelines:
- Always analyze the user query to identify relevant metadata.
- Use the most specific filter(s) possible to narrow down results.
- If multiple filters are relevant, combine them in the filters parameter (e.g., {'name': 'Jordan Mitchell', 'document_type': 'contract'}).
- Ensure the filter keys match the valid metadata filters: filter1, filter2, filter3.

Make sure to pass the filters as [Dict[str: Any]] to the tool. FOLLOW THIS STRUCTURE STRICTLY.
</knowledge_base>

Learn about agentic knowledge filters in more detail in the knowledge filters section.

Set the system message directly

You can manually set the system message using the system_message parameter. This will ignore all other settings and use the system message you provide.

from agno.agent import Agent

agent = Agent(system_message="Share a 2 sentence story about")
agent.print_response("Love in the year 12000.")

Set build_context=False and system_message=None to omit the system message. With context building disabled, settings such as markdown=True do not add a generated system message. An explicit system_message is still used.

User message context

The input sent to the Agent.run() or Agent.print_response() is used as the user message.

Additional user message context

You can add additional context to the user message using the following agent parameters:

The following agent parameters configure how the user message is built:

  • add_knowledge_to_context
  • add_dependencies_to_context
from agno.agent import Agent
agent = Agent(add_knowledge_to_context=True, add_dependencies_to_context=True)
agent.print_response("What is the capital of France?", dependencies={"name": "John Doe"})

This example has no attached Knowledge or retriever, so it adds only dependencies, not retrieved references. The user message is:

What is the capital of France?

<additional context>
{"name": "John Doe"}
</additional context>

See dependencies for how to do dependency injection for your user message.

Chat history

If you have database storage enabled on your agent, session history is automatically stored (see sessions).

You can now add the history of the conversation to the context using add_history_to_context.

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses

db = SqliteDb(db_file="tmp/agent.db")

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    db=db,
    session_id="chat_history",
    instructions="You are a helpful assistant that can answer questions about space and oceans.",
    add_history_to_context=True,
    num_history_runs=2,
)

agent.print_response("Where is the sea of tranquility?", stream=True)

agent.print_response("What was my first question?", stream=True)

This will add the history of the conversation to the context, which can be used to provide context for the next message.

See more details on sessions.

Managing Tool Calls

v2.2.1

The max_tool_calls_from_history parameter can be used to add only the n most recent tool calls from history to the context.

This helps manage context size and reduce token costs during agent runs.

Consider the following example:

from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
import random

def get_weather_for_city(city: str) -> str:
    conditions = ["Sunny", "Cloudy", "Rainy", "Snowy", "Foggy", "Windy"]
    temperature = random.randint(-10, 35)
    condition = random.choice(conditions)

    return f"{city}: {temperature}°C, {condition}"

agent = Agent(
    model=OpenAIResponses(id="gpt-5.2"),
    tools=[get_weather_for_city],
    db=SqliteDb(db_file="tmp/agent.db"),
    add_history_to_context=True,
    num_history_runs=6,  # Load last 6 runs from history
    max_tool_calls_from_history=3,  # Keep only last 3 tool calls in context
)
agent.print_response("What's the weather in Tokyo?")
agent.print_response("What's the weather in Paris?")  
agent.print_response("What's the weather in London?")
agent.print_response("What's the weather in Berlin?")
agent.print_response("What's the weather in Mumbai?")
agent.print_response("What's the weather in Miami?")
agent.print_response("What's the weather in New York?")
agent.print_response("What's the weather in above cities?")

The next request includes at most three historical tool exchanges from the loaded runs. Ordinary user messages and assistant summaries remain, and the model can make new tool calls, so this setting does not limit the answer to three cities.

In this example:

  • Run 1-3: Model sees tool calls [1], [1,2], [1,2,3]
  • Run 4: Model sees tool calls [1,2,3,4]
  • Run 5: Model sees tool calls [2,3,4,5] (tool call 1 filtered out)

Important: max_tool_calls_from_history filters tool calls from the runs loaded by num_history_runs. Your database always contains the complete history.

See the full example for a complete demonstration.

Few-shot learning with additional input

You can add entire additional messages to your agent's context using the additional_input parameter. These messages are added to the context as if they were part of the conversation history.

You can give your agent examples of how it should respond (also called "few-shot prompting"):

from agno.agent import Agent
from agno.models.message import Message
from agno.models.openai import OpenAIResponses

# Few-shot examples showing good support patterns
support_examples = [
    # Example 1: Simple issue resolution
    Message(role="user", content="I forgot my password and can't log in"),
    Message(
        role="assistant",
        content="""
    I'll help you reset your password right away.

    **Steps to Reset Your Password:**
    . Go to the login page and click "Forgot Password"
    . Enter your email address
    . Check your email for the reset link
    . Follow the link to create a new password
    . Use a strong password with uppercase, lowercase, numbers, and symbols

    f you don't receive the email within 5 minutes, please check your spam folder. Let me know if you need any assistance with these steps!
    """.strip(),
    ),
    # Example 2: Billing issue pattern
    Message(
        role="user",
        content="I've been charged twice for the same order and I'm frustrated!",
    ),
    Message(
        role="assistant",
        content="""
    I sincerely apologize for the billing error and the frustration this has caused you.

    **Immediate Action Plan:**
    1. I'll investigate your account to confirm the duplicate charge
    2. Process a full refund for the duplicate transaction
    3. Ensure this doesn't happen again by checking your payment method settings
    4. Provide you with a confirmation number once the refund is processed

    The refund typically takes 3-5 business days to appear on your statement. As an apology for this inconvenience, I'd also like to offer you a 10% discount on your next order.

    Is there anything else I can help you with regarding this issue?
    """.strip(),
    ),
    # Example 3: Technical support pattern
    Message(role="user", content="Your app keeps crashing when I try to upload photos"),
    Message(
        role="assistant",
        content="""
    I'm sorry you're experiencing crashes with photo uploads. Let me help you troubleshoot this issue.

    **Troubleshooting Steps:**
    1. **Check App Version:** Ensure you have the latest version from the app store
    2. **Restart the App:** Close completely and reopen the application
    3. **Check Storage:** Make sure you have sufficient device storage (at least 1GB free)
    4. **Photo Size:** Try uploading smaller photos (under 10MB each)
    5. **Network Connection:** Ensure you have a stable internet connection

    **If the issue persists:**
    - Try uploading one photo at a time instead of multiple
    - Clear the app cache in your device settings
    - Restart your device

    If none of these steps resolve the issue, please let me know your device type and OS version, and I'll escalate this to our technical team for further investigation.
    """.strip(),
    ),
]

if __name__ == "__main__":
    # Create agent with few-shot learning
    agent = Agent(
        name="Customer Support Specialist",
        model=OpenAIResponses(id="gpt-5.2"),
        add_name_to_context=True,
        additional_input=support_examples,  # few-shot learning examples
        instructions=[
            "You are an expert customer support specialist.",
            "Always be empathetic, professional, and solution-oriented.",
            "Provide clear, actionable steps to resolve customer issues.",
            "Follow the established patterns for consistent, high-quality support.",
        ],
        markdown=True,
    )

    agent.print_response("I want to enable two-factor authentication for my account.")

Context Caching

Most model providers support caching of system and user messages, though the implementation differs between providers.

The general approach is to cache repetitive content and common instructions, and then reuse that cached content in subsequent requests as the prefix of your system message. A supported provider can reuse processing for a repeated prefix, reducing latency or input charges under its caching rules. The prefix is still sent as input; caching does not shorten the context.

Agno's context construction is designed to place the most likely static content at the beginning of the system message.
If you wish to fine-tune this, the recommended approach is to manually set the system message.

Some examples of prompt caching:

Developer Resources