If you're building AI agents using Google's Agent Development Kit (ADK), you might have asked:
"How can I make my agent prompts context-aware without hardcoding a ton of data?"
Good news — while exploring how LlmAgent
handles session data and artifacts, we discovered a built-in (and kind of undocumented 👀) feature: placeholder substitution in the instruction
field.
Let’s explore how this feature works and how you can use it to dynamically inject values from session.state
and ArtifactService
into your prompts — cleanly and automatically.
🎯 The Problem: Making Prompts Smarter
Agents often need to reference:
- A user's previous input
- Output from another agent/tool
- A document or file uploaded earlier
ADK gives us tools like session.state
and ArtifactService
to manage that data.
But manually inserting all that into the prompt? 😵 That’s messy, hard to maintain, and likely to hit LLM context size limits.
Wouldn’t it be nice if your instruction could just say something like {user_name}
or {artifact.summary.txt}
— and the framework filled in the blanks?
🧩 The Solution: Built-In Placeholder Substitution
Turns out, you can do exactly that.
After diving into ADK's internals (especially _populate_values
in instructions.py
), we confirmed that ADK supports templated instructions using {}
syntax. These placeholders are evaluated before sending the prompt to the LLM.
🧠 Supported Placeholder Types
Here’s what you can use:
1. Session State
-
{key}
or{state.key}
→ looks upsession.state["key"]
-
{app:key}
→ gets value from app-scoped state -
{user:key}
→ gets value from user-scoped state -
{key?}
or{state.key?}
→ optional placeholder (returns empty string if key not found)
All values are converted to string with str()
before being inserted.
2. Artifact Content
-
{artifact.filename}
→ inserts the content of the given artifact file (if available)
🧪 Confirmed in Tests
We found unit tests in test_instructions.py
that confirm this behavior.
Tests like test_build_system_instruction
verify that placeholders like {customerId}
, {app:key}
, {user:key}
, and {artifact.filename}
are correctly replaced with session or artifact data during instruction building.
✅ The placeholder feature is clearly supported both by the ADK source code and validated through unit tests!
🛠️ Example 1: Using State in Prompts
Let’s say a generator_agent
writes a draft, and a reviewer_agent
checks it.
from google.adk.agents import Agent
# Generator saves its output into session.state['draft_text']
generator = Agent(
name="generator_agent",
model="gemini-2.0-flash",
instruction="Write a short paragraph about subject X.",
output_key="draft_text"
)
# Reviewer reads the state and uses it in the prompt
reviewer = Agent(
name="reviewer_agent",
model="gemini-2.0-flash",
instruction="Please review the following draft: {draft_text}. Check for factual accuracy and provide feedback."
)
No need to manually pass variables around — ADK handles it for you 🙌
📁 Example 2: Using Artifacts in Prompts
from google.adk.agents import Agent
summarizer = Agent(
name="summarizer_agent",
model="gemini-2.0-flash",
instruction="Summarize the document provided in the artifact named 'meeting_notes.txt': {artifact.meeting_notes.txt}"
)
This is a clean way to reference file contents without bloating your code or prompt logic.
🔄 Alternative: Build Instructions in Code (If You Must)
If you want more control, you can still build prompts manually using callbacks:
def update_instruction_before_call(callback_context: CallbackContext, llm_request: LlmRequest):
draft = callback_context.state.get('draft_text', '')
llm_request.config.system_instruction = f"Please review the following draft: {draft}. Check for factual accuracy."
return None
reviewer = Agent(
name="reviewer_agent",
model="gemini-2.0-flash",
instruction=None,
before_model_callback=update_instruction_before_call
)
It works — but for simple cases, placeholder substitution is much cleaner ✨
🧭 Final Thoughts
The placeholder feature ({state.key}
, {artifact.filename}
) lets you write smarter, more flexible agent instructions without string hacking or bloated callbacks.
💡 Important Note:
While this behavior is clearly present in the source code and unit tests, it is not emphasized in the official documentation.
In fact, the official example in the docs here shows state referencing using manual Python string interpolation instead of placeholders — which slightly contradicts the built-in behavior we observed.
So, while it works beautifully today, this feature may be considered an internal implementation detail, and future ADK versions could potentially change or formalize it.
Always check the latest ADK release notes when upgrading! 🚀
Use it wisely, and you’ll make your ADK agents a whole lot more powerful.
👍 Found this helpful? Let’s connect in the comments — and share if you discover any other hidden ADK tricks!