Skip to content

feature: Modular Domain Taxonomy #5895

@ViktorVeselov

Description

@ViktorVeselov

Read the contribution guide and file the issues in the right place.
Contribution guide.

🔴 Required Information

Is your feature request related to a specific problem?

Currently, Google's Agent Development Kit (ADK) 2.0 lacks a native, modular way to define, resolve, and bind a Domain Taxonomy within the framework.

Taxonomy is the core foundational layer needed to enable intelligent prompt routing and dynamic capability shaping. While developers might want to build their own independent implementation, introducing this as a native, low-level primitive in the SDK is highly beneficial. Many developers may not yet know how powerful domain taxonomies are for controlling agent workflows. By building this as a clean, pluggable framework primitive, it becomes incredibly easy for developers to use, implement, or modify.

Furthermore, having this native primitive directly benefits ecosystem tools like AntigravityCLI by enabling standardized dynamic prompt indexing.

Without a structured taxonomy layer at the center of the SDK, developers cannot:

  1. Define and Register Domain Taxonomies: There is no standard schema or mechanism to define custom, granular taxonomy structures (including grouping or nesting hierarchical taxonomies).
  2. Dynamically Bind Capabilities: Skills and tools cannot be bound to specific taxonomy nodes in metadata, making it impossible to control visibility on a per-domain basis.
  3. Trigger Taxonomy-Aware Behaviors: The framework cannot dynamically resolve active taxonomy states from conversation contexts or multi-agent history, which prevents triggering specialized tools or applying context-aware prompts (dynamic prompting) based on active domains.

Describe the Solution You'd Like

I would like to propose adding a Modular, Pluggable Policy & Taxonomy Security Plugin (TaxonomyPlugin) utilizing the ADK 2.0 BasePlugin hook interface.

By implementing this as a modular, opt-in plugin, we avoid intrusive modifications to the core SDK loop while providing a robust taxonomy registry, pipeline resolvers, and instruction-shaping policies. A structured taxonomy enables developers to make tools and agent behaviors much more controlled and reliable. It achieves this by allowing users to dynamically modify tool descriptions and parts of the prompt on the fly based on active domains (though how developers choose to create and structure those custom taxonomies remains a broader design question).

This framework feature introduces:

  1. Taxonomy Schema and Bindings: Support for declaring taxonomy-binds (as clean, normalized taxonomy domain URIs) in the skill YAML frontmatter, enabling skills and resources to align with custom taxonomy structures.
  2. TaxonomyResolver & TaxonomyPipeline: Abstract interfaces enabling developers to build custom taxonomy resolvers. The resolve_taxonomies(context, llm_request) method evaluates both runtime context parameters and outgoing LLM histories (including A2A dialogues and thought streams) to resolve active domains. TaxonomyPipeline executes a sequence of these resolvers to support multi-stage, group-based, or hierarchical taxonomy resolution.
  3. SkillPolicy behavioral engine: A clean abstraction with hooks to evaluate active taxonomies and execute dynamic prompting:
    • is_skill_allowed(skill, context, active_taxonomies) -> bool: Filters skill discovery based on active taxonomy domains.
    • shape_instructions(skill, context, original_instructions) -> str: Dynamically shapes/appends specialized prompts or tool description modifications depending on the resolved taxonomy domains.
  4. TaxonomyRegistry: Support for parsing flat Key-Value JSON schemas and standard JSON-LD with SKOS structures into a nested, traversable organizational taxonomy registry.

To maintain library performance and safety, the proposed implementation directly reuses the SDK's existing path validation utility (_validate_path_segment from the artifacts submodule) and standard errors (InputValidationError) to protect skill tool parameters and prevent directory traversal hacks.


Impact on your work

This core taxonomy architecture has a significant impact on future work across the ADK ecosystem. By establishing a unified taxonomy layer:

  • Ecosystem Tooling (AntigravityCLI): These taxonomy principles can be directly integrated into toolchains like AntigravityCLI to dynamically index and route prompts, vastly improving execution efficiency and routing accuracy.
  • Universal Applicability: The same taxonomy principles can be applied virtually to any part of the lifecycle—including filtering/sanitizing information retrieved from RAG, analyzing active thought-stream steps, or auditing communications in Agent-to-Agent (A2A) flows.
  • Handling Edge Cases & Hierarchies: Grouping taxonomies under other parent groups allows the system to scale smoothly, handling complex multi-domain edge cases in large-scale agent environments.

Willingness to contribute

Yes, I have mapped out the implementation footprint (core functional code estimated to be between 300 to 600 lines of code).


🟡 Recommended Information

Describe Alternatives You've Considered

  1. Boilerplate Wrapper Layers: Manually filtering and altering tools at the host application level before passing them to the agent. This is lossy and brittle because it fails to capture dynamic conversation shifts, thought-stream changes, or mid-turn multi-agent dialogue transitions.
  2. Ad-Hoc Prompt Appends: Injecting generic system prompts. This doesn't scale and lacks domain specificity, as it cannot be conditionally bound to discrete, structured taxonomy nodes.
  3. External Arbitrator Overlays: Overall, building heavy wrappers and external arbitration engines on top of the SDK defeats the primary purpose of an SDK. An SDK should stay simple, allow developers to easily modify parts of the code if or when needed, and connect directly to live production environments (Auth0, LaunchDarkly, Postgres) with zero overhead.

Proposed API / Implementation

Here is an architectural example of how the taxonomy and policy abstractions look in practice:

