Skip to content

Commit 7c79230

Browse files
lwangverizonwyf7107
authored andcommitted
fix: prevent empty responses after load_skill
Merge #6226 # fix(skills): prevent empty responses after `load_skill` Fixes #6225 ## Problem When an agent uses the skill tools, ADK appends a skill system instruction to the prompt (`_build_skill_system_instruction` in `src/google/adk/tools/skill_toolset.py`). The instruction does not tell the model what to do *after* a `load_skill` call returns. With Gemini models we observed a significant rate of **empty model responses** right after a `load_skill` call — the model loads the skill and ends its turn with no visible output. We saw this as a spike in empty responses in a Verizon production environment running ADK + Gemini, most acutely for tool-heavy skills whose next correct action after loading is to call more tools rather than reply. ## Change `_build_skill_system_instruction` only — covers both `DEFAULT_SKILL_SYSTEM_INSTRUCTION` and the per-toolset prefixed variant. **Add rule 7** (continue-after-load guard), appended after the existing rule 6: > 7. Loading a skill only retrieves its instructions; it does NOT complete your turn. > After a `load_skill` call returns, continue in the SAME turn: call whatever tools > the skill's steps require (search, data retrieval, render), then write your reply. > Never end your turn with an empty response right after loading a skill. No other rule is modified — rules 1–6 are left exactly as-is. The new rule uses the `{prefix}` substitution like the other rules (`{p}load_skill`), so the prefixed system instruction carries it too. ## Result This is the exact change we validated in production: appending rule 7 (rules 1–6 unchanged) dropped the empty-response rate for Gemini after skill loading substantially. ## Tests Adds focused tests in `tests/unittests/tools/test_skill_toolset.py`: - `test_system_instruction_marks_load_skill_as_non_terminal` — rule 7 present in the default instruction. - `test_prefixed_system_instruction_includes_continue_after_load_rule` — prefixed variant carries rule 7 with the prefix. Existing tests reference `DEFAULT_SKILL_SYSTEM_INSTRUCTION` by constant (not hardcoded text), so they are unaffected. ## Backwards compatibility Prompt-text-only, strictly additive change. No public API, signature, or existing-rule change. Co-authored-by: Yifan Wang <wanyif@google.com> COPYBARA_INTEGRATE_REVIEW=#6226 from lwangverizon:fix/skill-load-empty-response 663b28b PiperOrigin-RevId: 940550604
1 parent 8c7fcd1 commit 7c79230

2 files changed

Lines changed: 23 additions & 0 deletions

File tree

src/google/adk/tools/skill_toolset.py

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -97,6 +97,11 @@ def _build_skill_system_instruction(prefix: str | None = None) -> str:
9797
f"6. If `{p}run_skill_script` returns an error (for example "
9898
f"`SCRIPT_NOT_FOUND`), do not retry the same script or guess a "
9999
"different script path. Report the error to the user and stop.\n"
100+
f"7. Loading a skill only retrieves its instructions; it does NOT "
101+
f"complete your turn. After a `{p}load_skill` call returns, continue "
102+
"in the SAME turn: call whatever tools the skill's steps require "
103+
"(search, data retrieval, render), then write your reply. Never end "
104+
"your turn with an empty response right after loading a skill.\n"
100105
)
101106

102107

tests/unittests/tools/test_skill_toolset.py

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1758,6 +1758,24 @@ def test_system_instruction_references_run_skill_script():
17581758
)
17591759

17601760

1761+
def test_system_instruction_marks_load_skill_as_non_terminal():
1762+
"""Rule 7 must tell the model load_skill does not complete the turn.
1763+
1764+
Without it, some models (notably Gemini) treat the load_skill tool call as
1765+
the entire turn and stop with no visible output, producing empty responses.
1766+
"""
1767+
instruction = skill_toolset.DEFAULT_SKILL_SYSTEM_INSTRUCTION
1768+
assert "does NOT complete your turn" in instruction
1769+
assert "empty response" in instruction
1770+
1771+
1772+
def test_prefixed_system_instruction_includes_continue_after_load_rule():
1773+
"""The prefixed builder variant must also carry rule 7 (with the prefix)."""
1774+
instruction = skill_toolset._build_skill_system_instruction(prefix="my")
1775+
assert "does NOT complete your turn" in instruction
1776+
assert "my_load_skill" in instruction
1777+
1778+
17611779
# ── Finding 2: empty files are mounted (not silently dropped) ──
17621780

17631781

0 commit comments

Comments
 (0)