Skip to content

Commit db05cd9

Browse files
committed
feat(samples): add sample for skill state injection via adk_inject_state
Demonstrates reading session state from a skill without a custom getter tool. The code-review-skill opts in with metadata.adk_inject_state: true and references {dev_name}/{dev_language}/{dev_level} directly; LoadSkillTool fills them in from session state at load time, the same {var} interpolation LlmAgent.instruction already supports. A remember_developer_profile tool populates the state that the skill then reads.
1 parent 0f349ac commit db05cd9

4 files changed

Lines changed: 207 additions & 0 deletions

File tree

Lines changed: 76 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,76 @@
1+
# ADK Skill State Injection Sample
2+
3+
## Overview
4+
5+
This sample demonstrates **session state injection** into a skill via the
6+
`adk_inject_state` metadata flag.
7+
8+
Without this flag, a skill that needs to read a value the agent already
9+
holds — a user preference, conversation context, or any other state value —
10+
has to ship its own custom "getter" tool, wire it through
11+
`SkillToolset(additional_tools=[...])`, and instruct the model to call it. That
12+
is extra application code plus an extra LLM round-trip at runtime, just to read
13+
state.
14+
15+
`adk_inject_state` removes that boilerplate. When a skill's `SKILL.md`
16+
frontmatter sets `metadata.adk_inject_state: true`, `LoadSkillTool` renders the
17+
skill body through `inject_session_state` at load time, substituting any
18+
`{placeholder}` with the matching value from session state. It is the same
19+
`{var}` / `{var?}` interpolation that `LlmAgent.instruction` already supports —
20+
now available to skills as a one-line, declarative change.
21+
22+
This sample showcases:
23+
24+
1. **Opting into injection**: Setting `metadata.adk_inject_state: true` in
25+
`SKILL.md`.
26+
1. **Declarative state access**: Referencing session state directly with
27+
`{dev_name}`, `{dev_language}`, and `{dev_level}` placeholders — no getter
28+
tool required.
29+
1. **Populating state**: A `remember_developer_profile` tool that writes the
30+
profile into session state, which the skill later reads via injection.
31+
32+
## How It Works
33+
34+
```mermaid
35+
graph TD
36+
User -->|"1. introduces themselves"| Agent[Agent: skills_inject_state_agent]
37+
Agent -->|writes dev_name, dev_language, dev_level| State[(Session State)]
38+
User -->|"2. asks for a code review"| Agent
39+
Agent -->|load_skill code-review-skill| Toolset[SkillToolset]
40+
State -. injected into instructions .-> Toolset
41+
Toolset -->|instructions with state filled in| Agent
42+
```
43+
44+
## Sample Inputs
45+
46+
Run from the parent directory:
47+
48+
```shell
49+
adk web
50+
```
51+
52+
Then, in a single session, send these turns in order:
53+
54+
1. `Hi, I'm Alex. I mainly write Python and I'm a senior engineer.`
55+
56+
*The agent calls `remember_developer_profile`, storing the profile in
57+
session state.*
58+
59+
1. `Can you review this for me? def add(a, b): return a+b`
60+
61+
*The agent loads `code-review-skill`. Because the skill opts into
62+
`adk_inject_state`, the `{dev_name}` / `{dev_language}` / `{dev_level}`
63+
placeholders are already filled in from state when the instructions are
64+
returned — no separate tool call was needed to read the profile.*
65+
66+
## Placeholder Syntax
67+
68+
Placeholders map to session state keys:
69+
70+
- `{key}` — required; injection fails if the key is missing.
71+
- `{key?}` — optional; replaced with an empty string if the key is missing.
72+
- `{user:key}`, `{app:key}`, `{temp:key}` — read prefixed
73+
(user-/app-/temp-scoped) state.
74+
75+
This sample uses the optional form (`{dev_name?}`) so that loading the skill
76+
before a profile has been set degrades gracefully instead of erroring.
Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
from __future__ import annotations
16+
17+
from . import agent
Lines changed: 87 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,87 @@
1+
# Copyright 2026 Google LLC
2+
#
3+
# Licensed under the Apache License, Version 2.0 (the "License");
4+
# you may not use this file except in compliance with the License.
5+
# You may obtain a copy of the License at
6+
#
7+
# http://www.apache.org/licenses/LICENSE-2.0
8+
#
9+
# Unless required by applicable law or agreed to in writing, software
10+
# distributed under the License is distributed on an "AS IS" BASIS,
11+
# WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12+
# See the License for the specific language governing permissions and
13+
# limitations under the License.
14+
15+
"""Sample showing how a skill personalizes its instructions from session state.
16+
17+
A skill whose ``SKILL.md`` frontmatter sets ``metadata.adk_inject_state: true``
18+
has its instructions rendered through ``inject_session_state`` at load time.
19+
Any ``{placeholder}`` in the instructions is replaced with the matching value
20+
from session state, so the same skill yields different instructions per session.
21+
22+
Run from the parent directory with ``adk web``.
23+
"""
24+
25+
from __future__ import annotations
26+
27+
import pathlib
28+
29+
from google.adk.agents import Agent
30+
from google.adk.skills import load_skill_from_dir
31+
from google.adk.tools.skill_toolset import SkillToolset
32+
from google.adk.tools.tool_context import ToolContext
33+
34+
35+
def remember_developer_profile(
36+
name: str,
37+
primary_language: str,
38+
experience_level: str,
39+
tool_context: ToolContext,
40+
) -> dict:
41+
"""Saves the developer's profile into session state for later personalization.
42+
43+
Args:
44+
name: The developer's name.
45+
primary_language: The language they primarily work in, e.g. "Python".
46+
experience_level: Their experience level, e.g. "junior" or "senior".
47+
"""
48+
tool_context.state["dev_name"] = name
49+
tool_context.state["dev_language"] = primary_language
50+
tool_context.state["dev_level"] = experience_level
51+
return {
52+
"status": "ok",
53+
"stored": {
54+
"dev_name": name,
55+
"dev_language": primary_language,
56+
"dev_level": experience_level,
57+
},
58+
}
59+
60+
61+
# Load a directory-based skill. Its SKILL.md opts into state injection via
62+
# `metadata.adk_inject_state: true`, so `{dev_name}`, `{dev_language}`, and
63+
# `{dev_level}` in its instructions are substituted from session state when the
64+
# skill is loaded.
65+
code_review_skill = load_skill_from_dir(
66+
pathlib.Path(__file__).parent / "skills" / "code-review-skill"
67+
)
68+
69+
my_skill_toolset = SkillToolset(skills=[code_review_skill])
70+
71+
root_agent = Agent(
72+
name="skills_inject_state_agent",
73+
description=(
74+
"An agent that personalizes a code-review skill using session state."
75+
),
76+
instruction=(
77+
"You help developers review their code.\n"
78+
"- When a user introduces themselves, call"
79+
" `remember_developer_profile` to save who they are.\n"
80+
"- When a user asks for a code review, load the `code-review-skill`"
81+
" and follow its (personalized) instructions exactly."
82+
),
83+
tools=[
84+
remember_developer_profile,
85+
my_skill_toolset,
86+
],
87+
)
Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
---
2+
name: code-review-skill
3+
description: Reviews code with feedback tailored to the developer's profile in session state.
4+
metadata:
5+
adk_inject_state: true
6+
---
7+
8+
You are performing a personalized code review.
9+
10+
The developer's profile (from session state):
11+
- Name: {dev_name?}
12+
- Primary language: {dev_language?}
13+
- Experience level: {dev_level?}
14+
15+
If the profile above is empty, first ask the developer to introduce themselves
16+
(their name, primary language, and experience level) so the review can be
17+
personalized, then stop.
18+
19+
Otherwise, follow these steps:
20+
21+
1. Greet the developer by name.
22+
2. Review the code the user provided, focusing on idioms and best practices for
23+
their primary language.
24+
3. Calibrate the depth of your feedback to their experience level: keep it
25+
foundational for a junior developer, and concise and advanced for a senior
26+
developer.
27+
4. End with one concrete, actionable suggestion.

0 commit comments

Comments
 (0)