diff --git a/src/google/adk/a2a/utils/agent_card_builder.py b/src/google/adk/a2a/utils/agent_card_builder.py index cfe9804f475..bd7f43d762b 100644 --- a/src/google/adk/a2a/utils/agent_card_builder.py +++ b/src/google/adk/a2a/utils/agent_card_builder.py @@ -350,20 +350,20 @@ def _build_agent_description(agent: BaseNode) -> str: def _build_llm_agent_description_with_instructions(agent: LlmAgent) -> str: - """Build agent description including instructions for LlmAgents.""" + """Build agent description including static instructions for LlmAgents.""" description_parts = [] # Add agent description if agent.description: description_parts.append(agent.description) - # Add instruction (with pronoun replacement) - only for LlmAgent - if agent.instruction: + # Instruction providers require runtime context, which is unavailable while + # building a static agent card. + if isinstance(agent.instruction, str) and agent.instruction: instruction = _replace_pronouns(agent.instruction) description_parts.append(instruction) - # Add global instruction (with pronoun replacement) - only for LlmAgent - if agent.global_instruction: + if isinstance(agent.global_instruction, str) and agent.global_instruction: global_instruction = _replace_pronouns(agent.global_instruction) description_parts.append(global_instruction) @@ -551,8 +551,9 @@ async def _extract_examples_from_agent( except Exception as e: logger.warning('Failed to extract examples from tools: %s', e) - # If no example_tool found, try to extract examples from instruction - if agent.instruction: + # Dynamic instructions need runtime context and cannot provide static + # examples for an agent card. + if isinstance(agent.instruction, str) and agent.instruction: return _extract_examples_from_instruction(agent.instruction) return None diff --git a/tests/unittests/a2a/utils/test_agent_card_builder.py b/tests/unittests/a2a/utils/test_agent_card_builder.py index 22a554655fe..77adfc0e8cb 100644 --- a/tests/unittests/a2a/utils/test_agent_card_builder.py +++ b/tests/unittests/a2a/utils/test_agent_card_builder.py @@ -12,6 +12,7 @@ # See the License for the specific language governing permissions and # limitations under the License. +from unittest.mock import AsyncMock from unittest.mock import Mock from unittest.mock import patch @@ -322,6 +323,56 @@ async def test_build_succeeds_for_llm_agent(self): skill_ids = [skill.id for skill in card.skills] assert "writer" in skill_ids + async def test_build_omits_sync_instruction_provider(self): + """A static card omits a sync instruction that needs runtime context.""" + instruction_provider = Mock(return_value="You use runtime context.") + agent = LlmAgent( + name="provider_agent", + description="Uses runtime instructions.", + instruction=instruction_provider, + ) + + card = await AgentCardBuilder(agent=agent).build() + + assert card.skills[0].description == "Uses runtime instructions." + instruction_provider.assert_not_called() + + async def test_build_omits_async_instruction_provider(self): + """A static card omits an async instruction that needs runtime context.""" + instruction_provider = AsyncMock( + return_value="You use async runtime context." + ) + agent = LlmAgent( + name="async_provider_agent", + description="Uses async runtime instructions.", + instruction=instruction_provider, + ) + + card = await AgentCardBuilder(agent=agent).build() + + assert card.skills[0].description == "Uses async runtime instructions." + instruction_provider.assert_not_called() + + async def test_build_omits_global_instruction_provider(self): + """A static card omits a global instruction that needs runtime context.""" + global_instruction_provider = Mock( + return_value="You use global runtime context." + ) + agent = LlmAgent( + name="global_provider_agent", + description="Answers questions.", + instruction="You answer user questions.", + global_instruction=global_instruction_provider, + ) + + card = await AgentCardBuilder(agent=agent).build() + + assert ( + card.skills[0].description + == "Answers questions. I answer user questions." + ) + global_instruction_provider.assert_not_called() + async def test_build_succeeds_for_workflow_with_llm_agent_node(self): """AgentCardBuilder.build succeeds for a Workflow (no sub_agents).""" writer = LlmAgent(