-
Notifications
You must be signed in to change notification settings - Fork 2.9k
Description
Description
The ADK documentation states that double braces ({{ and }}) should be used to escape literal curly braces in agent instructions, but the implementation in instructions_utils.py does not properly handle this escaping mechanism.
Expected Behavior
According to the State documentation:
If your instruction text legitimately needs to include literal curly braces (e.g., as part of a code example or JSON snippet), you need to escape them... a common approach is doubling the braces:
{{becomes{and}}becomes}.
When using {{variable_name}} in an instruction, it should render as {variable_name} without attempting template substitution.
Actual Behavior
The regex pattern {+[^{}]*}+ in inject_session_state() matches {{variable_name}} and attempts to substitute it from session state, causing a KeyError when the variable doesn't exist.
The issue is in these lines of google/adk/utils/instructions_utils.py:
Line 124: Regex matches {{variable}}
return await _async_sub(r'{+[^{}]*}+', _replace_match, template)
Lines 82-83: Strips all braces, leaving just 'variable'
async def _replace_match(match) -> str:
var_name = match.group().lstrip('{').rstrip('}').strip()
Line 143: 'variable' passes identifier check
return var_name.isidentifier()
Lines 109-122: Tries to inject from state and fails
if var_name in invocation_context.session.state:
...
else:
raise KeyError(f'Context variable not found: {var_name}.')## Steps to Reproduce
- Create an agent with an instruction containing code examples with template literals:
instruction = """
Example Python code:
logger.error(f"User not found: {{user_id}}")
Example TypeScript code:
console.log(User: ${{userId}});
"""2. Run the agent
3. Observe: KeyError: 'Context variable not found: 'user_id'.
Environment
- ADK Version: 1.18.0 (tested with google-adk>=1.18.0)
- Python Version: 3.12
- OS: macOS 24.6.0
Workaround
Use property access (dots) in code examples instead of simple identifiers, since _is_valid_state_name() returns False for non-identifiers:
This works because 'user.id' is not a valid identifier
logger.error(f"User not found: {{user.id}}")## Suggested Fix
The _replace_match function should detect double-brace patterns and return the literal content without attempting substitution:
async def _replace_match(match) -> str:
matched_text = match.group()
# Check if this is an escaped pattern (starts with {{ )
if matched_text.startswith('{{') and matched_text.endswith('}}'):
# Return with single braces (unescape)
return matched_text[1:-1]
# Existing logic for template substitution
var_name = matched_text.lstrip('{').rstrip('}').strip()
...Alternatively, update the regex to exclude double-brace patterns from matching, though this is more complex.
Impact
This bug prevents using realistic code examples (especially Python f-strings and TypeScript/JavaScript template literals) in agent instructions, which is critical for code generation and software engineering agents.