1. Core Interfaces (google/adk/plugins/taxonomy/policy.py)

from abc import ABC, abstractmethod
from google.adk.agents.readonly_context import ReadonlyContext
from google.adk.models.llm_request import LlmRequest
from google.adk.skills.models import Skill

class TaxonomyResolver(ABC):
    """Abstract interface for resolving active taxonomy domains from context and history."""
    @abstractmethod
    async def resolve_taxonomies(self, context: ReadonlyContext, llm_request: LlmRequest) -> list[str]:
        pass

class TaxonomyPipeline(TaxonomyResolver):
    """Executes a sequence of taxonomy resolvers (composite pipeline pattern)."""
    def __init__(self, resolvers: list[TaxonomyResolver]):
        self.resolvers = resolvers
        
    async def resolve_taxonomies(self, context: ReadonlyContext, llm_request: LlmRequest) -> list[str]:
        active_domains = set()
        for resolver in self.resolvers:
            domains = await resolver.resolve_taxonomies(context, llm_request)
            if domains:
                active_domains.update(domains)
        return list(active_domains)

class SkillPolicy(ABC):
    """Abstract base class for dynamic skill validation and instruction shaping based on resolved taxonomy."""
    @abstractmethod
    def is_skill_allowed(self, skill: Skill, context: ReadonlyContext, active_taxonomies: list[str]) -> bool:
        pass
        
    @abstractmethod
    def shape_instructions(self, skill: Skill, context: ReadonlyContext, original_instructions: str) -> str:
        pass

2. Possible Multi-Agent & Thinking Auditor Example

class AdvancedSecurityResolver(TaxonomyResolver):
    """Resolves active domains by scanning multi-agent dialog history and model thought-streams."""
    async def resolve_taxonomies(self, context: ReadonlyContext, llm_request: LlmRequest) -> list[str]:
        active_domains = []
        
        # Audit dialog history
        for turn in llm_request.contents:
            for part in turn.parts:
                if part.text and "CONFIDENTIAL_REGULATORY" in part.text:
                    active_domains.append("urn:adk:domain:regulatory_strict")
                    
        # Audit Model Thought-Stream parts
        for turn in llm_request.contents:
            if turn.role == "model":
                for part in turn.parts:
                    if part.text and "thinking_pattern:secure_code" in part.text:
                        active_domains.append("urn:adk:domain:secure_coding")
                        
        return active_domains

3. Client Integration (Pluggable and fully opt-in)

from google.adk import Runner, TaxonomyPlugin, TaxonomyRegistry, DefaultSkillPolicy

class UserExamplePolicy(SkillPolicy):
    def __init__(self, ld_client):
        self.ld = ld_client

    def is_skill_allowed(self, skill: Skill, context: ReadonlyContext, active_taxonomies: list[str]) -> bool:
        user_id = context.user_id
        # Check standard binds against dynamic resolvers
        if "urn:adk:domain:beta" in skill.frontmatter.taxonomy_binds:
            if not self.ld.bool_variation("enable-beta-features", user_id, False):
                return False
        return True

    def shape_instructions(self, skill: Skill, context: ReadonlyContext, original_instructions: str) -> str:
        # Dynamically shape instructions based on resolved taxonomy domains
        if "urn:adk:domain:regulatory_strict" in skill.frontmatter.taxonomy_binds:
            return original_instructions + "\n\n[GUARDRAIL]: DO NOT store or log customer PII."
        return original_instructions

# 1. Parse your taxonomy tree (supports Flat Key-Value or SKOS JSON-LD formats)
registry = TaxonomyRegistry.from_flat_json(my_taxonomy_data)

# 2. Instantiate pluggable taxonomy security plugin
taxonomy_plugin = TaxonomyPlugin(
    taxonomy_registry=registry,
    resolver=TaxonomyPipeline([AdvancedSecurityResolver()]),
    policy=UserExamplePolicy(ld_client)
)

# 3. Register to Runner with zero overhead to core workflows
runner = Runner(
    ...,
    plugins=[taxonomy_plugin]
)

Additional Context

This feature request provides a more detailed, technical implementation proposal for the ongoing discussion at google/adk-python/discussions/5891. By establishing a native, pluggable taxonomy abstraction, we add a solid, adaptive prompting layer for dynamic prompt routing and toolchain efficiency with zero overhead to existing agent implementations.

Edited

Key Design Refinements (Pluggable vs. Core Modification)

During final design modeling and verification, we refined the implementation approach to maximize security, keep core code clean, and ensure maintainability:

  • Pluggable Event Interceptors: Instead of modifying the central execution engine (functions.py, runner.py) directly, we encapsulated all taxonomy filtering inside a pluggable TaxonomyPlugin (BasePlugin). Core SDK processes remain completely unmodified, ensuring 100% opt-in safety and zero overhead when the plugin is inactive.
  • Synchronous Policy Execution: Designed SkillPolicy.is_skill_allowed and shape_instructions as synchronous methods to avoid asynchronous context-switching delays during metadata parsing.
  • Unified Schema Registry: Introduced a dedicated TaxonomyRegistry model, providing clean out-of-the-box parsing for both Flat JSON and standard semantic JSON-LD with SKOS representations.
  • Safety Guards: Enforced standard SDK validation schemas by reusing the internal _validate_path_segment validator inside the Pydantic Frontmatter validator to prevent directory traversal and parameter injections in taxonomy tags.

Metadata

Metadata

Assignees

Labels

core[Component] This issue is related to the core interface and implementation
No fields configured for Feature.

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions