Context Engineering
Configure system messages, instructions, and context for teams.
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?"
Effective context engineering is an iterative process: refining the system message, trying out different descriptions and instructions, and using features such as schemas, delegation, and tool integrations.
The context of an Agno team consists of the following:
- System message: The system message is the main context that is sent to the team, including all additional context
- User message: The user message is the message that is sent to the team.
- Chat history: The chat history is the history of the conversation between the team 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:
- Description: A description that guides the overall behaviour of the team.
- Instructions: A list of precise, task-specific instructions on how to achieve its goal.
- Expected Output: A description of the expected output from the Team.
- Members: Information about team members, their roles, and capabilities.
The system message is built from the team’s description, instructions, member details, and other settings. A team leader’s system message additionally includes delegation rules and coordination guidelines. For example:
Install dependencies and set your key before running the examples:
pip install agno openai sqlalchemy yfinance
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.team import Team
from agno.tools.hackernews import HackerNewsTools
from agno.tools.yfinance import YFinanceTools
news_agent = Agent(
name="News Researcher",
role="You are a news researcher that can find information on HackerNews.",
instructions=[
"Use your HackerNews tool to find tech news and discussions.",
"Provide a summary of the information found.",
],
tools=[HackerNewsTools()],
markdown=True,
debug_mode=True,
)
finance_agent = Agent(
name="Finance Researcher",
role="You are a finance researcher that can get stock prices and market data.",
instructions=[
"Use your finance tools to get stock prices and financial data.",
"Provide a summary of the information found.",
],
tools=[YFinanceTools()],
markdown=True,
debug_mode=True,
)
team = Team(
members=[news_agent, finance_agent],
instructions=[
"You are a team of researchers that can find tech news and financial data.",
"After finding information about the topic, compile a joint report."
],
markdown=True,
debug_mode=True,
)
team.print_response("What is the latest news on AI and how is NVDA performing?", stream=True)Produces this system message (tool schemas are sent separately):
- You are a team of researchers that can find tech news and financial data.
- After finding information about the topic, compile a joint report.
<team>
You coordinate this team to fulfill the user's request. You have a team of specialists, listed below. Delegate to members when their expertise or tools are needed; answer directly — including with your own tools — when they are not.
<team_members>
<member id="news-researcher" name="News Researcher">
Role: You are a news researcher that can find information on HackerNews.
</member>
<member id="finance-researcher" name="Finance Researcher">
Role: You are a finance researcher that can get stock prices and market data.
</member>
</team_members>
<delegation>
You work in coordinate mode: you hand sub-tasks to members with `delegate_task_to_member` and write the answer yourself.
- Match each sub-task to the member whose role and description fit it best. When sub-tasks do not depend on each other, delegate them in the same turn instead of one per turn.
- A member's output is evidence, not your answer. When a member fails, refuses, or returns nothing, say so plainly and name what it reported — never supply a cause, source, or finding the member did not state.
- If a response is off-target, re-delegate with clearer instructions or try a better-suited member. If it still misses, answer with what you have and say what is missing — do not work through the roster.
- Write one answer. Resolve contradictions, add structure, and fill gaps only where you can state the basis for it. Never concatenate member outputs.
Members do not see this conversation. Each one gets only the text you write for it, so carry over every name, number and earlier answer it needs, and say what a good result looks like.
Member ids are the ids shown in the roster above, used exactly as written.
</delegation>
</team>
<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:
team = Team(
members=[news_agent, finance_agent],
instructions=["Coordinate the team to provide comprehensive research"],
use_instruction_tags=True, # Instructions will be wrapped in <instructions> tags
)System message Parameters
The Team creates a default system message that can be customized using the following parameters:
| Parameter | Type | Default | Description |
|---|---|---|---|
description | str | None | A description of the Team that is added to the start of the system message. |
instructions | List[str] | None | List of instructions added to the system prompt. Default instructions are also created depending on values for markdown, expected_output etc. |
use_instruction_tags | bool | False | If True, wrap the instructions in <instructions> tags. |
additional_context | str | None | Additional context added to the end of the system message. |
expected_output | str | None | Provide the expected output from the Team. This is added to the end of the system message. |
markdown | bool | False | Add an instruction to format the output using markdown. |
add_datetime_to_context | bool | False | If True, add the current datetime to the prompt to give the team a sense of time. This allows for relative times like "tomorrow" to be used in the prompt |
add_name_to_context | bool | False | If True, add the name of the team to the context. |
add_location_to_context | bool | False | If True, add the location of the team to the context. This allows for location-aware responses and local context. |
timezone_identifier | str | None | Allows for custom timezone for datetime instructions following the TZ Database format (e.g. "Etc/UTC") |
add_member_tools_to_context | bool | False | If True, add the tools available to team members to the context. |
add_session_summary_to_context | bool | None | If True, add the session summary to the context. Resolves to True when session summaries are enabled. See sessions for more information. |
add_memories_to_context | bool | None | If True, add the user memories to the context. Resolves to True when memory is enabled. See memory for more information. |
add_dependencies_to_context | bool | False | If True, add the dependencies to the context. See dependencies for more information. |
add_session_state_to_context | bool | False | If True, add the session state to the context. See state for more information. |
add_knowledge_to_context | bool | False | If True, add retrieved knowledge to the context, to enable RAG. See knowledge for more information. |
enable_agentic_knowledge_filters | bool | False | If True, let the team choose the knowledge filters. See knowledge for more information. |
system_message | str | None | Override the default system message. |
respond_directly | bool | False | If True, the team leader won't process responses from members and instead will return them directly. Cannot be used with delegate_to_all_members=True. |
delegate_to_all_members | bool | False | If True, the team leader will delegate the task to all members simultaneously, instead of one by one. When running async (using arun) members will run concurrently. Cannot be used with respond_directly=True. |
determine_input_for_members | bool | True | Set to false if you want to send the run input directly to the member agents. |
share_member_interactions | bool | False | If True, send all previous member interactions to members. |
get_member_information_tool | bool | False | If True, add a tool to get information about the team members. |
See the full Team reference for more information.
Configuration Warning: Setting delegate_to_all_members=True and respond_directly=True together logs a warning and disables respond_directly.
How the system message is built
Let's take the following example team:
from agno.agent import Agent
from agno.team import Team
web_agent = Agent(
name="Web Researcher",
role="You are a web researcher that can find information on the web.",
description="You are a helpful web research assistant",
instructions=["Search for accurate information"],
markdown=True,
)
team = Team(
members=[web_agent],
name="Research Team",
role="Team Lead",
description="You are a research team lead",
instructions=["Coordinate the team to provide comprehensive research"],
expected_output="You should format your response with detailed findings",
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,
)The system message starts with the description, role, and instructions, followed by the team roster and delegation rules. This is the opening excerpt:
<description>
You are a research team lead
</description>
<your_role>
Team Lead
</your_role>
Coordinate the team to provide comprehensive research
<team>
You coordinate this team to fulfill the user's request. You have a team of specialists, listed below. Delegate to members when their expertise or tools are needed; answer directly — including with your own tools — when they are not.
<team_members>
<member id="web-researcher" name="Web Researcher">
Role: You are a web researcher that can find information on the web.
Description: You are a helpful web research assistant
</member>
</team_members>
<delegation>
You work in coordinate mode: you hand sub-tasks to members with `delegate_task_to_member` and write the answer yourself.
- Match each sub-task to the member whose role and description fit it best. When sub-tasks do not depend on each other, delegate them in the same turn instead of one per turn.
- A member's output is evidence, not your answer. When a member fails, refuses, or returns nothing, say so plainly and name what it reported — never supply a cause, source, or finding the member did not state.
- If a response is off-target, re-delegate with clearer instructions or try a better-suited member. If it still misses, answer with what you have and say what is missing — do not work through the roster.
- Write one answer. Resolve contradictions, add structure, and fill gaps only where you can state the basis for it. Never concatenate member outputs.
Members do not see this conversation. Each one gets only the text you write for it, so carry over every name, number and earlier answer it needs, and say what a good result looks like.
Member ids are the ids shown in the roster above, used exactly as written.
</delegation>
</team>The remaining sections depend on available data. The example has no database, stored memories, or session summary, so it does not produce populated memory or summary blocks. Datetime uses the current server time; location, when available, is inferred from the server's public IP. Expected output and enabled formatting instructions are appended after the team context. Session state is included only when supplied.
Enable the settings your application needs. Adding a context flag does not create the underlying memories, session history, or summary.
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.team import Team
from agno.models.openai import OpenAIResponses
support_agent = Agent(name="Support Specialist", role="Explain support policies")
team = Team(
members=[support_agent],
model=OpenAIResponses(id="gpt-5.2"),
additional_context="Example support policy: Standard support hours are 09:00–17:00 UTC, Monday through Friday.",
)
team.print_response("When is standard support available?")Team Member Information
The member information is automatically injected into the system message. This includes the member ID, name, role, and description.
Set add_member_tools_to_context=True to also list each member's tools in the system message.
You can also give the team leader a tool to get information about the team members.
from agno.agent import Agent
from agno.team import Team
web_agent = Agent(
name="Web Researcher",
role="You are a web researcher that can find information on the web."
)
team = Team(
members=[web_agent],
get_member_information_tool=True, # Adds a tool to get information about team members
)Tool Instructions
If you are using a Toolkit on your team, 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.team import Team
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,
)
team = Team(
members=[Agent(name="Support Specialist", role="Help with customer support questions")],
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 team, the team 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 team, you can let the team 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.team import Team
team = Team(members=[], system_message="Share a 2 sentence story about")
team.print_response("Love in the year 12000.")User message context
The input sent to the Team.run() or Team.print_response() is used as the user message.
See dependencies for how to do dependency injection for your user message.
Additional user message context
By default, the user message is built using the input sent to the Team.run() or Team.print_response() functions.
The following team parameters configure how the user message is built:
add_knowledge_to_contextadd_dependencies_to_context
from agno.agent import Agent
from agno.team import Team
web_agent = Agent(
name="Web Researcher",
role="You are a web researcher that can find information on the web."
)
team = Team(
members=[web_agent],
add_knowledge_to_context=True,
add_dependencies_to_context=True
)
team.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>Chat history
If you have database storage enabled on your team, 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
from agno.team import Team
from agno.tools.hackernews import HackerNewsTools
db = SqliteDb(db_file="tmp/team.db")
news_researcher = Agent(
name="News Researcher",
role="You are a news researcher that can find information on HackerNews.",
tools=[HackerNewsTools()],
instructions=[
"Use your HackerNews tool to find tech news and discussions.",
"Provide a summary of the information found.",
],
)
team = Team(
members=[news_researcher],
model=OpenAIResponses(id="gpt-5.2"),
db=db,
session_id="chat_history",
instructions="You are a helpful assistant that can answer questions about technology.",
add_history_to_context=True,
num_history_runs=2,
)
team.print_response("What are the top stories on HackerNews?", stream=True)
team.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 chat history.
store_member_responses=True to store them.Managing Tool Calls
v2.2.1The 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 team runs.
from agno.agent import Agent
from agno.db.sqlite import SqliteDb
from agno.models.openai import OpenAIResponses
from agno.team import Team
from agno.tools.hackernews import HackerNewsTools
from agno.tools.yfinance import YFinanceTools
news_agent = Agent(
name="News Researcher",
model=OpenAIResponses(id="gpt-5.2"),
instructions="You are a news researcher. Search HackerNews for tech news and discussions.",
tools=[HackerNewsTools()],
)
finance_agent = Agent(
name="Finance Researcher",
model=OpenAIResponses(id="gpt-5.2"),
instructions="You are a finance researcher. Get stock prices and financial data.",
tools=[YFinanceTools()],
)
team = Team(
members=[news_agent, finance_agent],
model=OpenAIResponses(id="gpt-5.2"),
db=SqliteDb(db_file="tmp/filter_history_tool_calls_team.db"),
add_history_to_context=True,
max_tool_calls_from_history=5,
show_members_responses=True,
)
team.print_response("Search for AI news on HackerNews", stream=True)
team.print_response("Get the stock price for NVDA", stream=True)
team.print_response("Search for LLM discussions", stream=True)
# Older searches will be filtered from context
team.print_response("Get the stock price for AAPL", stream=True)
team.print_response("Search for startup news", stream=True)
team.print_response("What topics did I search for recently?", stream=True)In this example:
- Each run adds tool calls to the session history. The team leader's own tool calls and its delegations to members both count.
- Once the loaded history contains more than 5 tool calls, only the 5 most recent are kept in context.
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.
Additional input
You can add entire additional messages to your team'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 team examples of how it should respond (also called "few-shot prompting"):
from agno.agent import Agent
from agno.team import Team
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 team with few-shot learning
team = Team(
members=[Agent(name="Support Specialist", role="Help with customer support questions")],
name="Customer Support Team",
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,
)
team.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 prefix processing and reduce latency or input charges under its caching rules. The prefix remains part of the 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 want more control, you can fine-tune this by manually setting the system message.
For teams, member information, delegation instructions, and coordination guidelines are usually static and therefore strong candidates for caching.
Some examples of prompt caching:
- OpenAI's prompt caching
- Anthropic prompt caching -> See an Agno example of this
- OpenRouter prompt caching